Template an Idempotent Ansible Role
Write an Ansible role that converges to the same state on repeated runs, using declarative modules instead of shell tasks.
Task
Convert imperative shell steps into idempotent Ansible modules so reruns cause no changes.
When to use this
Re-running a playbook reports changes every time, making it unsafe to run in production.
Prerequisites
- Ansible installed
- An inventory to test against
- SSH access
Steps
1. Replace shell with a declarative module and handler.
- name: Configure service
template:
src: service.conf.j2
dest: /etc/app/service.conf
mode: '0644'
notify: restart appExpected output: Files are only rewritten when content differs, and the handler fires only on change.
2. Re-run the playbook and expect no changes.
ansible-playbook -i inventory site.yml
ansible-playbook -i inventory site.yml | tail -5Expected output: The second run reports changed=0, proving idempotency.
Troubleshooting
Problem
Handlers not firing
Solution
Ensure the task actually reports changed; handlers only run on change.
Problem
Command tasks always changed
Solution
Add creates or changed_when conditions to command and shell tasks.
Limitations
- Some operations need command and cannot be fully idempotent.
- Idempotency does not imply correctness of the desired state.
- Does not cover secret management.
What this recipe does
Convert imperative shell steps into idempotent Ansible modules so reruns cause no changes.
Re-running a playbook reports changes every time, making it unsafe to run in production.
Prerequisites
- Ansible installed
- An inventory to test against
- SSH access
Procedure
- Replace shell with a declarative module and handler.
- name: Configure service
template:
src: service.conf.j2
dest: /etc/app/service.conf
mode: '0644'
notify: restart app
Expected result: Files are only rewritten when content differs, and the handler fires only on change.
- Re-run the playbook and expect no changes.
ansible-playbook -i inventory site.yml
ansible-playbook -i inventory site.yml | tail -5
Expected result: The second run reports changed=0, proving idempotency.
How to confirm it worked
Run the commands above and confirm every expected result in order. If a step does not produce the stated output, use the troubleshooting notes before continuing. A passing run shows a second run reports zero changes and the configured service restarts only when its config actually changed.
Troubleshooting
Handlers not firing — Ensure the task actually reports changed; handlers only run on change.
Command tasks always changed — Add creates or changed_when conditions to command and shell tasks.
Limitations
- Some operations need command and cannot be fully idempotent.
- Idempotency does not imply correctness of the desired state.
- Does not cover secret management.