dissected.io
Lesson 19 of 23
Advancedadvancedterraformiacautomationstate11 min read

Infrastructure as Code — Terraform across all three providers

Clicking through a cloud console to create infrastructure is fine for learning. For anything that matters, your infrastructure should be defined in code, stored in version control, and deployed through automation. Infrastructure as Code (IaC) gives you reproducibility, peer review, audit trails, and the ability to rebuild everything from scratch when disaster strikes.

Infrastructure as Code -- Terraform Drift + State Lock

Plan + Apply Pipeline

Config (.tf)

Desired state

terraform plan

Three-way diff

EC2 + Security Group

Pending

State: S3 + DynamoDB

Drift Detection

Console Edit

Security Group changed manually

diverges

Real Infra

Differs from state

Plan: Revert

Drift detected + fixed

Resource Deleted

RDS gone from console

State: Still Listed

Plan: Recreate

Destructive: new RDS

State Lock: Concurrent Apply

Without Lock

Engineer A

apply

Engineer B

apply (same time)

State Corrupted

Resources orphaned

With DynamoDB Lock

Engineer A

apply (has lock)

Engineer B

waiting...

State Intact

Sequential, safe

Plan: Security Group

Ingress 0.0.0.0/0

Sentinel / OPA

Policy check

X

REJECTED

Before apply

Config + state + real infra: three-way diff catches drift. Locks prevent corruption. Policy stops bad plans before apply.

Why IaC Matters

Without IaC, your infrastructure is a snowflake — the product of hundreds of manual decisions that nobody documented. When it breaks at 3am, you're reverse-engineering what someone clicked six months ago. When you need a second environment, you're spending days clicking through the same screens and inevitably missing something.

IaC gives you four things. Reproducibility: run the same code, get the same infrastructure, every time. Code review: infrastructure changes go through pull requests, just like application code. Audit trail: git history shows who changed what, when, and why. Disaster recovery: your entire infrastructure can be rebuilt from a repo — the ultimate backup.

Declarative vs Imperative

Declarative tools (Terraform, CloudFormation, Bicep) describe the desired end state: "I want a VPC with these subnets." The tool figures out what to create, modify, or delete to reach that state. Imperative tools (scripts, Pulumi in imperative mode, CDK) describe the steps: "Create this VPC, then create this subnet, then attach this route table." Declarative is the dominant paradigm because it handles idempotency — you can run it multiple times safely.

Terraform: The Core Model

Terraform is the de facto standard for multi-cloud IaC. It works with all three major providers (and hundreds of others) through a plugin architecture.

Providers are plugins that know how to talk to a specific API — aws, google, azurerm. You configure them with credentials and region, and they expose resources you can manage.

resource "aws_s3_bucket" "data" {
  bucket = "my-app-data-prod"
}

resource "google_storage_bucket" "data" {
  name     = "my-app-data-prod"
  location = "US"
}

Data sources let you read existing infrastructure that Terraform doesn't manage. Variables parameterize your configuration. Outputs expose values for other configurations to consume. Modules package reusable infrastructure — your VPC module, your EKS cluster module, your standard database module.

State: The Hard Part

Terraform maintains a state file that maps your configuration to real resources. When you declare aws_s3_bucket.data, state records the actual bucket ID, ARN, and all its attributes. Without state, Terraform has no idea what already exists.

State must be remote and locked. If two engineers run terraform apply simultaneously with local state files, they'll create duplicate resources or corrupt the state. Use S3 + DynamoDB (AWS), GCS (GCP), or Azure Blob Storage with state locking.

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

Secrets in state are a real problem. When you create an RDS instance with a master password, that password is stored in plaintext in the state file. Encrypt your state backend. Restrict access to the state bucket. Treat state like a credential.

Plan and Apply

The terraform plan command shows you exactly what will change — resources to create, modify, or destroy — without doing anything. terraform apply executes those changes. This plan/apply cycle is your review gate: run plan in CI, post the output to the pull request, and require approval before apply runs.

