Modules & idempotency

The unit of work that runs safely twice.

Beginner12 min · lesson 3 of 12

A module is Ansible’s unit of work — a small program that does one thing well: apt/yum/dnf install packages, copy and template manage files, service and systemd manage services, user manages accounts, and hundreds more. You do not write shell; you declare the desired state to a module and it figures out the commands. This is what makes Ansible readable and, crucially, idempotent.

terminal
# a module declares desired state; Ansible reports whether it changed anything
$ ansible web -i inventory.ini -m apt -a "name=nginx state=present" --become
web1 | CHANGED => { "changed": true } # nginx was installed
$ ansible web -i inventory.ini -m apt -a "name=nginx state=present" --become
web1 | SUCCESS => { "changed": false } # already present — nothing done

Idempotency is the whole point

Notice the second run says changed: false. A good module checks the current state and acts only if reality differs from what you declared, so running the same automation repeatedly is safe — it converges rather than piling up duplicate changes. This is the core promise of configuration management: describe the end state, run as often as you like, and the system settles there. It is also why you avoid the raw command/shell modules when a real module exists.

command and shell break idempotency
The command and shell modules just run whatever you give them, every time, with no notion of state — so they always report changed and can redo work or cause harm on re-run. Prefer a purpose-built module (which is idempotent); when you must use shell, guard it with creates=/removes= or a when: condition so it does not fire needlessly. Reaching for shell is usually a sign a proper module exists.