Concept and mechanism
A playbook organizes plays and tasks; each task calls an action with arguments. Many modules inspect current state before changing a target, allowing repeated execution without new changes when state already matches the request. That does not make every arbitrary command idempotent. A script appending a line on every run still does so when changed_when is false. That option controls reporting and can influence handlers; it does not erase process effects. Choose a suitable module when it can represent desired state and reserve commands for requirements that module does not cover.
Guided application
In a fictional example, a configuration script reports changed on every run. Investigate whether it actually changes the file, only measures state, or returns a special code. Define changed_when and failed_when from that contract without hiding errors. ansible.builtin.command does not interpret shell pipes or redirection; argv helps separate arguments. Expansion of some variables by the module does not turn it into a shell. If shell is needed, control inputs and quoting. For reuse, a role organizes tasks, templates, and handlers. Place values consumers should override in defaults, document inputs, and use fully qualified module names to avoid collection collisions.
A read-only command can use changed_when=false; a modifying script needs coherent behavior and evidence.
Common pitfalls
Changed as health; false reporting as no effects; shell for every command; vars as defaults.
Related topics: Inventory and execution context · Validation and handlers · Batch orchestration
Make reporting match the action’s actual contract.
Reference: Ansible playbooks · Ansible community 14 / ansible-core 2.21 documentation consulted 2026-09-30; executor, collection and plugin compatibility requires confirmation