Multi-Environment AWS Infrastructure – Terraform
I built a multi-environment AWS infrastructure project to demonstrate Terraform workspace management and environment-specific configurations. It focuses on clean separation of concerns, automated CI/CD, and AWS Free Tier compliance while showcasing production-ready patterns.
- Infrastructure: VPC, subnets, security groups, IAM roles, EC2 instances, S3 buckets
- Environments: dev, stage, prod with workspace-specific configurations
- Platform: Terraform with S3 backend, DynamoDB state locking
- CI/CD: GitHub Actions with automated planning and manual deployments
- Why: Demonstrate infrastructure-as-code best practices and multi-environment management

Project goals
- Model realistic multi-environment infrastructure without over-engineering
- Demonstrate Terraform workspace best practices and environment isolation
- Implement automated CI/CD with GitHub Actions
- Ensure AWS Free Tier compliance for cost-effective learning
- Provide clear separation between development, staging, and production environments
Architecture overview
The project creates environment-specific AWS infrastructure using Terraform workspaces:
-
VPC Configuration
- Environment-specific CIDR blocks (dev: 10.0.0.0/16, stage: 10.1.0.0/16, prod: 10.2.0.0/16)
- 2 public subnets per environment across different availability zones
- Internet Gateway and Route Tables for internet connectivity
-
Security Groups
- Environment-specific security rules with progressive restrictions
- Dev: permissive SSH access (0.0.0.0/0)
- Stage: restricted SSH access to specific IP ranges
- Prod: strict SSH access only from VPN/bastion networks
-
EC2 Instances
- t3.micro instances (AWS Free Tier compliant)
- Environment-specific instance counts (dev: 1, stage: 1, prod: 2)
- IAM roles with SSM access and S3 read-only permissions
-
S3 Buckets
- Environment-specific naming with random suffixes
- Server-side encryption and versioning enabled
- Public access blocked for security
Environment configuration
The core of the project uses Terraform's locals block for environment-specific configurations:
locals {
workspace_config = {
dev = {
vpc_cidr = "10.0.0.0/16"
instance_count = 1
ssh_access_cidr = "0.0.0.0/0"
environment_tag = "dev"
}
stage = {
vpc_cidr = "10.1.0.0/16"
instance_count = 1
ssh_access_cidr = var.allowed_ip_cidr
environment_tag = "stage"
}
prod = {
vpc_cidr = "10.2.0.0/16"
instance_count = 2
ssh_access_cidr = var.vpn_cidr
environment_tag = "prod"
}
}
config = lookup(local.workspace_config, terraform.workspace, local.workspace_config["dev"])
common_tags = {
Environment = local.config.environment_tag
Project = "terraform-multi-env-aws"
ManagedBy = "terraform"
}
}Security implementation
Security groups implement environment-specific access controls:
resource "aws_security_group" "web" {
name_prefix = "${local.config.environment_tag}-web-"
vpc_id = aws_vpc.main.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [local.config.ssh_access_cidr]
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(local.common_tags, {
Name = "${local.config.environment_tag}-web-sg"
})
}CI/CD pipeline
GitHub Actions workflow provides automated planning and manual deployments:
name: Terraform Deploy
on:
push:
branches: [main]
paths: ['terraform/**']
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy'
required: true
default: 'dev'
type: choice
options:
- dev
- stage
- prod
jobs:
terraform-plan:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Terraform Init
run: terraform init -backend=false
working-directory: ./terraform
- name: Terraform Validate
run: terraform validate
working-directory: ./terraform
terraform-apply:
runs-on: ubuntu-latest
needs: terraform-plan
if: github.event_name == 'workflow_dispatch'
steps:
- name: Select or Create Workspace
run: |
terraform workspace new ${{ github.event.inputs.environment }} 2>/dev/null || true
terraform workspace select ${{ github.event.inputs.environment }}
echo "Active workspace:"
terraform workspace show
working-directory: ./terraform
- name: Terraform Apply
run: terraform apply -auto-approve
working-directory: ./terraform
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}State management
Terraform state is managed with S3 backend and DynamoDB locking:
terraform {
backend "s3" {
bucket = "BUCKET_NAME"
key = "terraform.tfstate"
region = "eu-central-1"
encrypt = true
dynamodb_table = "TABLE_NAME"
}
}Operational concerns
- Environment isolation: Each workspace maintains separate state and configurations
- Cost optimization: All resources are AWS Free Tier compliant (t3.micro instances, basic S3 storage)
- Security: Progressive security restrictions from dev to prod environments
- Automation: GitHub Actions handles validation on every push, manual deployments for specific environments
- State management: S3 backend with DynamoDB locking prevents concurrent modifications
Workspace management
The project demonstrates proper Terraform workspace usage:
# List available workspaces
terraform workspace list
# Create new environments
terraform workspace new dev
terraform workspace new stage
terraform workspace new prod
# Switch between environments
terraform workspace select dev
# Deploy specific environment
terraform plan
terraform applySecurity and compliance
- IAM roles: EC2 instances use least-privilege IAM roles with SSM and S3 read-only access
- Network security: Environment-specific security groups with progressive restrictions
- Encryption: S3 buckets use server-side encryption by default
- Access control: SSH access becomes more restrictive from dev to prod
- State security: Terraform state encrypted in S3 with DynamoDB locking
Performance and scalability
- Instance sizing: t3.micro instances suitable for development and small workloads
- Multi-AZ deployment: Subnets span multiple availability zones for high availability
- Resource tagging: Consistent tagging strategy for cost tracking and resource management
- State optimization: Separate state files per environment prevent cross-environment dependencies
Trade-offs and decisions
- Workspace vs. separate directories: Chose workspaces for simplicity while maintaining environment isolation
- Free Tier compliance: Prioritized cost-effectiveness over performance for learning purposes
- Manual deployments: GitHub Actions requires manual approval for production deployments
- Single region: Focused on eu-central-1 for consistency and Free Tier optimization
Running the project
- Local setup: Clone repository, configure AWS credentials, run
terraform initandterraform workspace select dev - Environment deployment: Use
terraform workspace select <env>andterraform apply - CI/CD: Push changes trigger validation; manual workflow dispatch for deployments
- Cleanup: Use
terraform destroyfor each environment to clean up resources
Future enhancements
- Monitoring: Add CloudWatch dashboards and alarms for infrastructure health
- Backup: Implement automated EBS snapshots and S3 lifecycle policies
- Scaling: Add Auto Scaling Groups for production workloads
- Networking: Implement private subnets and NAT Gateways for enhanced security
- Compliance: Add AWS Config rules for compliance monitoring
Cost analysis
- Free Tier compliance: All resources fall within AWS Free Tier limits
- Monthly costs: $0 for typical development usage
- Scaling costs: Additional instances and storage scale predictably
- State storage: Minimal S3 and DynamoDB costs for state management
Notes
This project demonstrates infrastructure-as-code best practices in a compact, educational format. The patterns (workspace management, environment isolation, and automated CI/CD) mirror how I approach larger infrastructure projects. The focus on AWS Free Tier compliance makes it accessible for learning while maintaining production-ready practices.