class

Krikri::Task

Inherits Reference < Object

Represents a single task in a playbook

Constructors

new(name : String, module_name : String)
Source

Instance methods

always_tasks
Source
always_tasks=(always_tasks : Array(Task) | Nil)
Source
ansible_collection_name

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).

Source
ansible_collection_name=(ansible_collection_name : String | Nil)

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).

Source
async_seconds

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.

Source
async_seconds=(async_seconds : Int32 | Nil)

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.

Source
become=(become : Bool)
Source
become?
Source
become_expr

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.

Source
become_expr=(become_expr : String | Nil)

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.

Source
become_user
Source
become_user=(become_user : String | Nil)
Source
block?
Source
block_name_chain

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.

Source
block_name_chain=(block_name_chain : Array(String) | Nil)

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.

Source
block_tasks

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.

Source
block_tasks=(block_tasks : Array(Task) | Nil)

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.

Source
changed_when

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.

Source
changed_when=(changed_when : String | Nil)

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.

Source
check_mode=(check_mode : Bool | Nil)
Source
check_mode?
Source
check_mode_expr

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.

Source
check_mode_expr=(check_mode_expr : String | Nil)

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.

Source
connection

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:.

Source
connection=(connection : String | Nil)

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:.

Source
debugger

debugger: at task scope - see Play#debugger.

Source
debugger=(debugger : String | Nil)

debugger: at task scope - see Play#debugger.

Source
delay
Source
delay=(delay : Int32)
Source
delegate_facts=(delegate_facts : Bool)

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).

Source
delegate_facts?

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).

Source
delegate_to

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).

Source
delegate_to=(delegate_to : String | Nil)

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).

Source
diff_mode=(diff_mode : Bool | Nil)
Source
diff_mode?
Source
environment

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.

Source
environment=(environment : Hash(String, String) | Nil)

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.

Source
environment_raw

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.

Source
environment_raw=(environment_raw : String | Nil)

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.

Source
failed_when
Source
failed_when=(failed_when : String | Nil)
Source
ignore_errors=(ignore_errors : Bool)
Source
ignore_errors?
Source
ignore_errors_expr

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).

Source
ignore_errors_expr=(ignore_errors_expr : String | Nil)

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).

Source
ignore_unreachable=(ignore_unreachable : Bool)

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).

Source
ignore_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).

Source
include_file

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.

Source
include_file=(include_file : String | Nil)

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.

Source
include_file_dir
Source
include_file_dir=(include_file_dir : String | Nil)
Source
include_role?
Source
include_role_dir
Source
include_role_dir=(include_role_dir : String | Nil)
Source
include_role_name

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:.

Source
include_role_name=(include_role_name : String | Nil)

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:.

Source
include_role_tasks_from

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).

Source
include_role_tasks_from=(include_role_tasks_from : String | Nil)

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).

Source
include_role_vars
Source
include_role_vars=(include_role_vars : Hash(String, JSON::Any) | Nil)
Source
include_tasks?
Source
include_vars

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).

Source
include_vars=(include_vars : Hash(String, JSON::Any) | Nil)

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).

Source
include_vars?
Source
include_vars_depth
Source
include_vars_depth=(include_vars_depth : String | Nil)
Source
include_vars_dir

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.

Source
include_vars_dir=(include_vars_dir : String | Nil)

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.

Source
include_vars_extensions
Source
include_vars_extensions=(include_vars_extensions : Array(String) | Nil)
Source
include_vars_file

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.

Source
include_vars_file=(include_vars_file : String | Nil)

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.

Source
include_vars_files_matching
Source
include_vars_files_matching=(include_vars_files_matching : String | Nil)
Source
include_vars_ignore_files
Source
include_vars_ignore_files=(include_vars_ignore_files : Array(String) | Nil)
Source
include_vars_ignore_unknown_extensions
Source
include_vars_ignore_unknown_extensions=(include_vars_ignore_unknown_extensions : Bool | Nil)
Source
include_vars_name
Source
include_vars_name=(include_vars_name : String | Nil)
Source
index_var

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.

Source
index_var=(index_var : String | Nil)

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.

Source
is_static_import=(is_static_import : Bool)

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.

Source
is_static_import?

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.

Source
listen

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.

Source
listen=(listen : Array(String) | Nil)

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.

Source
loop
Source
loop=(loop : Array(JSON::Any) | Nil)
Source
loop_extended=(loop_extended : Bool)

loop_control.extended - exposes the ansible_loop dict (index, first/last, allitems, nextitem/previtem, ...) for the iteration.

Source
loop_extended?

loop_control.extended - exposes the ansible_loop dict (index, first/last, allitems, nextitem/previtem, ...) for the iteration.

