Mar 1, 2025
How to Build a Terraform AWS Infrastructure Blueprint
A step-by-step guide to provisioning a containerized AWS backend service using reusable Terraform modules.
In this guide, we will walk through how to build a production-style Terraform AWS Infrastructure Blueprint. This blueprint provisions all the AWS resources needed to run a containerized backend service, including a VPC, security groups, ECS cluster, ECR repository, Application Load Balancer, IAM roles, and CloudWatch logs.
The Problem: Configuration Drift and Manual Setup
Every time a new environment is needed, creating AWS resources by hand through the console leads to several issues:
- Steps are easily forgotten.
- Configurations drift between development, staging, and production environments.
- There is no version-controlled record of what was built or why.
The solution is Infrastructure as Code (IaC). By defining all infrastructure in Terraform, any environment can be created or destroyed with a single command, keeping configurations consistent and tracked in git.
Architecture & Layout
Here is the directory structure for our Terraform blueprint:
terraform/
├── modules/
│ ├── vpc/ # VPC, subnets, internet gateway, route tables
│ ├── security/ # Security groups for ALB and ECS tasks
│ ├── ecr/ # ECR repository
│ ├── ecs/ # ECS cluster, task definition, service
│ ├── alb/ # Application Load Balancer, target group, listener
│ ├── iam/ # ECS task execution role and policies
│ └── cloudwatch/ # Log group and metric alarms
├── environments/
│ ├── dev/ # dev.tfvars + backend config
│ └── prod/ # prod.tfvars + backend config
├── main.tf
├── variables.tf
└── outputs.tf
Tech Stack Overview
- IaC: Terraform (HCL)
- Networking: AWS VPC, public and private subnets, NAT gateway, internet gateway
- Compute: Amazon ECS (Fargate), task definitions
- Registry: Amazon ECR
- Load Balancing: Application Load Balancer, target group, HTTPS listener
- Security: IAM roles and policies, security groups
- Observability: CloudWatch log groups, metric alarms
- State: S3 remote backend with DynamoDB state locking
Step-by-Step Implementation
1. Networking (VPC Module)
We start by building a secure networking foundation. The VPC module sets up:
- Public subnets for the Application Load Balancer (ALB) to handle incoming public requests.
- Private subnets for the ECS Fargate tasks to block direct access from the public internet.
- Internet Gateways and Route Tables to route public traffic.
2. Networking Security (Security Groups)
- The ALB security group allows inbound
80and443traffic from the internet. - The ECS tasks security group strictly allows traffic only from the ALB security group.
3. Container Management (ECR & ECS Modules)
- We provision an Amazon ECR repository with image scanning on push enabled.
- We set up an Amazon ECS cluster with Fargate launch type, including a task definition specifying CPU, memory, and container configurations.
4. Load Balancing & Traffic Routing (ALB Module)
- The Application Load Balancer listens on HTTP/HTTPS and forwards traffic to the ECS target group.
- Continuous health checks monitor container status, allowing ECS to auto-replace unhealthy instances.
5. Access Management & Logging (IAM & CloudWatch Modules)
- We configure an ECS task execution role with least-privilege policies to allow pulling ECR images, reading configurations, and writing to CloudWatch.
- We create a CloudWatch log group with a configurable retention policy to centralize container stdout/stderr.
Deployment Flow
To provision the infrastructure for any environment (e.g., dev):
- Configure credentials: Configure AWS credentials locally or in CI via OpenID Connect (OIDC).
- Initialize Terraform:
terraform init -backend-config=environments/dev/backend.hcl - Generate a plan: Preview changes to confirm what will be created:
terraform plan -var-file=environments/dev/dev.tfvars - Apply changes: Deploy to AWS:
terraform apply -var-file=environments/dev/dev.tfvars
Security Best Practices
- Least Privilege: The ECS task execution role only has permissions to access resources it explicitly needs.
- Vulnerability Scanning: ECR image scanning is active, blocking deployment of images with critical security vulnerabilities.
- State Protection: Remote state is stored in a private, encrypted S3 bucket with versioning. DynamoDB handles locking to prevent concurrent state changes.
- No Committed Secrets: Never hardcode secrets or AWS keys in code. Use environment variables and store production secrets in AWS Secrets Manager.
Key Takeaways
- Environment Isolation: Modular code makes maintaining dev, staging, and production environments from a single repository practical.
- State Management: Lock files and remote backend configurations are critical when working in teams to avoid overwriting state.
- IAM Overhead: Scoping IAM permissions correctly is time-consuming but essential for a secure production posture.