Never run apply without reading the plan. A misconfigured change can delete your production database. Terraform will tell you 1 to destroy — read it.

Drift

Drift happens when someone changes infrastructure outside Terraform — clicking in the console, running a CLI command, or another tool modifying the same resource. Now state disagrees with reality. terraform plan will show changes to bring reality back to what your code says, which might undo the manual change. This is why you need organizational discipline: once you adopt IaC, the console is read-only.

AWS Implementation

The aws provider is the most mature Terraform provider. It covers virtually every AWS service. State lives in S3 with DynamoDB locking. AWS also offers CloudFormation (AWS-native, YAML/JSON, tightly integrated with AWS services) and CDK (write CloudFormation in TypeScript, Python, etc.). CloudFormation handles state automatically but is AWS-only and verbose.

GCP Implementation

The google provider covers GCP services well. State lives in GCS with built-in locking. GCP also offers Deployment Manager (GCP-native, YAML/Jinja2), though it's less popular than Terraform. GCP's IAM model with service accounts for Terraform requires careful scoping — a Terraform service account with project-owner permissions is a significant security risk.

Azure Implementation

The azurerm provider covers Azure services. State lives in Azure Blob Storage with lease-based locking. Azure offers Bicep (a clean DSL that compiles to ARM templates) and ARM templates (verbose JSON). Bicep is genuinely good — if you're Azure-only, it's a strong alternative to Terraform with better type checking and IntelliSense.

Side-by-Side Comparison

AspectAWSGCPAzure
Terraform provideraws (most mature)googleazurerm
Native IaCCloudFormation / CDKDeployment ManagerBicep / ARM Templates
State backendS3 + DynamoDBGCS (built-in locking)Blob Storage (lease locking)
Provider-managed stateCloudFormationDeployment ManagerARM/Bicep
Terraform coverageExcellentVery goodVery good
Native IaC qualityCloudFormation is verbose, CDK is goodDeployment Manager is limitedBicep is excellent

Module Design and Alternatives

Good modules are opinionated. A VPC module shouldn't expose every possible parameter — it should encode your organization's network standards (CIDR scheme, subnet layout, flow logs enabled) and expose only what varies between environments. Version your modules with git tags and reference specific versions so a module update doesn't break every consumer.

Alternatives to Terraform: Pulumi lets you write IaC in real programming languages (TypeScript, Python, Go) with loops, conditionals, and testing — genuinely powerful but smaller ecosystem. Crossplane brings IaC into Kubernetes, managing cloud resources as custom resources. OpenTofu is the open-source fork of Terraform after HashiCorp's license change to BSL — API-compatible, community-governed.

Policy as Code and Testing

Policy as code enforces rules on your infrastructure before it deploys. Sentinel (HashiCorp, commercial) and OPA/Rego (open source) evaluate policies against Terraform plans. Checkov and tfsec scan your HCL files for security misconfigurations — public S3 buckets, unencrypted databases, overly permissive security groups. Run these in CI on every pull request.

Testing verifies your modules work. Terratest (Go library) provisions real infrastructure, validates it, and tears it down. Plan review in CI catches breaking changes. Ephemeral environments — spinning up a full environment per pull request — give you confidence that changes work before merging.

Key Takeaways

  • Infrastructure as Code gives you reproducibility, peer review, audit trails, and disaster recovery — the console becomes read-only once you adopt it
  • Terraform state maps config to real resources and must be remote, locked, and encrypted — concurrent applies without locking corrupt state
  • The plan/apply cycle is your review gate; never apply without reading the plan, especially lines that say "destroy"
  • Drift happens when someone changes infrastructure outside Terraform; organizational discipline is as important as tooling
  • Secrets end up in state in plaintext — encrypt your state backend and restrict access to it
  • Scan IaC with Checkov/tfsec in CI, test modules with Terratest, and version modules with git tags

Next, we'll look at how code gets from a repository to running infrastructure — CI/CD pipelines.

Enjoyed this breakdown?

Get new lessons in your inbox.