When building modern web applications, software engineers and founders often spend 20+ hours configuring baseline cloud infrastructure before writing a single line of application logic.
In this guide, we will break down how to structure a production-grade AWS infrastructure stack using HashiCorp Terraform 1.5+ and deploy a multi-container AWS ECS Fargate cluster with automated networking and security baselines.
1. Network Architecture: VPC & Subnet Segregation
A common anti-pattern in early cloud deployments is placing container workloads inside a default VPC without explicit subnet isolation. A production stack requires dedicated network boundaries.
Here is a minimal, modular VPC definition split across multiple availability zones:
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.environment}-vpc"
Environment = var.environment
}
}
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.environment}-public-subnet-${count.index + 1}"
}
}
data "aws_availability_zones" "available" { state = "available" }
2. Container Execution Layer: AWS ECS Fargate & IAM Policies
AWS ECS Fargate allows you to run containers serverlessly without managing underlying EC2 instances. However, task definitions frequently fail on initial launch if IAM task execution roles lack proper ECR pull permissions or CloudWatch log stream access.
We define the execution role explicitly:
resource "aws_ecs_cluster" "this" {
name = "${var.environment}-ecs-cluster"
}
resource "aws_iam_role" "ecs_execution" {
name = "${var.environment}-ecs-execution-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "ecs-tasks.amazonaws.com" }
}]
})
}
resource "aws_iam_role_policy_attachment" "ecs_execution" {
role = aws_iam_role.ecs_execution.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
3. Automated CI/CD Pipelines with GitHub Actions
Infrastructure code must be continuously validated to catch syntax errors and formatting issues before hitting your primary branch.
Add a GitHub Actions workflow (.github/workflows/deploy.yml) to validate HCL code automatically on every pull request:
name: "Terraform CI/CD Pipeline"
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
terraform:
name: "Terraform Lint & Validate"
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.5.0
- name: Terraform Init
run: cd environments/prod && terraform init -backend=false
- name: Terraform Validate
run: cd environments/prod && terraform validate
Production AWS Architecture Asset
For engineering teams and founders who need a full production stack—including Multi-AZ VPC networking, ECS Fargate cluster automation, IAM security execution roles, ECR repository integration, and GitHub Actions CI/CD deployment pipelines—you can grab the full infrastructure kit here:
👉 Get the Complete Production AWS & Terraform Infrastructure Kit ($149)
Summary Checklist for Cloud Infrastructure
Keep Modules Isolated: Separate vpc, ecs, and database logic into modular directories.
Never Hardcode Secrets: Pass sensitive database credentials and API tokens via environment variables or AWS Secrets Manager.
Automate Formatting: Run terraform fmt -recursive before committing code to maintain clean, readable HCL.
Top comments (0)