✅
Tasks & Task Modifiers
Tasks are line items in Plays or Roles that make real things happen - they are the only thing in Jet that really do. Each task is a parameterized call into Jet Modules - you should probably skim the list of them to understand what modules are! This will also make much more sense with our examples open in GitHub!
A task entry in a list of tasks looks like this:
- !module_name_goes_here
name: optional comment about what the task does
x: "value"
y: 42
The key things to identify are the name comment, the module line, and the parameters. Every task must have the !module_name_goes_here line (this is called a YAML tag), and all will have parameters - some parameters are required, some may be optional. The tag is not optional.
Exploring Modules will give you the best idea of what you can do with tasks. Beyond the module parameters, tasks can be further modified by the optional statements with and, per se, and, which are shown below.
Common Parameter | Description |
---|---|
name | Optional comment parameter that will be shown when the task executes. |
with | Structure. Details special behaviors that happen before the task executes |
and | Structure. Details special behaviors that happen after the task executes |
Many tasks will be fine in a playbook just invoking a module, but sometimes additional behaivor is required.
Keywords explained below - "with" and "and" futher modify tasks. Learn these after exploring the module documentation.
With statements are one of two types of task modifiers. With statements behaviors that happen before a module executes or otherwise alter the execution of a module.
With Parameter | Description |
---|---|
condition | Only execute the task when this condition is true |
delegate_to | Run this task on a different host using the hostname as a reference |
items | Loop over the task running it multiple times using items from a list |
subscribe | |
sudo | Change to a different user account when running this task. This can use tools other than sudo. |
tags | Apply labels that can be used with command line flags for selective execution |
condition runs a task only if certain conditions are true.
tasks:
- !shell
cmd: some_command --blarg
with:
condition: (ge xyz 42)
Condition can currently take the following boolean expressions.
eq
ne
gt
gte
lt
lte
and
or
not
The parentheses around the condition are required.
More operators and shortcuts may be added in the near future.
Normally in SSH mode Jet is looping over a number of hosts and performing actions all on the indicated host. The delegate_to keyword implies a step runs on a different host in inventory, or localhost, but all the variables and information about the remote host are available to the delegated host.
There is no concern that delegation from a large fleet of machines can overwhelm a host, but it can create a "slow" portion of a playbook when a large number of hosts are involved. Connections are guarded by per-host mutexes, so only one task can be run against the delegated target machine at a time.
The variable "delegate_host" is created automatically when using delegate_to and refers to the current host being looped over in inventory.
Why Localhost Delegation Requires A CLI Flag
Delegation specifically to localhost has potential security consequences because variables derived from remote facts may be too untrustworthy (for instance, the remote host might be compromised), especially when those variables are used to build module arguments. As we risk to avoid information leakage and other suprises, delegating to localhost requires the --allow-localhost-delegation CLI parameter to acknowledge that these potential consequences are accepted and any potential values of variables are acceptable to be used against localhost for any delegated task. If not provided, delegation is still allowed to other hosts. Jet will still check parameters for unsafe shell characters and so forth.
Localhost does not have to be in inventory to delegate tasks to localhost.
Other hostnames do not require an opt-in flag to use delegate_to.
- name: a playbook demonstrating delegation
groups:
- webservers
defaults:
monitoring_server: monitor.example.com
tasks:
- !shell
cmd: /usr/bin/monitoring_add_host --host={{ delegate_host }}
with:
delegate_to: "{{ monitoring_server }}"
items allows for tasks to be executed in a loop, preventing tedious data entry, and works with either variables or explicit lists:
- name: with_items demo
defaults:
apt_packages:
- cowsay
- tree
tasks:
- !apt
package: "{{ item }}"
with:
items: apt_packages
# OR ...
- !apt
package: "{{ item }}"
with:
items:
- cowsay
- tree
The variable "item" is provided to for each task and contains the value of each item that is looped over.
items also works lists of maps (also known as hash tables or dictionaries):
- name: with_items demo
defaults:
apt_packages:
- { name: uno, version: "1.23" }
- { name: dos, version: "3.45" }
- { name: tres, version: "4.6" }
tasks:
- !apt
package: "{{ item.name }}"
version: "{{ item.version }}"
with:
items: apt_packages
# OR ...
- !apt
package: "{{ item.name }}"
version: "{{ item.version }}"
with:
items:
- { name: uno, version: "1.23" }
- { name: dos, version: "3.45" }
- { name: tres, version: "4.6" }
There is a more generic example in the example repo also showing some template evaluation features used with the items loop.
Subscribe is used only in handler sections. It identifies the name of an event that will trigger the handler to conditionally run and must be matched exactly. For clarity in playbook auditing, variable names are not evaluated within subscribe, but they are within notify.
handlers:
- !sd_service
service: redis
restart: true
with:
subscribe: restart redis
Omitting a subscribe within a handlers section is a detectable error (it is required), just as is including a subscribe within a non-handlers section is disallowed. In other words, get used to putting 'subscribe' on your handlers, or jetp will tell you about it!
Sudo elevates the login user to a new user before running a task. The actual command used need not be sudo. For support of other sudo-replacements, see the documentation in Plays.
tasks:
- !shell
cmd: /usr/bin/foo
with:
sudo: root
Sudo can also be set at play level as described in Plays, in which case all steps in the play are executed by sudo without need to set sudo to each task. This value can also be set on the command line.
Tags are attributes that can be added to a task that normally don't do anything until the command line flag "--tags" is used. When --tags is provided, only the tasks with tags that match the command line are run. This can be used to execute part of a playbook. Tags can also be applied to roles directly, which results in much easier to read playbooks than tagging every single task.
tasks:
- !shell
cmd: /usr/bin/foo
with:
tags: ['foo']
$ jetp ssh --playbook pb1.yml --inventory ~/private_inventory --tags foo
"And" statements modify a task by adding more behavior that occurs after a task is run. It is fine to use "and" and "with" statements together. 'And' keywords are not allowed in 'with' sections and vice versa.
And Parameter | Description |
---|---|
ignore_errors | No matter what happens, don't count this module step as a failure, and continue to execute more tasks on this host. |
notify | Signal an event handler so that the named handler will run in a handlers section |
retry | Integer. Attempt this task multiple times until it succeeds |
delay | Integer. Wait this many seconds between retries. |
ignore errors will ignore the failure results from a task despite most anything going wrong. It is a very blunt instrument to use when you don't care if a task really succeeds or not, but you just want it to try. The shell module for example contains much more fine grained error status controls, and that is the better way to deal with return codes for that module.
tasks:
- !fail
name: this will not fail
and:
ignore_errors: true
modules return a "changed" status internally when they perform changes on remote hosts. This can be used to run certain tasks conditionally at the end of the play, only when required.
tasks:
- !template
src: templates/foo.conf
dest: /etc/foo/foo.conf
and:
notify: restart foo
If a task fails, Jet can re-attempt the task for a given finite number of times with a certain number of seconds delay between the retries.
The default value for retry is 0 (no retries) and the default value of delay to wait before attempting another retry is 1 second.
This would be most commonly used with 'netstat' when services are not immediately usable after they return from a successful start/restart or when waiting for a local or remote port to open.
# wait for a file to exist
- !shell
cmd: cat '{{ test_file }}'
and:
retry: 20
delay: 10
Some users may expect to see some task keywords features that are not listed here.
Saving variables from shell command results, or ignoring errors conditionally, are features directly embedded into the shell module (and possibly some other future modules), and are not generic task keywords. This was a language decision made to increase playbook clarity and readability. See the shell module documentation for details.
Last modified 4d ago