DevOps Engineering study materials
DevOps Engineering study resources

DevOps Engineering: Infrastructure as Code and Configuration Management

DevOps Engineering · Work through your exam, one topic at a time.

All guide sections

Infrastructure as Code and Configuration Management

This domain tests how you declare, review, and reconcile infrastructure: the Terraform/OpenTofu plan-apply lifecycle and state model, and idempotent configuration management with Ansible, Chef, Puppet or Salt.

Core rules

  • Two models coexist. Provisioning tools (Terraform, CloudFormation, Pulumi) manage resource lifecycles declaratively, tracking real IDs in state. Configuration management tools converge existing hosts to a desired state. Both must be idempotent: a second run changes nothing.
  • Terraform state maps declarations to real resources. Keep it in a remote backend with locking (S3 + DynamoDB, GCS, Terraform Cloud) so concurrent CI runs cannot corrupt it. State stores attribute values in plaintext, so the bucket is a secret-bearing asset.
  • Workflow: fmt → validate → plan saved as an artifact → human review → apply the saved plan. Avoid blind applies and avoid -target as routine practice; never hand-edit state without state mv or import.
  • Pin provider and module versions and commit the dependency lock file. Prefer for_each with stable keys over count for any collection that can be reordered.
  • Ansible: use modules over shell/command; dry-run with --check --diff; organise roles and inventories; use changed_when and failed_when so handlers and CI exit codes are truthful.
  • Policy as code (OPA/Conftest, Sentinel, Checkov) gates the plan before apply.

Worked example

A module creates subnets with count = length(var.subnets) for three entries, [a, b, c]. Someone deletes b from the list. Reasoning: count indexes by position, so index 1 (currently b) is updated to c and index 2 (the old c) is destroyed - one subnet is re-created with a new CIDR and one disappears, which in a VPC means re-IP-ing workloads. With for_each = { for s in var.subnets : s.name => s }, keys are subnet names: deleting b destroys exactly one resource and leaves a and c untouched.

Traps

  • Committing terraform.tfstate or secret-bearing *.tfvars; secrets passed as resource arguments land in state and in plan output.
  • Assuming a plan reviewed in a pull request equals what CI applies later - the plan goes stale as main moves.
  • Calling a playbook idempotent because it exits 0, while its shell tasks report changed every run.
  • Using workspaces instead of separate state and credentials when environments need real blast-radius isolation.