Source
loop_file

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).

Source
loop_file=(loop_file : Array(String) | Nil)

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).

Source
loop_fileglob

with_fileglob patterns, resolved at execution time (needs {{ vars }} substitution and filesystem access, neither available at parse time).

Source
loop_fileglob=(loop_fileglob : Array(String) | Nil)

with_fileglob patterns, resolved at execution time (needs {{ vars }} substitution and filesystem access, neither available at parse time).

Source
loop_filetree

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.

Source
loop_filetree=(loop_filetree : Array(String) | Nil)

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.

Source
loop_first_found

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.

Source
loop_first_found=(loop_first_found : Array(String) | Nil)

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.

Source
loop_first_found_paths

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.

Source
loop_first_found_paths=(loop_first_found_paths : Array(String) | Nil)

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.

Source
loop_first_found_skip=(loop_first_found_skip : Bool)
Source
loop_first_found_skip?
Source
loop_first_found_string_form=(loop_first_found_string_form : Bool)

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.
Source
loop_first_found_string_form?

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.
Source
loop_flattened

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.

Source
loop_flattened=(loop_flattened : Array(String) | Nil)

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.

Source
loop_items

Loop items already resolved at parse time (loop:, with_items:, with_dict:, with_nested:, with_sequence:, with_indexed_items:).

Source
loop_items=(loop_items : Array(JSON::Any) | Nil)

Loop items already resolved at parse time (loop:, with_items:, with_dict:, with_nested:, with_sequence:, with_indexed_items:).

Source
loop_items_needs_flatten=(loop_items_needs_flatten : Bool)

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 }}"].

Source
loop_items_needs_flatten?

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 }}"].

Source
loop_label

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.

Source
loop_label=(loop_label : String | Nil)

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.

Source
loop_nested_sources

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.

Source
loop_nested_sources=(loop_nested_sources : Array(String) | Nil)

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.

Source
loop_subelements_key
Source
loop_subelements_key=(loop_subelements_key : String | Nil)
Source
loop_subelements_list

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.

Source
loop_subelements_list=(loop_subelements_list : String | Nil)

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.

Source
loop_template
Source
loop_template=(loop_template : String | Nil)
Source
loop_template_array_wrapped=(loop_template_array_wrapped : Bool)

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).

Source
loop_template_array_wrapped?

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).

Source
loop_template_kind

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).

Source
loop_template_kind=(loop_template_kind : String | Nil)

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).

Source
loop_var

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".

Source
loop_var=(loop_var : String | Nil)

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".

Source
meta?
Source
meta_action

meta: - only set when module_name == "_meta". Holds the meta action ("clear_facts"); see TaskExecutor#execute_meta.

Source
meta_action=(meta_action : String | Nil)

meta: - only set when module_name == "_meta". Holds the meta action ("clear_facts"); see TaskExecutor#execute_meta.

Source
module_defaults

Block- and task-scope module_defaults: - see Play#module_defaults.

Source
module_defaults=(module_defaults : Hash(String, Hash(String, String)))

Block- and task-scope module_defaults: - see Play#module_defaults.

Source
module_name
Source
module_name=(module_name : String)
Source
name
Source
name=(name : String)
Source
no_log=(no_log : Bool)

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.

Source
no_log?

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.

Source
no_log_expr

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.

Source
no_log_expr=(no_log_expr : String | Nil)

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.

Source
notify
Source
notify=(notify : Array(String) | Nil)
Source
params
Source
params=(params : Hash(String, String))
Source
poll_seconds
Source
poll_seconds=(poll_seconds : Int32 | Nil)
Source
register
Source
register=(register : String | Nil)
Source
remote_user

remote_user: at task scope - see Play#remote_user.

Source
remote_user=(remote_user : String | Nil)

remote_user: at task scope - see Play#remote_user.

Source
rescue_tasks
Source
rescue_tasks=(rescue_tasks : Array(Task) | Nil)
Source
retries
Source
retries=(retries : Int32)
Source
role_defaults

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.

Source
role_defaults=(role_defaults : Hash(String, JSON::Any) | Nil)

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.

Source
role_files_dir
Source
role_files_dir=(role_files_dir : String | Nil)
Source
role_invocation_id

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.

Source
role_invocation_id=(role_invocation_id : String | Nil)

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.

Source
role_name

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.

Source
role_name=(role_name : String | Nil)

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.

Source
role_parent_names

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.

Source
role_parent_names=(role_parent_names : Array(String) | Nil)

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.

Source
role_parent_paths

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.

Source
role_parent_paths=(role_parent_paths : Array(String) | Nil)

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.

Source
role_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).

Source
role_path=(role_path : String | Nil)

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).

