Illustration: Idempotency is the whole idea, and most playbooks do not have it The point of configuration management is not that it runs commands on many machines. The point is that you can run it repeatedly and the result converges on what you declared, whatever state the machine started in.

That property is idempotency, and a playbook full of shell commands does not have it. It is a script with better inventory handling.

Why it matters practically

You can run it against everything, routinely. A playbook that is safe to re-run can go on a schedule. Drift gets corrected instead of accumulating.

Partial failures are recoverable. If a run dies halfway through on twenty hosts, you fix the cause and run it again. With a non-idempotent playbook, re-running may do the first half twice, and what that does is anybody’s guess.

The diff becomes meaningful. A run that reports no changes is telling you the estate matches the declaration. That single line of output is the most valuable thing the tool produces, and it only means something if the tasks genuinely detect state.

Where it breaks

The shell module. Any task that runs a raw command is opaque: the tool cannot know whether it needed to run, so it runs. Every time. Reported as changed, every time.

Sometimes there is no module and you have to. When that happens, guard it: a creates argument, a when condition based on a gathered fact, or a preceding check task whose result gates the command. It is two extra lines and it converts a script into configuration.

Appending to files. A task that adds a line to a configuration file adds it again on the next run. Use the modules that manage a block or a specific line by pattern, or better, template the whole file.

Templates are the honest answer. Managing a file by patching bits of it is fragile. Owning the whole file from a template means the file’s content is fully described in your repository, and the diff is complete.

The objection is that you have to template everything, including the parts you do not care about. That is the cost, and it is worth paying for any file that matters.

Commands with side effects in check mode. If your playbook cannot run in check mode without doing anything, you have lost the ability to preview changes, which is the feature you will want most on the day you are nervous.

The test

Run the playbook. Run it again immediately. The second run should report zero changed.

If it does not, find out which task changed and why. Nine times out of ten it is a shell command that should be guarded or a file being appended to.

This takes five minutes and it is the single most useful quality check available. I would make it a rule: no playbook merges until the second run is clean.

Handlers and the restart question

Restarting a service on every run is not idempotent in the sense that matters, even if the configuration ends up identical, because you caused an interruption.

Handlers exist for this: notify a restart only when a task actually changed something. Use them properly and a run against a healthy estate touches nothing and restarts nothing.

The mental shift

Stop thinking about what commands to run. Think about what state to declare, and let the tool work out whether anything needs doing.

That shift is the whole difference between automation that people trust enough to schedule and automation that somebody runs carefully by hand while watching, which is most of the automation I see in the field.