Setting Up an Enterprise-Grade Terraform Workflow

aditya narraaditya narra
4 min read

Terraform is a powerful infrastructure-as-code (IaC) tool that enables consistent, repeatable, and reliable management of cloud resources. When building Terraform projects at an enterprise scale, structuring your repository correctly is crucial. A well-organized Terraform project simplifies development, fosters collaboration, enhances maintainability, and ensures security best practices. In this comprehensive guide, we'll establish a clear, modular, and highly maintainable Terraform project structure suitable for enterprises. We’ll deeply explore each component to ensure clarity and facilitate smooth implementation.

Detailed Explanation of Structure

1. environments/

Separates Terraform configurations by deployment stages:

  • dev/: Lightweight for rapid development.

  • stage/: Mirrors production for testing.

  • prod/: Optimized for production-grade deployments.

Each environment contains:

  • main.tf: Infrastructure module definitions.

  • terraform.tfvars: Environment-specific variables.

  • backend.tf: Terraform state management configurations.

2. modules/

Understanding Modules in Terraform

Terraform modules encapsulate reusable infrastructure components, making your Terraform configurations modular, organized, and maintainable. Modules simplify the management of infrastructure, prevent repetitive code, and enable standardization across different environments.

Components of a Terraform Module

Each module typically contains:

  • main.tf: Defines core resources and their relationships.

  • variables.tf: Defines inputs (parameters) that can be customized when calling the module.

  • outputs.tf: Specifies outputs, enabling modules to expose resource attributes that other configurations can reference.

  • README.md: Detailed documentation and usage examples for the module.

Example Module: VPC Module

main.tf

resource "aws_vpc" "main" {
  cidr_block = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "main-vpc"
  }
}

variables.tf

variable "vpc_cidr" {
  description = "CIDR block for the VPC"
  type        = string
}

outputs.tf

output "vpc_id" {
  description = "The ID of the created VPC"
  value       = aws_vpc.main.id
}

Calling Modules

Modules are invoked from your environment configurations (environments/dev/main.tf):

module "vpc" {
  source = "../../modules/vpc"
  vpc_cidr = var.vpc_cidr
}

Here:

  • source specifies the relative path to the module.

  • vpc_cidr is an input variable passed to the module, defined in your environment's variables.

Module Outputs and Dependencies

Module outputs allow seamless integration and dependency management:

module "ecs" {
  source       = "../../modules/ecs"
  cluster_name = var.ecs_cluster_name
  vpc_id       = module.vpc.vpc_id
}

This example uses vpc_id output from the VPC module as input to the ECS module.Each module:

3. scripts/

Utility scripts to enhance workflow automation:

  • deploy.sh: Streamlines deployment with confirmation steps.

    • A Sample deploy script is as follows

        #!/bin/bash
      
        set -e
      
        ENVIRONMENT=$1
      
        if [[ -z "$ENVIRONMENT" ]]; then
          echo "Usage: ./deploy.sh <environment>"
          echo "Example: ./deploy.sh dev"
          exit 1
        fi
      
        WORKING_DIR="./environments/$ENVIRONMENT"
      
        if [[ ! -d "$WORKING_DIR" ]]; then
          echo "Error: Environment '$ENVIRONMENT' does not exist in $WORKING_DIR"
          exit 1
        fi
      
        echo "============================"
        echo "Terraform Deployment Script"
        echo "Environment: $ENVIRONMENT"
        echo "Working Dir: $WORKING_DIR"
        echo "============================"
      
        cd "$WORKING_DIR"
      
        echo "Initializing Terraform..."
        terraform init
      
        echo "Validating Terraform code..."
        terraform validate
      
        echo "Running Terraform Plan..."
        terraform plan -var-file="terraform.tfvars"
      
        echo ""
        read -p "Do you want to apply these changes? (yes/no): " CONFIRM
      
        if [[ "$CONFIRM" == "yes" ]]; then
          echo "Applying Terraform changes..."
          terraform apply -auto-approve -var-file="terraform.tfvars"
          echo "Terraform apply complete."
        else
          echo "Aborted by user."
          exit 0
        fi
      

      same way we can write script for destroy

  • destroy.sh: Safely destroys environments.

  • validate.sh: Runs format checks and validation plans.

4. Automated GitHub Actions Workflow

File location: .github/workflows/terraform-ci.yml

name: Terraform CI/CD

on:
  push:
    branches:
      - main
      - develop
      - feature/*
  pull_request:
    branches:
      - main
      - develop

jobs:
  terraform:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        environment: [dev, stage, prod]

    steps:
      - uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: '1.10.0'

      - name: Terraform Init
        working-directory: ./environments/${{ matrix.environment }}
        run: terraform init

      - name: Terraform Validate
        working-directory: ./environments/${{ matrix.environment }}
        run: terraform validate

      - name: Terraform Plan
        working-directory: ./environments/${{ matrix.environment }}
        run: terraform plan -var-file="terraform.tfvars"

      - name: Terraform Apply
        if: (github.ref == 'refs/heads/main' && matrix.environment == 'prod') ||
            (github.ref == 'refs/heads/develop' && matrix.environment == 'stage') ||
            (startsWith(github.ref, 'refs/heads/feature/') && matrix.environment == 'dev')
        working-directory: ./environments/${{ matrix.environment }}
        run: terraform apply -auto-approve -var-file="terraform.tfvars"

5. Detailed Branching Strategy

  • Main: Production deployments.

  • Develop: Stage deployments and integration.

  • Feature branches (feature/*): Development and testing in dev.

6. Terraform Workflow Summary

  1. terraform init

  2. terraform plan -var-file="terraform.tfvars"

  3. terraform apply -var-file="terraform.tfvars"

  4. terraform destroy -var-file="terraform.tfvars"

7. .gitignore

Protects sensitive state files:

.terraform/
*.tfstate
*.tfstate.backup
*.tfvars
*.log

8. terraform.tfvars.example

Example variable definitions to guide configuration.

aws_region       = "us-west-2"
vpc_cidr         = "10.0.0.0/16"
ecs_cluster_name = "example-cluster"

9. README.md

Write a Comprehensive documentation covering setup, usage, troubleshooting, and contributions which makes more easy to guide new engineers onboarding.

Conclusion

A meticulously structured enterprise-grade Terraform repository ensures scalability, maintainability, and security. Detailed documentation, clear module separation, and robust automation through GitHub Actions significantly streamline the management of cloud infrastructure, improving operational efficiency and DevOps productivity.

0
Subscribe to my newsletter

Read articles from aditya narra directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

aditya narra
aditya narra