This guide explains how to configure and deploy the Terraform infrastructure for QA and Production environments.
-
AWS CLI configured with credentials:
aws configure # Enter your AWS Access Key ID, Secret Key, and region -
Terraform installed (version 1.0+):
terraform --version
-
Required AWS IAM Permissions: Your AWS credentials need:
- S3 (for state management)
- DynamoDB (for state locking)
- EC2, VPC, ECS, ECR, ELB, IAM, CloudWatch
The backend stores Terraform state in S3 with DynamoDB locking. This is shared across both QA and Production.
cd terraform/backend-setup
# Run the setup script
./setup.shWhat this creates:
- S3 bucket:
rd-app-terraform-state-shared - DynamoDB table:
rd-app-terraform-locks-shared - Versioning enabled on S3 bucket
- Encryption enabled
Output will show:
Backend configuration to use in main Terraform:
terraform {
backend "s3" {
bucket = "rd-app-terraform-state-shared"
key = "terraform.tfstate"
region = "us-east-1"
encrypt = true
}
}
Note: Terraform now uses .terraform.lock.hcl for state locking instead of DynamoDB. The DynamoDB table is still created for backward compatibility but is no longer required in the backend configuration.
cd ../infrastructure
# Initialize Terraform with QA backend
terraform init -backend-config=../environments/qa.backend.hcl
# Review the QA configuration
cat ../environments/qa.tfvarsQA Configuration (terraform/environments/qa.tfvars):
- VPC CIDR:
10.0.0.0/16 - Container CPU: 256 (0.25 vCPU)
- Container Memory: 512 MB
- Desired Tasks: 1
- Auto-scaling: 1-2 tasks
Validate and Plan:
# Validate configuration
terraform validate
# See what will be created
terraform plan -var-file=../environments/qa.tfvarsApply (Deploy):
terraform apply -var-file=../environments/qa.tfvarsWhat this creates:
- VPC with public/private subnets across 2 AZs
- Internet Gateway and NAT Gateways
- Application Load Balancer
- ECR Repository:
rd-app-qa - ECS Cluster:
rd-app-qa-cluster - ECS Service with Fargate tasks
- CloudWatch Log Group
- Security Groups
- IAM Roles
Get the outputs:
terraform output
# Get the application URL
terraform output app_urlProduction uses separate state from QA. You need to reinitialize Terraform.
# Clean local state (don't worry, state is in S3)
rm -rf .terraform
rm .terraform.lock.hcl
# Initialize with Production backend
terraform init -backend-config=../environments/production.backend.hcl
# Review production configuration
cat ../environments/production.tfvarsProduction Configuration (terraform/environments/production.tfvars):
- VPC CIDR:
10.1.0.0/16(different from QA!) - Container CPU: 512 (0.5 vCPU)
- Container Memory: 1024 MB
- Desired Tasks: 2
- Auto-scaling: 2-10 tasks
Plan and Apply:
# Validate
terraform validate
# Plan
terraform plan -var-file=../environments/production.tfvars
# Apply
terraform apply -var-file=../environments/production.tfvarsAfter deployment, verify the infrastructure:
# Check ECS cluster
aws ecs list-clusters
# Check ECS services
aws ecs list-services --cluster rd-app-qa-cluster
# Get ALB DNS name
aws elbv2 describe-load-balancers \
--names rd-app-qa-alb \
--query 'LoadBalancers[0].DNSName' \
--output text
# Test health endpoint
curl http://<ALB-DNS-NAME>/api/healthEdit terraform/environments/qa.tfvars or production.tfvars:
# Increase container resources
container_cpu = 1024 # 1 vCPU
container_memory = 2048 # 2 GB
# Adjust auto-scaling
desired_count = 3
min_capacity = 2
max_capacity = 5Then re-apply:
terraform apply -var-file=../environments/qa.tfvarsAdd custom environment variables for your app:
app_environment_variables = {
"LOG_LEVEL" = "debug"
"API_BASE_URL" = "https://api.example.com"
"DATABASE_URL" = "postgresql://..."
"CUSTOM_VAR" = "value"
}- Update backend config (
qa.backend.hcl):
region = "us-west-2"- Update environment config (
qa.tfvars):
aws_region = "us-west-2"-
Re-run backend setup in the new region (if needed)
-
Re-initialize and apply:
terraform init -backend-config=../environments/qa.backend.hcl -reconfigure
terraform apply -var-file=../environments/qa.tfvarscd terraform/infrastructure
# Switch to QA
rm -rf .terraform .terraform.lock.hcl
terraform init -backend-config=../environments/qa.backend.hcl
# Switch to Production
rm -rf .terraform .terraform.lock.hcl
terraform init -backend-config=../environments/production.backend.hcl# List resources
terraform state list
# Show specific resource
terraform state show aws_ecs_cluster.main
# View outputs
terraform outputIf you have existing AWS resources:
terraform import aws_ecs_cluster.main rd-app-qa-cluster1. State Lock Error:
Error: Error acquiring the state lock
Solution: Wait for other operations to complete, or remove the lock file:
# Modern Terraform uses .terraform.lock.hcl
# If stuck, you can force unlock (use with caution):
terraform force-unlock <lock-id>2. Backend Not Configured:
Error: Backend initialization required
Solution: Initialize with backend config:
terraform init -backend-config=../environments/qa.backend.hcl3. Resource Already Exists:
Error: resource already exists
Solution: Import the existing resource or destroy and recreate:
terraform import <resource_type>.<resource_name> <resource_id>4. Insufficient Permissions:
Error: AccessDenied
Solution: Ensure your AWS credentials have the required permissions.
cd terraform/infrastructure
# Initialize with QA backend
terraform init -backend-config=../environments/qa.backend.hcl
# Destroy
terraform destroy -var-file=../environments/qa.tfvars# Reinitialize with Production backend
rm -rf .terraform .terraform.lock.hcl
terraform init -backend-config=../environments/production.backend.hcl
# Destroy
terraform destroy -var-file=../environments/production.tfvarscd terraform/backend-setup
./teardown.sh# Initialize
terraform init
# Validate syntax
terraform validate
# Format code
terraform fmt -recursive
# Plan changes
terraform plan -var-file=../environments/qa.tfvars
# Apply changes
terraform apply -var-file=../environments/qa.tfvars
# Show current state
terraform show
# List outputs
terraform output
# Refresh state
terraform refresh -var-file=../environments/qa.tfvars
# Destroy infrastructure
terraform destroy -var-file=../environments/qa.tfvars- Never commit state files to git (already in .gitignore)
- Use separate AWS accounts for QA and Production (recommended)
- Enable MFA on AWS accounts
- Use IAM roles instead of access keys when possible
- Enable CloudTrail for audit logging
- Regularly rotate AWS access keys
- Review security groups - ensure minimum required access
- Enable encryption on all resources (already configured)
- Use
terraform planbeforeapplyto review changes - Tag resources appropriately (already configured via default_tags)
- Use workspaces for environment isolation (alternative to backend configs)
- Keep Terraform version consistent across team members
- Use remote state for collaboration
- Regular backup S3 state bucket
- Monitor CloudWatch logs for application issues
- Use
terraform fmtto maintain consistent formatting