Directory-Based Approach


project-root/
β”œβ”€β”€ README.md                     # Project documentation
β”œβ”€β”€ .gitignore                    # Ignore .tfstate, .tfvars files, etc.
β”œβ”€β”€ scripts/                      # Automation scripts
β”‚   β”œβ”€β”€ init.sh                   # Initialize all Terraform directories
β”‚   β”œβ”€β”€ apply-all.sh              # Apply all environments in correct order
β”‚   β”œβ”€β”€ teardown.sh               # Destroy all resources in reverse order
β”‚   β”œβ”€β”€ lint.sh                   # Run terraform fmt and validation
β”‚   └── aws-nuke/                 # AWS-Nuke configuration for cleanup
β”‚       └── aws-nuke-config.yaml  # AWS-Nuke configuration file
β”œβ”€β”€ modules/                      # Reusable modules
β”‚   β”œβ”€β”€ networking/               # Example: VPC, subnets, security groups
β”‚   β”‚   β”œβ”€β”€ main.tf               # Module resources
β”‚   β”‚   β”œβ”€β”€ variables.tf          # Input variables
β”‚   β”‚   β”œβ”€β”€ outputs.tf            # Output values
β”‚   β”‚   └── README.md             # Module documentation
β”‚   β”œβ”€β”€ compute/                  # Example: EC2, auto-scaling groups
β”‚   β”‚   β”œβ”€β”€ main.tf
β”‚   β”‚   β”œβ”€β”€ variables.tf
β”‚   β”‚   β”œβ”€β”€ outputs.tf
β”‚   β”‚   └── README.md
β”‚   └── database/                 # Example: RDS, DynamoDB
β”‚       β”œβ”€β”€ main.tf
β”‚       β”œβ”€β”€ variables.tf
β”‚       β”œβ”€β”€ outputs.tf
β”‚       └── README.md
β”œβ”€β”€ global/                       # Resources shared across environments
β”‚   β”œβ”€β”€ iam/                      # Identity and Access Management
β”‚   β”‚   β”œβ”€β”€ main.tf               # IAM roles, policies, users
β”‚   β”‚   β”œβ”€β”€ variables.tf          # Input variables
β”‚   β”‚   β”œβ”€β”€ outputs.tf            # Outputs like role ARNs
β”‚   β”‚   β”œβ”€β”€ providers.tf          # Provider configuration
β”‚   β”‚   β”œβ”€β”€ backend.tf            # State configuration (e.g., S3)
β”‚   β”‚   └── versions.tf           # Terraform and provider versions
β”‚   β”œβ”€β”€ dns/                      # DNS zones and global records
β”‚   β”‚   β”œβ”€β”€ main.tf               # Route53 zones, ACM certificates
β”‚   β”‚   β”œβ”€β”€ variables.tf 
β”‚   β”‚   β”œβ”€β”€ outputs.tf            # Outputs like zone IDs
β”‚   β”‚   β”œβ”€β”€ providers.tf
β”‚   β”‚   β”œβ”€β”€ backend.tf
β”‚   β”‚   └── versions.tf
β”‚   └── monitoring/               # Global monitoring resources
β”‚       β”œβ”€β”€ main.tf               # CloudWatch dashboards, alarms
β”‚       β”œβ”€β”€ variables.tf
β”‚       β”œβ”€β”€ outputs.tf
β”‚       β”œβ”€β”€ providers.tf
β”‚       β”œβ”€β”€ backend.tf
β”‚       └── versions.tf
└── environments/                 # Environment-specific configurations
    β”œβ”€β”€ dev/                      # Development environment
    β”‚   β”œβ”€β”€ main.tf               # Calls modules with dev params
    β”‚   β”œβ”€β”€ variables.tf          # Environment variables
    β”‚   β”œβ”€β”€ terraform.tfvars      # Dev-specific values
    β”‚   β”œβ”€β”€ outputs.tf            # Environment outputs
    β”‚   β”œβ”€β”€ providers.tf          # Provider configuration
    β”‚   β”œβ”€β”€ backend.tf            # Dev state config
    β”‚   └── versions.tf           # Version constraints
    β”œβ”€β”€ staging/                  # Staging/QA environment
    β”‚   β”œβ”€β”€ main.tf               # Similar structure to dev
    β”‚   β”œβ”€β”€ variables.tf
    β”‚   β”œβ”€β”€ terraform.tfvars
    β”‚   β”œβ”€β”€ outputs.tf
    β”‚   β”œβ”€β”€ providers.tf
    β”‚   β”œβ”€β”€ backend.tf
    β”‚   └── versions.tf
    └── prod/                     # Production environment
        β”œβ”€β”€ main.tf               # Production configuration
        β”œβ”€β”€ variables.tf
        β”œβ”€β”€ terraform.tfvars
        β”œβ”€β”€ outputs.tf
        β”œβ”€β”€ providers.tf
        β”œβ”€β”€ backend.tf
        └── versions.tf

Global Resource Configuration

  • Global resources are configured and applied independently
global/iam/main.tf
# global/iam/main.tf
resource "aws_iam_role" "lambda_execution" {
  name = "lambda-execution-role"
  
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "lambda.amazonaws.com"
        }
      }
    ]
  })
}
 
