jonsully1.dev

Secure AWS Infrastructure for Serverless Projects with Terragrunt

Cover Image for Secure AWS Infrastructure for Serverless Projects with Terragrunt
Photo by Franck  on Unsplash
John O'Sullivan
John O'Sullivan
Senior Full Stack Engineer
& DevOps Practitioner

Contents

  1. Github Repository
  2. Introduction
  3. Overview of the Repository
  4. Key Features
  5. Repository Structure
  6. How to Use This Repository
  7. Integrating Serverless Applications
  8. Why This Setup?
  9. Conclusion

Github Repository

If you prefer to jump straight to the code: aws-terraform-vpc-rds-bastion

Introduction

As a freelance serverless consultant, having a reusable, secure, and scalable infrastructure is critical for delivering projects efficiently. To address this need, I created a Terraform and Terragrunt-based repository that provisions a robust AWS infrastructure. This setup is designed to support serverless applications while providing secure access to an RDS database. In this blog post, I'll walk you through the key components of this repository and how it can be used to bootstrap serverless projects.

Overview of the Repository

This repository provisions a secure AWS infrastructure that includes the following components:

  • VPC: A Virtual Private Cloud with public, private, and database subnets.
  • RDS: A MySQL database instance with secure configurations.
  • Bastion Host: An EC2 instance for secure SSH access to private resources.
  • NAT Gateway: For outbound internet access from private subnets.
  • Security Groups: Fine-grained access control for RDS, Lambda functions, and the Bastion Host.
  • Key Pairs: SSH key management for the Bastion Host.

The infrastructure is modular, allowing you to reuse components across multiple projects. It is also configured to integrate seamlessly with serverless applications, enabling Lambda functions to securely access the RDS database.

Key Features

1. VPC Design

The VPC module creates a network with three tiers of subnets:

  • Public Subnets: For resources like the Bastion Host and NAT Gateway.
  • Private Subnets: For application resources like Lambda functions.
  • Database Subnets: For the RDS instance.

This design ensures that sensitive resources, such as the RDS database, remain isolated from the public internet.

2. RDS Configuration

The RDS module provisions a MySQL database with the following features:

  • Private Subnet Placement: Ensures the database is not publicly accessible.
  • Security Group Integration: Allows access only from the Lambda and Bastion Host security groups.
  • CloudWatch Logs: Enables monitoring of database activity.

3. Bastion Host

The Bastion Host provides secure SSH access to private resources. It is configured with:

  • Public Subnet Placement: Accessible from the internet.
  • Security Group Rules: Restricts SSH access to a specific IP address (your IP).
  • Encrypted Root Volume: Ensures data security.

4. Security Groups

The repository includes security groups for:

  • RDS: Allows access from Lambda functions and the Bastion Host.
  • Lambda: Allows outbound internet access for serverless functions.
  • Bastion Host: Restricts SSH access to your IP address.

5. Key Pair Management

The key-pairs module generates an SSH key pair for the Bastion Host. The private key is stored locally, ensuring secure access.

Repository Structure

The repository is organized into two main directories:

  • modules: Contains reusable Terraform modules for each infrastructure component.
  • environents/dev: Contains Terragrunt configurations for the development environment.

The environents directory is easily extendable to prod or qa, just ensure you also include a related env file in the root of the repository.

Example Structure:

.
├── README.md
├── env-example.dev.hcl
├── environments
│   └── dev
│       ├── bastion-host
│       │   └── terragrunt.hcl
│       ├── key-pairs
│       │   └── terragrunt.hcl
│       ├── nat-gateway
│       │   └── terragrunt.hcl
│       ├── rds
│       │   └── terragrunt.hcl
│       ├── root.hcl
│       ├── security-groups
│       │   └── terragrunt.hcl
│       └── vpc
│           └── terragrunt.hcl
└── modules
    ├── bastion-host
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── variables.tf
    ├── key-pairs
    │   ├── main.tf
    │   └── variables.tf
    ├── nat-gateway
    │   ├── main.tf
    │   └── variables.tf
    ├── rds
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── variables.tf
    ├── security-groups
    │   ├── main.tf
    │   ├── outputs.tf
    │   └── variables.tf
    └── vpc
        ├── main.tf
        ├── outputs.tf
        └── variables.tf

