Why this matters
Infrastructure as Code (IaC) lets you define servers, networks, databases, and permissions as versioned code. As a Backend Engineer, you will:
- Review and merge pull requests that change infrastructure (e.g., add an API gateway, adjust an IAM role).
- Spin up dev/test environments on demand from code, not tickets.
- Automate plan/apply in CI to make releases repeatable and auditable.
- Detect and fix configuration drift between environments.
- Rollback safely by reverting code instead of clicking in consoles.
Concept explained simply
IaC means describing what you want your infrastructure to look like in files that can be version-controlled and executed by tools to create or update real resources.
Mental model
Think of your cloud as a state machine:
- Desired state: the code you commit.
- Current state: what currently exists in the cloud (tracked by a state file or inventory).
- Plan: a diff between desired and current state.
- Apply: the tool reconciles the diff to reach the desired state, ideally idempotently.
Key terms (open me)
- Declarative vs Imperative: Declarative says “what” (end state). Imperative says “how” (commands).
- Idempotency: Running the same code twice doesn’t change the result the second time.
- State: A record mapping your code to real resources to compute safe changes.
- Drift: Real infra doesn’t match code (manual changes or failed applies).
- Modules/Roles: Reusable blocks that standardize patterns (e.g., a service + DB + monitoring).
Core pieces of IaC for Backend Engineers
- Small, composable modules for repeatable patterns (service, queue, storage).
- Version control and code reviews for infra changes.
- Plan on pull requests; apply only on protected branches after approval.
- Remote state storage with locking to avoid concurrent apply conflicts.
- Separate configs per environment with shared modules and different variables.
- Secrets kept outside code (secure variables, vaults, or masked CI vars).
- Automated drift detection and scheduled conformance checks.
Worked examples
Example 1: Minimal declarative resource (Terraform-style)
Goal: Create a versioned object storage bucket with tags using declarative IaC.
# main.tf (illustrative)
terraform {
required_version = ">= 1.4.0"
}
provider "aws" {
region = var.region
}
resource "aws_s3_bucket" "logs" {
bucket = var.bucket_name
tags = {
app = var.app
env = var.env
owner = var.owner
}
}
resource "aws_s3_bucket_versioning" "logs_v" {
bucket = aws_s3_bucket.logs.id
versioning_configuration { status = "Enabled" }
}
variable "region" {}
variable "bucket_name" {}
variable "app" {}
variable "env" {}
variable "owner" {}
Mental check: Re-running apply should show no changes unless inputs change. Tags and versioning are enforced.
Example 2: CI pipeline gates (plan on PR, apply on main)
# Pseudo-CI steps (tool-agnostic)
# pull_request:
- checkout
- setup-terraform
- terraform init -backend-config=... (remote state)
- terraform validate
- terraform plan -out=plan.out
- upload plan summary as PR comment
# main branch (after review & approvals):
- checkout
- setup-terraform
- terraform init -backend-config=...
- terraform apply -auto-approve plan.out
Result: Reviewers see the exact changes before they land. Production applies only on approved, protected branches.
Example 3: Drift detection
Say someone manually disabled bucket versioning in the console. Your next plan shows a diff:
~ resource "aws_s3_bucket_versioning" "logs_v" {
versioning_configuration {
- status = "Suspended"
+ status = "Enabled"
}
}
Action: Apply the code or re-enable via code. Avoid manual fixes without code changes to prevent recurring drift.
Exercises
Practice here, then scroll to the Quick Test at the end.
Exercise 1: Author a minimal declarative resource
Create a minimal IaC snippet that:
- Declares a storage bucket named via a variable.
- Enables versioning.
- Adds tags: app, env, owner (variables too).
Output: a single code file (e.g., main.tf-like) with variables and resources. Do not include secrets.
Exercise 2: Read a plan and spot change types
Given this plan excerpt, identify creates, updates, and destroys, and whether it’s safe to apply now:
+ aws_s3_bucket.logs (new resource)
~ aws_iam_role.app_role (in-place update: policy doc change)
-/+ aws_security_group.web (replace: name changed)
Explain: Which lines correspond to create/update/replace? What risk does replace carry? What review should happen?
Exercise checklist
- Solution is declarative (no shell loops to create resources).
- Idempotent: second apply yields “no changes”.
- Variables isolate env and ownership (no hard-coded names).
- You can explain each plan symbol: + create, ~ update, - destroy, -/+ replace.
Common mistakes and self-check
- Mistake: Hard-coding names or regions. Self-check: Are env, region, and naming derived from variables?
- Mistake: No remote state/locking. Self-check: Does your backend lock during apply to prevent concurrent changes?
- Mistake: Applying plans straight from PRs. Self-check: Is apply restricted to protected branches only?
- Mistake: Secrets committed to repo. Self-check: Are secrets pulled from secure providers or masked CI vars?
- Mistake: Manual hotfixes in console. Self-check: Can you fix by code and verify with a clean plan?
Practical projects
- Reusable service module: One module that provisions a service + log storage + alarms using variables for env.
- Ephemeral preview env: On pull request, create a short-lived environment; destroy it on merge/close.
- Drift watcher: A scheduled pipeline that runs plan in read-only mode and posts a summary to team chat.
Learning path
- Write a tiny declarative resource (storage + tags + versioning).
- Add variables, remote state, and locking.
- Introduce modules to remove duplication.
- Wire CI: validate → plan on PR → apply on main.
- Add drift detection and environment promotion (dev → staging → prod).
Who this is for
- Backend Engineers who touch cloud resources or run services in production.
- Engineers integrating deployments into CI/CD pipelines.
Prerequisites
- Basic Git (branches, pull requests, reviews).
- Familiarity with a cloud provider’s basic services (compute, storage, IAM).
- Comfort running command-line tools.
Next steps
- Finish the exercises and take the Quick Test below.
- Refactor your IaC into modules and add CI plan/apply gates.
- Propose an IaC code review checklist for your team.
Mini challenge
Time-box (30–45 min): Convert a manual environment setup you did recently (e.g., a queue + permissions) into declarative code with variables for env and tags. Write a short README explaining how to plan/apply safely and how to rollback by code.
Quick Test
This test is available to everyone. If you’re logged in, your progress will be saved.