AnsibleVIII · Modules and the Module ModelThe module model
Check-mode support is a per-module property
What you'll learn
- Look up a module check-mode support level and interpret full, partial, none and N/A
- Read a check-mode recap and identify which tasks were evaluated and which were skipped
- Explain why a command task with no creates guard is skipped rather than predicted
- Assess before a change window whether a check run will prove anything
Prerequisites
Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11
--check is the closest thing Ansible has to a dry run, and it is not a
property of Ansible. It is a property of each module, declared
individually, and the modules that support it worst are the ones doing
the work you most want to preview.
The consequence is specific and it is the point of this lesson: a play can produce a completely green check run in which half the tasks were never evaluated. Nothing in the output distinguishes “predicted no change” from “did not look”.
Four tasks, one check run
Here is a play with one task of each support level:
# checkmode.yml
- name: What check mode actually evaluates
hosts: localhost
gather_facts: false
tasks:
- name: A file task - check_mode support is full
ansible.builtin.file:
path: /etc/hostname
state: file
- name: A command with no creates - check_mode support is partial
ansible.builtin.command: /bin/true
- name: A command with creates - the guard makes check mode meaningful
ansible.builtin.command: /bin/true
args:
creates: /etc/hostname
- name: A raw task - check_mode support is none
ansible.builtin.raw: /bin/true
$ ansible-playbook checkmode.yml --checkPLAY [What check mode actually evaluates] **************************************
TASK [A file task - check_mode support is full] ********************************
ok: [localhost]
TASK [A command with no creates - check_mode support is partial] ***************
skipping: [localhost]
TASK [A command with creates - the guard makes check mode meaningful] **********
ok: [localhost]
TASK [A raw task - check_mode support is none] *********************************
skipping: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0ok=2 changed=0 failed=0. On a dashboard, in a change request, or read
quickly at the end of a long day, that is a clean run.
It is a clean run in which half the play was not evaluated. The
skipped=2 is the entire signal, and it is the column nobody reads.
The support levels, precisely
| Level | Behaviour in --check | Examples |
|---|---|---|
full | The module predicts and reports changed accurately without writing. | file, copy, template, lineinfile, user, cron, apt, systemd_service |
partial | Works only under a stated condition. Read the details line. | command, shell, script, get_url, unarchive |
none | The task is skipped. Nothing is evaluated. | raw, uri, expect, tempfile, wait_for |
N/A | The module dispatches; ask the module it resolves to. | package, service |
In ansible-core 2.21 that is 52 modules at full, 8 at partial, 8 at
none and 2 at N/A, out of 70 documented.
partial for command and shell means one specific thing
The details line in the attributes table states it exactly:
while the command itself is arbitrary and cannot be subject to the check mode semantics it adds
creates/removesoptions as a workaround
And the module notes are more explicit still:
Check mode is supported when passing
createsorremoves. If running in check mode and either of these are specified, the module will check for the existence of the file and report the correct changed status. If these are not supplied, the task will be skipped.
So partial here is binary, not partial: with a guard, check mode works;
without one, the task is skipped entirely. That is exactly what the two
command tasks in the play above demonstrated — same module, same
command, one ok and one skipping.
$ ansible-doc -j ansible.builtin.raw | jq -r '.[].doc.attributes.check_mode.support'noneraw has no check mode at all
raw bypasses the module subsystem entirely — it pushes a command down
the connection with no Python and no JSON contract. There is nothing that
could implement a prediction, so --check skips it silently.
This matters most in the one place raw is legitimately used:
bootstrapping. A first play that installs Python with raw produces no
check-mode information whatsoever, which is fine as long as you know it
rather than discovering it.
Assessing a play before the change window
The question is not “does Ansible support check mode”. It is “for this
play, on this fleet, what fraction of the tasks will --check actually
evaluate?”
$ ansible-playbook --list-tasks site.yml$ for m in package template service command copy; do printf '%-10s %s\n' "$m" "$(ansible-doc -j ansible.builtin.$m | jq -r '.[].doc.attributes.check_mode.support')"; donepackage N/A
template full
service N/A
command partialIllustrative output
Then read the check run against that list. If the play has four command
tasks and the recap says skipped=4, the check told you nothing about
the four tasks that actually do the work.
Knowledge check
Knowledge check · 4 questions
Q1. A --check run of a four-task play reports ok=2 changed=0 failed=0 skipped=2. What is the correct reading?
Q2. Which of these will cause a task to be skipped during a --check run? Select all that apply.
Q3. A module declaring check_mode: full is guaranteed by the Ansible toolchain not to modify the target during a check run.
Q4. A read-only probe registers a version string that a later when: depends on. During a check run the whole play fails after the probe. What is the appropriate fix?
Passing score: 75%. Answers are checked in this browser.