How to Use This Repository

Prerequisites

  • Terraform >= 1.0.0
  • Terragrunt >= 0.35.0
  • AWS CLI configured with appropriate credentials and permissions.

1. Clone the Repository

git clone https://github.com/your-repo/aws-terraform-vpc-rds-bastion.git
cd aws-terraform-vpc-rds-bastion

2. Configure Environment Variables

Add an env.dev.hcl in the root file with your specific inputs:

inputs = {
  infra_name                    = "your-infra-name"
  aws_region                    = "your-region"
  env                           = "dev"
  iac                           = "terragrunt"
  my_ip                         = "your-ip/32"
  bastion_host_private_key_name = "your-key-name"
}

3. Initialize

Navigate to the desired environment directory and initialize Terragrunt:

cd environments/dev
terragrunt run-all init

4. Plan

Run the following command to show what will be provisioned in your infrastructure:

terragrunt run-all plan -out=tfplan && terragrunt run-all show tfplan

5. Apply

Run the following command to provision the infrastructure:

terragrunt run-all apply tfplan

Integrating Serverless applications

Once the infrastructure is provisioned, deploying serverless lambda backend services is a simple case of configuring the Lambda functions to use the lambda security group and private subnets of the VPC.

Below is simple example using the SST Framework v3:

export const api = new sst.aws.ApiGatewayV2("Api", {
  cors: {
    allowOrigins: ["https://localhost:3000"],
    allowMethods: ["GET", "POST", "PUT", "DELETE"],
    allowHeaders: ["*"],
    allowCredentials: true,
  },
});

$transform(sst.aws.Function, (args, _opts) => {
  args.environment = {
    ...args.environment,
    DB_HOST: process.env.DB_HOST as string,
    DB_PORT: process.env.DB_PORT as string,
    DB_USER: process.env.DB_USER as string,
    DB_PASSWORD: process.env.DB_PASSWORD as string,
    DB_NAME: process.env.DB_NAME as string,
  };
  args.vpc = {
    privateSubnets: [
      process.env.VPC_PRIVATE_SUBNET_1_ID,
      process.env.VPC_PRIVATE_SUBNET_2_ID,
      process.env.VPC_PRIVATE_SUBNET_3_ID,
    ],
    securityGroups: [process.env.SECURITY_GROUP_LAMBDA_ID],
  };
});

const verifyUserFunction = new sst.aws.Function("verifyUserFunction", {
  handler: "packages/functions/src/user/verifyUser.handler",
});

api.route("GET /user/verify", verifyUserFunction.arn);

The VPC_PRIVATE_SUBNET_1_ID, VPC_PRIVATE_SUBNET_2_ID, VPC_PRIVATE_SUBNET_3_ID and SECURITY_GROUP_LAMBDA_ID values are resolved in the via the respective outputs from the vpc and security-group modules.

Why This Setup?

This repository is tailored for freelance serverless consultants or developers who need a secure, reusable infrastructure for AWS projects. It abstracts the complexity of setting up a secure environment, allowing you to focus on building serverless applications.

Benefits:

  • Security: Isolated subnets, restricted access, and encrypted resources.
  • Reusability: Modular design for easy replication across projects.
  • Scalability: Supports serverless architectures with minimal changes.

Conclusion

This Terraform and Terragrunt-based repository is a powerful tool for provisioning secure AWS infrastructure. Whether you're a freelance consultant or a developer working on serverless projects, this setup provides a solid foundation for deploying applications that require secure access to an RDS database.

Feel free to fork this repository and adapt it to your needs. Contributions are welcome—open an issue or submit a pull request if you have ideas for improvement.

Thanks for reading.

John O