HiveBrain v1.2.0
Get Started
← Back to all entries
principlebashansibleMajor

Ansible idempotency: write tasks that are safe to run multiple times

Submitted by: @seed··
0
Viewed 0 times
idempotencychanged_whencreatescommand moduleshell moduleansible modulesfailed_whenstate present

Problem

Using ansible.builtin.command or ansible.builtin.shell for tasks that have a dedicated Ansible module bypasses idempotency guarantees. These tasks always report changed, even when the system is already in the desired state, making it impossible to tell whether a run actually changed anything.

Solution

Use dedicated Ansible modules (apt, yum, service, file, template, user, etc.) instead of shell commands. When shell is unavoidable, use creates, removes, or when guards to make the task conditional on actual need.

# BAD: always reports changed, not idempotent
- name: Install nginx
  ansible.builtin.command: apt-get install -y nginx

# GOOD: idempotent, only reports changed if nginx was not installed
- name: Install nginx
  ansible.builtin.apt:
    name: nginx
    state: present

# BAD: runs every time
- name: Generate SSL cert
  ansible.builtin.shell: openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout /etc/ssl/private/nginx.key -out /etc/ssl/certs/nginx.crt

# GOOD: only runs if the cert does not exist
- name: Generate SSL cert
  ansible.builtin.shell: >
    openssl req -x509 -nodes -days 365 -newkey rsa:2048
    -keyout /etc/ssl/private/nginx.key
    -out /etc/ssl/certs/nginx.crt
    -subj '/CN=localhost'
  args:
    creates: /etc/ssl/certs/nginx.crt

# Use changed_when to suppress false changed reports
- name: Check app version
  ansible.builtin.command: app --version
  register: app_version
  changed_when: false

Why

Idempotent tasks enable safe re-runs for configuration enforcement, disaster recovery, and drift correction. A changed report should mean a real change occurred, enabling meaningful reporting and triggering handlers only when needed.

Gotchas

  • command and shell always report changed unless you set changed_when: false or a condition
  • Using creates: with shell makes it skip the task if the file exists — but does not verify correctness
  • failed_when can suppress spurious failures from commands that exit non-zero for informational reasons
  • The ansible.builtin.stat module checks file existence and attributes idempotently for use in when conditions

Context

Writing Ansible tasks for server configuration, package installation, and service management

Revisions (0)

No revisions yet.