ansible/ansible
Task and block
A task is the leaf node of the playbook tree: an action plus its arguments and modifiers. A block is a grouping of tasks that share error-handling, conditionals, and other policies.
Files
| File | Class | Lines |
|---|---|---|
lib/ansible/playbook/task.py |
Task |
619 |
lib/ansible/playbook/block.py |
Block |
411 |
lib/ansible/playbook/task_include.py |
TaskInclude |
152 |
lib/ansible/playbook/conditional.py |
Conditional (mixin) |
35 |
lib/ansible/playbook/loop_control.py |
LoopControl |
70 |
lib/ansible/playbook/handler.py |
Handler |
75 |
lib/ansible/playbook/handler_task_include.py |
HandlerTaskInclude |
35 |
Task
A Task represents a single YAML dict like:
- name: install nginx
ansible.builtin.package:
name: nginx
state: present
when: ansible_distribution == 'Ubuntu'
become: true
tags:
- installThe Task class extends Base plus the mixins Conditional, Taggable, CollectionSearch, Notifiable, Delegatable. Each YAML key maps to a FieldAttribute:
class Task(Base, Conditional, Taggable, CollectionSearch, Notifiable, Delegatable):
name = FieldAttribute(isa='string', default='')
action = FieldAttribute(isa='string')
args = FieldAttribute(isa='dict', default=dict)
register = FieldAttribute(isa='string')
loop = FieldAttribute(isa='list')
loop_control = FieldAttribute(class_type=LoopControl, default=LoopControl)
until = FieldAttribute(isa='list')
retries = FieldAttribute(isa='int', default=3)
delay = FieldAttribute(isa='int', default=5)
failed_when = FieldAttribute(isa='list', default=list)
changed_when = FieldAttribute(isa='list', default=list)
notify = FieldAttribute(isa='list')
when = FieldAttribute(isa='list', default=list)
tags = FieldAttribute(isa='list', default=list)
no_log = FieldAttribute(isa='bool')
poll = FieldAttribute(isa='int', default=...)
async_val = FieldAttribute(isa='int', default=0)
timeout = FieldAttribute(isa='int', default=...)
delegate_to = FieldAttribute(isa='string')
delegate_facts = FieldAttribute(isa='bool', default=False)
run_once = FieldAttribute(isa='bool', default=False)
ignore_errors = FieldAttribute(isa='bool', default=False)
ignore_unreachable = FieldAttribute(isa='bool', default=False)
any_errors_fatal = FieldAttribute(isa='bool', default=False)
check_mode = FieldAttribute(isa='bool')
diff = FieldAttribute(isa='bool')
debugger = FieldAttribute(isa='string')
throttle = FieldAttribute(isa='int', default=0)Plus become, become_user, become_method, connection, port, remote_user, vars from inherited mixins and Base.
Action resolution
When a task is loaded, lib/ansible/parsing/mod_args.py:ModuleArgsParser figures out which YAML key represents the action call:
# Style 1: explicit module name as key
- ansible.builtin.copy:
src: foo
dest: /tmp/foo
# Style 2: action keyword
- action: ansible.builtin.copy src=foo dest=/tmp/foo
# Style 3: name + module name as key (most common)
- name: copy the file
ansible.builtin.copy:
src: foo
dest: /tmp/fooModuleArgsParser.parse() returns (action, args, delegate_to). The action is then resolved through the plugin loader at execution time.
Templating gates
Some attributes template at parse time, others at execution time, and a few are special. Conditionals (when/failed_when/changed_when) template specially: they're treated as bare Jinja expressions (not {{ }}-wrapped strings) and coerced to bool. The Conditional mixin handles this.
vars: are templated late, after the host's variable scope is built — otherwise you couldn't reference inventory vars from a task.
Block
A Block is a YAML element that groups tasks:
- block:
- command: risky-thing
- command: other-risky-thing
rescue:
- debug: msg=Something failed; cleaning up
always:
- command: cleanup
when: enable_risky | default(false)
become: true
tags:
- riskyImplementation in lib/ansible/playbook/block.py. A block has three task lists:
block:— the main tasks. Run unconditionally (subject towhen).rescue:— runs if any block task fails. Clears the failure for the host.always:— runs afterblock/rescue, regardless of failure.
Block-level keywords (become, tags, when, etc.) propagate to every task inside the block via attribute inheritance — the play iterator merges them when materializing each task.
Blocks can nest. The data model and the iterator handle arbitrary nesting depth, though most playbooks stop at 1-2 levels.
Block as universal grouping
Internally, every task list in a play is wrapped in a Block. play.tasks isn't a list[Task]; it's a list[Block]. A bare task at YAML level becomes an implicit single-task block. This unifies the iterator's view: it always walks blocks.
Block._get_parent_attribute() is what makes "play-level become: true propagates to every task in the play" work — a task's effective become is the first non-default value walking up parent → block → play.
TaskInclude
include_tasks (and the legacy include) are dynamic includes — resolved at task execution time. TaskInclude extends Task because it has the same modifiers (when, tags, vars). At iteration time, the strategy plugin sees a TaskInclude, asks IncludedFile to load the file, expands the result into a list of Blocks, and splices them into the per-host iterator state.
import_tasks is different: it's static, resolved at parse time by lib/ansible/playbook/helpers.py:load_list_of_blocks. The included file is parsed and its blocks are inserted into the parent play's task list before execution begins. when: on an import_tasks propagates to every imported task; on include_tasks the include itself is conditional but the imported tasks aren't pre-conditioned.
Loop and loop_control
loop: is the modern syntax for iterating a task. The value is a list (or a templating expression that resolves to a list); the task runs once per item.
- name: ensure users
user:
name: '{{ item.name }}'
state: present
loop:
- { name: alice }
- { name: bob }
loop_control:
label: '{{ item.name }}'
pause: 1loop_control (lib/ansible/playbook/loop_control.py:LoopControl) is a small object with:
loop_var— name of the variable holding each item (defaultitem).index_var— name of the variable holding the index.label— what to display in callback output (lets you avoid leaking sensitive item content).pause— sleep N seconds between iterations.extended— set extraansible_loop_*vars per iteration.
The legacy with_<lookup> keywords still work — they're translated by ModuleArgsParser into a loop plus the appropriate lookup.
Handler
A handler is a task that runs only when notified. lib/ansible/playbook/handler.py:Handler is a Task subclass with a listen: field added. Handlers live in a separate task list (play.handlers) and are triggered by notify: on regular tasks.
tasks:
- name: install nginx
package:
name: nginx
notify:
- reload nginx
handlers:
- name: reload nginx
service:
name: nginx
state: reloadedHandler ordering is definition order, not notification order. If you notify: [a, b] and the handler list is [b, a], b runs first.
listen: lets multiple handlers respond to a single name:
handlers:
- name: reload nginx
listen: web-config-changed
service: name=nginx state=reloaded
- name: reload haproxy
listen: web-config-changed
service: name=haproxy state=reloadedA notify: web-config-changed triggers both. Implementation in lib/ansible/playbook/handler.py and lib/ansible/plugins/strategy/__init__.py.
Conditional
The Conditional mixin (lib/ansible/playbook/conditional.py) provides evaluate_conditional(templar, all_vars). It walks the when list (each entry is a Jinja expression) and ANDs them all together. Returns a bool.
failed_when and changed_when use the same mechanism but evaluate against the task result.
Integration points
- Imported by:
lib/ansible/playbook/play.py, the executor, the strategy plugins. - Imports:
lib/ansible/parsing/mod_args.py(action parser),lib/ansible/_internal/_templating/_engine.py(conditionals),lib/ansible/plugins/loader.py(action resolution).
Entry points for modification
- A new task keyword — declare a
FieldAttributeonTask. Updatelib/ansible/keyword_desc.yml. - A new block-level keyword — same on
Block. Make sure inheritance through_get_parent_attributeworks. - A new include semantics — be ready for a deep dive. The static/dynamic distinction was hard-fought; new include keywords are unlikely to be accepted.
Cross-links
- Playbook and play — the parent containers.
- Role and handler — for role-level tasks and handler details.
- Executor — how tasks/blocks are walked.
- Templating — how conditionals and templated args render.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.