Source
role_templates_dir
Source
role_templates_dir=(role_templates_dir : String | Nil)
Source
role_vars
Source
role_vars=(role_vars : Hash(String, JSON::Any) | Nil)
Source
role_vars_dir

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:.

Source
role_vars_dir=(role_vars_dir : String | Nil)

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:.

Source
run_once=(run_once : Bool)

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.

Source
run_once?

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.

Source
tags
Source
tags=(tags : Array(String))
Source
templated_action

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.

Source
templated_action=(templated_action : String | Nil)

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.

Source
throttle

throttle: - cap how many hosts run this task at once, below the run's own --forks. 0/absent means no extra cap.

Source
throttle=(throttle : Int32)

throttle: - cap how many hosts run this task at once, below the run's own --forks. 0/absent means no extra cap.

Source
to_s(io : IO) : Nil

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>
Source
unavailable_module

Set for ANY module name that resolves to nothing this engine ships - a role-private library/<name>.py module (module_name above keeps the raw requested name, e.g. "sr_fingerprint", whose source PythonModuleRunner will find at run time), OR a plain unimplemented module (0.9.1050, reversing 0.9.903's parse-time hard-stop - round 811000: the unconditional raise aborted whole plays for tasks real Ansible would simply skip, e.g. robertdebock.podman's containers.podman.podman_container behind when: podman_containers is defined, false on the role's own defaults). Either way the task can't dispatch to a native plugin binary: with a library/ source it RUNS through the py_module runner (see executor_run_loop.cr); without one it takes the graceful per-task skip (task marked skipped, "skipping" line, play continued) - exactly what real Ansible does when evaluating when: before resolving the action - and register_reachable_unavailable_module records the name for the final exit-code decision if its own when: would have let it run. Previously an unimplemented task raised "Plugin not available" at PARSE time and was dropped entirely - before its own when: was ever evaluated - so a task gated behind e.g. when: ansible_facts['pkg_mgr'] == "zypper" (always false on RHEL/Ubuntu) vanished from the recap/task list completely. Found via round171's robertdebock.haproxy (seport, now a real plugin) and robertdebock.jenkins (zypper_repository, SUSE-only, out of this project's scope).

Source
unavailable_module=(unavailable_module : String | Nil)

Set for ANY module name that resolves to nothing this engine ships - a role-private library/<name>.py module (module_name above keeps the raw requested name, e.g. "sr_fingerprint", whose source PythonModuleRunner will find at run time), OR a plain unimplemented module (0.9.1050, reversing 0.9.903's parse-time hard-stop - round 811000: the unconditional raise aborted whole plays for tasks real Ansible would simply skip, e.g. robertdebock.podman's containers.podman.podman_container behind when: podman_containers is defined, false on the role's own defaults). Either way the task can't dispatch to a native plugin binary: with a library/ source it RUNS through the py_module runner (see executor_run_loop.cr); without one it takes the graceful per-task skip (task marked skipped, "skipping" line, play continued) - exactly what real Ansible does when evaluating when: before resolving the action - and register_reachable_unavailable_module records the name for the final exit-code decision if its own when: would have let it run. Previously an unimplemented task raised "Plugin not available" at PARSE time and was dropped entirely - before its own when: was ever evaluated - so a task gated behind e.g. when: ansible_facts['pkg_mgr'] == "zypper" (always false on RHEL/Ubuntu) vanished from the recap/task list completely. Found via round171's robertdebock.haproxy (seport, now a real plugin) and robertdebock.jenkins (zypper_repository, SUSE-only, out of this project's scope).

Source
until_condition

until: / retries: / delay: - retry a task until a condition passes.

Source
until_condition=(until_condition : String | Nil)

until: / retries: / delay: - retry a task until a condition passes.

Source
validate_argument_spec?
Source
validate_argument_spec_options

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.

Source
validate_argument_spec_options=(validate_argument_spec_options : Hash(String, JSON::Any) | Nil)

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.

Source
vars
Source
vars=(vars : Hash(String, JSON::Any))
Source
when_condition
Source
when_condition=(when_condition : String | Nil)
Source
when_condition_list

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-string when: str_var and bool_var PASSES there (Python's and returns 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 one and string and strict-checking only the JOINED result made crazikpl.logging's when: [(...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 joined when_condition remains the source of truth for every non-strict consumer (loop pre-gates, register:/ batcher scans, block/import inheritance display).
Source
when_condition_list=(when_condition_list : Array(String) | Nil)

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-string when: str_var and bool_var PASSES there (Python's and returns 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 one and string and strict-checking only the JOINED result made crazikpl.logging's when: [(...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 joined when_condition remains the source of truth for every non-strict consumer (loop pre-gates, register:/ batcher scans, block/import inheritance display).
Source