Krikri::Task
Represents a single task in a playbook
Constructors
Instance methods
The namespace.collection this task's own role belongs to -
exposed as the ansible_collection_name magic var, only ever set
for a role invoked via its full namespace.collection.role FQCN
(a collection-shipped role, as opposed to a plain Galaxy role
install). Several real collections use it to strip their own
namespace prefix back off ansible_parent_role_names (prometheus.
prometheus._common's own regex_replace(ansible_collection_name ~ '.', ''), computing a short service/tag name from the invoking
role's FQCN).
The namespace.collection this task's own role belongs to -
exposed as the ansible_collection_name magic var, only ever set
for a role invoked via its full namespace.collection.role FQCN
(a collection-shipped role, as opposed to a plain Galaxy role
install). Several real collections use it to strip their own
namespace prefix back off ansible_parent_role_names (prometheus.
prometheus._common's own regex_replace(ansible_collection_name ~ '.', ''), computing a short service/tag name from the invoking
role's FQCN).
async: / poll: - run the module in the background (as a detached OS process, not a Fiber, so it outlives the poll loop) up to async: seconds, checking every poll: seconds (default 10, matching real Ansible) until it finishes or the async: timeout elapses; poll: 0 returns immediately with the job id instead of waiting at all. Local connections only - see TaskExecutor#execute_async.
async: / poll: - run the module in the background (as a detached OS process, not a Fiber, so it outlives the poll loop) up to async: seconds, checking every poll: seconds (default 10, matching real Ansible) until it finishes or the async: timeout elapses; poll: 0 returns immediately with the job id instead of waiting at all. Local connections only - see TaskExecutor#execute_async.
Raw {{ ... }} text when become: is a templated expression rather
than a literal boolean (ansible-community.ansible-vault's own
become: "{{ vault_privileged_install }}", defaulting false).
become above still holds a best-effort parse-time guess (used as
a fallback if this can't be rendered for some reason), but the
executor re-renders and overrides it at execution time, once real
host/role vars are available - parse time never has that context.
Raw {{ ... }} text when become: is a templated expression rather
than a literal boolean (ansible-community.ansible-vault's own
become: "{{ vault_privileged_install }}", defaulting false).
become above still holds a best-effort parse-time guess (used as
a fallback if this can't be rendered for some reason), but the
executor re-renders and overrides it at execution time, once real
host/role vars are available - parse time never has that context.
Raw (unrendered) name: of every enclosing block:/rescue:/always:
wrapper, outermost first - nil for a task with no enclosing block.
Real ansible-core 2.19 templates a BLOCK's name keyword strictly
when one of its children actually goes to run, failing that child
with "Task failed: Error processing keyword 'name': 'X' is
undefined" (ikke_t.podman_container_systemd round 813203: the block
named do tasks when "{{ service_name }}" state is "running"
hard-failed real Ansible when the role ran without grafana_podman's
container_name above it - this engine rendered the name leniently
and kept executing). A TASK's own name stays lenient there (real
Ansible banners it as "<< error 1 - 'nope' is undefined >>" and
still runs/skips it normally), so only the enclosing-chain names
are tracked.
Raw (unrendered) name: of every enclosing block:/rescue:/always:
wrapper, outermost first - nil for a task with no enclosing block.
Real ansible-core 2.19 templates a BLOCK's name keyword strictly
when one of its children actually goes to run, failing that child
with "Task failed: Error processing keyword 'name': 'X' is
undefined" (ikke_t.podman_container_systemd round 813203: the block
named do tasks when "{{ service_name }}" state is "running"
hard-failed real Ansible when the role ran without grafana_podman's
container_name above it - this engine rendered the name leniently
and kept executing). A TASK's own name stays lenient there (real
Ansible banners it as "<< error 1 - 'nope' is undefined >>" and
still runs/skips it normally), so only the enclosing-chain names
are tracked.
block: / rescue: / always: - only set when module_name == "_block" (a pseudo-module marking this Task as a block rather than a plugin invocation). Blocks can nest, since these are themselves Task lists.
block: / rescue: / always: - only set when module_name == "_block" (a pseudo-module marking this Task as a block rather than a plugin invocation). Blocks can nest, since these are themselves Task lists.
changed_when: / failed_when: - override the module's own changed/failed verdict with a condition evaluated against the task's result (accessible via its own register: name, same as any other post-task condition; a bare literal like "false" needs no register: at all). Same string/eval pipeline as when_condition/until_condition: substituted for {{ }} expressions, then handed to ConditionalEvaluator.
changed_when: / failed_when: - override the module's own changed/failed verdict with a condition evaluated against the task's result (accessible via its own register: name, same as any other post-task condition; a bare literal like "false" needs no register: at all). Same string/eval pipeline as when_condition/until_condition: substituted for {{ }} expressions, then handed to ConditionalEvaluator.
Raw {{ ... }} text when check_mode: is a templated expression
rather than a literal boolean - same deferred-evaluation shape
become_expr uses, and evaluated in TaskExecutor#
resolve_task_check_mode against live vars.
Raw {{ ... }} text when check_mode: is a templated expression
rather than a literal boolean - same deferred-evaluation shape
become_expr uses, and evaluated in TaskExecutor#
resolve_task_check_mode against live vars.
connection: - overrides the connection plugin for just this task (almost always "local"), independent of delegate_to: - it changes HOW the task's module runs (locally on the controller vs. over SSH), not WHICH host's vars/facts/register apply (that's still delegate_to:'s job). robertdebock.backup's own "Create backup_ directory" (writes to the controller's filesystem while still being attributed to the current host's own inventory_hostname) uses exactly this combination: connection: local, no delegate_to:.
connection: - overrides the connection plugin for just this task (almost always "local"), independent of delegate_to: - it changes HOW the task's module runs (locally on the controller vs. over SSH), not WHICH host's vars/facts/register apply (that's still delegate_to:'s job). robertdebock.backup's own "Create backup_ directory" (writes to the controller's filesystem while still being attributed to the current host's own inventory_hostname) uses exactly this combination: connection: local, no delegate_to:.
delegate_facts: - when true alongside delegate_to:, a module's returned ansible_facts (set_fact:, fact-gathering modules) attach to the delegate_to: target's own hostvars instead of the delegating host's - real Ansible's own documented meaning ("apply facts to a delegated host instead of the inventory_hostname"). register: is unaffected either way (always attaches to the delegating host).
delegate_facts: - when true alongside delegate_to:, a module's returned ansible_facts (set_fact:, fact-gathering modules) attach to the delegate_to: target's own hostvars instead of the delegating host's - real Ansible's own documented meaning ("apply facts to a delegated host instead of the inventory_hostname"). register: is unaffected either way (always attaches to the delegating host).
delegate_to: - run this task's actual module/connection against a different host than the one the play is iterating (e.g. "localhost"), while variables/facts/register/stats still belong to the original host. May be templated ({{ vars }}), so kept as a raw string and resolved at execution time (same reasoning as include_file).
delegate_to: - run this task's actual module/connection against a different host than the one the play is iterating (e.g. "localhost"), while variables/facts/register/stats still belong to the original host. May be templated ({{ vars }}), so kept as a raw string and resolved at execution time (same reasoning as include_file).
environment: - per-task env vars (real Ansible keyword). Raw,
unsubstituted string values, same convention as params - the
executor substitutes them at run time and forwards the result to
the plugin, which applies them around its own shelled-out commands.
environment: - per-task env vars (real Ansible keyword). Raw,
unsubstituted string values, same convention as params - the
executor substitutes them at run time and forwards the result to
the plugin, which applies them around its own shelled-out commands.
Raw {{ ... }} string form of environment: (environment: "{{ proxy_env }}", ryandaniels.server_update_reboot's own apt/yum
tasks). Real Ansible accepts a single templated value here and
evaluates it to the env-var dict at task finalization - failing the
task when the referenced variable is undefined ("Error processing
keyword 'environment': 'proxy_env' is undefined"). The parser has
no vars context to resolve it against, so the raw text is stashed
here and the executor substitutes it strictly at run time.
Raw {{ ... }} string form of environment: (environment: "{{ proxy_env }}", ryandaniels.server_update_reboot's own apt/yum
tasks). Real Ansible accepts a single templated value here and
evaluates it to the env-var dict at task finalization - failing the
task when the referenced variable is undefined ("Error processing
keyword 'environment': 'proxy_env' is undefined"). The parser has
no vars context to resolve it against, so the raw text is stashed
here and the executor substitutes it strictly at run time.
Raw {{ ... }} text when ignore_errors: is a templated expression
rather than a literal boolean (dj-wasabi.telegraf's own
ignore_errors: "{{ ansible_check_mode }}", whose parse-time
fallback guess below is wrong on real runs - see
TaskExecutor#resolve_task_ignore_errors, which re-renders this
against live vars (ansible_check_mode is bound there) and
overrides the guess, the same deferred-evaluation shape
check_mode_expr/become_expr use).
Raw {{ ... }} text when ignore_errors: is a templated expression
rather than a literal boolean (dj-wasabi.telegraf's own
ignore_errors: "{{ ansible_check_mode }}", whose parse-time
fallback guess below is wrong on real runs - see
TaskExecutor#resolve_task_ignore_errors, which re-renders this
against live vars (ansible_check_mode is bound there) and
overrides the guess, the same deferred-evaluation shape
check_mode_expr/become_expr use).
ignore_unreachable: true - an unreachable host does not fail the
play at this task; it is reported, counted as ignored, and the host
carries on to the next task (which may itself be unreachable).
ignore_unreachable: true - an unreachable host does not fail the
play at this task; it is reported, counted as ignored, and the host
carries on to the next task (which may itself be unreachable).
include_tasks: - only set when module_name == "_include_tasks". Unlike import_tasks (resolved at parse time), the file path may be templated ({{ vars }}) and isn't resolved until this task actually runs, so both the raw path and the directory to resolve it against (wherever the include_tasks: line itself lives) are carried on the Task for the executor to use at run time.
include_tasks: - only set when module_name == "_include_tasks". Unlike import_tasks (resolved at parse time), the file path may be templated ({{ vars }}) and isn't resolved until this task actually runs, so both the raw path and the directory to resolve it against (wherever the include_tasks: line itself lives) are carried on the Task for the executor to use at run time.
include_role: - only set when module_name == "_include_role". The dynamic counterpart to a roles: list entry: resolved at execution time (role name may be templated), via RoleLoader, same as roles:.
include_role: - only set when module_name == "_include_role". The dynamic counterpart to a roles: list entry: resolved at execution time (role name may be templated), via RoleLoader, same as roles:.
tasks_from: - loads tasks/<name>.yml instead of tasks/main.yml. Common in collection-shipped "shared logic" roles (e.g. prometheus.prometheus's own _common role, invoked repeatedly by every exporter role with a different tasks_from: per call - one role directory, several distinct task-file entry points).
tasks_from: - loads tasks/<name>.yml instead of tasks/main.yml. Common in collection-shipped "shared logic" roles (e.g. prometheus.prometheus's own _common role, invoked repeatedly by every exporter role with a different tasks_from: per call - one role directory, several distinct task-file entry points).
vars: on an include_tasks: statement - visible to every task in the included file (unlike import_tasks:'s vars:, which is merged directly into each imported task at parse time, this has to be carried on the Task and propagated at execution time, same as loop:'s item).
vars: on an include_tasks: statement - visible to every task in the included file (unlike import_tasks:'s vars:, which is merged directly into each imported task at parse time, this has to be carried on the Task and propagated at execution time, same as loop:'s item).
dir:-mode parameters (real Ansible's include_vars directory form,
lib/ansible/plugins/action/include_vars.py) - include_vars_dir is
the (possibly templated) directory to load every vars file from;
include_vars_depth is the raw depth: string (templated, resolved
at run time - real Ansible's depth: 0 default means UNLIMITED
recursion, depth: 1 means top-level files only); files_matching/
ignore_files/extensions/ignore_unknown_extensions mirror the
module's own options. The executor's execute_include_vars_dir
owns the details.
dir:-mode parameters (real Ansible's include_vars directory form,
lib/ansible/plugins/action/include_vars.py) - include_vars_dir is
the (possibly templated) directory to load every vars file from;
include_vars_depth is the raw depth: string (templated, resolved
at run time - real Ansible's depth: 0 default means UNLIMITED
recursion, depth: 1 means top-level files only); files_matching/
ignore_files/extensions/ignore_unknown_extensions mirror the
module's own options. The executor's execute_include_vars_dir
owns the details.
include_vars: - only set when module_name == "_include_vars".
include_vars_file is the file to load (may be templated, so it is
resolved at run time); include_vars_name is the optional name:
parameter, which loads the file into a single dict variable of that
name instead of merging its keys into the context.
include_vars: - only set when module_name == "_include_vars".
include_vars_file is the file to load (may be templated, so it is
resolved at run time); include_vars_name is the optional name:
parameter, which loads the file into a single dict variable of that
name instead of merging its keys into the context.
loop_control.index_var - exposes the current loop iteration's
zero-based index under this variable name (real Ansible's own
loop_control: { index_var: idx }, commonly paired with a
register:ed loop result so some_registered.results[idx] can be
looked up against the SAME item currently being processed - e.g.
results[index].stat.exists as a when: guard skipping re-work
already verified by an earlier per-item stat: loop). Previously
entirely unimplemented - parsed nowhere, injected into vars_context
nowhere - so {{ index }} (or whatever name was configured) always
resolved to "undefined" throughout the loop body, silently breaking
any downstream results[index] lookup. Found via robertdebock.
mount's own "Create mountpoint" task.
loop_control.index_var - exposes the current loop iteration's
zero-based index under this variable name (real Ansible's own
loop_control: { index_var: idx }, commonly paired with a
register:ed loop result so some_registered.results[idx] can be
looked up against the SAME item currently being processed - e.g.
results[index].stat.exists as a when: guard skipping re-work
already verified by an earlier per-item stat: loop). Previously
entirely unimplemented - parsed nowhere, injected into vars_context
nowhere - so {{ index }} (or whatever name was configured) always
resolved to "undefined" throughout the loop body, silently breaking
any downstream results[index] lookup. Found via robertdebock.
mount's own "Create mountpoint" task.
True when this _include_role task actually came from import_role:
(statically resolved), not include_role: (dynamic). Both currently
share the same runtime inclusion machinery (see parse_include_role's
own comment on that pragmatic approximation), but real Ansible's
import_role: produces NO task result of its own at all - no "TASK
[...]" banner, no ok/skipped recap increment - since it's a true
parse-time splice; only include_role: (genuinely dynamic) does.
Used by the executor to suppress the wrapper's own display/counting
for the import_role: case while still running the included role's
own tasks (each gets its own normal banner) exactly the same way
either directive reaches them. Found via round171's robertdebock.
revealmd (import_role: name: robertdebock.service): real Ansible's
recap was ok=17, crystal's was ok=18 - an extra "Create revealmd
service" TASK banner + ok that real Ansible never shows at all.
True when this _include_role task actually came from import_role:
(statically resolved), not include_role: (dynamic). Both currently
share the same runtime inclusion machinery (see parse_include_role's
own comment on that pragmatic approximation), but real Ansible's
import_role: produces NO task result of its own at all - no "TASK
[...]" banner, no ok/skipped recap increment - since it's a true
parse-time splice; only include_role: (genuinely dynamic) does.
Used by the executor to suppress the wrapper's own display/counting
for the import_role: case while still running the included role's
own tasks (each gets its own normal banner) exactly the same way
either directive reaches them. Found via round171's robertdebock.
revealmd (import_role: name: robertdebock.service): real Ansible's
recap was ok=17, crystal's was ok=18 - an extra "Create revealmd
service" TASK banner + ok that real Ansible never shows at all.
Real Ansible's handler listen: accepts a single topic string OR a
list of topics (CVi.thanos's own handlers/main.yml listens on three
at once, round 811339) - same single-string-or-list shape as
notify: above, parsed identically. nil when absent.
Real Ansible's handler listen: accepts a single topic string OR a
list of topics (CVi.thanos's own handlers/main.yml listens on three
at once, round 811339) - same single-string-or-list shape as
notify: above, parsed identically. nil when absent.
loop_control.extended - exposes the ansible_loop dict (index,
first/last, allitems, nextitem/previtem, ...) for the iteration.
loop_control.extended - exposes the ansible_loop dict (index,
first/last, allitems, nextitem/previtem, ...) for the iteration.
with_file: entries - unlike with_fileglob (a pattern to match
filenames), each entry names a specific file whose CONTENT becomes
item (real Ansible's file lookup plugin). Resolved at execution
time for the same reasons as loop_fileglob (needs {{ vars }}
substitution and filesystem access, plus role_path for a relative
entry - conventionally searched under the role's own files/ dir).
with_file: entries - unlike with_fileglob (a pattern to match
filenames), each entry names a specific file whose CONTENT becomes
item (real Ansible's file lookup plugin). Resolved at execution
time for the same reasons as loop_fileglob (needs {{ vars }}
substitution and filesystem access, plus role_path for a relative
entry - conventionally searched under the role's own files/ dir).
with_fileglob patterns, resolved at execution time (needs {{ vars }} substitution and filesystem access, neither available at parse time).
with_fileglob patterns, resolved at execution time (needs {{ vars }} substitution and filesystem access, neither available at parse time).
with_community.general.filetree sources - the directories to walk,
kept as their raw ({{ }}-unsubstituted) strings since a source is
ordinarily {{ role_path }}/templates/<something> and can only be
resolved at execution time once the variable context exists. See
FiletreeLookup for the real lookup-plugin semantics the executor's
resolve_loop_filetree hands these to.
with_community.general.filetree sources - the directories to walk,
kept as their raw ({{ }}-unsubstituted) strings since a source is
ordinarily {{ role_path }}/templates/<something> and can only be
resolved at execution time once the variable context exists. See
FiletreeLookup for the real lookup-plugin semantics the executor's
resolve_loop_filetree hands these to.
with_first_found candidate paths, resolved at execution time for the
same reasons as loop_fileglob. Unlike a glob this yields at most one
item - the first candidate that exists. loop_first_found_skip is the
skip: true form, which makes "none of them exist" a skipped task
rather than an error.
with_first_found candidate paths, resolved at execution time for the
same reasons as loop_fileglob. Unlike a glob this yields at most one
item - the first candidate that exists. loop_first_found_skip is the
skip: true form, which makes "none of them exist" a skipped task
rather than an error.
with_first_found:'s own paths: sub-key (as opposed to the lookup('first_found', {files:, paths:})/query() function-call idiom, which already threads paths: through via evaluate_first_found since it goes through a raw hash, not this dedicated keyword parser). Previously silently discarded - resolution always fell back to the hardcoded files/templates/vars/role-root search roots regardless of what paths: actually specified.
with_first_found:'s own paths: sub-key (as opposed to the lookup('first_found', {files:, paths:})/query() function-call idiom, which already threads paths: through via evaluate_first_found since it goes through a raw hash, not this dedicated keyword parser). Previously silently discarded - resolution always fell back to the hardcoded files/templates/vars/role-root search roots regardless of what paths: actually specified.
true only for the scalar string form (with_first_found: "{{ var }}"
- parse_first_found wraps it into a one-element list, so the executor
couldn't otherwise tell it apart from a one-element literal list).
The distinction decides WHERE the undefined-ness of a reference is
surfaced: real Ansible templates the keyword's own value strictly
(a scalar
{{ undefined_var }}source fails the task), but hands a literal list's candidate strings to the first_found lookup plugin, which templates each term leniently - an undefined reference inside a list candidate renders to nothing and just never matches a file.
true only for the scalar string form (with_first_found: "{{ var }}"
- parse_first_found wraps it into a one-element list, so the executor
couldn't otherwise tell it apart from a one-element literal list).
The distinction decides WHERE the undefined-ness of a reference is
surfaced: real Ansible templates the keyword's own value strictly
(a scalar
{{ undefined_var }}source fails the task), but hands a literal list's candidate strings to the first_found lookup plugin, which templates each term leniently - an undefined reference inside a list candidate renders to nothing and just never matches a file.
with_community.general.flattened sources, kept as their raw task
strings. Each is ordinarily a {{ some_list_var }} reference to a
list; like loop_fileglob/loop_first_found they can only be resolved at
execution time once the variable context exists, so the parser stores
them verbatim and TaskExecutor flattens the resolved lists.
with_community.general.flattened sources, kept as their raw task
strings. Each is ordinarily a {{ some_list_var }} reference to a
list; like loop_fileglob/loop_first_found they can only be resolved at
execution time once the variable context exists, so the parser stores
them verbatim and TaskExecutor flattens the resolved lists.
Loop items already resolved at parse time (loop:, with_items:, with_dict:, with_nested:, with_sequence:, with_indexed_items:).
Loop items already resolved at parse time (loop:, with_items:, with_dict:, with_nested:, with_sequence:, with_indexed_items:).
True only when loop_items came from a literal with_items: array
(not loop:, which has no such behavior). Real Ansible's with_items:
implicitly applies flatten(levels=1) across ALL rendered elements -
with_items: ["{{ list_a }}", "{{ list_b }}"] where each renders to
its own list yields one iteration per INNER element (list_a's items
then list_b's items), not one iteration per outer element holding a
whole list as item. Found via nicolai86.prepare-release's own
with_items: ["{{ default_directories }}", "{{ directories }}"].
True only when loop_items came from a literal with_items: array
(not loop:, which has no such behavior). Real Ansible's with_items:
implicitly applies flatten(levels=1) across ALL rendered elements -
with_items: ["{{ list_a }}", "{{ list_b }}"] where each renders to
its own list yields one iteration per INNER element (list_a's items
then list_b's items), not one iteration per outer element holding a
whole list as item. Found via nicolai86.prepare-release's own
with_items: ["{{ default_directories }}", "{{ directories }}"].
loop_control.label - what the per-item result line shows instead of
the raw item, e.g. label: "{{ item.name }}" to keep a big dict
out of the output.
loop_control.label - what the per-item result line shows instead of
the raw item, e.g. label: "{{ item.name }}" to keep a big dict
out of the output.
with_nested: given as an array whose entries include one or more
{{ ... }}-templated scalars (the classic
with_nested: ["{{ users }}", "{{ groups }}"] shape). The cartesian
product's FACTOR SIZES are only knowable at execution time (a source
var's real length, including zero), so unlike a fully-literal
with_nested array this can't be resolved by LoopResolver at parse
time - the old parse-time branch wrapped every templated scalar as a
ONE-element literal list, pinning each factor to size 1 and iterating
once with item = the whole rendered list, no matter how many
elements the variable actually held. Sources are kept as their raw
strings (a literal sub-array entry serializes to JSON text) and
resolved + multiplied by TaskExecutor#resolve_loop_nested, mirroring
loop_flattened's own defer-until-runtime design.
with_nested: given as an array whose entries include one or more
{{ ... }}-templated scalars (the classic
with_nested: ["{{ users }}", "{{ groups }}"] shape). The cartesian
product's FACTOR SIZES are only knowable at execution time (a source
var's real length, including zero), so unlike a fully-literal
with_nested array this can't be resolved by LoopResolver at parse
time - the old parse-time branch wrapped every templated scalar as a
ONE-element literal list, pinning each factor to size 1 and iterating
once with item = the whole rendered list, no matter how many
elements the variable actually held. Sources are kept as their raw
strings (a literal sub-array entry serializes to JSON text) and
resolved + multiplied by TaskExecutor#resolve_loop_nested, mirroring
loop_flattened's own defer-until-runtime design.
with_subelements: the raw list template (usually a {{ registered_var .results }} reference) and the subelement key. Both kept verbatim and
resolved at execution time once the variable context + registered vars
exist.
with_subelements: the raw list template (usually a {{ registered_var .results }} reference) and the subelement key. Both kept verbatim and
resolved at execution time once the variable context + registered vars
exist.
true only for the single-element-array template form (with_items: ["{{ some_list }}"]) - see #find_loop_template's own comment for why
that shape deliberately flattens a resolved-to-scalar value into one
loop item instead of raising. The direct scalar form (loop: "{{ var }}", false here) does NOT get that legacy flattening: real Ansible
hard-fails a loop:/with_items: source that resolves to anything
other than a real list ("The loop value must resolve to a 'list',
not '<type>'.") - round174 differential matrix scenarios 11a/11c,
live-verified against ansible-core 2.19.12. Only the array-wrapped
form keeps the old lenient single-item behavior
(loop_scalar_flatten_spec.cr).
true only for the single-element-array template form (with_items: ["{{ some_list }}"]) - see #find_loop_template's own comment for why
that shape deliberately flattens a resolved-to-scalar value into one
loop item instead of raising. The direct scalar form (loop: "{{ var }}", false here) does NOT get that legacy flattening: real Ansible
hard-fails a loop:/with_items: source that resolves to anything
other than a real list ("The loop value must resolve to a 'list',
not '<type>'.") - round174 differential matrix scenarios 11a/11c,
live-verified against ansible-core 2.19.12. Only the array-wrapped
form keeps the old lenient single-item behavior
(loop_scalar_flatten_spec.cr).
loop:/with_items:/with_dict:/with_nested:/with_indexed_items: given as a Jinja variable reference ("{{ some_var }}") rather than a literal inline list/dict. Unresolvable at parse time since the YAML value is just a scalar string until the variable context exists, so the raw source keyword and template string are carried for the executor to resolve at execution time (mirrors loop_fileglob).
loop:/with_items:/with_dict:/with_nested:/with_indexed_items: given as a Jinja variable reference ("{{ some_var }}") rather than a literal inline list/dict. Unresolvable at parse time since the YAML value is just a scalar string until the variable context exists, so the raw source keyword and template string are carried for the executor to resolve at execution time (mirrors loop_fileglob).
loop_control.loop_var - the variable name the loop item is exposed
under (Ansible default "item"). Roles like dev-sec os_hardening set
loop_control: { loop_var: mount } so an include_tasks/loop can refer
to mount.path, mount.owner, etc. rather than always item. Kept
verbatim and resolved at execution time; nil means the default "item".
loop_control.loop_var - the variable name the loop item is exposed
under (Ansible default "item"). Roles like dev-sec os_hardening set
loop_control: { loop_var: mount } so an include_tasks/loop can refer
to mount.path, mount.owner, etc. rather than always item. Kept
verbatim and resolved at execution time; nil means the default "item".
meta: - only set when module_name == "_meta". Holds the meta action ("clear_facts"); see TaskExecutor#execute_meta.
meta: - only set when module_name == "_meta". Holds the meta action ("clear_facts"); see TaskExecutor#execute_meta.
Block- and task-scope module_defaults: - see Play#module_defaults.
no_log: true - suppress this task's result detail. A SECURITY
control: it is how a playbook keeps a password, token or key out
of the log. Previously unparsed and unused, so every such task
printed its secret in full.
no_log: true - suppress this task's result detail. A SECURITY
control: it is how a playbook keeps a password, token or key out
of the log. Previously unparsed and unused, so every such task
printed its secret in full.
Raw {{ ... }} text when no_log: is a templated expression rather
than a literal boolean (newrelic.newrelic-infra's own no_log: "{{ nrinfragent_hide_config_values }}", defaulting false). The
parse-time guess above (parse_become_value) defaults ANY templated
value to true - the safe direction for a SECURITY control (never
under-hides a real secret), but it means a task like this one has
its failure message suppressed on EVERY run regardless of the
real value, masking real errors for debugging (found chasing
newrelic.newrelic-infra's own merge_yaml failure, whose actual
message was invisible in every log because of this). Re-rendered
in TaskExecutor#resolve_task_no_log against live vars, same
deferred-evaluation shape ignore_errors_expr/check_mode_expr use.
Raw {{ ... }} text when no_log: is a templated expression rather
than a literal boolean (newrelic.newrelic-infra's own no_log: "{{ nrinfragent_hide_config_values }}", defaulting false). The
parse-time guess above (parse_become_value) defaults ANY templated
value to true - the safe direction for a SECURITY control (never
under-hides a real secret), but it means a task like this one has
its failure message suppressed on EVERY run regardless of the
real value, masking real errors for debugging (found chasing
newrelic.newrelic-infra's own merge_yaml failure, whose actual
message was invisible in every log because of this). Re-rendered
in TaskExecutor#resolve_task_no_log against live vars, same
deferred-evaluation shape ignore_errors_expr/check_mode_expr use.
Set on every task loaded from a role (tasks/main.yml and handlers/main.yml alike) by RoleLoader. role_defaults is the lowest precedence tier (role's defaults/main.yml); role_vars sits above play/host vars but below the task's own vars: (role's vars/main.yml, merged with the role invocation's own vars:). role_files_dir/ role_templates_dir let the executor resolve a copy:/template: src: relative to the role's files//templates/ directory, since the plugin subprocess itself has no concept of roles.
Set on every task loaded from a role (tasks/main.yml and handlers/main.yml alike) by RoleLoader. role_defaults is the lowest precedence tier (role's defaults/main.yml); role_vars sits above play/host vars but below the task's own vars: (role's vars/main.yml, merged with the role invocation's own vars:). role_files_dir/ role_templates_dir let the executor resolve a copy:/template: src: relative to the role's files//templates/ directory, since the plugin subprocess itself has no concept of roles.
Identity of ONE dynamic include_role: execution (a fresh random
token per run_include_role_once - per host, per loop item). What
meta: end_role keys its per-host "role ended" flag on: two
invocations of the same role in one play are independent for
end_role purposes (verified against ansible-core 2.19.4 - a looped
include_role whose first item ends the role still runs the second
item in full). Statically loaded role tasks (roles:/import_role:)
leave this nil and key on role_path instead - a static role body
executes exactly once per play, so the path is already a unique
invocation identity there.
Identity of ONE dynamic include_role: execution (a fresh random
token per run_include_role_once - per host, per loop item). What
meta: end_role keys its per-host "role ended" flag on: two
invocations of the same role in one play are independent for
end_role purposes (verified against ansible-core 2.19.4 - a looped
include_role whose first item ends the role still runs the second
item in full). Statically loaded role tasks (roles:/import_role:)
leave this nil and key on role_path instead - a static role body
executes exactly once per play, so the path is already a unique
invocation identity there.
The role's invocation name, exactly as written in role:/name:
(a bare name or a full local path) - exposed to templates as the
ansible_role_name magic var (dev-sec nginx_hardening's own
hardening.conf.j2: # Generated by Ansible role {{ ansible_role_ name }}). Real Ansible sets this to the same string the play
actually invoked the role with, not a normalized basename -
verified against a real ansible-playbook run using a full local
path for role:, which echoed that exact path back.
The role's invocation name, exactly as written in role:/name:
(a bare name or a full local path) - exposed to templates as the
ansible_role_name magic var (dev-sec nginx_hardening's own
hardening.conf.j2: # Generated by Ansible role {{ ansible_role_ name }}). Real Ansible sets this to the same string the play
actually invoked the role with, not a normalized basename -
verified against a real ansible-playbook run using a full local
path for role:, which echoed that exact path back.
The chain of ancestor role names (root-first, NOT including this
role's own name) that led to this role being invoked via
include_role: from within another role's own tasks - exposed as
the ansible_parent_role_names magic var. Empty/nil for a role
listed directly under a play's own roles: (not nested inside any
other role). Several real collections (prometheus.prometheus's own
_common shared-logic role, invoked by every exporter role) guard
against direct invocation with ansible_parent_role_names is defined and ansible_parent_role_names | length > 0.
The chain of ancestor role names (root-first, NOT including this
role's own name) that led to this role being invoked via
include_role: from within another role's own tasks - exposed as
the ansible_parent_role_names magic var. Empty/nil for a role
listed directly under a play's own roles: (not nested inside any
other role). Several real collections (prometheus.prometheus's own
_common shared-logic role, invoked by every exporter role) guard
against direct invocation with ansible_parent_role_names is defined and ansible_parent_role_names | length > 0.
The role_path (filesystem root) of each ancestor role, same
ordering/population rules as role_parent_names above - real
Ansible searches a role task's ENTIRE parent-role chain (not just
the currently-executing role's own templates/files dir) for a
relative template:/copy: src:. Found via prometheus.prometheus._
common's own "Create systemd service unit" task: src: "{{ _common_service_name }}.service.j2" lives in the CALLING role's
own templates/ dir (node_exporter/templates/node_exporter.
service.j2), not _common's - a shared/generic role deliberately
relying on this real Ansible search-path behavior to let each
exporter role supply its own service unit template.
The role_path (filesystem root) of each ancestor role, same
ordering/population rules as role_parent_names above - real
Ansible searches a role task's ENTIRE parent-role chain (not just
the currently-executing role's own templates/files dir) for a
relative template:/copy: src:. Found via prometheus.prometheus._
common's own "Create systemd service unit" task: src: "{{ _common_service_name }}.service.j2" lives in the CALLING role's
own templates/ dir (node_exporter/templates/node_exporter.
service.j2), not _common's - a shared/generic role deliberately
relying on this real Ansible search-path behavior to let each
exporter role supply its own service unit template.
The role's own root directory on disk - exposed to templates as the
role_path magic var (linux-system-roles/logging's own include_role: name: "{{ role_path }}/roles/rsyslog", a common pattern for a role to
reference one of its own private subroles by absolute path).
The role's own root directory on disk - exposed to templates as the
role_path magic var (linux-system-roles/logging's own include_role: name: "{{ role_path }}/roles/rsyslog", a common pattern for a role to
reference one of its own private subroles by absolute path).
The role's vars/ directory - where include_vars: and with_first_found: resolve a relative filename from, the same way role_files_dir serves copy:'s src:.
The role's vars/ directory - where include_vars: and with_first_found: resolve a relative filename from, the same way role_files_dir serves copy:'s src:.
run_once: - only actually execute this task for the first host in the play; later hosts skip it outright (no output/stats), same as real Ansible.
run_once: - only actually execute this task for the first host in the play; later hosts skip it outright (no output/stats), same as real Ansible.
A legacy action:/local_action: free-form directive whose module
name is itself a {{ }} template (action: "{{ ansible_pkg_mgr }} state=present name={{ item }}", jdauphant.intellij) can't resolve
at parse time - the raw free-form string is kept here and resolved
at execution (TaskExecutor#resolve_templated_action): the
substituted first token names the real module, the rest re-parses
as its params. Deliberately distinct from unavailable_module: a
module name that is only resolvable at run time must FAIL the task
when it doesn't resolve (real Ansible: "couldn't resolve
module/action 'x'"), never be skipped like an unknown module.
A legacy action:/local_action: free-form directive whose module
name is itself a {{ }} template (action: "{{ ansible_pkg_mgr }} state=present name={{ item }}", jdauphant.intellij) can't resolve
at parse time - the raw free-form string is kept here and resolved
at execution (TaskExecutor#resolve_templated_action): the
substituted first token names the real module, the rest re-parses
as its params. Deliberately distinct from unavailable_module: a
module name that is only resolvable at run time must FAIL the task
when it doesn't resolve (real Ansible: "couldn't resolve
module/action 'x'"), never be skipped like an unknown module.
throttle: - cap how many hosts run this task at once, below the
run's own --forks. 0/absent means no extra cap.
throttle: - cap how many hosts run this task at once, below the
run's own --forks. 0/absent means no extra cap.
Appends a short String representation of this object which includes its class name and its object address.
class Person
def initialize(@name : String, @age : Int32)
end
end
Person.new("John", 32).to_s # => #<Person:0x10a199f20>
until: / retries: / delay: - retry a task until a condition passes.
Synthesized by RoleLoader when a role has meta/argument_specs.yml -
only set when module_name == "_validate_argument_spec". Real Ansible
auto-inserts this as the role's first task ("Validating arguments
against arg spec 'main' - <short_description>"); holds the entry
point's options: map (name -> {type, required, ...}) for
TaskExecutor#execute_validate_argument_spec to check the effective
vars against.
Synthesized by RoleLoader when a role has meta/argument_specs.yml -
only set when module_name == "_validate_argument_spec". Real Ansible
auto-inserts this as the role's first task ("Validating arguments
against arg spec 'main' - <short_description>"); holds the entry
point's options: map (name -> {type, required, ...}) for
TaskExecutor#execute_validate_argument_spec to check the effective
vars against.
The when: LIST's own per-item condition strings, kept alongside the
" and "-joined when_condition so the STRICT when: evaluation path
(TaskExecutor#evaluate_when) can type-check each item SEPARATELY,
exactly as ansible-core 2.19 does: a list-form when: is a sequence
of independent conditionals, each required to end in a real boolean
when: [str_var, bool_var]fails ("Conditional result ... was derived from value of type 'str'") even though the equivalent single-stringwhen: str_var and bool_varPASSES there (Python'sandreturns the last operand, so the whole expression's result type is bool and only the whole result gets the strict check - both behaviors verified live against 2.19.4). Joining the list into oneandstring and strict-checking only the JOINED result made crazikpl.logging'swhen: [(...or ...), use_rsyslog]pass silently where real Ansible failed the task. nil when when: wasn't a multi-item list (single-item lists and scalars evaluate identically either way); the joinedwhen_conditionremains the source of truth for every non-strict consumer (loop pre-gates, register:/ batcher scans, block/import inheritance display).
The when: LIST's own per-item condition strings, kept alongside the
" and "-joined when_condition so the STRICT when: evaluation path
(TaskExecutor#evaluate_when) can type-check each item SEPARATELY,
exactly as ansible-core 2.19 does: a list-form when: is a sequence
of independent conditionals, each required to end in a real boolean
when: [str_var, bool_var]fails ("Conditional result ... was derived from value of type 'str'") even though the equivalent single-stringwhen: str_var and bool_varPASSES there (Python'sandreturns the last operand, so the whole expression's result type is bool and only the whole result gets the strict check - both behaviors verified live against 2.19.4). Joining the list into oneandstring and strict-checking only the JOINED result made crazikpl.logging'swhen: [(...or ...), use_rsyslog]pass silently where real Ansible failed the task. nil when when: wasn't a multi-item list (single-item lists and scalars evaluate identically either way); the joinedwhen_conditionremains the source of truth for every non-strict consumer (loop pre-gates, register:/ batcher scans, block/import inheritance display).