# Define outputs that environments will need
# global/iam/outputs.tf
output "lambda_execution_role_arn" {
  value       = aws_iam_role.lambda_execution.arn
  description = "ARN of the Lambda execution role"
}

Environment Access to Global Resources

  • Environments access global resources via remote state
environments/dev/main.tf
# environments/dev/main.tf
data "terraform_remote_state" "global_iam" {
  backend = "s3"
  config = {
    bucket = "my-terraform-states"
    key    = "global/iam/terraform.tfstate"
    region = "us-west-2"
  }
}
 
module "api_service" {
  source = "../../modules/compute"
  
  # Pass the global IAM role to the module
  lambda_role_arn = data.terraform_remote_state.global_iam.outputs.lambda_execution_role_arn
  
  # Environment-specific variables
  instance_count  = 2
  instance_type   = "t3.small"
  environment     = "dev"
}

Automation Scripts to Create and Destroy

  • Including automation scripts helps standardize operations and reduces human error
init.sh
#!/bin/bash
# Initialize all Terraform directories
 
echo "Initializing global resources..."
cd global/iam && terraform init
cd ../dns && terraform init
cd ../monitoring && terraform init
 
echo "Initializing environments..."
cd ../../environments/dev && terraform init
cd ../staging && terraform init
cd ../prod && terraform init
 
echo "Terraform initialization complete!"
apply-all.sh
#!/bin/bash
# Apply all resources in the correct order
set -e  # Exit on any error
 
echo "Applying global IAM resources..."
cd global/iam 
terraform apply -auto-approve
 
echo "Applying global DNS resources..."
cd ../dns
terraform apply -auto-approve
 
echo "Applying global monitoring resources..."
cd ../monitoring
terraform apply -auto-approve
 
echo "Applying dev environment..."
cd ../../environments/dev
terraform apply -auto-approve
 
echo "Applying staging environment..."
cd ../staging
terraform apply -auto-approve
 
echo "Applying production environment..."
cd ../prod
terraform apply -auto-approve
 
echo "All resources applied successfully!"
teardown.sh
#!/bin/bash
# Destroy all resources in reverse order with AWS-Nuke fallback
set -e  # Exit on any error
 
read -p "WARNING: This will destroy ALL infrastructure. Are you sure? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]
then
    exit 1
fi
 
# Function to handle terraform destroy with error catching
terraform_destroy() {
    local dir=$1
    local env=$2
    echo "Destroying $env..."
    cd $dir
    terraform destroy -auto-approve
    if [ $? -ne 0 ]; then
        echo "WARNING: Terraform destroy failed for $env. Resources may remain."
        FAILED_ENVS="$FAILED_ENVS $env"
    fi
}
 
# Initialize empty array for environments where destroy failed
FAILED_ENVS=""
 
echo "Destroying environments (in reverse order)..."
terraform_destroy "environments/prod" "production"
terraform_destroy "environments/staging" "staging"
terraform_destroy "environments/dev" "development"
 
echo "Destroying global resources..."
terraform_destroy "../../global/monitoring" "global monitoring"
terraform_destroy "../dns" "global DNS"
terraform_destroy "../iam" "global IAM"
 
# Check if any destroys failed
if [ ! -z "$FAILED_ENVS" ]; then
    echo "Some environments failed to destroy completely:$FAILED_ENVS"
    read -p "Would you like to run AWS-Nuke to clean up remaining resources? (y/n) " -n 1 -r
    echo
    if [[ $REPLY =~ ^[Yy]$ ]]; then
        echo "Running AWS-Nuke..."
        
        # Change to the directory containing AWS-Nuke config
        cd ../../scripts/aws-nuke
        
        # Run AWS-Nuke with appropriate config
        # Note: You should have AWS-Nuke installed and configured properly
        aws-nuke -c aws-nuke-config.yaml --profile default --no-dry-run
        
        if [ $? -eq 0 ]; then
            echo "AWS-Nuke completed successfully."
        else
            echo "WARNING: AWS-Nuke encountered issues. Some resources may still exist."
            echo "Please check the AWS Console for any remaining resources."
        fi
    else
        echo "Skipping AWS-Nuke. Some resources may still exist in your AWS account."
    fi
else
    echo "All resources destroyed successfully!"
fi
aws-nuke-config.yaml
# scripts/aws-nuke/aws-nuke-config.yaml
regions:
  - "global"
  - "us-east-1"
  - "us-west-2"  # Add all regions you use
 
account-blocklist:
  - "999999999999"  # Replace with production account IDs you want to protect
 
accounts:
  "123456789012": # Replace with your account ID
    presets:
      - "terraform-managed"
 
resource-types:
  # Exclude resources that should not be deleted
  excludes:
    - "IAMUser:root"
    - "IAMUserPolicyAttachment:root"
    - "IAMUserAccessKey:root"
 
presets:
  terraform-managed:
    filters:
      # Filter resources with specific tags managed by your Terraform project
      IAMRole:
        - property: "tag:Project"
          value: "MyTerraformProject"
      EC2Instance:
        - property: "tag:Environment"
          values:
            - "dev"
            - "staging"
            - "prod"
      # Add more resource types and filters as needed
  • Refer to AWS Nuke for the setup and other details