Infrastructure as Code Discipline

IaC only pays off if you enforce it: plan before apply, review every change, and hunt down drift before it bites you at 3 a.m.

Writing a Terraform or CloudFormation template is the easy 20%. The senior skill is the discipline around it — the habits that keep your declared state and your real state honest over months of changes.

The non-negotiables

  • Plan before apply. terraform plan / CloudFormation change sets show the diff before anything mutates. Post that plan on the pull request so a human sees what will change.
  • Remote, locked state. Terraform state lives in S3 with DynamoDB locking (or use S3 native locking) so two engineers can't apply at once and corrupt it.
  • No console cowboy edits. Every change goes through the repo. A hotfix clicked into the console becomes drift — reality no longer matches code.
  • Detect drift on a schedule. terraform plan -detailed-exitcode or CloudFormation detect-stack-drift in a nightly job catches out-of-band changes early.

Why drift is dangerous

Drift is silent until the next deploy, when IaC tries to "correct" a manual change and reverts a fix — or worse, deletes a resource someone created by hand. Treat a drift alert like a failing test.

Example

Example · yaml
# .github/workflows/terraform.yml — plan on PR, apply on merge
jobs:
  plan:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    permissions: { id-token: write, contents: read, pull-requests: write }
    steps:
      - uses: actions/checkout@v4
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/gha-terraform-plan
          aws-region: us-east-1
      - uses: hashicorp/setup-terraform@v3
      - run: terraform init
      - run: terraform plan -no-color -out=tfplan   # diff posted to the PR

  drift-check:
    if: github.event_name == 'schedule'
    runs-on: ubuntu-latest
    steps:
      - run: terraform plan -detailed-exitcode || echo '::warning::drift detected'

When to use it

  • A team enforces a policy that no one can create AWS resources via the console in prod, only via Terraform, so the state file is always the source of truth.
  • An engineer runs terraform plan in CI on every pull request and posts the diff as a comment so reviewers see infrastructure changes alongside code changes.
  • Ops detects that a security group was manually widened in the console by running terraform plan, which shows an unexpected update, and reverts the drift.

More examples

Enforce IaC-only via SCP

Service Control Policy that denies EC2 and RDS creation unless the call comes through CloudFormation, enforcing IaC-only access.

Example · json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Deny",
    "Action": ["ec2:RunInstances", "rds:CreateDBInstance"],
    "Resource": "*",
    "Condition": {
      "StringNotEquals": {
        "aws:CalledVia": ["cloudformation.amazonaws.com"]
      }
    }
  }]
}

Detect Terraform drift in CI

Uses terraform plan's exit code 2 (changes present) to fail CI when actual infrastructure drifts from the Terraform state.

Example · bash
terraform plan -detailed-exitcode
EXIT=$?
if [ $EXIT -eq 2 ]; then
  echo 'Drift detected: plan has changes'
  exit 1
elif [ $EXIT -eq 0 ]; then
  echo 'No drift — state matches infrastructure'
fi

Lock Terraform state with DynamoDB

Configures S3 backend with DynamoDB locking so concurrent applies are prevented and state is encrypted at rest.

Example · hcl
terraform {
  backend "s3" {
    bucket         = "my-tf-state"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    dynamodb_table = "tf-state-locks"
    encrypt        = true
  }
}

Discussion

  • Be the first to comment on this lesson.
Infrastructure as Code Discipline — AWS Deployment | SoundsCode