Configuration Management on VMs
The part of IaC that lives after boot
Provisioning creates the box; configuration management (Chef, Puppet, Ansible) shapes the box after it boots. Ansible is agentless; Chef/Puppet use agents with pull-based convergence. Both belong in the 'after boot' phase of the IaC story.
What you will learn
- Place config management relative to provisioning
- Use Ansible plays/roles idempotently
- Decide agent vs agentless for your estate
After Terraform/CloudFormation hands over the VM, config management ensures packages, files, users, and services match policy — Ansible (SSH, no agent) or Chef/Puppet (agent + server pull). Horses for courses: both are tiers above hand-rolled scripts.
What is this?
Ansible: inventory (hosts/groups), modules (yum/apt/template/systemd…), playbooks/roles/collections; agentless over SSH; YAML state targets; idempotent modules ('ensure'). Chef: recipes/cookbooks, server + agent pull, ohai facts, converge-and-report. Puppet: manifests, catalog, agent pull. Others: SaltStack. Cross-over: cloud-init (first-boot user data), launch templates' userdata for bake-vs-bake cycles, packer for golden images. Drift vs bake: config management converges live boxes (drift-friendly); golden images (Packer) convert config into pre-baked state (fast + immutable).
Why do you need it?
- Bare provisioning doesn't configure: packages, users, firewall rules, app files still need a second act.
- Config management converges drift: a manually-changed config gets corrected at the next pull.
- Ansible's no-agent model keeps SSH-only environments manageable.
- Roles/playbooks make server setup reviewable and repeatable like the infra code above it.
Mental model
Real-estate: architect draws (Terraform) and the fit-out team outfits the rooms (config mgmt)
Provisioning vs config management
The shell exists after construction; furnishing, fixtures and wiring-in (packages, users, config files) is a separate, repeatable contract with a checklist that converges.
How it works
- Inventory maps hosts → groups with variables.
- Playbooks declare plays: hosts, tasks (module + args), handlers.
- Modules check desired state and only act on diff (idempotent).
- Handlers fire on changes (e.g., restart nginx only when config changed).
- Agent versions pull from a central server; agentless runs from a control host via SSH.
Config management flow.
Examples
Idempotent play for nginx
- hosts: web become: true tasks: - name: install nginx ansible.builtin.apt: name: nginx state: present - name: config from template ansible.builtin.template: src: nginx.conf.j2 dest: /etc/nginx/nginx.conf - name: ensure running ansible.builtin.service: name: nginx state: started enabled: trueState: present/started/enabled — re-runs converge without redeploying; templates render from vars.
In production
- Ansible for workstation/VM fleet config: packages, users, ssh keys, hardening across clouds.
- Agent-based Chef/Puppet for large pull-based estates with audit/reporting.
- Golden-image workflow: Packer bakes config so new instances boot ready (fast, immutable).
Best practices
- Prefer idempotent 'ensure' modules; test running a play twice = unchanged.
- Keep hosts/repo driven by IaC inventory, not hand-maintained files.
- Separate secrets from playbooks (ansible-vault, secret mounts).
- Prefer golden images for scale; config management for drift-prone mutable fleet.
- Restrict arbitrary command/shell tasks to genuine holes in module coverage.
Common mistakes
- Terraform 'then' scripts that never rerun against a live changing box.
- Playbooks that do 'one big shell' — loses idempotency and reporting.
- Running config mgmt against a fleet that should be immutable, introducing drift.
- False sense of security: 'we manage config' but templates drift from tests.
Debugging tips
- Task reports changed every run → missing state in the module (e.g., version not pinned).
- Play succeeds but app broken → compare os-release/arch inventory vars vs role assumptions.
- Agentless timing-out on big fleets → test one host, check SSH multiplexing/limits.
- Diff the template against the live file to see exactly what a run will change.
Interview checkpoint
Quick quiz
Ansible's default model is:
A play run twice that reports 'ok' with no changes is: