A.8.9 in ISO 27001:2022 says organizations should have “agreed-upon set of rules for managing, maintaining and controlling the configurations of infrastructure and system software.” Three sentences of policy text that sound fine in a SoA but hide a brutal operational truth: configuration drift is inevitable unless you measure it continuously.
I’ve seen three patterns for configuration management across organizations of every size. Only one survives past six months without a manual cleanup sprint.
The Three Patterns
Pattern 1: The Golden Image
You build a reference VM or container image. You snapshot it. You deploy from the snapshot. When a patch comes in, you rebuild the image, test it, replace the old one.
This works. For a while. Then the team adds a manual fix to production to work around a bug. The fix works for three weeks, gets promoted to staging, then production needs a different variant. Now there are three golden images with three different SSH keys, three different log levels, and nobody remembers which one has the cert fix.
Golden images are fragile because they capture a point in time. They don’t capture intent.
Pattern 2: The Config Repository
Every config is in Git. Every change goes through PR. The CI/CD pipeline applies the PR to production. You have a baseline, you have history, you can diff any two points.
This is better. But it has a blind spot: the running state and the repo diverge between deployments. If someone SSHes into a server and changes a setting directly, the repo is lying. If the deployment pipeline fails halfway, the server is halfway between two states and neither the repo nor the operator knows what’s actually running.
Config repos solve versioning but not convergence.
Pattern 3: The Control Loop
This is what people mean when they say “infrastructure as code done right.” Every server reports its current configuration. Every server is compared against a desired state. The system generates a diff. It either auto-corrects or flags a human. The desired state lives in a repo, but the repo is one source — the running systems are the other, and they talk to each other continuously.
The key difference: Pattern 1 and Pattern 2 are push-based (you push a config to the system). Pattern 3 is pull-based (the system tells you what it has and compares it to what it should have). Push works until something breaks. Pull works because the truth is always the live state.
What Actually Goes Wrong
Three classes of drift, in order of frequency:
1. Emergency changes. A prod database needs a timeout adjusted at 2 AM. You sed -i the file, restart, go home. The change is not committed to the repo. Three weeks later, the server reboots. The timeout reverts. Production degrades. The 2 AM fix is gone.
2. Dependency upgrades. You upgrade a package on one server. It pulls in a dependency that changes a default behavior. Another server with a different upgrade timeline doesn’t have the new default. Same image, same repo config, different behavior.
3. Human overrides. A developer SSHes in, tweaks a setting to test something, forgets to revert. Or reverts to the wrong value. Or sets something in /etc/profile because the app reads from /etc/profile on startup and that’s not in the managed config set.
The Control Loop in Practice
You don’t need Kubernetes or a fancy toolchain. The pattern works at any scale:
# 1. Desired state lives in a repo
# (ansible playbook, terraform, or even a directory of .conf files)
# 2. Current state is collected on a schedule
# (cron job running audit commands, or a lightweight agent)
# 3. Diff is computed
diff -u /desired-state/production/server.conf \
/current-state/production/server.conf
# 4. Alert or auto-remediate
On a larger scale, tools like osquery (for endpoint state), Chef InSpec or OpenSCAP (for compliance auditing), or even Ansible’s --check --diff mode do the same thing: they compare live state to desired state and produce a actionable diff.
The metric that matters is mean time to convergence — how long it takes for the running state to match the desired state after a drift event. Your goal is to get that number below your MTTR for configuration-related incidents.
A Practical Baseline Audit
Here’s a baseline audit I run on new servers before they enter production:
#!/bin/bash
# baseline-audit.sh — quick config snapshot of a Linux server
echo "=== Packages ==="
rpm -qa 2>/dev/null || dpkg -l 2>/dev/null
echo "=== Listening ports ==="
ss -tlnp 2>/dev/null || netstat -tlnp 2>/dev/null
echo "=== Cron jobs ==="
crontab -l 2>/dev/null
ls -la /etc/cron.* 2>/dev/null
echo "=== SSH config ==="
cat /etc/ssh/sshd_config 2>/dev/null | grep -v "^#" | grep -v "^$"
echo "=== NTP sync ==="
timedatectl status 2>/dev/null || date
echo "=== Filesystem mounts ==="
mount | grep -E "^/dev"
echo "=== Key services ==="
systemctl list-units --type=service --state=running 2>/dev/null | tail -20
Save the output. Hash it. That’s your baseline. When the next audit runs, diff against this hash. If anything changed, the diff tells you what.
What A.8.9 Actually Demands
The ISO 27001 control isn’t satisfied by having a config file. It demands:
- Identified configuration items — you know what’s in scope (servers, containers, network devices, cloud resources)
- Baseline configuration — you have a documented reference state
- Change control — changes are approved, tested, and applied
- Verification — you check that the running state matches the baseline
- Access control — you control who can change configurations
The fourth item (verification) is where most programs fail. They have baselines. They have change processes. They just don’t check if the server actually matches.
Trade-offs
| Approach | Pros | Cons |
|---|---|---|
| Golden images | Simple, repeatable | Manual fixes drift, hard to track what’s different |
| Config repo | Versioned, diffable | Push-only, live state can diverge |
| Control loop | Continuous convergence | Requires tooling, operational overhead |
The control loop pattern requires the most setup but pays for itself within a few weeks by catching drift before it causes incidents. The others are fine for small fleets (under 20 systems) where a weekly manual check is feasible.
The One Thing Nobody Does
Rebuild servers from the desired state on a schedule. If you can’t rebuild a server in under an hour and have it behave identically to the original, your config management is documentation, not management. Period.
This is the hardest habit to build because it means tearing things down. But a server that can be rebuilt from config alone is a server that has no secrets, no manual fixes, no accumulated drift. That’s what A.8.9 is actually asking for — you just need to read between the three sentences to see it.