Docs: Issue 78082 Create playbook guide (#78262)

pull/78328/head
Don Naro 4 years ago committed by GitHub
parent 61af59c808
commit 7aada8d499
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -42,6 +42,7 @@ Ansible releases a new major release approximately twice a year. The core applic
:caption: Using Ansible
user_guide/index
playbook_guide/index
win_guide/index
tips_tricks/index

@ -49,6 +49,7 @@ This documentation covers the version of ``ansible-core`` noted in the upper lef
:caption: Using Ansible Core
user_guide/index
playbook_guide/index
win_guide/index
tips_tricks/index

@ -0,0 +1,31 @@
.. _playbook_guide_index:
#######################
Using Ansible playbooks
#######################
.. note::
**Making Open Source More Inclusive**
Red Hat is committed to replacing problematic language in our code, documentation, and web properties. We are beginning with these four terms: master, slave, blacklist, and whitelist. We ask that you open an issue or pull request if you come upon a term that we have missed. For more details, see `our CTO Chris Wright's message <https://www.redhat.com/en/blog/making-open-source-more-inclusive-eradicating-problematic-language>`_.
Welcome to the Ansible playbooks guide.
Playbooks are automation blueprints, in ``YAML`` format, that Ansible uses to deploy and configure nodes in an inventory.
This guide introduces you to playbooks and then covers different use cases for tasks and plays, such as:
* Executing tasks with elevated privileges or as a different user.
* Using loops to repeat tasks for items in a list.
* Delegating playbooks to execute tasks on different machines.
* Running conditional tasks and evaluating conditions with playbook tests.
* Using blocks to group sets of tasks.
You can also learn how to use Ansible playbooks more effectively by creating re-usable files and roles, including and importing playbooks, and running selected parts of a playbook with tags.
.. toctree::
:maxdepth: 2
playbooks_intro
playbooks
playbooks_execution
playbooks_advanced_syntax

@ -0,0 +1,42 @@
.. _working_with_playbooks:
Working with playbooks
======================
Playbooks record and execute Ansible's configuration, deployment, and orchestration functions.
They can describe a policy you want your remote systems to enforce, or a set of steps in a general IT process.
If Ansible modules are the tools in your workshop, playbooks are your instruction manuals, and your inventory of hosts are your raw material.
At a basic level, playbooks can be used to manage configurations of and deployments to remote machines.
At a more advanced level, they can sequence multi-tier rollouts involving rolling updates, and can delegate actions to other hosts, interacting with monitoring servers and load balancers along the way.
Playbooks are designed to be human-readable and are developed in a basic text language.
There are multiple ways to organize playbooks and the files they include, and we'll offer up some suggestions on that and making the most out of Ansible.
You should look at `Example Playbooks <https://github.com/ansible/ansible-examples>`_ while reading along with the playbook documentation.
These illustrate best practices as well as how to put many of the various concepts together.
.. toctree::
:maxdepth: 2
playbooks_templating
playbooks_filters
playbooks_tests
playbooks_lookups
playbooks_python_version
playbooks_templating_now
playbooks_loops
playbooks_delegation
playbooks_conditionals
playbooks_blocks
playbooks_handlers
playbooks_error_handling
playbooks_environment
playbooks_reuse
playbooks_reuse_roles
playbooks_module_defaults
playbooks_prompts
playbooks_variables
playbooks_vars_facts
guide_rolling_upgrade

@ -0,0 +1,122 @@
.. _playbooks_advanced_syntax:
************************
Advanced playbook syntax
************************
The advanced YAML syntax examples on this page give you more control over the data placed in YAML files used by Ansible.
You can find additional information about Python-specific YAML in the official `PyYAML Documentation <https://pyyaml.org/wiki/PyYAMLDocumentation#YAMLtagsandPythontypes>`_.
.. _unsafe_strings:
Unsafe or raw strings
=====================
When handling values returned by lookup plugins, Ansible uses a data type called ``unsafe`` to block templating. Marking data as unsafe prevents malicious users from abusing Jinja2 templates to execute arbitrary code on target machines. The Ansible implementation ensures that unsafe values are never templated. It is more comprehensive than escaping Jinja2 with ``{% raw %} ... {% endraw %}`` tags.
You can use the same ``unsafe`` data type in variables you define, to prevent templating errors and information disclosure. You can mark values supplied by :ref:`vars_prompts<unsafe_prompts>` as unsafe. You can also use ``unsafe`` in playbooks. The most common use cases include passwords that allow special characters like ``{`` or ``%``, and JSON arguments that look like templates but should not be templated. For example:
.. code-block:: yaml
---
mypassword: !unsafe 234%234{435lkj{{lkjsdf
In a playbook:
.. code-block:: yaml
---
hosts: all
vars:
my_unsafe_variable: !unsafe 'unsafe % value'
tasks:
...
For complex variables such as hashes or arrays, use ``!unsafe`` on the individual elements:
.. code-block:: yaml
---
my_unsafe_array:
- !unsafe 'unsafe element'
- 'safe element'
my_unsafe_hash:
unsafe_key: !unsafe 'unsafe value'
.. _anchors_and_aliases:
YAML anchors and aliases: sharing variable values
=================================================
`YAML anchors and aliases <https://yaml.org/spec/1.2/spec.html#id2765878>`_ help you define, maintain, and use shared variable values in a flexible way.
You define an anchor with ``&``, then refer to it using an alias, denoted with ``*``. Here's an example that sets three values with an anchor, uses two of those values with an alias, and overrides the third value:
.. code-block:: yaml
---
...
vars:
app1:
jvm: &jvm_opts
opts: '-Xms1G -Xmx2G'
port: 1000
path: /usr/lib/app1
app2:
jvm:
<<: *jvm_opts
path: /usr/lib/app2
...
Here, ``app1`` and ``app2`` share the values for ``opts`` and ``port`` using the anchor ``&jvm_opts`` and the alias ``*jvm_opts``.
The value for ``path`` is merged by ``<<`` or `merge operator <https://yaml.org/type/merge.html>`_.
Anchors and aliases also let you share complex sets of variable values, including nested variables. If you have one variable value that includes another variable value, you can define them separately:
.. code-block:: yaml
vars:
webapp_version: 1.0
webapp_custom_name: ToDo_App-1.0
This is inefficient and, at scale, means more maintenance. To incorporate the version value in the name, you can use an anchor in ``app_version`` and an alias in ``custom_name``:
.. code-block:: yaml
vars:
webapp:
version: &my_version 1.0
custom_name:
- "ToDo_App"
- *my_version
Now, you can re-use the value of ``app_version`` within the value of ``custom_name`` and use the output in a template:
.. code-block:: yaml
---
- name: Using values nested inside dictionary
hosts: localhost
vars:
webapp:
version: &my_version 1.0
custom_name:
- "ToDo_App"
- *my_version
tasks:
- name: Using Anchor value
ansible.builtin.debug:
msg: My app is called "{{ webapp.custom_name | join('-') }}".
You've anchored the value of ``version`` with the ``&my_version`` anchor, and re-used it with the ``*my_version`` alias. Anchors and aliases let you access nested values inside dictionaries.
.. seealso::
:ref:`playbooks_variables`
All about variables
:ref:`complex_data_manipulation`
Doing complex data manipulation in Ansible
`User Mailing List <https://groups.google.com/group/ansible-project>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,189 @@
.. _playbooks_async:
Asynchronous actions and polling
================================
By default Ansible runs tasks synchronously, holding the connection to the remote node open until the action is completed. This means within a playbook, each task blocks the next task by default, meaning subsequent tasks will not run until the current task completes. This behavior can create challenges. For example, a task may take longer to complete than the SSH session allows for, causing a timeout. Or you may want a long-running process to execute in the background while you perform other tasks concurrently. Asynchronous mode lets you control how long-running tasks execute.
.. contents::
:local:
Asynchronous ad hoc tasks
-------------------------
You can execute long-running operations in the background with :ref:`ad hoc tasks <intro_adhoc>`. For example, to execute ``long_running_operation`` asynchronously in the background, with a timeout (``-B``) of 3600 seconds, and without polling (``-P``):
.. code-block:: bash
$ ansible all -B 3600 -P 0 -a "/usr/bin/long_running_operation --do-stuff"
To check on the job status later, use the ``async_status`` module, passing it the job ID that was returned when you ran the original job in the background:
.. code-block:: bash
$ ansible web1.example.com -m async_status -a "jid=488359678239.2844"
Ansible can also check on the status of your long-running job automatically with polling. In most cases, Ansible will keep the connection to your remote node open between polls. To run for 30 minutes and poll for status every 60 seconds:
.. code-block:: bash
$ ansible all -B 1800 -P 60 -a "/usr/bin/long_running_operation --do-stuff"
Poll mode is smart so all jobs will be started before polling begins on any machine. Be sure to use a high enough ``--forks`` value if you want to get all of your jobs started very quickly. After the time limit (in seconds) runs out (``-B``), the process on the remote nodes will be terminated.
Asynchronous mode is best suited to long-running shell commands or software upgrades. Running the copy module asynchronously, for example, does not do a background file transfer.
Asynchronous playbook tasks
---------------------------
:ref:`Playbooks <working_with_playbooks>` also support asynchronous mode and polling, with a simplified syntax. You can use asynchronous mode in playbooks to avoid connection timeouts or to avoid blocking subsequent tasks. The behavior of asynchronous mode in a playbook depends on the value of `poll`.
Avoid connection timeouts: poll > 0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you want to set a longer timeout limit for a certain task in your playbook, use ``async`` with ``poll`` set to a positive value. Ansible will still block the next task in your playbook, waiting until the async task either completes, fails or times out. However, the task will only time out if it exceeds the timeout limit you set with the ``async`` parameter.
To avoid timeouts on a task, specify its maximum runtime and how frequently you would like to poll for status:
.. code-block:: yaml
---
- hosts: all
remote_user: root
tasks:
- name: Simulate long running op (15 sec), wait for up to 45 sec, poll every 5 sec
ansible.builtin.command: /bin/sleep 15
async: 45
poll: 5
.. note::
The default poll value is set by the :ref:`DEFAULT_POLL_INTERVAL` setting.
There is no default for the async time limit. If you leave off the
'async' keyword, the task runs synchronously, which is Ansible's
default.
.. note::
As of Ansible 2.3, async does not support check mode and will fail the
task when run in check mode. See :ref:`check_mode_dry` on how to
skip a task in check mode.
.. note::
When an async task completes with polling enabled, the temporary async job cache
file (by default in ~/.ansible_async/) is automatically removed.
Run tasks concurrently: poll = 0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you want to run multiple tasks in a playbook concurrently, use ``async`` with ``poll`` set to 0. When you set ``poll: 0``, Ansible starts the task and immediately moves on to the next task without waiting for a result. Each async task runs until it either completes, fails or times out (runs longer than its ``async`` value). The playbook run ends without checking back on async tasks.
To run a playbook task asynchronously:
.. code-block:: yaml
---
- hosts: all
remote_user: root
tasks:
- name: Simulate long running op, allow to run for 45 sec, fire and forget
ansible.builtin.command: /bin/sleep 15
async: 45
poll: 0
.. note::
Do not specify a poll value of 0 with operations that require exclusive locks (such as yum transactions) if you expect to run other commands later in the playbook against those same resources.
.. note::
Using a higher value for ``--forks`` will result in kicking off asynchronous tasks even faster. This also increases the efficiency of polling.
.. note::
When running with ``poll: 0``, Ansible will not automatically cleanup the async job cache file.
You will need to manually clean this up with the :ref:`async_status <async_status_module>` module
with ``mode: cleanup``.
If you need a synchronization point with an async task, you can register it to obtain its job ID and use the :ref:`async_status <async_status_module>` module to observe it in a later task. For example:
.. code-block:: yaml+jinja
- name: Run an async task
ansible.builtin.yum:
name: docker-io
state: present
async: 1000
poll: 0
register: yum_sleeper
- name: Check on an async task
async_status:
jid: "{{ yum_sleeper.ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 100
delay: 10
.. note::
If the value of ``async:`` is not high enough, this will cause the
"check on it later" task to fail because the temporary status file that
the ``async_status:`` is looking for will not have been written or no longer exist
.. note::
Asynchronous playbook tasks always return changed. If the task is using a module
that requires the user to annotate changes with ``changed_when``, ``creates``, and so on,
then those should be added to the following ``async_status`` task.
To run multiple asynchronous tasks while limiting the number of tasks running concurrently:
.. code-block:: yaml+jinja
#####################
# main.yml
#####################
- name: Run items asynchronously in batch of two items
vars:
sleep_durations:
- 1
- 2
- 3
- 4
- 5
durations: "{{ item }}"
include_tasks: execute_batch.yml
loop: "{{ sleep_durations | batch(2) | list }}"
#####################
# execute_batch.yml
#####################
- name: Async sleeping for batched_items
ansible.builtin.command: sleep {{ async_item }}
async: 45
poll: 0
loop: "{{ durations }}"
loop_control:
loop_var: "async_item"
register: async_results
- name: Check sync status
async_status:
jid: "{{ async_result_item.ansible_job_id }}"
loop: "{{ async_results.results }}"
loop_control:
loop_var: "async_result_item"
register: async_poll_results
until: async_poll_results.finished
retries: 30
.. seealso::
:ref:`playbooks_strategies`
Options for controlling playbook execution
:ref:`playbooks_intro`
An introduction to playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,189 @@
.. _playbooks_blocks:
******
Blocks
******
Blocks create logical groups of tasks. Blocks also offer ways to handle task errors, similar to exception handling in many programming languages.
.. contents::
:local:
Grouping tasks with blocks
==========================
All tasks in a block inherit directives applied at the block level. Most of what you can apply to a single task (with the exception of loops) can be applied at the block level, so blocks make it much easier to set data or directives common to the tasks. The directive does not affect the block itself, it is only inherited by the tasks enclosed by a block. For example, a `when` statement is applied to the tasks within a block, not to the block itself.
.. code-block:: YAML
:emphasize-lines: 3
:caption: Block example with named tasks inside the block
tasks:
- name: Install, configure, and start Apache
block:
- name: Install httpd and memcached
ansible.builtin.yum:
name:
- httpd
- memcached
state: present
- name: Apply the foo config template
ansible.builtin.template:
src: templates/src.j2
dest: /etc/foo.conf
- name: Start service bar and enable it
ansible.builtin.service:
name: bar
state: started
enabled: True
when: ansible_facts['distribution'] == 'CentOS'
become: true
become_user: root
ignore_errors: yes
In the example above, the 'when' condition will be evaluated before Ansible runs each of the three tasks in the block. All three tasks also inherit the privilege escalation directives, running as the root user. Finally, ``ignore_errors: yes`` ensures that Ansible continues to execute the playbook even if some of the tasks fail.
Names for blocks have been available since Ansible 2.3. We recommend using names in all tasks, within blocks or elsewhere, for better visibility into the tasks being executed when you run the playbook.
.. _block_error_handling:
Handling errors with blocks
===========================
You can control how Ansible responds to task errors using blocks with ``rescue`` and ``always`` sections.
Rescue blocks specify tasks to run when an earlier task in a block fails. This approach is similar to exception handling in many programming languages. Ansible only runs rescue blocks after a task returns a 'failed' state. Bad task definitions and unreachable hosts will not trigger the rescue block.
.. _block_rescue:
.. code-block:: YAML
:emphasize-lines: 3,14
:caption: Block error handling example
tasks:
- name: Handle the error
block:
- name: Print a message
ansible.builtin.debug:
msg: 'I execute normally'
- name: Force a failure
ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I never execute, due to the above task failing, :-('
rescue:
- name: Print when errors
ansible.builtin.debug:
msg: 'I caught an error, can do stuff here to fix it, :-)'
You can also add an ``always`` section to a block. Tasks in the ``always`` section run no matter what the task status of the previous block is.
.. _block_always:
.. code-block:: YAML
:emphasize-lines: 2,13
:caption: Block with always section
- name: Always do X
block:
- name: Print a message
ansible.builtin.debug:
msg: 'I execute normally'
- name: Force a failure
ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I never execute :-('
always:
- name: Always do this
ansible.builtin.debug:
msg: "This always executes, :-)"
Together, these elements offer complex error handling.
.. code-block:: YAML
:emphasize-lines: 2,13,24
:caption: Block with all sections
- name: Attempt and graceful roll back demo
block:
- name: Print a message
ansible.builtin.debug:
msg: 'I execute normally'
- name: Force a failure
ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I never execute, due to the above task failing, :-('
rescue:
- name: Print when errors
ansible.builtin.debug:
msg: 'I caught an error'
- name: Force a failure in middle of recovery! >:-)
ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I also never execute :-('
always:
- name: Always do this
ansible.builtin.debug:
msg: "This always executes"
The tasks in the ``block`` execute normally. If any tasks in the block return ``failed``, the ``rescue`` section executes tasks to recover from the error. The ``always`` section runs regardless of the results of the ``block`` and ``rescue`` sections.
If an error occurs in the block and the rescue task succeeds, Ansible reverts the failed status of the original task for the run and continues to run the play as if the original task had succeeded. The rescued task is considered successful, and does not trigger ``max_fail_percentage`` or ``any_errors_fatal`` configurations. However, Ansible still reports a failure in the playbook statistics.
You can use blocks with ``flush_handlers`` in a rescue task to ensure that all handlers run even if an error occurs:
.. code-block:: YAML
:emphasize-lines: 3,12
:caption: Block run handlers in error handling
tasks:
- name: Attempt and graceful roll back demo
block:
- name: Print a message
ansible.builtin.debug:
msg: 'I execute normally'
changed_when: yes
notify: run me even after an error
- name: Force a failure
ansible.builtin.command: /bin/false
rescue:
- name: Make sure all handlers run
meta: flush_handlers
handlers:
- name: Run me even after an error
ansible.builtin.debug:
msg: 'This handler runs even on error'
.. versionadded:: 2.1
Ansible provides a couple of variables for tasks in the ``rescue`` portion of a block:
ansible_failed_task
The task that returned 'failed' and triggered the rescue. For example, to get the name use ``ansible_failed_task.name``.
ansible_failed_result
The captured return result of the failed task that triggered the rescue. This would equate to having used this var in the ``register`` keyword.
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_reuse_roles`
Playbook organization by roles
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,107 @@
.. _check_mode_dry:
******************************************
Validating tasks: check mode and diff mode
******************************************
Ansible provides two modes of execution that validate tasks: check mode and diff mode. These modes can be used separately or together. They are useful when you are creating or editing a playbook or role and you want to know what it will do. In check mode, Ansible runs without making any changes on remote systems. Modules that support check mode report the changes they would have made. Modules that do not support check mode report nothing and do nothing. In diff mode, Ansible provides before-and-after comparisons. Modules that support diff mode display detailed information. You can combine check mode and diff mode for detailed validation of your playbook or role.
.. contents::
:local:
Using check mode
================
Check mode is just a simulation. It will not generate output for tasks that use :ref:`conditionals based on registered variables <conditionals_registered_vars>` (results of prior tasks). However, it is great for validating configuration management playbooks that run on one node at a time. To run a playbook in check mode:
.. code-block:: console
ansible-playbook foo.yml --check
.. _forcing_to_run_in_check_mode:
Enforcing or preventing check mode on tasks
-------------------------------------------
.. versionadded:: 2.2
If you want certain tasks to run in check mode always, or never, regardless of whether you run the playbook with or without ``--check``, you can add the ``check_mode`` option to those tasks:
- To force a task to run in check mode, even when the playbook is called without ``--check``, set ``check_mode: yes``.
- To force a task to run in normal mode and make changes to the system, even when the playbook is called with ``--check``, set ``check_mode: no``.
For example:
.. code-block:: yaml
tasks:
- name: This task will always make changes to the system
ansible.builtin.command: /something/to/run --even-in-check-mode
check_mode: no
- name: This task will never make changes to the system
ansible.builtin.lineinfile:
line: "important config"
dest: /path/to/myconfig.conf
state: present
check_mode: yes
register: changes_to_important_config
Running single tasks with ``check_mode: yes`` can be useful for testing Ansible modules, either to test the module itself or to test the conditions under which a module would make changes. You can register variables (see :ref:`playbooks_conditionals`) on these tasks for even more detail on the potential changes.
.. note:: Prior to version 2.2 only the equivalent of ``check_mode: no`` existed. The notation for that was ``always_run: yes``.
Skipping tasks or ignoring errors in check mode
-----------------------------------------------
.. versionadded:: 2.1
If you want to skip a task or ignore errors on a task when you run Ansible in check mode, you can use a boolean magic variable ``ansible_check_mode``, which is set to ``True`` when Ansible runs in check mode. For example:
.. code-block:: yaml
tasks:
- name: This task will be skipped in check mode
ansible.builtin.git:
repo: ssh://git@github.com/mylogin/hello.git
dest: /home/mylogin/hello
when: not ansible_check_mode
- name: This task will ignore errors in check mode
ansible.builtin.git:
repo: ssh://git@github.com/mylogin/hello.git
dest: /home/mylogin/hello
ignore_errors: "{{ ansible_check_mode }}"
.. _diff_mode:
Using diff mode
===============
The ``--diff`` option for ansible-playbook can be used alone or with ``--check``. When you run in diff mode, any module that supports diff mode reports the changes made or, if used with ``--check``, the changes that would have been made. Diff mode is most common in modules that manipulate files (for example, the template module) but other modules might also show 'before and after' information (for example, the user module).
Diff mode produces a large amount of output, so it is best used when checking a single host at a time. For example:
.. code-block:: console
ansible-playbook foo.yml --check --diff --limit foo.example.com
.. versionadded:: 2.4
Enforcing or preventing diff mode on tasks
------------------------------------------
Because the ``--diff`` option can reveal sensitive information, you can disable it for a task by specifying ``diff: no``. For example:
.. code-block:: yaml
tasks:
- name: This task will not report a diff when the file changes
ansible.builtin.template:
src: secret.conf.j2
dest: /etc/secret.conf
owner: root
group: root
mode: '0600'
diff: no

@ -0,0 +1,551 @@
.. _playbooks_conditionals:
************
Conditionals
************
In a playbook, you may want to execute different tasks, or have different goals, depending on the value of a fact (data about the remote system), a variable, or the result of a previous task. You may want the value of some variables to depend on the value of other variables. Or you may want to create additional groups of hosts based on whether the hosts match other criteria. You can do all of these things with conditionals.
Ansible uses Jinja2 :ref:`tests <playbooks_tests>` and :ref:`filters <playbooks_filters>` in conditionals. Ansible supports all the standard tests and filters, and adds some unique ones as well.
.. note::
There are many options to control execution flow in Ansible. You can find more examples of supported conditionals at `<https://jinja.palletsprojects.com/en/latest/templates/#comparisons>`_.
.. contents::
:local:
.. _the_when_statement:
Basic conditionals with ``when``
================================
The simplest conditional statement applies to a single task. Create the task, then add a ``when`` statement that applies a test. The ``when`` clause is a raw Jinja2 expression without double curly braces (see :ref:`group_by_module`). When you run the task or playbook, Ansible evaluates the test for all hosts. On any host where the test passes (returns a value of True), Ansible runs that task. For example, if you are installing mysql on multiple machines, some of which have SELinux enabled, you might have a task to configure SELinux to allow mysql to run. You would only want that task to run on machines that have SELinux enabled:
.. code-block:: yaml
tasks:
- name: Configure SELinux to start mysql on any port
ansible.posix.seboolean:
name: mysql_connect_any
state: true
persistent: yes
when: ansible_selinux.status == "enabled"
# all variables can be used directly in conditionals without double curly braces
Conditionals based on ansible_facts
-----------------------------------
Often you want to execute or skip a task based on facts. Facts are attributes of individual hosts, including IP address, operating system, the status of a filesystem, and many more. With conditionals based on facts:
- You can install a certain package only when the operating system is a particular version.
- You can skip configuring a firewall on hosts with internal IP addresses.
- You can perform cleanup tasks only when a filesystem is getting full.
See :ref:`commonly_used_facts` for a list of facts that frequently appear in conditional statements. Not all facts exist for all hosts. For example, the 'lsb_major_release' fact used in an example below only exists when the lsb_release package is installed on the target host. To see what facts are available on your systems, add a debug task to your playbook:
.. code-block:: yaml
- name: Show facts available on the system
ansible.builtin.debug:
var: ansible_facts
Here is a sample conditional based on a fact:
.. code-block:: yaml
tasks:
- name: Shut down Debian flavored systems
ansible.builtin.command: /sbin/shutdown -t now
when: ansible_facts['os_family'] == "Debian"
If you have multiple conditions, you can group them with parentheses:
.. code-block:: yaml
tasks:
- name: Shut down CentOS 6 and Debian 7 systems
ansible.builtin.command: /sbin/shutdown -t now
when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
(ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")
You can use `logical operators <https://jinja.palletsprojects.com/en/latest/templates/#logic>`_ to combine conditions. When you have multiple conditions that all need to be true (that is, a logical ``and``), you can specify them as a list:
.. code-block:: yaml
tasks:
- name: Shut down CentOS 6 systems
ansible.builtin.command: /sbin/shutdown -t now
when:
- ansible_facts['distribution'] == "CentOS"
- ansible_facts['distribution_major_version'] == "6"
If a fact or variable is a string, and you need to run a mathematical comparison on it, use a filter to ensure that Ansible reads the value as an integer:
.. code-block:: yaml
tasks:
- ansible.builtin.shell: echo "only on Red Hat 6, derivatives, and later"
when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release'] | int >= 6
.. _conditionals_registered_vars:
Conditions based on registered variables
----------------------------------------
Often in a playbook you want to execute or skip a task based on the outcome of an earlier task. For example, you might want to configure a service after it is upgraded by an earlier task. To create a conditional based on a registered variable:
#. Register the outcome of the earlier task as a variable.
#. Create a conditional test based on the registered variable.
You create the name of the registered variable using the ``register`` keyword. A registered variable always contains the status of the task that created it as well as any output that task generated. You can use registered variables in templates and action lines as well as in conditional ``when`` statements. You can access the string contents of the registered variable using ``variable.stdout``. For example:
.. code-block:: yaml
- name: Test play
hosts: all
tasks:
- name: Register a variable
ansible.builtin.shell: cat /etc/motd
register: motd_contents
- name: Use the variable in conditional statement
ansible.builtin.shell: echo "motd contains the word hi"
when: motd_contents.stdout.find('hi') != -1
You can use registered results in the loop of a task if the variable is a list. If the variable is not a list, you can convert it into a list, with either ``stdout_lines`` or with ``variable.stdout.split()``. You can also split the lines by other fields:
.. code-block:: yaml
- name: Registered variable usage as a loop list
hosts: all
tasks:
- name: Retrieve the list of home directories
ansible.builtin.command: ls /home
register: home_dirs
- name: Add home dirs to the backup spooler
ansible.builtin.file:
path: /mnt/bkspool/{{ item }}
src: /home/{{ item }}
state: link
loop: "{{ home_dirs.stdout_lines }}"
# same as loop: "{{ home_dirs.stdout.split() }}"
The string content of a registered variable can be empty. If you want to run another task only on hosts where the stdout of your registered variable is empty, check the registered variable's string contents for emptiness:
.. code-block:: yaml
- name: check registered variable for emptiness
hosts: all
tasks:
- name: List contents of directory
ansible.builtin.command: ls mydir
register: contents
- name: Check contents for emptiness
ansible.builtin.debug:
msg: "Directory is empty"
when: contents.stdout == ""
Ansible always registers something in a registered variable for every host, even on hosts where a task fails or Ansible skips a task because a condition is not met. To run a follow-up task on these hosts, query the registered variable for ``is skipped`` (not for "undefined" or "default"). See :ref:`registered_variables` for more information. Here are sample conditionals based on the success or failure of a task. Remember to ignore errors if you want Ansible to continue executing on a host when a failure occurs:
.. code-block:: yaml
tasks:
- name: Register a variable, ignore errors and continue
ansible.builtin.command: /bin/false
register: result
ignore_errors: true
- name: Run only if the task that registered the "result" variable fails
ansible.builtin.command: /bin/something
when: result is failed
- name: Run only if the task that registered the "result" variable succeeds
ansible.builtin.command: /bin/something_else
when: result is succeeded
- name: Run only if the task that registered the "result" variable is skipped
ansible.builtin.command: /bin/still/something_else
when: result is skipped
.. note:: Older versions of Ansible used ``success`` and ``fail``, but ``succeeded`` and ``failed`` use the correct tense. All of these options are now valid.
Conditionals based on variables
-------------------------------
You can also create conditionals based on variables defined in the playbooks or inventory. Because conditionals require boolean input (a test must evaluate as True to trigger the condition), you must apply the ``| bool`` filter to non boolean variables, such as string variables with content like 'yes', 'on', '1', or 'true'. You can define variables like this:
.. code-block:: yaml
vars:
epic: true
monumental: "yes"
With the variables above, Ansible would run one of these tasks and skip the other:
.. code-block:: yaml
tasks:
- name: Run the command if "epic" or "monumental" is true
ansible.builtin.shell: echo "This certainly is epic!"
when: epic or monumental | bool
- name: Run the command if "epic" is false
ansible.builtin.shell: echo "This certainly isn't epic!"
when: not epic
If a required variable has not been set, you can skip or fail using Jinja2's `defined` test. For example:
.. code-block:: yaml
tasks:
- name: Run the command if "foo" is defined
ansible.builtin.shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
when: foo is defined
- name: Fail if "bar" is undefined
ansible.builtin.fail: msg="Bailing out. This play requires 'bar'"
when: bar is undefined
This is especially useful in combination with the conditional import of vars files (see below).
As the examples show, you do not need to use `{{ }}` to use variables inside conditionals, as these are already implied.
.. _loops_and_conditionals:
Using conditionals in loops
---------------------------
If you combine a ``when`` statement with a :ref:`loop <playbooks_loops>`, Ansible processes the condition separately for each item. This is by design, so you can execute the task on some items in the loop and skip it on other items. For example:
.. code-block:: yaml
tasks:
- name: Run with items greater than 5
ansible.builtin.command: echo {{ item }}
loop: [ 0, 2, 4, 6, 8, 10 ]
when: item > 5
If you need to skip the whole task when the loop variable is undefined, use the `|default` filter to provide an empty iterator. For example, when looping over a list:
.. code-block:: yaml
- name: Skip the whole task when a loop variable is undefined
ansible.builtin.command: echo {{ item }}
loop: "{{ mylist|default([]) }}"
when: item > 5
You can do the same thing when looping over a dict:
.. code-block:: yaml
- name: The same as above using a dict
ansible.builtin.command: echo {{ item.key }}
loop: "{{ query('dict', mydict|default({})) }}"
when: item.value > 5
.. _loading_in_custom_facts:
Loading custom facts
--------------------
You can provide your own facts, as described in :ref:`developing_modules`. To run them, just make a call to your own custom fact gathering module at the top of your list of tasks, and variables returned there will be accessible to future tasks:
.. code-block:: yaml
tasks:
- name: Gather site specific fact data
action: site_facts
- name: Use a custom fact
ansible.builtin.command: /usr/bin/thingy
when: my_custom_fact_just_retrieved_from_the_remote_system == '1234'
.. _when_with_reuse:
Conditionals with re-use
------------------------
You can use conditionals with re-usable tasks files, playbooks, or roles. Ansible executes these conditional statements differently for dynamic re-use (includes) and for static re-use (imports). See :ref:`playbooks_reuse` for more information on re-use in Ansible.
.. _conditional_imports:
Conditionals with imports
^^^^^^^^^^^^^^^^^^^^^^^^^
When you add a conditional to an import statement, Ansible applies the condition to all tasks within the imported file. This behavior is the equivalent of :ref:`tag_inheritance`. Ansible applies the condition to every task, and evaluates each task separately. For example, if you want to define and then display a variable that was not previously defined, you might have a playbook called ``main.yml`` and a tasks file called ``other_tasks.yml``:
.. code-block:: yaml
# all tasks within an imported file inherit the condition from the import statement
# main.yml
- hosts: all
tasks:
- import_tasks: other_tasks.yml # note "import"
when: x is not defined
# other_tasks.yml
- name: Set a variable
ansible.builtin.set_fact:
x: foo
- name: Print a variable
ansible.builtin.debug:
var: x
Ansible expands this at execution time to the equivalent of:
.. code-block:: yaml
- name: Set a variable if not defined
ansible.builtin.set_fact:
x: foo
when: x is not defined
# this task sets a value for x
- name: Do the task if "x" is not defined
ansible.builtin.debug:
var: x
when: x is not defined
# Ansible skips this task, because x is now defined
If ``x`` is initially defined, both tasks are skipped as intended. But if ``x`` is initially undefined, the debug task will be skipped since the conditional is evaluated for every imported task. The conditional will evaluate to ``true`` for the ``set_fact`` task, which will define the variable and cause the ``debug`` conditional to evaluate to ``false``.
If this is not the behavior you want, use an ``include_*`` statement to apply a condition only to that statement itself.
.. code-block:: yaml
# using a conditional on include_* only applies to the include task itself
# main.yml
- hosts: all
tasks:
- include_tasks: other_tasks.yml # note "include"
when: x is not defined
Now if ``x`` is initially undefined, the debug task will not be skipped because the conditional is evaluated at the time of the include and does not apply to the individual tasks.
You can apply conditions to ``import_playbook`` as well as to the other ``import_*`` statements. When you use this approach, Ansible returns a 'skipped' message for every task on every host that does not match the criteria, creating repetitive output. In many cases the :ref:`group_by module <group_by_module>` can be a more streamlined way to accomplish the same objective; see :ref:`os_variance`.
.. _conditional_includes:
Conditionals with includes
^^^^^^^^^^^^^^^^^^^^^^^^^^
When you use a conditional on an ``include_*`` statement, the condition is applied only to the include task itself and not to any other tasks within the included file(s). To contrast with the example used for conditionals on imports above, look at the same playbook and tasks file, but using an include instead of an import:
.. code-block:: yaml
# Includes let you re-use a file to define a variable when it is not already defined
# main.yml
- include_tasks: other_tasks.yml
when: x is not defined
# other_tasks.yml
- name: Set a variable
ansible.builtin.set_fact:
x: foo
- name: Print a variable
ansible.builtin.debug:
var: x
Ansible expands this at execution time to the equivalent of:
.. code-block:: yaml
# main.yml
- include_tasks: other_tasks.yml
when: x is not defined
# if condition is met, Ansible includes other_tasks.yml
# other_tasks.yml
- name: Set a variable
ansible.builtin.set_fact:
x: foo
# no condition applied to this task, Ansible sets the value of x to foo
- name: Print a variable
ansible.builtin.debug:
var: x
# no condition applied to this task, Ansible prints the debug statement
By using ``include_tasks`` instead of ``import_tasks``, both tasks from ``other_tasks.yml`` will be executed as expected. For more information on the differences between ``include`` v ``import`` see :ref:`playbooks_reuse`.
Conditionals with roles
^^^^^^^^^^^^^^^^^^^^^^^
There are three ways to apply conditions to roles:
- Add the same condition or conditions to all tasks in the role by placing your ``when`` statement under the ``roles`` keyword. See the example in this section.
- Add the same condition or conditions to all tasks in the role by placing your ``when`` statement on a static ``import_role`` in your playbook.
- Add a condition or conditions to individual tasks or blocks within the role itself. This is the only approach that allows you to select or skip some tasks within the role based on your ``when`` statement. To select or skip tasks within the role, you must have conditions set on individual tasks or blocks, use the dynamic ``include_role`` in your playbook, and add the condition or conditions to the include. When you use this approach, Ansible applies the condition to the include itself plus any tasks in the role that also have that ``when`` statement.
When you incorporate a role in your playbook statically with the ``roles`` keyword, Ansible adds the conditions you define to all the tasks in the role. For example:
.. code-block:: yaml
- hosts: webservers
roles:
- role: debian_stock_config
when: ansible_facts['os_family'] == 'Debian'
.. _conditional_variable_and_files:
Selecting variables, files, or templates based on facts
-------------------------------------------------------
Sometimes the facts about a host determine the values you want to use for certain variables or even the file or template you want to select for that host. For example, the names of packages are different on CentOS and on Debian. The configuration files for common services are also different on different OS flavors and versions. To load different variables file, templates, or other files based on a fact about the hosts:
1) name your vars files, templates, or files to match the Ansible fact that differentiates them
2) select the correct vars file, template, or file for each host with a variable based on that Ansible fact
Ansible separates variables from tasks, keeping your playbooks from turning into arbitrary code with nested conditionals. This approach results in more streamlined and auditable configuration rules because there are fewer decision points to track.
Selecting variables files based on facts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can create a playbook that works on multiple platforms and OS versions with a minimum of syntax by placing your variable values in vars files and conditionally importing them. If you want to install Apache on some CentOS and some Debian servers, create variables files with YAML keys and values. For example:
.. code-block:: yaml
---
# for vars/RedHat.yml
apache: httpd
somethingelse: 42
Then import those variables files based on the facts you gather on the hosts in your playbook:
.. code-block:: yaml
---
- hosts: webservers
remote_user: root
vars_files:
- "vars/common.yml"
- [ "vars/{{ ansible_facts['os_family'] }}.yml", "vars/os_defaults.yml" ]
tasks:
- name: Make sure apache is started
ansible.builtin.service:
name: '{{ apache }}'
state: started
Ansible gathers facts on the hosts in the webservers group, then interpolates the variable "ansible_facts['os_family']" into a list of filenames. If you have hosts with Red Hat operating systems (CentOS, for example), Ansible looks for 'vars/RedHat.yml'. If that file does not exist, Ansible attempts to load 'vars/os_defaults.yml'. For Debian hosts, Ansible first looks for 'vars/Debian.yml', before falling back on 'vars/os_defaults.yml'. If no files in the list are found, Ansible raises an error.
Selecting files and templates based on facts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can use the same approach when different OS flavors or versions require different configuration files or templates. Select the appropriate file or template based on the variables assigned to each host. This approach is often much cleaner than putting a lot of conditionals into a single template to cover multiple OS or package versions.
For example, you can template out a configuration file that is very different between, say, CentOS and Debian:
.. code-block:: yaml
- name: Template a file
ansible.builtin.template:
src: "{{ item }}"
dest: /etc/myapp/foo.conf
loop: "{{ query('first_found', { 'files': myfiles, 'paths': mypaths}) }}"
vars:
myfiles:
- "{{ ansible_facts['distribution'] }}.conf"
- default.conf
mypaths: ['search_location_one/somedir/', '/opt/other_location/somedir/']
.. _commonly_used_facts:
Commonly-used facts
===================
The following Ansible facts are frequently used in conditionals.
.. _ansible_distribution:
ansible_facts['distribution']
-----------------------------
Possible values (sample, not complete list):
.. code-block:: text
Alpine
Altlinux
Amazon
Archlinux
ClearLinux
Coreos
CentOS
Debian
Fedora
Gentoo
Mandriva
NA
OpenWrt
OracleLinux
RedHat
Slackware
SLES
SMGL
SUSE
Ubuntu
VMwareESX
.. See `OSDIST_LIST`
.. _ansible_distribution_major_version:
ansible_facts['distribution_major_version']
-------------------------------------------
The major version of the operating system. For example, the value is `16` for Ubuntu 16.04.
.. _ansible_os_family:
ansible_facts['os_family']
--------------------------
Possible values (sample, not complete list):
.. code-block:: text
AIX
Alpine
Altlinux
Archlinux
Darwin
Debian
FreeBSD
Gentoo
HP-UX
Mandrake
RedHat
SGML
Slackware
Solaris
Suse
Windows
.. Ansible checks `OS_FAMILY_MAP`; if there's no match, it returns the value of `platform.system()`.
.. seealso::
:ref:`working_with_playbooks`
An introduction to playbooks
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`tips_and_tricks`
Tips and tricks for playbooks
:ref:`playbooks_variables`
All about variables
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,342 @@
.. _playbook_debugger:
***************
Debugging tasks
***************
Ansible offers a task debugger so you can fix errors during execution instead of editing your playbook and running it again to see if your change worked. You have access to all of the features of the debugger in the context of the task. You can check or set the value of variables, update module arguments, and re-run the task with the new variables and arguments. The debugger lets you resolve the cause of the failure and continue with playbook execution.
.. contents::
:local:
Enabling the debugger
=====================
The debugger is not enabled by default. If you want to invoke the debugger during playbook execution, you must enable it first.
Use one of these three methods to enable the debugger:
* with the debugger keyword
* in configuration or an environment variable, or
* as a strategy
Enabling the debugger with the ``debugger`` keyword
---------------------------------------------------
.. versionadded:: 2.5
You can use the ``debugger`` keyword to enable (or disable) the debugger for a specific play, role, block, or task. This option is especially useful when developing or extending playbooks, plays, and roles. You can enable the debugger on new or updated tasks. If they fail, you can fix the errors efficiently. The ``debugger`` keyword accepts five values:
.. table::
:class: documentation-table
========================= ======================================================
Value Result
========================= ======================================================
always Always invoke the debugger, regardless of the outcome
never Never invoke the debugger, regardless of the outcome
on_failed Only invoke the debugger if a task fails
on_unreachable Only invoke the debugger if a host is unreachable
on_skipped Only invoke the debugger if the task is skipped
========================= ======================================================
When you use the ``debugger`` keyword, the value you specify overrides any global configuration to enable or disable the debugger. If you define ``debugger`` at multiple levels, such as in a role and in a task, Ansible honors the most granular definition. The definition at the play or role level applies to all blocks and tasks within that play or role, unless they specify a different value. The definition at the block level overrides the definition at the play or role level, and applies to all tasks within that block, unless they specify a different value. The definition at the task level always applies to the task; it overrides the definitions at the block, play, or role level.
Examples of using the ``debugger`` keyword
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Example of setting the ``debugger`` keyword on a task:
.. code-block:: yaml
- name: Execute a command
ansible.builtin.command: "false"
debugger: on_failed
Example of setting the ``debugger`` keyword on a play:
.. code-block:: yaml
- name: My play
hosts: all
debugger: on_skipped
tasks:
- name: Execute a command
ansible.builtin.command: "true"
when: False
Example of setting the ``debugger`` keyword at multiple levels:
.. code-block:: yaml
- name: Play
hosts: all
debugger: never
tasks:
- name: Execute a command
ansible.builtin.command: "false"
debugger: on_failed
In this example, the debugger is set to ``never`` at the play level and to ``on_failed`` at the task level. If the task fails, Ansible invokes the debugger, because the definition on the task overrides the definition on its parent play.
Enabling the debugger in configuration or an environment variable
-----------------------------------------------------------------
.. versionadded:: 2.5
You can enable the task debugger globally with a setting in ansible.cfg or with an environment variable. The only options are ``True`` or ``False``. If you set the configuration option or environment variable to ``True``, Ansible runs the debugger on failed tasks by default.
To enable the task debugger from ansible.cfg, add this setting to the defaults section:
.. code-block:: yaml
[defaults]
enable_task_debugger = True
To enable the task debugger with an environment variable, pass the variable when you run your playbook:
.. code-block:: shell
ANSIBLE_ENABLE_TASK_DEBUGGER=True ansible-playbook -i hosts site.yml
When you enable the debugger globally, every failed task invokes the debugger, unless the role, play, block, or task explicitly disables the debugger. If you need more granular control over what conditions trigger the debugger, use the ``debugger`` keyword.
Enabling the debugger as a strategy
-----------------------------------
If you are running legacy playbooks or roles, you may see the debugger enabled as a :ref:`strategy <strategy_plugins>`. You can do this at the play level, in ansible.cfg, or with the environment variable ``ANSIBLE_STRATEGY=debug``. For example:
.. code-block:: yaml
- hosts: test
strategy: debug
tasks:
...
Or in ansible.cfg:
.. code-block:: ini
[defaults]
strategy = debug
.. note::
This backwards-compatible method, which matches Ansible versions before 2.5, may be removed in a future release.
Resolving errors in the debugger
================================
After Ansible invokes the debugger, you can use the seven :ref:`debugger commands <available_commands>` to resolve the error that Ansible encountered. Consider this example playbook, which defines the ``var1`` variable but uses the undefined ``wrong_var`` variable in a task by mistake.
.. code-block:: yaml
- hosts: test
debugger: on_failed
gather_facts: no
vars:
var1: value1
tasks:
- name: Use a wrong variable
ansible.builtin.ping: data={{ wrong_var }}
If you run this playbook, Ansible invokes the debugger when the task fails. From the debug prompt, you can change the module arguments or the variables and run the task again.
.. code-block:: ansible-output
PLAY ***************************************************************************
TASK [wrong variable] **********************************************************
fatal: [192.0.2.10]: FAILED! => {"failed": true, "msg": "ERROR! 'wrong_var' is undefined"}
Debugger invoked
[192.0.2.10] TASK: wrong variable (debug)> p result._result
{'failed': True,
'msg': 'The task includes an option with an undefined variable. The error '
"was: 'wrong_var' is undefined\n"
'\n'
'The error appears to have been in '
"'playbooks/debugger.yml': line 7, "
'column 7, but may\n'
'be elsewhere in the file depending on the exact syntax problem.\n'
'\n'
'The offending line appears to be:\n'
'\n'
' tasks:\n'
' - name: wrong variable\n'
' ^ here\n'}
[192.0.2.10] TASK: wrong variable (debug)> p task.args
{u'data': u'{{ wrong_var }}'}
[192.0.2.10] TASK: wrong variable (debug)> task.args['data'] = '{{ var1 }}'
[192.0.2.10] TASK: wrong variable (debug)> p task.args
{u'data': '{{ var1 }}'}
[192.0.2.10] TASK: wrong variable (debug)> redo
ok: [192.0.2.10]
PLAY RECAP *********************************************************************
192.0.2.10 : ok=1 changed=0 unreachable=0 failed=0
Changing the task arguments in the debugger to use ``var1`` instead of ``wrong_var`` makes the task run successfully.
.. _available_commands:
Available debug commands
========================
You can use these seven commands at the debug prompt:
.. table::
:class: documentation-table
========================== ============ =========================================================
Command Shortcut Action
========================== ============ =========================================================
print p Print information about the task
task.args[*key*] = *value* no shortcut Update module arguments
task_vars[*key*] = *value* no shortcut Update task variables (you must ``update_task`` next)
update_task u Recreate a task with updated task variables
redo r Run the task again
continue c Continue executing, starting with the next task
quit q Quit the debugger
========================== ============ =========================================================
For more details, see the individual descriptions and examples below.
.. _pprint_command:
Print command
-------------
``print *task/task.args/task_vars/host/result*`` prints information about the task.
.. code-block:: ansible-output
[192.0.2.10] TASK: install package (debug)> p task
TASK: install package
[192.0.2.10] TASK: install package (debug)> p task.args
{u'name': u'{{ pkg_name }}'}
[192.0.2.10] TASK: install package (debug)> p task_vars
{u'ansible_all_ipv4_addresses': [u'192.0.2.10'],
u'ansible_architecture': u'x86_64',
...
}
[192.0.2.10] TASK: install package (debug)> p task_vars['pkg_name']
u'bash'
[192.0.2.10] TASK: install package (debug)> p host
192.0.2.10
[192.0.2.10] TASK: install package (debug)> p result._result
{'_ansible_no_log': False,
'changed': False,
u'failed': True,
...
u'msg': u"No package matching 'not_exist' is available"}
.. _update_args_command:
Update args command
-------------------
``task.args[*key*] = *value*`` updates a module argument. This sample playbook has an invalid package name.
.. code-block:: yaml
- hosts: test
strategy: debug
gather_facts: yes
vars:
pkg_name: not_exist
tasks:
- name: Install a package
ansible.builtin.apt: name={{ pkg_name }}
When you run the playbook, the invalid package name triggers an error, and Ansible invokes the debugger. You can fix the package name by viewing, then updating the module argument.
.. code-block:: ansible-output
[192.0.2.10] TASK: install package (debug)> p task.args
{u'name': u'{{ pkg_name }}'}
[192.0.2.10] TASK: install package (debug)> task.args['name'] = 'bash'
[192.0.2.10] TASK: install package (debug)> p task.args
{u'name': 'bash'}
[192.0.2.10] TASK: install package (debug)> redo
After you update the module argument, use ``redo`` to run the task again with the new args.
.. _update_vars_command:
Update vars command
-------------------
``task_vars[*key*] = *value*`` updates the ``task_vars``. You could fix the playbook above by viewing, then updating the task variables instead of the module args.
.. code-block:: ansible-output
[192.0.2.10] TASK: install package (debug)> p task_vars['pkg_name']
u'not_exist'
[192.0.2.10] TASK: install package (debug)> task_vars['pkg_name'] = 'bash'
[192.0.2.10] TASK: install package (debug)> p task_vars['pkg_name']
'bash'
[192.0.2.10] TASK: install package (debug)> update_task
[192.0.2.10] TASK: install package (debug)> redo
After you update the task variables, you must use ``update_task`` to load the new variables before using ``redo`` to run the task again.
.. note::
In 2.5 this was updated from ``vars`` to ``task_vars`` to avoid conflicts with the ``vars()`` python function.
.. _update_task_command:
Update task command
-------------------
.. versionadded:: 2.8
``u`` or ``update_task`` recreates the task from the original task data structure and templates with updated task variables. See the entry :ref:`update_vars_command` for an example of use.
.. _redo_command:
Redo command
------------
``r`` or ``redo`` runs the task again.
.. _continue_command:
Continue command
----------------
``c`` or ``continue`` continues executing, starting with the next task.
.. _quit_command:
Quit command
------------
``q`` or ``quit`` quits the debugger. The playbook execution is aborted.
How the debugger interacts with the free strategy
=================================================
With the default ``linear`` strategy enabled, Ansible halts execution while the debugger is active, and runs the debugged task immediately after you enter the ``redo`` command. With the ``free`` strategy enabled, however, Ansible does not wait for all hosts, and may queue later tasks on one host before a task fails on another host. With the ``free`` strategy, Ansible does not queue or execute any tasks while the debugger is active. However, all queued tasks remain in the queue and run as soon as you exit the debugger. If you use ``redo`` to reschedule a task from the debugger, other queued tasks may execute before your rescheduled task. For more information about strategies, see :ref:`playbooks_strategies`.
.. seealso::
:ref:`playbooks_start_and_step`
Running playbooks while debugging or testing
:ref:`playbooks_intro`
An introduction to playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,174 @@
.. _playbooks_delegation:
Controlling where tasks run: delegation and local actions
=========================================================
By default Ansible gathers facts and executes all tasks on the machines that match the ``hosts`` line of your playbook. This page shows you how to delegate tasks to a different machine or group, delegate facts to specific machines or groups, or run an entire playbook locally. Using these approaches, you can manage inter-related environments precisely and efficiently. For example, when updating your webservers, you might need to remove them from a load-balanced pool temporarily. You cannot perform this task on the webservers themselves. By delegating the task to localhost, you keep all the tasks within the same play.
.. contents::
:local:
Tasks that cannot be delegated
------------------------------
Some tasks always execute on the controller. These tasks, including ``include``, ``add_host``, and ``debug``, cannot be delegated.
.. _delegation:
Delegating tasks
----------------
If you want to perform a task on one host with reference to other hosts, use the ``delegate_to`` keyword on a task. This is ideal for managing nodes in a load balanced pool or for controlling outage windows. You can use delegation with the :ref:`serial <rolling_update_batch_size>` keyword to control the number of hosts executing at one time:
.. code-block:: yaml
---
- hosts: webservers
serial: 5
tasks:
- name: Take out of load balancer pool
ansible.builtin.command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
- name: Actual steps would go here
ansible.builtin.yum:
name: acme-web-stack
state: latest
- name: Add back to load balancer pool
ansible.builtin.command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
The first and third tasks in this play run on 127.0.0.1, which is the machine running Ansible. There is also a shorthand syntax that you can use on a per-task basis: ``local_action``. Here is the same playbook as above, but using the shorthand syntax for delegating to 127.0.0.1:
.. code-block:: yaml
---
# ...
tasks:
- name: Take out of load balancer pool
local_action: ansible.builtin.command /usr/bin/take_out_of_pool {{ inventory_hostname }}
# ...
- name: Add back to load balancer pool
local_action: ansible.builtin.command /usr/bin/add_back_to_pool {{ inventory_hostname }}
You can use a local action to call 'rsync' to recursively copy files to the managed servers:
.. code-block:: yaml
---
# ...
tasks:
- name: Recursively copy files from management server to target
local_action: ansible.builtin.command rsync -a /path/to/files {{ inventory_hostname }}:/path/to/target/
Note that you must have passphrase-less SSH keys or an ssh-agent configured for this to work, otherwise rsync asks for a passphrase.
To specify more arguments, use the following syntax:
.. code-block:: yaml
---
# ...
tasks:
- name: Send summary mail
local_action:
module: community.general.mail
subject: "Summary Mail"
to: "{{ mail_recipient }}"
body: "{{ mail_body }}"
run_once: True
.. note::
- The `ansible_host` variable and other connection variables, if present, reflects information about the host a task is delegated to, not the inventory_hostname.
.. warning::
Although you can ``delegate_to`` a host that does not exist in inventory (by adding IP address, DNS name or whatever requirement the connection plugin has), doing so does not add the host to your inventory and might cause issues. Hosts delegated to in this way do not inherit variables from the "all" group', so variables like connection user and key are missing. If you must ``delegate_to`` a non-inventory host, use the :ref:`add host module <add_host_module>`.
.. _delegate_parallel:
Delegation and parallel execution
---------------------------------
By default Ansible tasks are executed in parallel. Delegating a task does not change this and does not handle concurrency issues (multiple forks writing to the same file).
Most commonly, users are affected by this when updating a single file on a single delegated to host for all hosts (using the ``copy``, ``template``, or ``lineinfile`` modules, for example). They will still operate in parallel forks (default 5) and overwrite each other.
This can be handled in several ways:
.. code-block:: yaml
- name: "handle concurrency with a loop on the hosts with `run_once: true`"
lineinfile: "<options here>"
run_once: true
loop: '{{ ansible_play_hosts_all }}'
By using an intermediate play with `serial: 1` or using `throttle: 1` at task level, for more detail see :ref:`playbooks_strategies`
.. _delegate_facts:
Delegating facts
----------------
Delegating Ansible tasks is like delegating tasks in the real world - your groceries belong to you, even if someone else delivers them to your home. Similarly, any facts gathered by a delegated task are assigned by default to the `inventory_hostname` (the current host), not to the host which produced the facts (the delegated to host). To assign gathered facts to the delegated host instead of the current host, set ``delegate_facts`` to ``true``:
.. code-block:: yaml
---
- hosts: app_servers
tasks:
- name: Gather facts from db servers
ansible.builtin.setup:
delegate_to: "{{ item }}"
delegate_facts: true
loop: "{{ groups['dbservers'] }}"
This task gathers facts for the machines in the dbservers group and assigns the facts to those machines, even though the play targets the app_servers group. This way you can lookup `hostvars['dbhost1']['ansible_default_ipv4']['address']` even though dbservers were not part of the play, or left out by using `--limit`.
.. _local_playbooks:
Local playbooks
---------------
It may be useful to use a playbook locally on a remote host, rather than by connecting over SSH. This can be useful for assuring the configuration of a system by putting a playbook in a crontab. This may also be used
to run a playbook inside an OS installer, such as an Anaconda kickstart.
To run an entire playbook locally, just set the ``hosts:`` line to ``hosts: 127.0.0.1`` and then run the playbook like so:
.. code-block:: shell
ansible-playbook playbook.yml --connection=local
Alternatively, a local connection can be used in a single playbook play, even if other plays in the playbook
use the default remote connection type:
.. code-block:: yaml
---
- hosts: 127.0.0.1
connection: local
.. note::
If you set the connection to local and there is no ansible_python_interpreter set, modules will run under /usr/bin/python and not
under {{ ansible_playbook_python }}. Be sure to set ansible_python_interpreter: "{{ ansible_playbook_python }}" in
host_vars/localhost.yml, for example. You can avoid this issue by using ``local_action`` or ``delegate_to: localhost`` instead.
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_strategies`
More ways to control how and where Ansible executes
`Ansible Examples on GitHub <https://github.com/ansible/ansible-examples>`_
Many examples of full-stack deployments
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,153 @@
.. _playbooks_environment:
Setting the remote environment
==============================
.. versionadded:: 1.1
You can use the ``environment`` keyword at the play, block, or task level to set an environment variable for an action on a remote host. With this keyword, you can enable using a proxy for a task that does http requests, set the required environment variables for language-specific version managers, and more.
When you set a value with ``environment:`` at the play or block level, it is available only to tasks within the play or block that are executed by the same user. The ``environment:`` keyword does not affect Ansible itself, Ansible configuration settings, the environment for other users, or the execution of other plugins like lookups and filters. Variables set with ``environment:`` do not automatically become Ansible facts, even when you set them at the play level. You must include an explicit ``gather_facts`` task in your playbook and set the ``environment`` keyword on that task to turn these values into Ansible facts.
.. contents::
:local:
Setting the remote environment in a task
----------------------------------------
You can set the environment directly at the task level.
.. code-block:: yaml
- hosts: all
remote_user: root
tasks:
- name: Install cobbler
ansible.builtin.package:
name: cobbler
state: present
environment:
http_proxy: http://proxy.example.com:8080
You can re-use environment settings by defining them as variables in your play and accessing them in a task as you would access any stored Ansible variable.
.. code-block:: yaml
- hosts: all
remote_user: root
# create a variable named "proxy_env" that is a dictionary
vars:
proxy_env:
http_proxy: http://proxy.example.com:8080
tasks:
- name: Install cobbler
ansible.builtin.package:
name: cobbler
state: present
environment: "{{ proxy_env }}"
You can store environment settings for re-use in multiple playbooks by defining them in a group_vars file.
.. code-block:: yaml
---
# file: group_vars/boston
ntp_server: ntp.bos.example.com
backup: bak.bos.example.com
proxy_env:
http_proxy: http://proxy.bos.example.com:8080
https_proxy: http://proxy.bos.example.com:8080
You can set the remote environment at the play level.
.. code-block:: yaml
- hosts: testing
roles:
- php
- nginx
environment:
http_proxy: http://proxy.example.com:8080
These examples show proxy settings, but you can provide any number of settings this way.
Working with language-specific version managers
===============================================
Some language-specific version managers (such as rbenv and nvm) require you to set environment variables while these tools are in use. When using these tools manually, you usually source some environment variables from a script or from lines added to your shell configuration file. In Ansible, you can do this with the environment keyword at the play level.
.. code-block:: yaml+jinja
---
### A playbook demonstrating a common npm workflow:
# - Check for package.json in the application directory
# - If package.json exists:
# * Run npm prune
# * Run npm install
- hosts: application
become: false
vars:
node_app_dir: /var/local/my_node_app
environment:
NVM_DIR: /var/local/nvm
PATH: /var/local/nvm/versions/node/v4.2.1/bin:{{ ansible_env.PATH }}
tasks:
- name: Check for package.json
ansible.builtin.stat:
path: '{{ node_app_dir }}/package.json'
register: packagejson
- name: Run npm prune
ansible.builtin.command: npm prune
args:
chdir: '{{ node_app_dir }}'
when: packagejson.stat.exists
- name: Run npm install
community.general.npm:
path: '{{ node_app_dir }}'
when: packagejson.stat.exists
.. note::
The example above uses ``ansible_env`` as part of the PATH. Basing variables on ``ansible_env`` is risky. Ansible populates ``ansible_env`` values by gathering facts, so the value of the variables depends on the remote_user or become_user Ansible used when gathering those facts. If you change remote_user/become_user the values in ``ansible_env`` may not be the ones you expect.
.. warning::
Environment variables are normally passed in clear text (shell plugin dependent) so they are not a recommended way of passing secrets to the module being executed.
You can also specify the environment at the task level.
.. code-block:: yaml+jinja
---
- name: Install ruby 2.3.1
ansible.builtin.command: rbenv install {{ rbenv_ruby_version }}
args:
creates: '{{ rbenv_root }}/versions/{{ rbenv_ruby_version }}/bin/ruby'
vars:
rbenv_root: /usr/local/rbenv
rbenv_ruby_version: 2.3.1
environment:
CONFIGURE_OPTS: '--disable-install-doc'
RBENV_ROOT: '{{ rbenv_root }}'
PATH: '{{ rbenv_root }}/bin:{{ rbenv_root }}/shims:{{ rbenv_plugins }}/ruby-build/bin:{{ ansible_env.PATH }}'
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,279 @@
.. _playbooks_error_handling:
***************************
Error handling in playbooks
***************************
When Ansible receives a non-zero return code from a command or a failure from a module, by default it stops executing on that host and continues on other hosts. However, in some circumstances you may want different behavior. Sometimes a non-zero return code indicates success. Sometimes you want a failure on one host to stop execution on all hosts. Ansible provides tools and settings to handle these situations and help you get the behavior, output, and reporting you want.
.. contents::
:local:
.. _ignoring_failed_commands:
Ignoring failed commands
========================
By default Ansible stops executing tasks on a host when a task fails on that host. You can use ``ignore_errors`` to continue on in spite of the failure.
.. code-block:: yaml
- name: Do not count this as a failure
ansible.builtin.command: /bin/false
ignore_errors: yes
The ``ignore_errors`` directive only works when the task is able to run and returns a value of 'failed'. It does not make Ansible ignore undefined variable errors, connection failures, execution issues (for example, missing packages), or syntax errors.
.. _ignore_unreachable:
Ignoring unreachable host errors
================================
.. versionadded:: 2.7
You can ignore a task failure due to the host instance being 'UNREACHABLE' with the ``ignore_unreachable`` keyword. Ansible ignores the task errors, but continues to execute future tasks against the unreachable host. For example, at the task level:
.. code-block:: yaml
- name: This executes, fails, and the failure is ignored
ansible.builtin.command: /bin/true
ignore_unreachable: yes
- name: This executes, fails, and ends the play for this host
ansible.builtin.command: /bin/true
And at the playbook level:
.. code-block:: yaml
- hosts: all
ignore_unreachable: yes
tasks:
- name: This executes, fails, and the failure is ignored
ansible.builtin.command: /bin/true
- name: This executes, fails, and ends the play for this host
ansible.builtin.command: /bin/true
ignore_unreachable: no
.. _resetting_unreachable:
Resetting unreachable hosts
===========================
If Ansible cannot connect to a host, it marks that host as 'UNREACHABLE' and removes it from the list of active hosts for the run. You can use `meta: clear_host_errors` to reactivate all hosts, so subsequent tasks can try to reach them again.
.. _handlers_and_failure:
Handlers and failure
====================
Ansible runs :ref:`handlers <handlers>` at the end of each play. If a task notifies a handler but
another task fails later in the play, by default the handler does *not* run on that host,
which may leave the host in an unexpected state. For example, a task could update
a configuration file and notify a handler to restart some service. If a
task later in the same play fails, the configuration file might be changed but
the service will not be restarted.
You can change this behavior with the ``--force-handlers`` command-line option,
by including ``force_handlers: True`` in a play, or by adding ``force_handlers = True``
to ansible.cfg. When handlers are forced, Ansible will run all notified handlers on
all hosts, even hosts with failed tasks. (Note that certain errors could still prevent
the handler from running, such as a host becoming unreachable.)
.. _controlling_what_defines_failure:
Defining failure
================
Ansible lets you define what "failure" means in each task using the ``failed_when`` conditional. As with all conditionals in Ansible, lists of multiple ``failed_when`` conditions are joined with an implicit ``and``, meaning the task only fails when *all* conditions are met. If you want to trigger a failure when any of the conditions is met, you must define the conditions in a string with an explicit ``or`` operator.
You may check for failure by searching for a word or phrase in the output of a command
.. code-block:: yaml
- name: Fail task when the command error output prints FAILED
ansible.builtin.command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: "'FAILED' in command_result.stderr"
or based on the return code
.. code-block:: yaml
- name: Fail task when both files are identical
ansible.builtin.raw: diff foo/file1 bar/file2
register: diff_cmd
failed_when: diff_cmd.rc == 0 or diff_cmd.rc >= 2
You can also combine multiple conditions for failure. This task will fail if both conditions are true:
.. code-block:: yaml
- name: Check if a file exists in temp and fail task if it does
ansible.builtin.command: ls /tmp/this_should_not_be_here
register: result
failed_when:
- result.rc == 0
- '"No such" not in result.stdout'
If you want the task to fail when only one condition is satisfied, change the ``failed_when`` definition to
.. code-block:: yaml
failed_when: result.rc == 0 or "No such" not in result.stdout
If you have too many conditions to fit neatly into one line, you can split it into a multi-line YAML value with ``>``.
.. code-block:: yaml
- name: example of many failed_when conditions with OR
ansible.builtin.shell: "./myBinary"
register: ret
failed_when: >
("No such file or directory" in ret.stdout) or
(ret.stderr != '') or
(ret.rc == 10)
.. _override_the_changed_result:
Defining "changed"
==================
Ansible lets you define when a particular task has "changed" a remote node using the ``changed_when`` conditional. This lets you determine, based on return codes or output, whether a change should be reported in Ansible statistics and whether a handler should be triggered or not. As with all conditionals in Ansible, lists of multiple ``changed_when`` conditions are joined with an implicit ``and``, meaning the task only reports a change when *all* conditions are met. If you want to report a change when any of the conditions is met, you must define the conditions in a string with an explicit ``or`` operator. For example:
.. code-block:: yaml
tasks:
- name: Report 'changed' when the return code is not equal to 2
ansible.builtin.shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
changed_when: "bass_result.rc != 2"
- name: This will never report 'changed' status
ansible.builtin.shell: wall 'beep'
changed_when: False
You can also combine multiple conditions to override "changed" result.
.. code-block:: yaml
- name: Combine multiple conditions to override 'changed' result
ansible.builtin.command: /bin/fake_command
register: result
ignore_errors: True
changed_when:
- '"ERROR" in result.stderr'
- result.rc == 2
.. note::
Just like ``when`` these two conditionals do not require templating delimiters (``{{ }}``) as they are implied.
See :ref:`controlling_what_defines_failure` for more conditional syntax examples.
Ensuring success for command and shell
======================================
The :ref:`command <command_module>` and :ref:`shell <shell_module>` modules care about return codes, so if you have a command whose successful exit code is not zero, you can do this:
.. code-block:: yaml
tasks:
- name: Run this command and ignore the result
ansible.builtin.shell: /usr/bin/somecommand || /bin/true
Aborting a play on all hosts
============================
Sometimes you want a failure on a single host, or failures on a certain percentage of hosts, to abort the entire play on all hosts. You can stop play execution after the first failure happens with ``any_errors_fatal``. For finer-grained control, you can use ``max_fail_percentage`` to abort the run after a given percentage of hosts has failed.
Aborting on the first error: any_errors_fatal
---------------------------------------------
If you set ``any_errors_fatal`` and a task returns an error, Ansible finishes the fatal task on all hosts in the current batch, then stops executing the play on all hosts. Subsequent tasks and plays are not executed. You can recover from fatal errors by adding a :ref:`rescue section <block_error_handling>` to the block. You can set ``any_errors_fatal`` at the play or block level.
.. code-block:: yaml
- hosts: somehosts
any_errors_fatal: true
roles:
- myrole
- hosts: somehosts
tasks:
- block:
- include_tasks: mytasks.yml
any_errors_fatal: true
You can use this feature when all tasks must be 100% successful to continue playbook execution. For example, if you run a service on machines in multiple data centers with load balancers to pass traffic from users to the service, you want all load balancers to be disabled before you stop the service for maintenance. To ensure that any failure in the task that disables the load balancers will stop all other tasks:
.. code-block:: yaml
---
- hosts: load_balancers_dc_a
any_errors_fatal: true
tasks:
- name: Shut down datacenter 'A'
ansible.builtin.command: /usr/bin/disable-dc
- hosts: frontends_dc_a
tasks:
- name: Stop service
ansible.builtin.command: /usr/bin/stop-software
- name: Update software
ansible.builtin.command: /usr/bin/upgrade-software
- hosts: load_balancers_dc_a
tasks:
- name: Start datacenter 'A'
ansible.builtin.command: /usr/bin/enable-dc
In this example Ansible starts the software upgrade on the front ends only if all of the load balancers are successfully disabled.
.. _maximum_failure_percentage:
Setting a maximum failure percentage
------------------------------------
By default, Ansible continues to execute tasks as long as there are hosts that have not yet failed. In some situations, such as when executing a rolling update, you may want to abort the play when a certain threshold of failures has been reached. To achieve this, you can set a maximum failure percentage on a play:
.. code-block:: yaml
---
- hosts: webservers
max_fail_percentage: 30
serial: 10
The ``max_fail_percentage`` setting applies to each batch when you use it with :ref:`serial <rolling_update_batch_size>`. In the example above, if more than 3 of the 10 servers in the first (or any) batch of servers failed, the rest of the play would be aborted.
.. note::
The percentage set must be exceeded, not equaled. For example, if serial were set to 4 and you wanted the task to abort the play when 2 of the systems failed, set the max_fail_percentage at 49 rather than 50.
Controlling errors in blocks
============================
You can also use blocks to define responses to task errors. This approach is similar to exception handling in many programming languages. See :ref:`block_error_handling` for details and examples.
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`tips_and_tricks`
Tips and tricks for playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_variables`
All about variables
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,22 @@
.. _executing_playbooks:
Executing playbooks
===================
Ready to run your Ansible playbook?
Running complex playbooks requires some trial and error so learn about some of the abilities that Ansible gives you to ensure successful execution.
You can validate your tasks with "dry run" playbooks, use the start-at-task and step mode options to efficiently troubleshoot playbooks.
You can also use Ansible debugger to correct tasks during execution.
Ansible also offers flexibility with asynchronous playbook execution and tags that let you run specific parts of your playbook.
.. toctree::
:maxdepth: 2
playbooks_checkmode
playbooks_privilege_escalation
playbooks_tags
playbooks_startnstep
playbooks_debugger
playbooks_async
playbooks_strategies

File diff suppressed because it is too large Load Diff

@ -0,0 +1,190 @@
.. _handlers:
Handlers: running operations on change
======================================
Sometimes you want a task to run only when a change is made on a machine. For example, you may want to restart a service if a task updates the configuration of that service, but not if the configuration is unchanged. Ansible uses handlers to address this use case. Handlers are tasks that only run when notified.
.. contents::
:local:
Handler example
---------------
This playbook, ``verify-apache.yml``, contains a single play with a handler.
.. code-block:: yaml
---
- name: Verify apache installation
hosts: webservers
vars:
http_port: 80
max_clients: 200
remote_user: root
tasks:
- name: Ensure apache is at the latest version
ansible.builtin.yum:
name: httpd
state: latest
- name: Write the apache config file
ansible.builtin.template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
notify:
- Restart apache
- name: Ensure apache is running
ansible.builtin.service:
name: httpd
state: started
handlers:
- name: Restart apache
ansible.builtin.service:
name: httpd
state: restarted
In this example playbook, the Apache server is restarted by the handler after all tasks complete in the play.
Notifying handlers
------------------
Tasks can instruct one or more handlers to execute using the ``notify`` keyword. The ``notify`` keyword can be applied to a task and accepts a list of handler names that are notified on a task change. Alternately, a string containing a single handler name can be supplied as well. The following example demonstrates how multiple handlers can be notified by a single task:
.. code-block:: yaml
tasks:
- name: Template configuration file
ansible.builtin.template:
src: template.j2
dest: /etc/foo.conf
notify:
- Restart apache
- Restart memcached
handlers:
- name: Restart memcached
ansible.builtin.service:
name: memcached
state: restarted
- name: Restart apache
ansible.builtin.service:
name: apache
state: restarted
In the above example the handlers are executed on task change in the following order: ``Restart memcached``, ``Restart apache``. Handlers are executed in the order they are defined in the ``handlers`` section, not in the order listed in the ``notify`` statement. Notifying the same handler multiple times will result in executing the handler only once regardless of how many tasks notify it. For example, if multiple tasks update a configuration file and notify a handler to restart Apache, Ansible only bounces Apache once to avoid unnecessary restarts.
Naming handlers
---------------
Handlers must be named in order for tasks to be able to notify them using the ``notify`` keyword.
Alternately, handlers can utilize the ``listen`` keyword. Using this handler keyword, handlers can listen on topics that can group multiple handlers as follows:
.. code-block:: yaml
tasks:
- name: Restart everything
command: echo "this task will restart the web services"
notify: "restart web services"
handlers:
- name: Restart memcached
service:
name: memcached
state: restarted
listen: "restart web services"
- name: Restart apache
service:
name: apache
state: restarted
listen: "restart web services"
Notifying the ``restart web services`` topic results in executing all handlers listening to that topic regardless of how those handlers are named.
This use makes it much easier to trigger multiple handlers. It also decouples handlers from their names, making it easier to share handlers among playbooks and roles (especially when using third-party roles from a shared source such as Ansible Galaxy).
Each handler should have a globally unique name. If multiple handlers are defined with the same name, only the last one defined is notified with ``notify``, effectively shadowing all of the previous handlers with the same name. Alternately handlers sharing the same name can all be notified and executed if they listen on the same topic by notifying that topic.
There is only one global scope for handlers (handler names and listen topics) regardless of where the handlers are defined. This also includes handlers defined in roles.
Controlling when handlers run
-----------------------------
By default, handlers run after all the tasks in a particular play have been completed. Notified handlers are executed automatically after each of the following sections, in the following order: ``pre_tasks``, ``roles``/``tasks`` and ``post_tasks``. This approach is efficient, because the handler only runs once, regardless of how many tasks notify it. For example, if multiple tasks update a configuration file and notify a handler to restart Apache, Ansible only bounces Apache once to avoid unnecessary restarts.
If you need handlers to run before the end of the play, add a task to flush them using the :ref:`meta module <meta_module>`, which executes Ansible actions:
.. code-block:: yaml
tasks:
- name: Some tasks go here
ansible.builtin.shell: ...
- name: Flush handlers
meta: flush_handlers
- name: Some other tasks
ansible.builtin.shell: ...
The ``meta: flush_handlers`` task triggers any handlers that have been notified at that point in the play.
Once handlers are executed, either automatically after each mentioned section or manually by the ``flush_handlers`` meta task, they can be notified and run again in later sections of the play.
Using variables with handlers
-----------------------------
You may want your Ansible handlers to use variables. For example, if the name of a service varies slightly by distribution, you want your output to show the exact name of the restarted service for each target machine. Avoid placing variables in the name of the handler. Since handler names are templated early on, Ansible may not have a value available for a handler name like this:
.. code-block:: yaml+jinja
handlers:
# This handler name may cause your play to fail!
- name: Restart "{{ web_service_name }}"
If the variable used in the handler name is not available, the entire play fails. Changing that variable mid-play **will not** result in newly created handler.
Instead, place variables in the task parameters of your handler. You can load the values using ``include_vars`` like this:
.. code-block:: yaml+jinja
tasks:
- name: Set host variables based on distribution
include_vars: "{{ ansible_facts.distribution }}.yml"
handlers:
- name: Restart web service
ansible.builtin.service:
name: "{{ web_service_name | default('httpd') }}"
state: restarted
While handler names can contain a template, ``listen`` topics cannot.
Handlers in roles
-----------------
Handlers from roles are not just contained in their roles but rather inserted into global scope with all other handlers from a play. As such they can be used outside of the role they are defined in. It also means that their name can conflict with handlers from outside the role. To ensure that a handler from a role is notified as opposed to one from outside the role with the same name, notify the handler by using its name in the following form: ``role_name : handler_name``.
Handlers notified within the ``roles`` section are automatically flushed at the end of the ``tasks`` section, but before any ``tasks`` handlers.
Includes and imports in handlers
--------------------------------
Notifying a dynamic include such as ``include_task`` as a handler results in executing all tasks from within the include. It is not possible to notify a handler defined inside a dynamic include.
Having a static include such as ``import_task`` as a handler results in that handler being effectively rewritten by handlers from within that import before the play execution. A static include itself cannot be notified, the tasks from withing that include, on the other hand, can be notified individually.
Limitations
-----------
A handler cannot run ``import_role`` or ``include_role``.

@ -0,0 +1,159 @@
.. _about_playbooks:
.. _playbooks_intro:
*****************
Ansible playbooks
*****************
Ansible Playbooks offer a repeatable, re-usable, simple configuration management and multi-machine deployment system, one that is well suited to deploying complex applications. If you need to execute a task with Ansible more than once, write a playbook and put it under source control. Then you can use the playbook to push out new configuration or confirm the configuration of remote systems. The playbooks in the `ansible-examples repository <https://github.com/ansible/ansible-examples>`_ illustrate many useful techniques. You may want to look at these in another tab as you read the documentation.
Playbooks can:
* declare configurations
* orchestrate steps of any manual ordered process, on multiple sets of machines, in a defined order
* launch tasks synchronously or :ref:`asynchronously <playbooks_async>`
.. contents::
:local:
.. _playbook_language_example:
Playbook syntax
===============
Playbooks are expressed in YAML format with a minimum of syntax. If you are not familiar with YAML, look at our overview of :ref:`yaml_syntax` and consider installing an add-on for your text editor (see :ref:`other_tools_and_programs`) to help you write clean YAML syntax in your playbooks.
A playbook is composed of one or more 'plays' in an ordered list. The terms 'playbook' and 'play' are sports analogies. Each play executes part of the overall goal of the playbook, running one or more tasks. Each task calls an Ansible module.
Playbook execution
==================
A playbook runs in order from top to bottom. Within each play, tasks also run in order from top to bottom. Playbooks with multiple 'plays' can orchestrate multi-machine deployments, running one play on your webservers, then another play on your database servers, then a third play on your network infrastructure, and so on. At a minimum, each play defines two things:
* the managed nodes to target, using a :ref:`pattern <intro_patterns>`
* at least one task to execute
.. note::
In Ansible 2.10 and later, we recommend you use the fully-qualified collection name in your playbooks to ensure the correct module is selected, because multiple collections can contain modules with the same name (for example, ``user``). See :ref:`collections_using_playbook`.
In this example, the first play targets the web servers; the second play targets the database servers.
.. code-block:: yaml
---
- name: Update web servers
hosts: webservers
remote_user: root
tasks:
- name: Ensure apache is at the latest version
ansible.builtin.yum:
name: httpd
state: latest
- name: Write the apache config file
ansible.builtin.template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
- name: Update db servers
hosts: databases
remote_user: root
tasks:
- name: Ensure postgresql is at the latest version
ansible.builtin.yum:
name: postgresql
state: latest
- name: Ensure that postgresql is started
ansible.builtin.service:
name: postgresql
state: started
Your playbook can include more than just a hosts line and tasks. For example, the playbook above sets a ``remote_user`` for each play. This is the user account for the SSH connection. You can add other :ref:`playbook_keywords` at the playbook, play, or task level to influence how Ansible behaves. Playbook keywords can control the :ref:`connection plugin <connection_plugins>`, whether to use :ref:`privilege escalation <become>`, how to handle errors, and more. To support a variety of environments, Ansible lets you set many of these parameters as command-line flags, in your Ansible configuration, or in your inventory. Learning the :ref:`precedence rules <general_precedence_rules>` for these sources of data will help you as you expand your Ansible ecosystem.
.. _tasks_list:
Task execution
--------------
By default, Ansible executes each task in order, one at a time, against all machines matched by the host pattern. Each task executes a module with specific arguments. When a task has executed on all target machines, Ansible moves on to the next task. You can use :ref:`strategies <playbooks_strategies>` to change this default behavior. Within each play, Ansible applies the same task directives to all hosts. If a task fails on a host, Ansible takes that host out of the rotation for the rest of the playbook.
When you run a playbook, Ansible returns information about connections, the ``name`` lines of all your plays and tasks, whether each task has succeeded or failed on each machine, and whether each task has made a change on each machine. At the bottom of the playbook execution, Ansible provides a summary of the nodes that were targeted and how they performed. General failures and fatal "unreachable" communication attempts are kept separate in the counts.
.. _idempotency:
Desired state and 'idempotency'
-------------------------------
Most Ansible modules check whether the desired final state has already been achieved, and exit without performing any actions if that state has been achieved, so that repeating the task does not change the final state. Modules that behave this way are often called 'idempotent.' Whether you run a playbook once, or multiple times, the outcome should be the same. However, not all playbooks and not all modules behave this way. If you are unsure, test your playbooks in a sandbox environment before running them multiple times in production.
.. _executing_a_playbook:
Running playbooks
-----------------
To run your playbook, use the :ref:`ansible-playbook` command.
.. code-block:: bash
ansible-playbook playbook.yml -f 10
Use the ``--verbose`` flag when running your playbook to see detailed output from successful modules as well as unsuccessful ones.
.. _playbook_ansible-pull:
Ansible-Pull
============
Should you want to invert the architecture of Ansible, so that nodes check in to a central location, instead
of pushing configuration out to them, you can.
The ``ansible-pull`` is a small script that will checkout a repo of configuration instructions from git, and then
run ``ansible-playbook`` against that content.
Assuming you load balance your checkout location, ``ansible-pull`` scales essentially infinitely.
Run ``ansible-pull --help`` for details.
There's also a `clever playbook <https://github.com/ansible/ansible-examples/blob/master/language_features/ansible_pull.yml>`_ available to configure ``ansible-pull`` via a crontab from push mode.
Verifying playbooks
===================
You may want to verify your playbooks to catch syntax errors and other problems before you run them. The :ref:`ansible-playbook` command offers several options for verification, including ``--check``, ``--diff``, ``--list-hosts``, ``--list-tasks``, and ``--syntax-check``. The :ref:`validate-playbook-tools` describes other tools for validating and testing playbooks.
.. _linting_playbooks:
ansible-lint
------------
You can use `ansible-lint <https://docs.ansible.com/ansible-lint/index.html>`_ for detailed, Ansible-specific feedback on your playbooks before you execute them. For example, if you run ``ansible-lint`` on the playbook called ``verify-apache.yml`` near the top of this page, you should get the following results:
.. code-block:: bash
$ ansible-lint verify-apache.yml
[403] Package installs should not use latest
verify-apache.yml:8
Task/Handler: ensure apache is at the latest version
The `ansible-lint default rules <https://docs.ansible.com/ansible-lint/rules/default_rules.html>`_ page describes each error. For ``[403]``, the recommended fix is to change ``state: latest`` to ``state: present`` in the playbook.
.. seealso::
`ansible-lint <https://docs.ansible.com/ansible-lint/index.html>`_
Learn how to test Ansible Playbooks syntax
:ref:`yaml_syntax`
Learn about YAML syntax
:ref:`tips_and_tricks`
Tips for managing playbooks in the real world
:ref:`list_of_collections`
Browse existing collections, modules, and plugins
:ref:`developing_modules`
Learn to extend Ansible by writing your own modules
:ref:`intro_patterns`
Learn about how to select hosts
`GitHub examples directory <https://github.com/ansible/ansible-examples>`_
Complete end-to-end playbook examples
`Mailing List <https://groups.google.com/group/ansible-project>`_
Questions? Help? Ideas? Stop by the list on Google Groups

@ -0,0 +1,39 @@
.. _playbooks_lookups:
*******
Lookups
*******
Lookup plugins retrieve data from outside sources such as files, databases, key/value stores, APIs, and other services. Like all templating, lookups execute and are evaluated on the Ansible control machine. Ansible makes the data returned by a lookup plugin available using the standard templating system. Before Ansible 2.5, lookups were mostly used indirectly in ``with_<lookup>`` constructs for looping. Starting with Ansible 2.5, lookups are used more explicitly as part of Jinja2 expressions fed into the ``loop`` keyword.
.. _lookups_and_variables:
Using lookups in variables
==========================
You can populate variables using lookups. Ansible evaluates the value each time it is executed in a task (or template).
.. code-block:: yaml+jinja
vars:
motd_value: "{{ lookup('file', '/etc/motd') }}"
tasks:
- debug:
msg: "motd value is {{ motd_value }}"
For more details and a list of lookup plugins in ansible-core, see :ref:`plugins_lookup`. You may also find lookup plugins in collections. You can review a list of lookup plugins installed on your control machine with the command ``ansible-doc -l -t lookup``.
.. seealso::
:ref:`working_with_playbooks`
An introduction to playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_variables`
All about variables
:ref:`playbooks_loops`
Looping in playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,499 @@
.. _playbooks_loops:
*****
Loops
*****
Ansible offers the ``loop``, ``with_<lookup>``, and ``until`` keywords to execute a task multiple times. Examples of commonly-used loops include changing ownership on several files and/or directories with the :ref:`file module <file_module>`, creating multiple users with the :ref:`user module <user_module>`, and
repeating a polling step until a certain result is reached.
.. note::
* We added ``loop`` in Ansible 2.5. It is not yet a full replacement for ``with_<lookup>``, but we recommend it for most use cases.
* We have not deprecated the use of ``with_<lookup>`` - that syntax will still be valid for the foreseeable future.
* We are looking to improve ``loop`` syntax - watch this page and the `changelog <https://github.com/ansible/ansible/tree/devel/changelogs>`_ for updates.
.. contents::
:local:
Comparing ``loop`` and ``with_*``
=================================
* The ``with_<lookup>`` keywords rely on :ref:`lookup_plugins` - even ``items`` is a lookup.
* The ``loop`` keyword is equivalent to ``with_list``, and is the best choice for simple loops.
* The ``loop`` keyword will not accept a string as input, see :ref:`query_vs_lookup`.
* Generally speaking, any use of ``with_*`` covered in :ref:`migrating_to_loop` can be updated to use ``loop``.
* Be careful when changing ``with_items`` to ``loop``, as ``with_items`` performed implicit single-level flattening. You may need to use ``flatten(1)`` with ``loop`` to match the exact outcome. For example, to get the same output as:
.. code-block:: yaml
with_items:
- 1
- [2,3]
- 4
you would need
.. code-block:: yaml+jinja
loop: "{{ [1, [2, 3], 4] | flatten(1) }}"
* Any ``with_*`` statement that requires using ``lookup`` within a loop should not be converted to use the ``loop`` keyword. For example, instead of doing:
.. code-block:: yaml+jinja
loop: "{{ lookup('fileglob', '*.txt', wantlist=True) }}"
it's cleaner to keep
.. code-block:: yaml
with_fileglob: '*.txt'
.. _standard_loops:
Standard loops
==============
Iterating over a simple list
----------------------------
Repeated tasks can be written as standard loops over a simple list of strings. You can define the list directly in the task.
.. code-block:: yaml+jinja
- name: Add several users
ansible.builtin.user:
name: "{{ item }}"
state: present
groups: "wheel"
loop:
- testuser1
- testuser2
You can define the list in a variables file, or in the 'vars' section of your play, then refer to the name of the list in the task.
.. code-block:: yaml+jinja
loop: "{{ somelist }}"
Either of these examples would be the equivalent of
.. code-block:: yaml
- name: Add user testuser1
ansible.builtin.user:
name: "testuser1"
state: present
groups: "wheel"
- name: Add user testuser2
ansible.builtin.user:
name: "testuser2"
state: present
groups: "wheel"
You can pass a list directly to a parameter for some plugins. Most of the packaging modules, like :ref:`yum <yum_module>` and :ref:`apt <apt_module>`, have this capability. When available, passing the list to a parameter is better than looping over the task. For example
.. code-block:: yaml+jinja
- name: Optimal yum
ansible.builtin.yum:
name: "{{ list_of_packages }}"
state: present
- name: Non-optimal yum, slower and may cause issues with interdependencies
ansible.builtin.yum:
name: "{{ item }}"
state: present
loop: "{{ list_of_packages }}"
Check the :ref:`module documentation <modules_by_category>` to see if you can pass a list to any particular module's parameter(s).
Iterating over a list of hashes
-------------------------------
If you have a list of hashes, you can reference subkeys in a loop. For example:
.. code-block:: yaml+jinja
- name: Add several users
ansible.builtin.user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
loop:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }
When combining :ref:`conditionals <playbooks_conditionals>` with a loop, the ``when:`` statement is processed separately for each item.
See :ref:`the_when_statement` for examples.
Iterating over a dictionary
---------------------------
To loop over a dict, use the :ref:`dict2items <dict_filter>`:
.. code-block:: yaml+jinja
- name: Using dict2items
ansible.builtin.debug:
msg: "{{ item.key }} - {{ item.value }}"
loop: "{{ tag_data | dict2items }}"
vars:
tag_data:
Environment: dev
Application: payment
Here, we are iterating over `tag_data` and printing the key and the value from it.
Registering variables with a loop
=================================
You can register the output of a loop as a variable. For example
.. code-block:: yaml+jinja
- name: Register loop output as a variable
ansible.builtin.shell: "echo {{ item }}"
loop:
- "one"
- "two"
register: echo
When you use ``register`` with a loop, the data structure placed in the variable will contain a ``results`` attribute that is a list of all responses from the module. This differs from the data structure returned when using ``register`` without a loop.
.. code-block:: json
{
"changed": true,
"msg": "All items completed",
"results": [
{
"changed": true,
"cmd": "echo \"one\" ",
"delta": "0:00:00.003110",
"end": "2013-12-19 12:00:05.187153",
"invocation": {
"module_args": "echo \"one\"",
"module_name": "shell"
},
"item": "one",
"rc": 0,
"start": "2013-12-19 12:00:05.184043",
"stderr": "",
"stdout": "one"
},
{
"changed": true,
"cmd": "echo \"two\" ",
"delta": "0:00:00.002920",
"end": "2013-12-19 12:00:05.245502",
"invocation": {
"module_args": "echo \"two\"",
"module_name": "shell"
},
"item": "two",
"rc": 0,
"start": "2013-12-19 12:00:05.242582",
"stderr": "",
"stdout": "two"
}
]
}
Subsequent loops over the registered variable to inspect the results may look like
.. code-block:: yaml+jinja
- name: Fail if return code is not 0
ansible.builtin.fail:
msg: "The command ({{ item.cmd }}) did not have a 0 return code"
when: item.rc != 0
loop: "{{ echo.results }}"
During iteration, the result of the current item will be placed in the variable.
.. code-block:: yaml+jinja
- name: Place the result of the current item in the variable
ansible.builtin.shell: echo "{{ item }}"
loop:
- one
- two
register: echo
changed_when: echo.stdout != "one"
.. _complex_loops:
Complex loops
=============
Iterating over nested lists
---------------------------
You can use Jinja2 expressions to iterate over complex lists. For example, a loop can combine nested lists.
.. code-block:: yaml+jinja
- name: Give users access to multiple databases
community.mysql.mysql_user:
name: "{{ item[0] }}"
priv: "{{ item[1] }}.*:ALL"
append_privs: yes
password: "foo"
loop: "{{ ['alice', 'bob'] | product(['clientdb', 'employeedb', 'providerdb']) | list }}"
.. _do_until_loops:
Retrying a task until a condition is met
----------------------------------------
.. versionadded:: 1.4
You can use the ``until`` keyword to retry a task until a certain condition is met. Here's an example:
.. code-block:: yaml
- name: Retry a task until a certain condition is met
ansible.builtin.shell: /usr/bin/foo
register: result
until: result.stdout.find("all systems go") != -1
retries: 5
delay: 10
This task runs up to 5 times with a delay of 10 seconds between each attempt. If the result of any attempt has "all systems go" in its stdout, the task succeeds. The default value for "retries" is 3 and "delay" is 5.
To see the results of individual retries, run the play with ``-vv``.
When you run a task with ``until`` and register the result as a variable, the registered variable will include a key called "attempts", which records the number of the retries for the task.
.. note:: You must set the ``until`` parameter if you want a task to retry. If ``until`` is not defined, the value for the ``retries`` parameter is forced to 1.
Looping over inventory
----------------------
To loop over your inventory, or just a subset of it, you can use a regular ``loop`` with the ``ansible_play_batch`` or ``groups`` variables.
.. code-block:: yaml+jinja
- name: Show all the hosts in the inventory
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ groups['all'] }}"
- name: Show all the hosts in the current play
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ ansible_play_batch }}"
There is also a specific lookup plugin ``inventory_hostnames`` that can be used like this
.. code-block:: yaml+jinja
- name: Show all the hosts in the inventory
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ query('inventory_hostnames', 'all') }}"
- name: Show all the hosts matching the pattern, ie all but the group www
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ query('inventory_hostnames', 'all:!www') }}"
More information on the patterns can be found in :ref:`intro_patterns`.
.. _query_vs_lookup:
Ensuring list input for ``loop``: using ``query`` rather than ``lookup``
========================================================================
The ``loop`` keyword requires a list as input, but the ``lookup`` keyword returns a string of comma-separated values by default. Ansible 2.5 introduced a new Jinja2 function named :ref:`query <query>` that always returns a list, offering a simpler interface and more predictable output from lookup plugins when using the ``loop`` keyword.
You can force ``lookup`` to return a list to ``loop`` by using ``wantlist=True``, or you can use ``query`` instead.
The following two examples do the same thing.
.. code-block:: yaml+jinja
loop: "{{ query('inventory_hostnames', 'all') }}"
loop: "{{ lookup('inventory_hostnames', 'all', wantlist=True) }}"
.. _loop_control:
Adding controls to loops
========================
.. versionadded:: 2.1
The ``loop_control`` keyword lets you manage your loops in useful ways.
Limiting loop output with ``label``
-----------------------------------
.. versionadded:: 2.2
When looping over complex data structures, the console output of your task can be enormous. To limit the displayed output, use the ``label`` directive with ``loop_control``.
.. code-block:: yaml+jinja
- name: Create servers
digital_ocean:
name: "{{ item.name }}"
state: present
loop:
- name: server1
disks: 3gb
ram: 15Gb
network:
nic01: 100Gb
nic02: 10Gb
...
loop_control:
label: "{{ item.name }}"
The output of this task will display just the ``name`` field for each ``item`` instead of the entire contents of the multi-line ``{{ item }}`` variable.
.. note:: This is for making console output more readable, not protecting sensitive data. If there is sensitive data in ``loop``, set ``no_log: yes`` on the task to prevent disclosure.
Pausing within a loop
---------------------
.. versionadded:: 2.2
To control the time (in seconds) between the execution of each item in a task loop, use the ``pause`` directive with ``loop_control``.
.. code-block:: yaml+jinja
# main.yml
- name: Create servers, pause 3s before creating next
community.digitalocean.digital_ocean:
name: "{{ item }}"
state: present
loop:
- server1
- server2
loop_control:
pause: 3
Tracking progress through a loop with ``index_var``
---------------------------------------------------
.. versionadded:: 2.5
To keep track of where you are in a loop, use the ``index_var`` directive with ``loop_control``. This directive specifies a variable name to contain the current loop index.
.. code-block:: yaml+jinja
- name: Count our fruit
ansible.builtin.debug:
msg: "{{ item }} with index {{ my_idx }}"
loop:
- apple
- banana
- pear
loop_control:
index_var: my_idx
.. note:: `index_var` is 0 indexed.
Defining inner and outer variable names with ``loop_var``
---------------------------------------------------------
.. versionadded:: 2.1
You can nest two looping tasks using ``include_tasks``. However, by default Ansible sets the loop variable ``item`` for each loop. This means the inner, nested loop will overwrite the value of ``item`` from the outer loop.
You can specify the name of the variable for each loop using ``loop_var`` with ``loop_control``.
.. code-block:: yaml+jinja
# main.yml
- include_tasks: inner.yml
loop:
- 1
- 2
- 3
loop_control:
loop_var: outer_item
# inner.yml
- name: Print outer and inner items
ansible.builtin.debug:
msg: "outer item={{ outer_item }} inner item={{ item }}"
loop:
- a
- b
- c
.. note:: If Ansible detects that the current loop is using a variable which has already been defined, it will raise an error to fail the task.
Extended loop variables
-----------------------
.. versionadded:: 2.8
As of Ansible 2.8 you can get extended loop information using the ``extended`` option to loop control. This option will expose the following information.
========================== ===========
Variable Description
-------------------------- -----------
``ansible_loop.allitems`` The list of all items in the loop
``ansible_loop.index`` The current iteration of the loop. (1 indexed)
``ansible_loop.index0`` The current iteration of the loop. (0 indexed)
``ansible_loop.revindex`` The number of iterations from the end of the loop (1 indexed)
``ansible_loop.revindex0`` The number of iterations from the end of the loop (0 indexed)
``ansible_loop.first`` ``True`` if first iteration
``ansible_loop.last`` ``True`` if last iteration
``ansible_loop.length`` The number of items in the loop
``ansible_loop.previtem`` The item from the previous iteration of the loop. Undefined during the first iteration.
``ansible_loop.nextitem`` The item from the following iteration of the loop. Undefined during the last iteration.
========================== ===========
::
loop_control:
extended: yes
.. note:: When using ``loop_control.extended`` more memory will be utilized on the control node. This is a result of ``ansible_loop.allitems`` containing a reference to the full loop data for every loop. When serializing the results for display in callback plugins within the main ansible process, these references may be dereferenced causing memory usage to increase.
.. versionadded:: 2.14
To disable the ``ansible_loop.allitems`` item, to reduce memory consumption, set ``loop_control.extended_allitems: no``.
::
loop_control:
extended: yes
extended_allitems: no
Accessing the name of your loop_var
-----------------------------------
.. versionadded:: 2.8
As of Ansible 2.8 you can get the name of the value provided to ``loop_control.loop_var`` using the ``ansible_loop_var`` variable
For role authors, writing roles that allow loops, instead of dictating the required ``loop_var`` value, you can gather the value via the following
.. code-block:: yaml+jinja
"{{ lookup('vars', ansible_loop_var) }}"
.. _migrating_to_loop:
Migrating from with_X to loop
=============================
.. include:: shared_snippets/with2loop.txt
.. seealso::
:ref:`about_playbooks`
An introduction to playbooks
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`tips_and_tricks`
Tips and tricks for playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_variables`
All about variables
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,175 @@
.. _module_defaults:
Module defaults
===============
If you frequently call the same module with the same arguments, it can be useful to define default arguments for that particular module using the ``module_defaults`` keyword.
Here is a basic example:
.. code-block:: YAML
- hosts: localhost
module_defaults:
ansible.builtin.file:
owner: root
group: root
mode: 0755
tasks:
- name: Create file1
ansible.builtin.file:
state: touch
path: /tmp/file1
- name: Create file2
ansible.builtin.file:
state: touch
path: /tmp/file2
- name: Create file3
ansible.builtin.file:
state: touch
path: /tmp/file3
The ``module_defaults`` keyword can be used at the play, block, and task level. Any module arguments explicitly specified in a task will override any established default for that module argument.
.. code-block:: YAML
- block:
- name: Print a message
ansible.builtin.debug:
msg: "Different message"
module_defaults:
ansible.builtin.debug:
msg: "Default message"
You can remove any previously established defaults for a module by specifying an empty dict.
.. code-block:: YAML
- name: Create file1
ansible.builtin.file:
state: touch
path: /tmp/file1
module_defaults:
file: {}
.. note::
Any module defaults set at the play level (and block/task level when using ``include_role`` or ``import_role``) will apply to any roles used, which may cause unexpected behavior in the role.
Here are some more realistic use cases for this feature.
Interacting with an API that requires auth.
.. code-block:: YAML
- hosts: localhost
module_defaults:
ansible.builtin.uri:
force_basic_auth: true
user: some_user
password: some_password
tasks:
- name: Interact with a web service
ansible.builtin.uri:
url: http://some.api.host/v1/whatever1
- name: Interact with a web service
ansible.builtin.uri:
url: http://some.api.host/v1/whatever2
- name: Interact with a web service
ansible.builtin.uri:
url: http://some.api.host/v1/whatever3
Setting a default AWS region for specific EC2-related modules.
.. code-block:: YAML
- hosts: localhost
vars:
my_region: us-west-2
module_defaults:
amazon.aws.ec2:
region: '{{ my_region }}'
community.aws.ec2_instance_info:
region: '{{ my_region }}'
amazon.aws.ec2_vpc_net_info:
region: '{{ my_region }}'
.. _module_defaults_groups:
Module defaults groups
----------------------
.. versionadded:: 2.7
Ansible 2.7 adds a preview-status feature to group together modules that share common sets of parameters. This makes it easier to author playbooks making heavy use of API-based modules such as cloud modules.
+---------+---------------------------+-----------------+
| Group | Purpose | Ansible Version |
+=========+===========================+=================+
| aws | Amazon Web Services | 2.7 |
+---------+---------------------------+-----------------+
| azure | Azure | 2.7 |
+---------+---------------------------+-----------------+
| gcp | Google Cloud Platform | 2.7 |
+---------+---------------------------+-----------------+
| k8s | Kubernetes | 2.8 |
+---------+---------------------------+-----------------+
| os | OpenStack | 2.8 |
+---------+---------------------------+-----------------+
| acme | ACME | 2.10 |
+---------+---------------------------+-----------------+
| docker* | Docker | 2.10 |
+---------+---------------------------+-----------------+
| ovirt | oVirt | 2.10 |
+---------+---------------------------+-----------------+
| vmware | VMware | 2.10 |
+---------+---------------------------+-----------------+
* The `docker_stack <docker_stack_module>`_ module is not included in the ``docker`` defaults group.
Use the groups with ``module_defaults`` by prefixing the group name with ``group/`` - for example ``group/aws``.
In a playbook, you can set module defaults for whole groups of modules, such as setting a common AWS region.
.. code-block:: YAML
# example_play.yml
- hosts: localhost
module_defaults:
group/aws:
region: us-west-2
tasks:
- name: Get info
aws_s3_bucket_info:
# now the region is shared between both info modules
- name: Get info
ec2_ami_info:
filters:
name: 'RHEL*7.5*'
In ansible-core 2.12, collections can define their own groups in the ``meta/runtime.yml`` file. ``module_defaults`` does not take the ``collections`` keyword into account, so the fully qualified group name must be used for new groups in ``module_defaults``.
Here is an example ``runtime.yml`` file for a collection and a sample playbook using the group.
.. code-block:: YAML
# collections/ansible_collections/ns/coll/meta/runtime.yml
action_groups:
groupname:
- module
- another.collection.module
.. code-block:: YAML
- hosts: localhost
module_defaults:
group/ns.coll.groupname:
option_name: option_value
tasks:
- ns.coll.module:
- another.collection.module

@ -0,0 +1,774 @@
.. _become:
.. _playbooks_privilege_escalation:
******************************************
Understanding privilege escalation: become
******************************************
Ansible uses existing privilege escalation systems to execute tasks with root privileges or with another user's permissions. Because this feature allows you to 'become' another user, different from the user that logged into the machine (remote user), we call it ``become``. The ``become`` keyword uses existing privilege escalation tools like `sudo`, `su`, `pfexec`, `doas`, `pbrun`, `dzdo`, `ksu`, `runas`, `machinectl` and others.
.. contents::
:local:
Using become
============
You can control the use of ``become`` with play or task directives, connection variables, or at the command line. If you set privilege escalation properties in multiple ways, review the :ref:`general precedence rules<general_precedence_rules>` to understand which settings will be used.
A full list of all become plugins that are included in Ansible can be found in the :ref:`become_plugin_list`.
Become directives
-----------------
You can set the directives that control ``become`` at the play or task level. You can override these by setting connection variables, which often differ from one host to another. These variables and directives are independent. For example, setting ``become_user`` does not set ``become``.
become
set to ``yes`` to activate privilege escalation.
become_user
set to user with desired privileges — the user you `become`, NOT the user you login as. Does NOT imply ``become: yes``, to allow it to be set at host level. Default value is ``root``.
become_method
(at play or task level) overrides the default method set in ansible.cfg, set to use any of the :ref:`become_plugins`.
become_flags
(at play or task level) permit the use of specific flags for the tasks or role. One common use is to change the user to nobody when the shell is set to nologin. Added in Ansible 2.2.
For example, to manage a system service (which requires ``root`` privileges) when connected as a non-``root`` user, you can use the default value of ``become_user`` (``root``):
.. code-block:: yaml
- name: Ensure the httpd service is running
service:
name: httpd
state: started
become: yes
To run a command as the ``apache`` user:
.. code-block:: yaml
- name: Run a command as the apache user
command: somecommand
become: yes
become_user: apache
To do something as the ``nobody`` user when the shell is nologin:
.. code-block:: yaml
- name: Run a command as nobody
command: somecommand
become: yes
become_method: su
become_user: nobody
become_flags: '-s /bin/sh'
To specify a password for sudo, run ``ansible-playbook`` with ``--ask-become-pass`` (``-K`` for short).
If you run a playbook utilizing ``become`` and the playbook seems to hang, most likely it is stuck at the privilege escalation prompt. Stop it with `CTRL-c`, then execute the playbook with ``-K`` and the appropriate password.
Become connection variables
---------------------------
You can define different ``become`` options for each managed node or group. You can define these variables in inventory or use them as normal variables.
ansible_become
overrides the ``become`` directive, decides if privilege escalation is used or not.
ansible_become_method
which privilege escalation method should be used
ansible_become_user
set the user you become through privilege escalation; does not imply ``ansible_become: yes``
ansible_become_password
set the privilege escalation password. See :ref:`playbooks_vault` for details on how to avoid having secrets in plain text
ansible_common_remote_group
determines if Ansible should try to ``chgrp`` its temporary files to a group if ``setfacl`` and ``chown`` both fail. See `Risks of becoming an unprivileged user`_ for more information. Added in version 2.10.
For example, if you want to run all tasks as ``root`` on a server named ``webserver``, but you can only connect as the ``manager`` user, you could use an inventory entry like this:
.. code-block:: text
webserver ansible_user=manager ansible_become=yes
.. note::
The variables defined above are generic for all become plugins but plugin specific ones can also be set instead.
Please see the documentation for each plugin for a list of all options the plugin has and how they can be defined.
A full list of become plugins in Ansible can be found at :ref:`become_plugins`.
Become command-line options
---------------------------
--ask-become-pass, -K
ask for privilege escalation password; does not imply become will be used. Note that this password will be used for all hosts.
--become, -b
run operations with become (no password implied)
--become-method=BECOME_METHOD
privilege escalation method to use (default=sudo),
valid choices: [ sudo | su | pbrun | pfexec | doas | dzdo | ksu | runas | machinectl ]
--become-user=BECOME_USER
run operations as this user (default=root), does not imply --become/-b
Risks and limitations of become
===============================
Although privilege escalation is mostly intuitive, there are a few limitations
on how it works. Users should be aware of these to avoid surprises.
Risks of becoming an unprivileged user
--------------------------------------
Ansible modules are executed on the remote machine by first substituting the
parameters into the module file, then copying the file to the remote machine,
and finally executing it there.
Everything is fine if the module file is executed without using ``become``,
when the ``become_user`` is root, or when the connection to the remote machine
is made as root. In these cases Ansible creates the module file with
permissions that only allow reading by the user and root, or only allow reading
by the unprivileged user being switched to.
However, when both the connection user and the ``become_user`` are unprivileged,
the module file is written as the user that Ansible connects as (the
``remote_user``), but the file needs to be readable by the user Ansible is set
to ``become``. The details of how Ansible solves this can vary based on platform.
However, on POSIX systems, Ansible solves this problem in the following way:
First, if :command:`setfacl` is installed and available in the remote ``PATH``,
and the temporary directory on the remote host is mounted with POSIX.1e
filesystem ACL support, Ansible will use POSIX ACLs to share the module file
with the second unprivileged user.
Next, if POSIX ACLs are **not** available or :command:`setfacl` could not be
run, Ansible will attempt to change ownership of the module file using
:command:`chown` for systems which support doing so as an unprivileged user.
New in Ansible 2.11, at this point, Ansible will try :command:`chmod +a` which
is a macOS-specific way of setting ACLs on files.
New in Ansible 2.10, if all of the above fails, Ansible will then check the
value of the configuration setting ``ansible_common_remote_group``. Many
systems will allow a given user to change the group ownership of a file to a
group the user is in. As a result, if the second unprivileged user (the
``become_user``) has a UNIX group in common with the user Ansible is connected
as (the ``remote_user``), and if ``ansible_common_remote_group`` is defined to
be that group, Ansible can try to change the group ownership of the module file
to that group by using :command:`chgrp`, thereby likely making it readable to
the ``become_user``.
At this point, if ``ansible_common_remote_group`` was defined and a
:command:`chgrp` was attempted and returned successfully, Ansible assumes (but,
importantly, does not check) that the new group ownership is enough and does not
fall back further. That is, Ansible **does not check** that the ``become_user``
does in fact share a group with the ``remote_user``; so long as the command
exits successfully, Ansible considers the result successful and does not proceed
to check ``allow_world_readable_tmpfiles`` per below.
If ``ansible_common_remote_group`` is **not** set and the chown above it failed,
or if ``ansible_common_remote_group`` *is* set but the :command:`chgrp` (or
following group-permissions :command:`chmod`) returned a non-successful exit
code, Ansible will lastly check the value of
``allow_world_readable_tmpfiles``. If this is set, Ansible will place the module
file in a world-readable temporary directory, with world-readable permissions to
allow the ``become_user`` (and incidentally any other user on the system) to
read the contents of the file. **If any of the parameters passed to the module
are sensitive in nature, and you do not trust the remote machines, then this is
a potential security risk.**
Once the module is done executing, Ansible deletes the temporary file.
Several ways exist to avoid the above logic flow entirely:
* Use `pipelining`. When pipelining is enabled, Ansible does not save the
module to a temporary file on the client. Instead it pipes the module to
the remote python interpreter's stdin. Pipelining does not work for
python modules involving file transfer (for example: :ref:`copy <copy_module>`,
:ref:`fetch <fetch_module>`, :ref:`template <template_module>`), or for non-python modules.
* Avoid becoming an unprivileged
user. Temporary files are protected by UNIX file permissions when you
``become`` root or do not use ``become``. In Ansible 2.1 and above, UNIX
file permissions are also secure if you make the connection to the managed
machine as root and then use ``become`` to access an unprivileged account.
.. warning:: Although the Solaris ZFS filesystem has filesystem ACLs, the ACLs
are not POSIX.1e filesystem acls (they are NFSv4 ACLs instead). Ansible
cannot use these ACLs to manage its temp file permissions so you may have
to resort to ``allow_world_readable_tmpfiles`` if the remote machines use ZFS.
.. versionchanged:: 2.1
Ansible makes it hard to unknowingly use ``become`` insecurely. Starting in Ansible 2.1,
Ansible defaults to issuing an error if it cannot execute securely with ``become``.
If you cannot use pipelining or POSIX ACLs, must connect as an unprivileged user,
must use ``become`` to execute as a different unprivileged user,
and decide that your managed nodes are secure enough for the
modules you want to run there to be world readable, you can turn on
``allow_world_readable_tmpfiles`` in the :file:`ansible.cfg` file. Setting
``allow_world_readable_tmpfiles`` will change this from an error into
a warning and allow the task to run as it did prior to 2.1.
.. versionchanged:: 2.10
Ansible 2.10 introduces the above-mentioned ``ansible_common_remote_group``
fallback. As mentioned above, if enabled, it is used when ``remote_user`` and
``become_user`` are both unprivileged users. Refer to the text above for details
on when this fallback happens.
.. warning:: As mentioned above, if ``ansible_common_remote_group`` and
``allow_world_readable_tmpfiles`` are both enabled, it is unlikely that the
world-readable fallback will ever trigger, and yet Ansible might still be
unable to access the module file. This is because after the group ownership
change is successful, Ansible does not fall back any further, and also does
not do any check to ensure that the ``become_user`` is actually a member of
the "common group". This is a design decision made by the fact that doing
such a check would require another round-trip connection to the remote
machine, which is a time-expensive operation. Ansible does, however, emit a
warning in this case.
Not supported by all connection plugins
---------------------------------------
Privilege escalation methods must also be supported by the connection plugin
used. Most connection plugins will warn if they do not support become. Some
will just ignore it as they always run as root (jail, chroot, and so on).
Only one method may be enabled per host
---------------------------------------
Methods cannot be chained. You cannot use ``sudo /bin/su -`` to become a user,
you need to have privileges to run the command as that user in sudo or be able
to su directly to it (the same for pbrun, pfexec or other supported methods).
Privilege escalation must be general
------------------------------------
You cannot limit privilege escalation permissions to certain commands.
Ansible does not always
use a specific command to do something but runs modules (code) from
a temporary file name which changes every time. If you have '/sbin/service'
or '/bin/chmod' as the allowed commands this will fail with ansible as those
paths won't match with the temporary file that Ansible creates to run the
module. If you have security rules that constrain your sudo/pbrun/doas environment
to running specific command paths only, use Ansible from a special account that
does not have this constraint, or use AWX or the :ref:`ansible_platform` to manage indirect access to SSH credentials.
May not access environment variables populated by pamd_systemd
--------------------------------------------------------------
For most Linux distributions using ``systemd`` as their init, the default
methods used by ``become`` do not open a new "session", in the sense of
systemd. Because the ``pam_systemd`` module will not fully initialize a new
session, you might have surprises compared to a normal session opened through
ssh: some environment variables set by ``pam_systemd``, most notably
``XDG_RUNTIME_DIR``, are not populated for the new user and instead inherited
or just emptied.
This might cause trouble when trying to invoke systemd commands that depend on
``XDG_RUNTIME_DIR`` to access the bus:
.. code-block:: console
$ echo $XDG_RUNTIME_DIR
$ systemctl --user status
Failed to connect to bus: Permission denied
To force ``become`` to open a new systemd session that goes through
``pam_systemd``, you can use ``become_method: machinectl``.
For more information, see `this systemd issue
<https://github.com/systemd/systemd/issues/825#issuecomment-127917622>`_.
Resolving Temporary File Error Messsages
----------------------------------------
* Failed to set permissions on the temporary files Ansible needs to create when becoming an unprivileged user"
* This error can be resolved by installing the package that provides the ``setfacl`` command. (This is frequently the ``acl`` package but check your OS documentation.)
.. _become_network:
Become and network automation
=============================
As of version 2.6, Ansible supports ``become`` for privilege escalation (entering ``enable`` mode or privileged EXEC mode) on all Ansible-maintained network platforms that support ``enable`` mode. Using ``become`` replaces the ``authorize`` and ``auth_pass`` options in a ``provider`` dictionary.
You must set the connection type to either ``connection: ansible.netcommon.network_cli`` or ``connection: ansible.netcommon.httpapi`` to use ``become`` for privilege escalation on network devices. Check the :ref:`platform_options` documentation for details.
You can use escalated privileges on only the specific tasks that need them, on an entire play, or on all plays. Adding ``become: yes`` and ``become_method: enable`` instructs Ansible to enter ``enable`` mode before executing the task, play, or playbook where those parameters are set.
If you see this error message, the task that generated it requires ``enable`` mode to succeed:
.. code-block:: console
Invalid input (privileged mode required)
To set ``enable`` mode for a specific task, add ``become`` at the task level:
.. code-block:: yaml
- name: Gather facts (eos)
arista.eos.eos_facts:
gather_subset:
- "!hardware"
become: yes
become_method: enable
To set enable mode for all tasks in a single play, add ``become`` at the play level:
.. code-block:: yaml
- hosts: eos-switches
become: yes
become_method: enable
tasks:
- name: Gather facts (eos)
arista.eos.eos_facts:
gather_subset:
- "!hardware"
Setting enable mode for all tasks
---------------------------------
Often you wish for all tasks in all plays to run using privilege mode, that is best achieved by using ``group_vars``:
**group_vars/eos.yml**
.. code-block:: yaml
ansible_connection: ansible.netcommon.network_cli
ansible_network_os: arista.eos.eos
ansible_user: myuser
ansible_become: yes
ansible_become_method: enable
Passwords for enable mode
^^^^^^^^^^^^^^^^^^^^^^^^^
If you need a password to enter ``enable`` mode, you can specify it in one of two ways:
* providing the :option:`--ask-become-pass <ansible-playbook --ask-become-pass>` command line option
* setting the ``ansible_become_password`` connection variable
.. warning::
As a reminder passwords should never be stored in plain text. For information on encrypting your passwords and other secrets with Ansible Vault, see :ref:`vault`.
authorize and auth_pass
-----------------------
Ansible still supports ``enable`` mode with ``connection: local`` for legacy network playbooks. To enter ``enable`` mode with ``connection: local``, use the module options ``authorize`` and ``auth_pass``:
.. code-block:: yaml
- hosts: eos-switches
ansible_connection: local
tasks:
- name: Gather facts (eos)
eos_facts:
gather_subset:
- "!hardware"
provider:
authorize: yes
auth_pass: " {{ secret_auth_pass }}"
We recommend updating your playbooks to use ``become`` for network-device ``enable`` mode consistently. The use of ``authorize`` and of ``provider`` dictionaries will be deprecated in future. Check the :ref:`platform_options` and :ref:`network_modules` documentation for details.
.. _become_windows:
Become and Windows
==================
Since Ansible 2.3, ``become`` can be used on Windows hosts through the
``runas`` method. Become on Windows uses the same inventory setup and
invocation arguments as ``become`` on a non-Windows host, so the setup and
variable names are the same as what is defined in this document.
While ``become`` can be used to assume the identity of another user, there are other uses for
it with Windows hosts. One important use is to bypass some of the
limitations that are imposed when running on WinRM, such as constrained network
delegation or accessing forbidden system calls like the WUA API. You can use
``become`` with the same user as ``ansible_user`` to bypass these limitations
and run commands that are not normally accessible in a WinRM session.
.. Note::
On Windows you cannot connect with an underprivileged account and use become
to elevate your rights. Become can only be used if your connection account
is already an Administrator of the target host.
Administrative rights
---------------------
Many tasks in Windows require administrative privileges to complete. When using
the ``runas`` become method, Ansible will attempt to run the module with the
full privileges that are available to the become user. If it fails to elevate
the user token, it will continue to use the limited token during execution.
A user must have the ``SeDebugPrivilege`` to run a become process with elevated
privileges. This privilege is assigned to Administrators by default. If the
debug privilege is not available, the become process will run with a limited
set of privileges and groups.
To determine the type of token that Ansible was able to get, run the following
task:
.. code-block:: yaml
- Check my user name
ansible.windows.win_whoami:
become: yes
The output will look something similar to the below:
.. code-block:: ansible-output
ok: [windows] => {
"account": {
"account_name": "vagrant-domain",
"domain_name": "DOMAIN",
"sid": "S-1-5-21-3088887838-4058132883-1884671576-1105",
"type": "User"
},
"authentication_package": "Kerberos",
"changed": false,
"dns_domain_name": "DOMAIN.LOCAL",
"groups": [
{
"account_name": "Administrators",
"attributes": [
"Mandatory",
"Enabled by default",
"Enabled",
"Owner"
],
"domain_name": "BUILTIN",
"sid": "S-1-5-32-544",
"type": "Alias"
},
{
"account_name": "INTERACTIVE",
"attributes": [
"Mandatory",
"Enabled by default",
"Enabled"
],
"domain_name": "NT AUTHORITY",
"sid": "S-1-5-4",
"type": "WellKnownGroup"
},
],
"impersonation_level": "SecurityAnonymous",
"label": {
"account_name": "High Mandatory Level",
"domain_name": "Mandatory Label",
"sid": "S-1-16-12288",
"type": "Label"
},
"login_domain": "DOMAIN",
"login_time": "2018-11-18T20:35:01.9696884+00:00",
"logon_id": 114196830,
"logon_server": "DC01",
"logon_type": "Interactive",
"privileges": {
"SeBackupPrivilege": "disabled",
"SeChangeNotifyPrivilege": "enabled-by-default",
"SeCreateGlobalPrivilege": "enabled-by-default",
"SeCreatePagefilePrivilege": "disabled",
"SeCreateSymbolicLinkPrivilege": "disabled",
"SeDebugPrivilege": "enabled",
"SeDelegateSessionUserImpersonatePrivilege": "disabled",
"SeImpersonatePrivilege": "enabled-by-default",
"SeIncreaseBasePriorityPrivilege": "disabled",
"SeIncreaseQuotaPrivilege": "disabled",
"SeIncreaseWorkingSetPrivilege": "disabled",
"SeLoadDriverPrivilege": "disabled",
"SeManageVolumePrivilege": "disabled",
"SeProfileSingleProcessPrivilege": "disabled",
"SeRemoteShutdownPrivilege": "disabled",
"SeRestorePrivilege": "disabled",
"SeSecurityPrivilege": "disabled",
"SeShutdownPrivilege": "disabled",
"SeSystemEnvironmentPrivilege": "disabled",
"SeSystemProfilePrivilege": "disabled",
"SeSystemtimePrivilege": "disabled",
"SeTakeOwnershipPrivilege": "disabled",
"SeTimeZonePrivilege": "disabled",
"SeUndockPrivilege": "disabled"
},
"rights": [
"SeNetworkLogonRight",
"SeBatchLogonRight",
"SeInteractiveLogonRight",
"SeRemoteInteractiveLogonRight"
],
"token_type": "TokenPrimary",
"upn": "vagrant-domain@DOMAIN.LOCAL",
"user_flags": []
}
Under the ``label`` key, the ``account_name`` entry determines whether the user
has Administrative rights. Here are the labels that can be returned and what
they represent:
* ``Medium``: Ansible failed to get an elevated token and ran under a limited
token. Only a subset of the privileges assigned to user are available during
the module execution and the user does not have administrative rights.
* ``High``: An elevated token was used and all the privileges assigned to the
user are available during the module execution.
* ``System``: The ``NT AUTHORITY\System`` account is used and has the highest
level of privileges available.
The output will also show the list of privileges that have been granted to the
user. When the privilege value is ``disabled``, the privilege is assigned to
the logon token but has not been enabled. In most scenarios these privileges
are automatically enabled when required.
If running on a version of Ansible that is older than 2.5 or the normal
``runas`` escalation process fails, an elevated token can be retrieved by:
* Set the ``become_user`` to ``System`` which has full control over the
operating system.
* Grant ``SeTcbPrivilege`` to the user Ansible connects with on
WinRM. ``SeTcbPrivilege`` is a high-level privilege that grants
full control over the operating system. No user is given this privilege by
default, and care should be taken if you grant this privilege to a user or group.
For more information on this privilege, please see
`Act as part of the operating system <https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/dn221957(v=ws.11)>`_.
You can use the below task to set this privilege on a Windows host:
.. code-block:: yaml
- name: grant the ansible user the SeTcbPrivilege right
ansible.windows.win_user_right:
name: SeTcbPrivilege
users: '{{ansible_user}}'
action: add
* Turn UAC off on the host and reboot before trying to become the user. UAC is
a security protocol that is designed to run accounts with the
``least privilege`` principle. You can turn UAC off by running the following
tasks:
.. code-block:: yaml
- name: turn UAC off
win_regedit:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\system
name: EnableLUA
data: 0
type: dword
state: present
register: uac_result
- name: reboot after disabling UAC
win_reboot:
when: uac_result is changed
.. Note:: Granting the ``SeTcbPrivilege`` or turning UAC off can cause Windows
security vulnerabilities and care should be given if these steps are taken.
Local service accounts
----------------------
Prior to Ansible version 2.5, ``become`` only worked on Windows with a local or domain
user account. Local service accounts like ``System`` or ``NetworkService``
could not be used as ``become_user`` in these older versions. This restriction
has been lifted since the 2.5 release of Ansible. The three service accounts
that can be set under ``become_user`` are:
* System
* NetworkService
* LocalService
Because local service accounts do not have passwords, the
``ansible_become_password`` parameter is not required and is ignored if
specified.
Become without setting a password
---------------------------------
As of Ansible 2.8, ``become`` can be used to become a Windows local or domain account
without requiring a password for that account. For this method to work, the
following requirements must be met:
* The connection user has the ``SeDebugPrivilege`` privilege assigned
* The connection user is part of the ``BUILTIN\Administrators`` group
* The ``become_user`` has either the ``SeBatchLogonRight`` or ``SeNetworkLogonRight`` user right
Using become without a password is achieved in one of two different methods:
* Duplicating an existing logon session's token if the account is already logged on
* Using S4U to generate a logon token that is valid on the remote host only
In the first scenario, the become process is spawned from another logon of that
user account. This could be an existing RDP logon, console logon, but this is
not guaranteed to occur all the time. This is similar to the
``Run only when user is logged on`` option for a Scheduled Task.
In the case where another logon of the become account does not exist, S4U is
used to create a new logon and run the module through that. This is similar to
the ``Run whether user is logged on or not`` with the ``Do not store password``
option for a Scheduled Task. In this scenario, the become process will not be
able to access any network resources like a normal WinRM process.
To make a distinction between using become with no password and becoming an
account that has no password make sure to keep ``ansible_become_password`` as
undefined or set ``ansible_become_password:``.
.. Note:: Because there are no guarantees an existing token will exist for a
user when Ansible runs, there's a high change the become process will only
have access to local resources. Use become with a password if the task needs
to access network resources
Accounts without a password
---------------------------
.. Warning:: As a general security best practice, you should avoid allowing accounts without passwords.
Ansible can be used to become a Windows account that does not have a password (like the
``Guest`` account). To become an account without a password, set up the
variables like normal but set ``ansible_become_password: ''``.
Before become can work on an account like this, the local policy
`Accounts: Limit local account use of blank passwords to console logon only <https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/jj852174(v=ws.11)>`_
must be disabled. This can either be done through a Group Policy Object (GPO)
or with this Ansible task:
.. code-block:: yaml
- name: allow blank password on become
ansible.windows.win_regedit:
path: HKLM:\SYSTEM\CurrentControlSet\Control\Lsa
name: LimitBlankPasswordUse
data: 0
type: dword
state: present
.. Note:: This is only for accounts that do not have a password. You still need
to set the account's password under ``ansible_become_password`` if the
become_user has a password.
Become flags for Windows
------------------------
Ansible 2.5 added the ``become_flags`` parameter to the ``runas`` become method.
This parameter can be set using the ``become_flags`` task directive or set in
Ansible's configuration using ``ansible_become_flags``. The two valid values
that are initially supported for this parameter are ``logon_type`` and
``logon_flags``.
.. Note:: These flags should only be set when becoming a normal user account, not a local service account like LocalSystem.
The key ``logon_type`` sets the type of logon operation to perform. The value
can be set to one of the following:
* ``interactive``: The default logon type. The process will be run under a
context that is the same as when running a process locally. This bypasses all
WinRM restrictions and is the recommended method to use.
* ``batch``: Runs the process under a batch context that is similar to a
scheduled task with a password set. This should bypass most WinRM
restrictions and is useful if the ``become_user`` is not allowed to log on
interactively.
* ``new_credentials``: Runs under the same credentials as the calling user, but
outbound connections are run under the context of the ``become_user`` and
``become_password``, similar to ``runas.exe /netonly``. The ``logon_flags``
flag should also be set to ``netcredentials_only``. Use this flag if
the process needs to access a network resource (like an SMB share) using a
different set of credentials.
* ``network``: Runs the process under a network context without any cached
credentials. This results in the same type of logon session as running a
normal WinRM process without credential delegation, and operates under the same
restrictions.
* ``network_cleartext``: Like the ``network`` logon type, but instead caches
the credentials so it can access network resources. This is the same type of
logon session as running a normal WinRM process with credential delegation.
For more information, see
`dwLogonType <https://docs.microsoft.com/en-gb/windows/desktop/api/winbase/nf-winbase-logonusera>`_.
The ``logon_flags`` key specifies how Windows will log the user on when creating
the new process. The value can be set to none or multiple of the following:
* ``with_profile``: The default logon flag set. The process will load the
user's profile in the ``HKEY_USERS`` registry key to ``HKEY_CURRENT_USER``.
* ``netcredentials_only``: The process will use the same token as the caller
but will use the ``become_user`` and ``become_password`` when accessing a remote
resource. This is useful in inter-domain scenarios where there is no trust
relationship, and should be used with the ``new_credentials`` ``logon_type``.
By default ``logon_flags=with_profile`` is set, if the profile should not be
loaded set ``logon_flags=`` or if the profile should be loaded with
``netcredentials_only``, set ``logon_flags=with_profile,netcredentials_only``.
For more information, see `dwLogonFlags <https://docs.microsoft.com/en-gb/windows/desktop/api/winbase/nf-winbase-createprocesswithtokenw>`_.
Here are some examples of how to use ``become_flags`` with Windows tasks:
.. code-block:: yaml
- name: copy a file from a fileshare with custom credentials
ansible.windows.win_copy:
src: \\server\share\data\file.txt
dest: C:\temp\file.txt
remote_src: yes
vars:
ansible_become: yes
ansible_become_method: runas
ansible_become_user: DOMAIN\user
ansible_become_password: Password01
ansible_become_flags: logon_type=new_credentials logon_flags=netcredentials_only
- name: run a command under a batch logon
ansible.windows.win_whoami:
become: yes
become_flags: logon_type=batch
- name: run a command and not load the user profile
ansible.windows.win_whomai:
become: yes
become_flags: logon_flags=
Limitations of become on Windows
--------------------------------
* Running a task with ``async`` and ``become`` on Windows Server 2008, 2008 R2
and Windows 7 only works when using Ansible 2.7 or newer.
* By default, the become user logs on with an interactive session, so it must
have the right to do so on the Windows host. If it does not inherit the
``SeAllowLogOnLocally`` privilege or inherits the ``SeDenyLogOnLocally``
privilege, the become process will fail. Either add the privilege or set the
``logon_type`` flag to change the logon type used.
* Prior to Ansible version 2.3, become only worked when
``ansible_winrm_transport`` was either ``basic`` or ``credssp``. This
restriction has been lifted since the 2.4 release of Ansible for all hosts
except Windows Server 2008 (non R2 version).
* The Secondary Logon service ``seclogon`` must be running to use ``ansible_become_method: runas``
* The connection user must already be an Administrator on the Windows host to
use ``runas``. The target become user does not need to be an Administrator
though.
.. seealso::
`Mailing List <https://groups.google.com/forum/#!forum/ansible-project>`_
Questions? Help? Ideas? Stop by the list on Google Groups
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,124 @@
.. _playbooks_prompts:
**************************
Interactive input: prompts
**************************
If you want your playbook to prompt the user for certain input, add a 'vars_prompt' section. Prompting the user for variables lets you avoid recording sensitive data like passwords. In addition to security, prompts support flexibility. For example, if you use one playbook across multiple software releases, you could prompt for the particular release version.
.. contents::
:local:
Here is a most basic example:
.. code-block:: yaml
---
- hosts: all
vars_prompt:
- name: username
prompt: What is your username?
private: no
- name: password
prompt: What is your password?
tasks:
- name: Print a message
ansible.builtin.debug:
msg: 'Logging in as {{ username }}'
The user input is hidden by default but it can be made visible by setting ``private: no``.
.. note::
Prompts for individual ``vars_prompt`` variables will be skipped for any variable that is already defined through the command line ``--extra-vars`` option, or when running from a non-interactive session (such as cron or Ansible AWX). See :ref:`passing_variables_on_the_command_line`.
If you have a variable that changes infrequently, you can provide a default value that can be overridden.
.. code-block:: yaml
vars_prompt:
- name: release_version
prompt: Product release version
default: "1.0"
Hashing values supplied by ``vars_prompt``
------------------------------------------
You can hash the entered value so you can use it, for instance, with the user module to define a password:
.. code-block:: yaml
vars_prompt:
- name: my_password2
prompt: Enter password2
private: yes
encrypt: sha512_crypt
confirm: yes
salt_size: 7
If you have `Passlib <https://passlib.readthedocs.io/en/stable/>`_ installed, you can use any crypt scheme the library supports:
- *des_crypt* - DES Crypt
- *bsdi_crypt* - BSDi Crypt
- *bigcrypt* - BigCrypt
- *crypt16* - Crypt16
- *md5_crypt* - MD5 Crypt
- *bcrypt* - BCrypt
- *sha1_crypt* - SHA-1 Crypt
- *sun_md5_crypt* - Sun MD5 Crypt
- *sha256_crypt* - SHA-256 Crypt
- *sha512_crypt* - SHA-512 Crypt
- *apr_md5_crypt* - Apache's MD5-Crypt variant
- *phpass* - PHPass' Portable Hash
- *pbkdf2_digest* - Generic PBKDF2 Hashes
- *cta_pbkdf2_sha1* - Cryptacular's PBKDF2 hash
- *dlitz_pbkdf2_sha1* - Dwayne Litzenberger's PBKDF2 hash
- *scram* - SCRAM Hash
- *bsd_nthash* - FreeBSD's MCF-compatible nthash encoding
The only parameters accepted are 'salt' or 'salt_size'. You can use your own salt by defining
'salt', or have one generated automatically using 'salt_size'. By default Ansible generates a salt
of size 8.
.. versionadded:: 2.7
If you do not have Passlib installed, Ansible uses the `crypt <https://docs.python.org/3/library/crypt.html>`_ library as a fallback. Ansible supports at most four crypt schemes, depending on your platform at most the following crypt schemes are supported:
- *bcrypt* - BCrypt
- *md5_crypt* - MD5 Crypt
- *sha256_crypt* - SHA-256 Crypt
- *sha512_crypt* - SHA-512 Crypt
.. versionadded:: 2.8
.. _unsafe_prompts:
Allowing special characters in ``vars_prompt`` values
-----------------------------------------------------
Some special characters, such as ``{`` and ``%`` can create templating errors. If you need to accept special characters, use the ``unsafe`` option:
.. code-block:: yaml
vars_prompt:
- name: my_password_with_weird_chars
prompt: Enter password
unsafe: yes
private: yes
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_variables`
All about variables
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,226 @@
.. _playbooks_reuse:
**************************
Re-using Ansible artifacts
**************************
You can write a simple playbook in one very large file, and most users learn the one-file approach first. However, breaking your automation work up into smaller files is an excellent way to organize complex sets of tasks and reuse them. Smaller, more distributed artifacts let you re-use the same variables, tasks, and plays in multiple playbooks to address different use cases. You can use distributed artifacts across multiple parent playbooks or even multiple times within one playbook. For example, you might want to update your customer database as part of several different playbooks. If you put all the tasks related to updating your database in a tasks file or a role, you can re-use them in many playbooks while only maintaining them in one place.
.. contents::
:local:
Creating re-usable files and roles
==================================
Ansible offers four distributed, re-usable artifacts: variables files, task files, playbooks, and roles.
- A variables file contains only variables.
- A task file contains only tasks.
- A playbook contains at least one play, and may contain variables, tasks, and other content. You can re-use tightly focused playbooks, but you can only re-use them statically, not dynamically.
- A role contains a set of related tasks, variables, defaults, handlers, and even modules or other plugins in a defined file-tree. Unlike variables files, task files, or playbooks, roles can be easily uploaded and shared via Ansible Galaxy. See :ref:`playbooks_reuse_roles` for details about creating and using roles.
.. versionadded:: 2.4
Re-using playbooks
==================
You can incorporate multiple playbooks into a main playbook. However, you can only use imports to re-use playbooks. For example:
.. code-block:: yaml
- import_playbook: webservers.yml
- import_playbook: databases.yml
Importing incorporates playbooks in other playbooks statically. Ansible runs the plays and tasks in each imported playbook in the order they are listed, just as if they had been defined directly in the main playbook.
You can select which playbook you want to import at runtime by defining your imported playbook filename with a variable, then passing the variable with either ``--extra-vars`` or the ``vars`` keyword. For example:
.. code-block:: yaml
- import_playbook: "/path/to/{{ import_from_extra_var }}"
- import_playbook: "{{ import_from_vars }}"
vars:
import_from_vars: /path/to/one_playbook.yml
If you run this playbook with ``ansible-playbook my_playbook -e import_from_extra_var=other_playbook.yml``, Ansible imports both one_playbook.yml and other_playbook.yml.
When to turn a playbook into a role
===================================
For some use cases, simple playbooks work well. However, starting at a certain level of complexity, roles work better than playbooks. A role lets you store your defaults, handlers, variables, and tasks in separate directories, instead of in a single long document. Roles are easy to share on Ansible Galaxy. For complex use cases, most users find roles easier to read, understand, and maintain than all-in-one playbooks.
Re-using files and roles
========================
Ansible offers two ways to re-use files and roles in a playbook: dynamic and static.
- For dynamic re-use, add an ``include_*`` task in the tasks section of a play:
- :ref:`include_role <include_role_module>`
- :ref:`include_tasks <include_tasks_module>`
- :ref:`include_vars <include_vars_module>`
- For static re-use, add an ``import_*`` task in the tasks section of a play:
- :ref:`import_role <import_role_module>`
- :ref:`import_tasks <import_tasks_module>`
Task include and import statements can be used at arbitrary depth.
You can still use the bare :ref:`roles <roles_keyword>` keyword at the play level to incorporate a role in a playbook statically. However, the bare :ref:`include <include_module>` keyword, once used for both task files and playbook-level includes, is now deprecated.
Includes: dynamic re-use
------------------------
Including roles, tasks, or variables adds them to a playbook dynamically. Ansible processes included files and roles as they come up in a playbook, so included tasks can be affected by the results of earlier tasks within the top-level playbook. Included roles and tasks are similar to handlers - they may or may not run, depending on the results of other tasks in the top-level playbook.
The primary advantage of using ``include_*`` statements is looping. When a loop is used with an include, the included tasks or role will be executed once for each item in the loop.
The filenames for included roles, tasks, and vars are templated before inclusion.
You can pass variables into includes. See :ref:`ansible_variable_precedence` for more details on variable inheritance and precedence.
Imports: static re-use
----------------------
Importing roles, tasks, or playbooks adds them to a playbook statically. Ansible pre-processes imported files and roles before it runs any tasks in a playbook, so imported content is never affected by other tasks within the top-level playbook.
The filenames for imported roles and tasks support templating, but the variables must be available when Ansible is pre-processing the imports. This can be done with the ``vars`` keyword or by using ``--extra-vars``.
You can pass variables to imports. You must pass variables if you want to run an imported file more than once in a playbook. For example:
.. code-block:: yaml
tasks:
- import_tasks: wordpress.yml
vars:
wp_user: timmy
- import_tasks: wordpress.yml
vars:
wp_user: alice
- import_tasks: wordpress.yml
vars:
wp_user: bob
See :ref:`ansible_variable_precedence` for more details on variable inheritance and precedence.
.. _dynamic_vs_static:
Comparing includes and imports: dynamic and static re-use
------------------------------------------------------------
Each approach to re-using distributed Ansible artifacts has advantages and limitations. You may choose dynamic re-use for some playbooks and static re-use for others. Although you can use both dynamic and static re-use in a single playbook, it is best to select one approach per playbook. Mixing static and dynamic re-use can introduce difficult-to-diagnose bugs into your playbooks. This table summarizes the main differences so you can choose the best approach for each playbook you create.
.. table::
:class: documentation-table
========================= ======================================== ========================================
.. Include_* Import_*
========================= ======================================== ========================================
Type of re-use Dynamic Static
When processed At runtime, when encountered Pre-processed during playbook parsing
Task or play All includes are tasks ``import_playbook`` cannot be a task
Task options Apply only to include task itself Apply to all child tasks in import
Calling from loops Executed once for each loop item Cannot be used in a loop
Using ``--list-tags`` Tags within includes not listed All tags appear with ``--list-tags``
Using ``--list-tasks`` Tasks within includes not listed All tasks appear with ``--list-tasks``
Notifying handlers Cannot trigger handlers within includes Can trigger individual imported handlers
Using ``--start-at-task`` Cannot start at tasks within includes Can start at imported tasks
Using inventory variables Can ``include_*: {{ inventory_var }}`` Cannot ``import_*: {{ inventory_var }}``
With playbooks No ``include_playbook`` Can import full playbooks
With variables files Can include variables files Use ``vars_files:`` to import variables
========================= ======================================== ========================================
.. note::
* There are also big differences in resource consumption and performance, imports are quite lean and fast, while includes require a lot of management
and accounting.
Re-using tasks as handlers
==========================
You can also use includes and imports in the :ref:`handlers` section of a playbook. For instance, if you want to define how to restart Apache, you only have to do that once for all of your playbooks. You might make a ``restarts.yml`` file that looks like:
.. code-block:: yaml
# restarts.yml
- name: Restart apache
ansible.builtin.service:
name: apache
state: restarted
- name: Restart mysql
ansible.builtin.service:
name: mysql
state: restarted
You can trigger handlers from either an import or an include, but the procedure is different for each method of re-use. If you include the file, you must notify the include itself, which triggers all the tasks in ``restarts.yml``. If you import the file, you must notify the individual task(s) within ``restarts.yml``. You can mix direct tasks and handlers with included or imported tasks and handlers.
Triggering included (dynamic) handlers
--------------------------------------
Includes are executed at run-time, so the name of the include exists during play execution, but the included tasks do not exist until the include itself is triggered. To use the ``Restart apache`` task with dynamic re-use, refer to the name of the include itself. This approach triggers all tasks in the included file as handlers. For example, with the task file shown above:
.. code-block:: yaml
- name: Trigger an included (dynamic) handler
hosts: localhost
handlers:
- name: Restart services
include_tasks: restarts.yml
tasks:
- command: "true"
notify: Restart services
Triggering imported (static) handlers
-------------------------------------
Imports are processed before the play begins, so the name of the import no longer exists during play execution, but the names of the individual imported tasks do exist. To use the ``Restart apache`` task with static re-use, refer to the name of each task or tasks within the imported file. For example, with the task file shown above:
.. code-block:: yaml
- name: Trigger an imported (static) handler
hosts: localhost
handlers:
- name: Restart services
import_tasks: restarts.yml
tasks:
- command: "true"
notify: Restart apache
- command: "true"
notify: Restart mysql
.. seealso::
:ref:`utilities_modules`
Documentation of the ``include*`` and ``import*`` modules discussed here.
:ref:`working_with_playbooks`
Review the basic Playbook language features
:ref:`playbooks_variables`
All about variables in playbooks
:ref:`playbooks_conditionals`
Conditionals in playbooks
:ref:`playbooks_loops`
Loops in playbooks
:ref:`tips_and_tricks`
Tips and tricks for playbooks
:ref:`ansible_galaxy`
How to share roles on galaxy, role management
`GitHub Ansible examples <https://github.com/ansible/ansible-examples>`_
Complete playbook files from the GitHub project source
`Mailing List <https://groups.google.com/group/ansible-project>`_
Questions? Help? Ideas? Stop by the list on Google Groups

@ -0,0 +1,611 @@
.. _playbooks_reuse_roles:
*****
Roles
*****
Roles let you automatically load related vars, files, tasks, handlers, and other Ansible artifacts based on a known file structure. After you group your content in roles, you can easily reuse them and share them with other users.
.. contents::
:local:
Role directory structure
========================
An Ansible role has a defined directory structure with eight main standard directories. You must include at least one of these directories in each role. You can omit any directories the role does not use. For example:
.. code-block:: text
# playbooks
site.yml
webservers.yml
fooservers.yml
.. include:: shared_snippets/role_directory.txt
By default Ansible will look in each directory within a role for a ``main.yml`` file for relevant content (also ``main.yaml`` and ``main``):
- ``tasks/main.yml`` - the main list of tasks that the role executes.
- ``handlers/main.yml`` - handlers, which may be used within or outside this role.
- ``library/my_module.py`` - modules, which may be used within this role (see :ref:`embedding_modules_and_plugins_in_roles` for more information).
- ``defaults/main.yml`` - default variables for the role (see :ref:`playbooks_variables` for more information). These variables have the lowest priority of any variables available, and can be easily overridden by any other variable, including inventory variables.
- ``vars/main.yml`` - other variables for the role (see :ref:`playbooks_variables` for more information).
- ``files/main.yml`` - files that the role deploys.
- ``templates/main.yml`` - templates that the role deploys.
- ``meta/main.yml`` - metadata for the role, including role dependencies.
You can add other YAML files in some directories. For example, you can place platform-specific tasks in separate files and refer to them in the ``tasks/main.yml`` file:
.. code-block:: yaml
# roles/example/tasks/main.yml
- name: Install the correct web server for RHEL
import_tasks: redhat.yml
when: ansible_facts['os_family']|lower == 'redhat'
- name: Install the correct web server for Debian
import_tasks: debian.yml
when: ansible_facts['os_family']|lower == 'debian'
# roles/example/tasks/redhat.yml
- name: Install web server
ansible.builtin.yum:
name: "httpd"
state: present
# roles/example/tasks/debian.yml
- name: Install web server
ansible.builtin.apt:
name: "apache2"
state: present
Roles may also include modules and other plugin types in a directory called ``library``. For more information, please refer to :ref:`embedding_modules_and_plugins_in_roles` below.
.. _role_search_path:
Storing and finding roles
=========================
By default, Ansible looks for roles in the following locations:
- in collections, if you are using them
- in a directory called ``roles/``, relative to the playbook file
- in the configured :ref:`roles_path <DEFAULT_ROLES_PATH>`. The default search path is ``~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles``.
- in the directory where the playbook file is located
If you store your roles in a different location, set the :ref:`roles_path <DEFAULT_ROLES_PATH>` configuration option so Ansible can find your roles. Checking shared roles into a single location makes them easier to use in multiple playbooks. See :ref:`intro_configuration` for details about managing settings in ansible.cfg.
Alternatively, you can call a role with a fully qualified path:
.. code-block:: yaml
---
- hosts: webservers
roles:
- role: '/path/to/my/roles/common'
Using roles
===========
You can use roles in three ways:
- at the play level with the ``roles`` option: This is the classic way of using roles in a play.
- at the tasks level with ``include_role``: You can reuse roles dynamically anywhere in the ``tasks`` section of a play using ``include_role``.
- at the tasks level with ``import_role``: You can reuse roles statically anywhere in the ``tasks`` section of a play using ``import_role``.
.. _roles_keyword:
Using roles at the play level
-----------------------------
The classic (original) way to use roles is with the ``roles`` option for a given play:
.. code-block:: yaml
---
- hosts: webservers
roles:
- common
- webservers
When you use the ``roles`` option at the play level, for each role 'x':
- If roles/x/tasks/main.yml exists, Ansible adds the tasks in that file to the play.
- If roles/x/handlers/main.yml exists, Ansible adds the handlers in that file to the play.
- If roles/x/vars/main.yml exists, Ansible adds the variables in that file to the play.
- If roles/x/defaults/main.yml exists, Ansible adds the variables in that file to the play.
- If roles/x/meta/main.yml exists, Ansible adds any role dependencies in that file to the list of roles.
- Any copy, script, template or include tasks (in the role) can reference files in roles/x/{files,templates,tasks}/ (dir depends on task) without having to path them relatively or absolutely.
When you use the ``roles`` option at the play level, Ansible treats the roles as static imports and processes them during playbook parsing. Ansible executes each play in this order:
- Any ``pre_tasks`` defined in the play.
- Any handlers triggered by pre_tasks.
- Each role listed in ``roles:``, in the order listed. Any role dependencies defined in the role's ``meta/main.yml`` run first, subject to tag filtering and conditionals. See :ref:`role_dependencies` for more details.
- Any ``tasks`` defined in the play.
- Any handlers triggered by the roles or tasks.
- Any ``post_tasks`` defined in the play.
- Any handlers triggered by post_tasks.
.. note::
If using tags with tasks in a role, be sure to also tag your pre_tasks, post_tasks, and role dependencies and pass those along as well, especially if the pre/post tasks and role dependencies are used for monitoring outage window control or load balancing. See :ref:`tags` for details on adding and using tags.
You can pass other keywords to the ``roles`` option:
.. code-block:: yaml
---
- hosts: webservers
roles:
- common
- role: foo_app_instance
vars:
dir: '/opt/a'
app_port: 5000
tags: typeA
- role: foo_app_instance
vars:
dir: '/opt/b'
app_port: 5001
tags: typeB
When you add a tag to the ``role`` option, Ansible applies the tag to ALL tasks within the role.
When using ``vars:`` within the ``roles:`` section of a playbook, the variables are added to the play variables, making them available to all tasks within the play before and after the role. This behavior can be changed by :ref:`DEFAULT_PRIVATE_ROLE_VARS`.
Including roles: dynamic reuse
------------------------------
You can reuse roles dynamically anywhere in the ``tasks`` section of a play using ``include_role``. While roles added in a ``roles`` section run before any other tasks in a play, included roles run in the order they are defined. If there are other tasks before an ``include_role`` task, the other tasks will run first.
To include a role:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Print a message
ansible.builtin.debug:
msg: "this task runs before the example role"
- name: Include the example role
include_role:
name: example
- name: Print a message
ansible.builtin.debug:
msg: "this task runs after the example role"
You can pass other keywords, including variables and tags, when including roles:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Include the foo_app_instance role
include_role:
name: foo_app_instance
vars:
dir: '/opt/a'
app_port: 5000
tags: typeA
...
When you add a :ref:`tag <tags>` to an ``include_role`` task, Ansible applies the tag `only` to the include itself. This means you can pass ``--tags`` to run only selected tasks from the role, if those tasks themselves have the same tag as the include statement. See :ref:`selective_reuse` for details.
You can conditionally include a role:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Include the some_role role
include_role:
name: some_role
when: "ansible_facts['os_family'] == 'RedHat'"
Importing roles: static reuse
-----------------------------
You can reuse roles statically anywhere in the ``tasks`` section of a play using ``import_role``. The behavior is the same as using the ``roles`` keyword. For example:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Print a message
ansible.builtin.debug:
msg: "before we run our role"
- name: Import the example role
import_role:
name: example
- name: Print a message
ansible.builtin.debug:
msg: "after we ran our role"
You can pass other keywords, including variables and tags, when importing roles:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Import the foo_app_instance role
import_role:
name: foo_app_instance
vars:
dir: '/opt/a'
app_port: 5000
...
When you add a tag to an ``import_role`` statement, Ansible applies the tag to `all` tasks within the role. See :ref:`tag_inheritance` for details.
Role argument validation
========================
Beginning with version 2.11, you may choose to enable role argument validation based on an argument
specification. This specification is defined in the ``meta/argument_specs.yml`` file (or with the ``.yaml``
file extension). When this argument specification is defined, a new task is inserted at the beginning of role execution
that will validate the parameters supplied for the role against the specification. If the parameters fail
validation, the role will fail execution.
.. note::
Ansible also supports role specifications defined in the role ``meta/main.yml`` file, as well. However,
any role that defines the specs within this file will not work on versions below 2.11. For this reason,
we recommend using the ``meta/argument_specs.yml`` file to maintain backward compatibility.
.. note::
When role argument validation is used on a role that has defined :ref:`dependencies <role_dependencies>`,
then validation on those dependencies will run before the dependent role, even if argument validation fails
for the dependent role.
Specification format
--------------------
The role argument specification must be defined in a top-level ``argument_specs`` block within the
role ``meta/argument_specs.yml`` file. All fields are lower-case.
:entry-point-name:
* The name of the role entry point.
* This should be ``main`` in the case of an unspecified entry point.
* This will be the base name of the tasks file to execute, with no ``.yml`` or ``.yaml`` file extension.
:short_description:
* A short, one-line description of the entry point.
* The ``short_description`` is displayed by ``ansible-doc -t role -l``.
:description:
* A longer description that may contain multiple lines.
:author:
* Name of the entry point authors.
* Use a multi-line list if there is more than one author.
:options:
* Options are often called "parameters" or "arguments". This section defines those options.
* For each role option (argument), you may include:
:option-name:
* The name of the option/argument.
:description:
* Detailed explanation of what this option does. It should be written in full sentences.
:type:
* The data type of the option. See :ref:`Argument spec <argument_spec>` for allowed values for ``type``. Default is ``str``.
* If an option is of type ``list``, ``elements`` should be specified.
:required:
* Only needed if ``true``.
* If missing, the option is not required.
:default:
* If ``required`` is false/missing, ``default`` may be specified (assumed 'null' if missing).
* Ensure that the default value in the docs matches the default value in the code. The actual
default for the role variable will always come from ``defaults/main.yml``.
* The default field must not be listed as part of the description, unless it requires additional information or conditions.
* If the option is a boolean value, you can use any of the boolean values recognized by Ansible:
(such as true/false or yes/no). Choose the one that reads better in the context of the option.
:choices:
* List of option values.
* Should be absent if empty.
:elements:
* Specifies the data type for list elements when type is ``list``.
:options:
* If this option takes a dict or list of dicts, you can define the structure here.
Sample specification
--------------------
.. code-block:: yaml
# roles/myapp/meta/argument_specs.yml
---
argument_specs:
# roles/myapp/tasks/main.yml entry point
main:
short_description: The main entry point for the myapp role.
options:
myapp_int:
type: "int"
required: false
default: 42
description: "The integer value, defaulting to 42."
myapp_str:
type: "str"
required: true
description: "The string value"
# roles/maypp/tasks/alternate.yml entry point
alternate:
short_description: The alternate entry point for the myapp role.
options:
myapp_int:
type: "int"
required: false
default: 1024
description: "The integer value, defaulting to 1024."
.. _run_role_twice:
Running a role multiple times in one play
=========================================
Ansible only executes each role once in a play, even if you define it multiple times, unless the parameters defined on the role are different for each definition. For example, Ansible only runs the role ``foo`` once in a play like this:
.. code-block:: yaml
---
- hosts: webservers
roles:
- foo
- bar
- foo
You have two options to force Ansible to run a role more than once.
Passing different parameters
----------------------------
If you pass different parameters in each role definition, Ansible runs the role more than once. Providing different variable values is not the same as passing different role parameters. You must use the ``roles`` keyword for this behavior, since ``import_role`` and ``include_role`` do not accept role parameters.
This play runs the ``foo`` role twice:
.. code-block:: yaml
---
- hosts: webservers
roles:
- { role: foo, message: "first" }
- { role: foo, message: "second" }
This syntax also runs the ``foo`` role twice;
.. code-block:: yaml
---
- hosts: webservers
roles:
- role: foo
message: "first"
- role: foo
message: "second"
In these examples, Ansible runs ``foo`` twice because each role definition has different parameters.
Using ``allow_duplicates: true``
--------------------------------
Add ``allow_duplicates: true`` to the ``meta/main.yml`` file for the role:
.. code-block:: yaml
# playbook.yml
---
- hosts: webservers
roles:
- foo
- foo
# roles/foo/meta/main.yml
---
allow_duplicates: true
In this example, Ansible runs ``foo`` twice because we have explicitly enabled it to do so.
.. _role_dependencies:
Using role dependencies
=======================
Role dependencies let you automatically pull in other roles when using a role.
Role dependencies are prerequisites, not true dependencies. The roles do not have a parent/child relationship. Ansible loads all listed roles, runs the roles listed under ``dependencies`` first, then runs the role that lists them. The play object is the parent of all roles, including roles called by a ``dependencies`` list.
Role dependencies are stored in the ``meta/main.yml`` file within the role directory. This file should contain a list of roles and parameters to insert before the specified role. For example:
.. code-block:: yaml
# roles/myapp/meta/main.yml
---
dependencies:
- role: common
vars:
some_parameter: 3
- role: apache
vars:
apache_port: 80
- role: postgres
vars:
dbname: blarg
other_parameter: 12
Ansible always executes roles listed in ``dependencies`` before the role that lists them. Ansible executes this pattern recursively when you use the ``roles`` keyword. For example, if you list role ``foo`` under ``roles:``, role ``foo`` lists role ``bar`` under ``dependencies`` in its meta/main.yml file, and role ``bar`` lists role ``baz`` under ``dependencies`` in its meta/main.yml, Ansible executes ``baz``, then ``bar``, then ``foo``.
Running role dependencies multiple times in one play
----------------------------------------------------
Ansible treats duplicate role dependencies like duplicate roles listed under ``roles:``: Ansible only executes role dependencies once, even if defined multiple times, unless the parameters, tags, or when clause defined on the role are different for each definition. If two roles in a play both list a third role as a dependency, Ansible only runs that role dependency once, unless you pass different parameters, tags, when clause, or use ``allow_duplicates: true`` in the role you want to run multiple times. See :ref:`Galaxy role dependencies <galaxy_dependencies>` for more details.
.. note::
Role deduplication does not consult the invocation signature of parent roles. Additionally, when using ``vars:`` instead of role params, there is a side effect of changing variable scoping. Using ``vars:`` results in those variables being scoped at the play level. In the below example, using ``vars:`` would cause ``n`` to be defined as ``4`` through the entire play, including roles called before it.
In addition to the above, users should be aware that role de-duplication occurs before variable evaluation. This means that :term:`Lazy Evaluation` may make seemingly different role invocations equivalently the same, preventing the role from running more than once.
For example, a role named ``car`` depends on a role named ``wheel`` as follows:
.. code-block:: yaml
---
dependencies:
- role: wheel
n: 1
- role: wheel
n: 2
- role: wheel
n: 3
- role: wheel
n: 4
And the ``wheel`` role depends on two roles: ``tire`` and ``brake``. The ``meta/main.yml`` for wheel would then contain the following:
.. code-block:: yaml
---
dependencies:
- role: tire
- role: brake
And the ``meta/main.yml`` for ``tire`` and ``brake`` would contain the following:
.. code-block:: yaml
---
allow_duplicates: true
The resulting order of execution would be as follows:
.. code-block:: text
tire(n=1)
brake(n=1)
wheel(n=1)
tire(n=2)
brake(n=2)
wheel(n=2)
...
car
To use ``allow_duplicates: true`` with role dependencies, you must specify it for the role listed under ``dependencies``, not for the role that lists it. In the example above, ``allow_duplicates: true`` appears in the ``meta/main.yml`` of the ``tire`` and ``brake`` roles. The ``wheel`` role does not require ``allow_duplicates: true``, because each instance defined by ``car`` uses different parameter values.
.. note::
See :ref:`playbooks_variables` for details on how Ansible chooses among variable values defined in different places (variable inheritance and scope).
Also deduplication happens ONLY at the play level, so multiple plays in the same playbook may rerun the roles.
.. _embedding_modules_and_plugins_in_roles:
Embedding modules and plugins in roles
======================================
If you write a custom module (see :ref:`developing_modules`) or a plugin (see :ref:`developing_plugins`), you might wish to distribute it as part of a role. For example, if you write a module that helps configure your company's internal software, and you want other people in your organization to use this module, but you do not want to tell everyone how to configure their Ansible library path, you can include the module in your internal_config role.
To add a module or a plugin to a role:
Alongside the 'tasks' and 'handlers' structure of a role, add a directory named 'library' and then include the module directly inside the 'library' directory.
Assuming you had this:
.. code-block:: text
roles/
my_custom_modules/
library/
module1
module2
The module will be usable in the role itself, as well as any roles that are called *after* this role, as follows:
.. code-block:: yaml
---
- hosts: webservers
roles:
- my_custom_modules
- some_other_role_using_my_custom_modules
- yet_another_role_using_my_custom_modules
If necessary, you can also embed a module in a role to modify a module in Ansible's core distribution. For example, you can use the development version of a particular module before it is released in production releases by copying the module and embedding the copy in a role. Use this approach with caution, as API signatures may change in core components, and this workaround is not guaranteed to work.
The same mechanism can be used to embed and distribute plugins in a role, using the same schema. For example, for a filter plugin:
.. code-block:: text
roles/
my_custom_filter/
filter_plugins
filter1
filter2
These filters can then be used in a Jinja template in any role called after 'my_custom_filter'.
Sharing roles: Ansible Galaxy
=============================
`Ansible Galaxy <https://galaxy.ansible.com>`_ is a free site for finding, downloading, rating, and reviewing all kinds of community-developed Ansible roles and can be a great way to get a jumpstart on your automation projects.
The client ``ansible-galaxy`` is included in Ansible. The Galaxy client allows you to download roles from Ansible Galaxy, and also provides an excellent default framework for creating your own roles.
Read the `Ansible Galaxy documentation <https://galaxy.ansible.com/docs/>`_ page for more information
.. seealso::
:ref:`ansible_galaxy`
How to create new roles, share roles on Galaxy, role management
:ref:`yaml_syntax`
Learn about YAML syntax
:ref:`working_with_playbooks`
Review the basic Playbook language features
:ref:`tips_and_tricks`
Tips and tricks for playbooks
:ref:`playbooks_variables`
Variables in playbooks
:ref:`playbooks_conditionals`
Conditionals in playbooks
:ref:`playbooks_loops`
Loops in playbooks
:ref:`tags`
Using tags to select or skip roles/tasks in long playbooks
:ref:`list_of_collections`
Browse existing collections, modules, and plugins
:ref:`developing_modules`
Extending Ansible by writing your own modules
`GitHub Ansible examples <https://github.com/ansible/ansible-examples>`_
Complete playbook files from the GitHub project source
`Mailing List <https://groups.google.com/group/ansible-project>`_
Questions? Help? Ideas? Stop by the list on Google Groups

@ -0,0 +1,46 @@
.. _playbooks_start_and_step:
***************************************
Executing playbooks for troubleshooting
***************************************
When you are testing new plays or debugging playbooks, you may need to run the same play multiple times. To make this more efficient, Ansible offers two alternative ways to execute a playbook: start-at-task and step mode.
.. _start_at_task:
start-at-task
-------------
To start executing your playbook at a particular task (usually the task that failed on the previous run), use the ``--start-at-task`` option.
.. code-block:: shell
ansible-playbook playbook.yml --start-at-task="install packages"
In this example, Ansible starts executing your playbook at a task named "install packages". This feature does not work with tasks inside dynamically re-used roles or tasks (``include_*``), see :ref:`dynamic_vs_static`.
.. _step:
Step mode
---------
To execute a playbook interactively, use ``--step``.
.. code-block:: shell
ansible-playbook playbook.yml --step
With this option, Ansible stops on each task, and asks if it should execute that task. For example, if you have a task called "configure ssh", the playbook run will stop and ask.
.. code-block:: shell
Perform task: configure ssh (y/n/c):
Answer "y" to execute the task, answer "n" to skip the task, and answer "c" to exit step mode, executing all remaining tasks without asking.
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbook_debugger`
Using the Ansible debugger

@ -0,0 +1,254 @@
.. _playbooks_strategies:
Controlling playbook execution: strategies and more
===================================================
By default, Ansible runs each task on all hosts affected by a play before starting the next task on any host, using 5 forks. If you want to change this default behavior, you can use a different strategy plugin, change the number of forks, or apply one of several keywords like ``serial``.
.. contents::
:local:
Selecting a strategy
--------------------
The default behavior described above is the :ref:`linear strategy<linear_strategy>`. Ansible offers other strategies, including the :ref:`debug strategy<debug_strategy>` (see also :ref:`playbook_debugger`) and the :ref:`free strategy<free_strategy>`, which allows each host to run until the end of the play as fast as it can:
.. code-block:: yaml
- hosts: all
strategy: free
tasks:
# ...
You can select a different strategy for each play as shown above, or set your preferred strategy globally in ``ansible.cfg``, under the ``defaults`` stanza:
.. code-block:: ini
[defaults]
strategy = free
All strategies are implemented as :ref:`strategy plugins<strategy_plugins>`. Please review the documentation for each strategy plugin for details on how it works.
Setting the number of forks
---------------------------
If you have the processing power available and want to use more forks, you can set the number in ``ansible.cfg``:
.. code-block:: ini
[defaults]
forks = 30
or pass it on the command line: `ansible-playbook -f 30 my_playbook.yml`.
Using keywords to control execution
-----------------------------------
In addition to strategies, several :ref:`keywords<playbook_keywords>` also affect play execution. You can set a number, a percentage, or a list of numbers of hosts you want to manage at a time with ``serial``. Ansible completes the play on the specified number or percentage of hosts before starting the next batch of hosts. You can restrict the number of workers allotted to a block or task with ``throttle``. You can control how Ansible selects the next host in a group to execute against with ``order``. You can run a task on a single host with ``run_once``. These keywords are not strategies. They are directives or options applied to a play, block, or task.
Other keywords that affect play execution include ``ignore_errors``, ``ignore_unreachable``, and ``any_errors_fatal``. These options are documented in :ref:`playbooks_error_handling`.
.. _rolling_update_batch_size:
Setting the batch size with ``serial``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, Ansible runs in parallel against all the hosts in the :ref:`pattern <intro_patterns>` you set in the ``hosts:`` field of each play. If you want to manage only a few machines at a time, for example during a rolling update, you can define how many hosts Ansible should manage at a single time using the ``serial`` keyword:
.. code-block:: yaml
---
- name: test play
hosts: webservers
serial: 3
gather_facts: False
tasks:
- name: first task
command: hostname
- name: second task
command: hostname
In the above example, if we had 6 hosts in the group 'webservers', Ansible would execute the play completely (both tasks) on 3 of the hosts before moving on to the next 3 hosts:
.. code-block:: ansible-output
PLAY [webservers] ****************************************
TASK [first task] ****************************************
changed: [web3]
changed: [web2]
changed: [web1]
TASK [second task] ***************************************
changed: [web1]
changed: [web2]
changed: [web3]
PLAY [webservers] ****************************************
TASK [first task] ****************************************
changed: [web4]
changed: [web5]
changed: [web6]
TASK [second task] ***************************************
changed: [web4]
changed: [web5]
changed: [web6]
PLAY RECAP ***********************************************
web1 : ok=2 changed=2 unreachable=0 failed=0
web2 : ok=2 changed=2 unreachable=0 failed=0
web3 : ok=2 changed=2 unreachable=0 failed=0
web4 : ok=2 changed=2 unreachable=0 failed=0
web5 : ok=2 changed=2 unreachable=0 failed=0
web6 : ok=2 changed=2 unreachable=0 failed=0
.. note::
Setting the batch size with ``serial`` changes the scope of the Ansible failures to the batch size, not the entire host list. You can use :ref:`ignore_unreachable <ignore_unreachable>` or :ref:`max_fail_percentage <maximum_failure_percentage>` to modify this behavior.
You can also specify a percentage with the ``serial`` keyword. Ansible applies the percentage to the total number of hosts in a play to determine the number of hosts per pass:
.. code-block:: yaml
---
- name: test play
hosts: webservers
serial: "30%"
If the number of hosts does not divide equally into the number of passes, the final pass contains the remainder. In this example, if you had 20 hosts in the webservers group, the first batch would contain 6 hosts, the second batch would contain 6 hosts, the third batch would contain 6 hosts, and the last batch would contain 2 hosts.
You can also specify batch sizes as a list. For example:
.. code-block:: yaml
---
- name: test play
hosts: webservers
serial:
- 1
- 5
- 10
In the above example, the first batch would contain a single host, the next would contain 5 hosts, and (if there are any hosts left), every following batch would contain either 10 hosts or all the remaining hosts, if fewer than 10 hosts remained.
You can list multiple batch sizes as percentages:
.. code-block:: yaml
---
- name: test play
hosts: webservers
serial:
- "10%"
- "20%"
- "100%"
You can also mix and match the values:
.. code-block:: yaml
---
- name: test play
hosts: webservers
serial:
- 1
- 5
- "20%"
.. note::
No matter how small the percentage, the number of hosts per pass will always be 1 or greater.
Restricting execution with ``throttle``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``throttle`` keyword limits the number of workers for a particular task. It can be set at the block and task level. Use ``throttle`` to restrict tasks that may be CPU-intensive or interact with a rate-limiting API:
.. code-block:: yaml
tasks:
- command: /path/to/cpu_intensive_command
throttle: 1
If you have already restricted the number of forks or the number of machines to execute against in parallel, you can reduce the number of workers with ``throttle``, but you cannot increase it. In other words, to have an effect, your ``throttle`` setting must be lower than your ``forks`` or ``serial`` setting if you are using them together.
Ordering execution based on inventory
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``order`` keyword controls the order in which hosts are run. Possible values for order are:
inventory:
(default) The order provided by the inventory for the selection requested (see note below)
reverse_inventory:
The same as above, but reversing the returned list
sorted:
Sorted alphabetically sorted by name
reverse_sorted:
Sorted by name in reverse alphabetical order
shuffle:
Randomly ordered on each run
.. note::
the 'inventory' order does not equate to the order in which hosts/groups are defined in the inventory source file, but the 'order in which a selection is returned from the compiled inventory'. This is a backwards compatible option and while reproducible it is not normally predictable. Due to the nature of inventory, host patterns, limits, inventory plugins and the ability to allow multiple sources it is almost impossible to return such an order. For simple cases this might happen to match the file definition order, but that is not guaranteed.
.. _run_once:
Running on a single machine with ``run_once``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you want a task to run only on the first host in your batch of hosts, set ``run_once`` to true on that task:
.. code-block:: yaml
---
# ...
tasks:
# ...
- command: /opt/application/upgrade_db.py
run_once: true
# ...
Ansible executes this task on the first host in the current batch and applies all results and facts to all the hosts in the same batch. This approach is similar to applying a conditional to a task such as:
.. code-block:: yaml
- command: /opt/application/upgrade_db.py
when: inventory_hostname == webservers[0]
However, with ``run_once``, the results are applied to all the hosts. To run the task on a specific host, instead of the first host in the batch, delegate the task:
.. code-block:: yaml
- command: /opt/application/upgrade_db.py
run_once: true
delegate_to: web01.example.org
As always with :ref:`delegation <playbooks_delegation>`, the action will be executed on the delegated host, but the information is still that of the original host in the task.
.. note::
When used together with ``serial``, tasks marked as ``run_once`` will be run on one host in *each* serial batch. If the task must run only once regardless of ``serial`` mode, use
:code:`when: inventory_hostname == ansible_play_hosts_all[0]` construct.
.. note::
Any conditional (in other words, `when:`) will use the variables of the 'first host' to decide if the task runs or not, no other hosts will be tested.
.. note::
If you want to avoid the default behavior of setting the fact for all hosts, set ``delegate_facts: True`` for the specific task or block.
.. seealso::
:ref:`about_playbooks`
An introduction to playbooks
:ref:`playbooks_delegation`
Running tasks on or assigning facts to specific machines
:ref:`playbooks_reuse_roles`
Playbook organization by roles
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,432 @@
.. _tags:
****
Tags
****
If you have a large playbook, it may be useful to run only specific parts of it instead of running the entire playbook. You can do this with Ansible tags. Using tags to execute or skip selected tasks is a two-step process:
#. Add tags to your tasks, either individually or with tag inheritance from a block, play, role, or import.
#. Select or skip tags when you run your playbook.
.. contents::
:local:
Adding tags with the tags keyword
=================================
You can add tags to a single task or include. You can also add tags to multiple tasks by defining them at the level of a block, play, role, or import. The keyword ``tags`` addresses all these use cases. The ``tags`` keyword always defines tags and adds them to tasks; it does not select or skip tasks for execution. You can only select or skip tasks based on tags at the command line when you run a playbook. See :ref:`using_tags` for more details.
Adding tags to individual tasks
-------------------------------
At the simplest level, you can apply one or more tags to an individual task. You can add tags to tasks in playbooks, in task files, or within a role. Here is an example that tags two tasks with different tags:
.. code-block:: yaml
tasks:
- name: Install the servers
ansible.builtin.yum:
name:
- httpd
- memcached
state: present
tags:
- packages
- webservers
- name: Configure the service
ansible.builtin.template:
src: templates/src.j2
dest: /etc/foo.conf
tags:
- configuration
You can apply the same tag to more than one individual task. This example tags several tasks with the same tag, "ntp":
.. code-block:: yaml
---
# file: roles/common/tasks/main.yml
- name: Install ntp
ansible.builtin.yum:
name: ntp
state: present
tags: ntp
- name: Configure ntp
ansible.builtin.template:
src: ntp.conf.j2
dest: /etc/ntp.conf
notify:
- restart ntpd
tags: ntp
- name: Enable and run ntpd
ansible.builtin.service:
name: ntpd
state: started
enabled: yes
tags: ntp
- name: Install NFS utils
ansible.builtin.yum:
name:
- nfs-utils
- nfs-util-lib
state: present
tags: filesharing
If you ran these four tasks in a playbook with ``--tags ntp``, Ansible would run the three tasks tagged ``ntp`` and skip the one task that does not have that tag.
.. _tags_on_includes:
Adding tags to includes
-----------------------
You can apply tags to dynamic includes in a playbook. As with tags on an individual task, tags on an ``include_*`` task apply only to the include itself, not to any tasks within the included file or role. If you add ``mytag`` to a dynamic include, then run that playbook with ``--tags mytag``, Ansible runs the include itself, runs any tasks within the included file or role tagged with ``mytag``, and skips any tasks within the included file or role without that tag. See :ref:`selective_reuse` for more details.
You add tags to includes the same way you add tags to any other task:
.. code-block:: yaml
---
# file: roles/common/tasks/main.yml
- name: Dynamic re-use of database tasks
include_tasks: db.yml
tags: db
You can add a tag only to the dynamic include of a role. In this example, the ``foo`` tag will `not` apply to tasks inside the ``bar`` role:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Include the bar role
include_role:
name: bar
tags:
- foo
With plays, blocks, the ``role`` keyword, and static imports, Ansible applies tag inheritance, adding the tags you define to every task inside the play, block, role, or imported file. However, tag inheritance does *not* apply to dynamic re-use with ``include_role`` and ``include_tasks``. With dynamic re-use (includes), the tags you define apply only to the include itself. If you need tag inheritance, use a static import. If you cannot use an import because the rest of your playbook uses includes, see :ref:`apply_keyword` for ways to work around this behavior.
.. _tag_inheritance:
Tag inheritance: adding tags to multiple tasks
----------------------------------------------
If you want to apply the same tag or tags to multiple tasks without adding a ``tags`` line to every task, you can define the tags at the level of your play or block, or when you add a role or import a file. Ansible applies the tags down the dependency chain to all child tasks. With roles and imports, Ansible appends the tags set by the ``roles`` section or import to any tags set on individual tasks or blocks within the role or imported file. This is called tag inheritance. Tag inheritance is convenient, because you do not have to tag every task. However, the tags still apply to the tasks individually.
Adding tags to blocks
^^^^^^^^^^^^^^^^^^^^^
If you want to apply a tag to many, but not all, of the tasks in your play, use a :ref:`block <playbooks_blocks>` and define the tags at that level. For example, we could edit the NTP example shown above to use a block:
.. code-block:: yaml
# myrole/tasks/main.yml
- name: ntp tasks
tags: ntp
block:
- name: Install ntp
ansible.builtin.yum:
name: ntp
state: present
- name: Configure ntp
ansible.builtin.template:
src: ntp.conf.j2
dest: /etc/ntp.conf
notify:
- restart ntpd
- name: Enable and run ntpd
ansible.builtin.service:
name: ntpd
state: started
enabled: yes
- name: Install NFS utils
ansible.builtin.yum:
name:
- nfs-utils
- nfs-util-lib
state: present
tags: filesharing
Adding tags to plays
^^^^^^^^^^^^^^^^^^^^
If all the tasks in a play should get the same tag, you can add the tag at the level of the play. For example, if you had a play with only the NTP tasks, you could tag the entire play:
.. code-block:: yaml
- hosts: all
tags: ntp
tasks:
- name: Install ntp
ansible.builtin.yum:
name: ntp
state: present
- name: Configure ntp
ansible.builtin.template:
src: ntp.conf.j2
dest: /etc/ntp.conf
notify:
- restart ntpd
- name: Enable and run ntpd
ansible.builtin.service:
name: ntpd
state: started
enabled: yes
- hosts: fileservers
tags: filesharing
tasks:
...
Adding tags to roles
^^^^^^^^^^^^^^^^^^^^
There are three ways to add tags to roles:
#. Add the same tag or tags to all tasks in the role by setting tags under ``roles``. See examples in this section.
#. Add the same tag or tags to all tasks in the role by setting tags on a static ``import_role`` in your playbook. See examples in :ref:`tags_on_imports`.
#. Add a tag or tags to individual tasks or blocks within the role itself. This is the only approach that allows you to select or skip some tasks within the role. To select or skip tasks within the role, you must have tags set on individual tasks or blocks, use the dynamic ``include_role`` in your playbook, and add the same tag or tags to the include. When you use this approach, and then run your playbook with ``--tags foo``, Ansible runs the include itself plus any tasks in the role that also have the tag ``foo``. See :ref:`tags_on_includes` for details.
When you incorporate a role in your playbook statically with the ``roles`` keyword, Ansible adds any tags you define to all the tasks in the role. For example:
.. code-block:: yaml
roles:
- role: webserver
vars:
port: 5000
tags: [ web, foo ]
or:
.. code-block:: yaml
---
- hosts: webservers
roles:
- role: foo
tags:
- bar
- baz
# using YAML shorthand, this is equivalent to:
# - { role: foo, tags: ["bar", "baz"] }
.. _tags_on_imports:
Adding tags to imports
^^^^^^^^^^^^^^^^^^^^^^
You can also apply a tag or tags to all the tasks imported by the static ``import_role`` and ``import_tasks`` statements:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Import the foo role
import_role:
name: foo
tags:
- bar
- baz
- name: Import tasks from foo.yml
import_tasks: foo.yml
tags: [ web, foo ]
.. _apply_keyword:
Tag inheritance for includes: blocks and the ``apply`` keyword
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, Ansible does not apply :ref:`tag inheritance <tag_inheritance>` to dynamic re-use with ``include_role`` and ``include_tasks``. If you add tags to an include, they apply only to the include itself, not to any tasks in the included file or role. This allows you to execute selected tasks within a role or task file - see :ref:`selective_reuse` when you run your playbook.
If you want tag inheritance, you probably want to use imports. However, using both includes and imports in a single playbook can lead to difficult-to-diagnose bugs. For this reason, if your playbook uses ``include_*`` to re-use roles or tasks, and you need tag inheritance on one include, Ansible offers two workarounds. You can use the ``apply`` keyword:
.. code-block:: yaml
- name: Apply the db tag to the include and to all tasks in db.yaml
include_tasks:
file: db.yml
# adds 'db' tag to tasks within db.yml
apply:
tags: db
# adds 'db' tag to this 'include_tasks' itself
tags: db
Or you can use a block:
.. code-block:: yaml
- block:
- name: Include tasks from db.yml
include_tasks: db.yml
tags: db
.. _special_tags:
Special tags: always and never
==============================
Ansible reserves two tag names for special behavior: always and never. If you assign the ``always`` tag to a task or play, Ansible will always run that task or play, unless you specifically skip it (``--skip-tags always``).
For example:
.. code-block:: yaml
tasks:
- name: Print a message
ansible.builtin.debug:
msg: "Always runs"
tags:
- always
- name: Print a message
ansible.builtin.debug:
msg: "runs when you use tag1"
tags:
- tag1
.. warning::
* Fact gathering is tagged with 'always' by default. It is only skipped if
you apply a tag and then use a different tag in ``--tags`` or the same
tag in ``--skip-tags``.
.. warning::
* The role argument specification validation task is tagged with 'always' by default. This validation
will be skipped if you use ``--skip-tags always``.
.. versionadded:: 2.5
If you assign the ``never`` tag to a task or play, Ansible will skip that task or play unless you specifically request it (``--tags never``).
For example:
.. code-block:: yaml
tasks:
- name: Run the rarely-used debug task
ansible.builtin.debug:
msg: '{{ showmevar }}'
tags: [ never, debug ]
The rarely-used debug task in the example above only runs when you specifically request the ``debug`` or ``never`` tags.
.. _using_tags:
Selecting or skipping tags when you run a playbook
==================================================
Once you have added tags to your tasks, includes, blocks, plays, roles, and imports, you can selectively execute or skip tasks based on their tags when you run :ref:`ansible-playbook`. Ansible runs or skips all tasks with tags that match the tags you pass at the command line. If you have added a tag at the block or play level, with ``roles``, or with an import, that tag applies to every task within the block, play, role, or imported role or file. If you have a role with lots of tags and you want to call subsets of the role at different times, either :ref:`use it with dynamic includes <selective_reuse>`, or split the role into multiple roles.
:ref:`ansible-playbook` offers five tag-related command-line options:
* ``--tags all`` - run all tasks, ignore tags (default behavior)
* ``--tags [tag1, tag2]`` - run only tasks with either the tag ``tag1`` or the tag ``tag2``
* ``--skip-tags [tag3, tag4]`` - run all tasks except those with either the tag ``tag3`` or the tag ``tag4``
* ``--tags tagged`` - run only tasks with at least one tag
* ``--tags untagged`` - run only tasks with no tags
For example, to run only tasks and blocks tagged ``configuration`` and ``packages`` in a very long playbook:
.. code-block:: bash
ansible-playbook example.yml --tags "configuration,packages"
To run all tasks except those tagged ``packages``:
.. code-block:: bash
ansible-playbook example.yml --skip-tags "packages"
Previewing the results of using tags
------------------------------------
When you run a role or playbook, you might not know or remember which tasks have which tags, or which tags exist at all. Ansible offers two command-line flags for :ref:`ansible-playbook` that help you manage tagged playbooks:
* ``--list-tags`` - generate a list of available tags
* ``--list-tasks`` - when used with ``--tags tagname`` or ``--skip-tags tagname``, generate a preview of tagged tasks
For example, if you do not know whether the tag for configuration tasks is ``config`` or ``conf`` in a playbook, role, or tasks file, you can display all available tags without running any tasks:
.. code-block:: bash
ansible-playbook example.yml --list-tags
If you do not know which tasks have the tags ``configuration`` and ``packages``, you can pass those tags and add ``--list-tasks``. Ansible lists the tasks but does not execute any of them.
.. code-block:: bash
ansible-playbook example.yml --tags "configuration,packages" --list-tasks
These command-line flags have one limitation: they cannot show tags or tasks within dynamically included files or roles. See :ref:`dynamic_vs_static` for more information on differences between static imports and dynamic includes.
.. _selective_reuse:
Selectively running tagged tasks in re-usable files
---------------------------------------------------
If you have a role or a tasks file with tags defined at the task or block level, you can selectively run or skip those tagged tasks in a playbook if you use a dynamic include instead of a static import. You must use the same tag on the included tasks and on the include statement itself. For example you might create a file with some tagged and some untagged tasks:
.. code-block:: yaml
# mixed.yml
tasks:
- name: Run the task with no tags
ansible.builtin.debug:
msg: this task has no tags
- name: Run the tagged task
ansible.builtin.debug:
msg: this task is tagged with mytag
tags: mytag
- block:
- name: Run the first block task with mytag
...
- name: Run the second block task with mytag
...
tags:
- mytag
And you might include the tasks file above in a playbook:
.. code-block:: yaml
# myplaybook.yml
- hosts: all
tasks:
- name: Run tasks from mixed.yml
include_tasks:
name: mixed.yml
tags: mytag
When you run the playbook with ``ansible-playbook -i hosts myplaybook.yml --tags "mytag"``, Ansible skips the task with no tags, runs the tagged individual task, and runs the two tasks in the block.
Configuring tags globally
-------------------------
If you run or skip certain tags by default, you can use the :ref:`TAGS_RUN` and :ref:`TAGS_SKIP` options in Ansible configuration to set those defaults.
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_reuse_roles`
Playbook organization by roles
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,34 @@
.. _playbooks_templating:
*******************
Templating (Jinja2)
*******************
Ansible uses Jinja2 templating to enable dynamic expressions and access to :ref:`variables <playbooks_variables>` and :ref:`facts <vars_and_facts>`.
You can use templating with the :ref:`template module <template_module>`.
For example, you can create a template for a configuration file, then deploy that configuration file to multiple environments and supply the correct data (IP address, hostname, version) for each environment.
You can also use templating in playbooks directly, by templating task names and more.
You can use all the :ref:`standard filters and tests <jinja2:builtin-filters>` included in Jinja2.
Ansible includes additional specialized filters for selecting and transforming data, tests for evaluating template expressions, and :ref:`lookup_plugins` for retrieving data from external sources such as files, APIs, and databases for use in templating.
All templating happens on the Ansible controller **before** the task is sent and executed on the target machine.
This approach minimizes the package requirements on the target (jinja2 is only required on the controller).
It also limits the amount of data Ansible passes to the target machine.
Ansible parses templates on the controller and passes only the information needed for each task to the target machine, instead of passing all the data on the controller and parsing it on the target.
.. note::
Files and data used by the :ref:`template module <template_module>` must be utf-8 encoded.
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbook_tips`
Tips and tricks for playbooks
`Jinja2 Docs <https://jinja.palletsprojects.com/en/latest/templates/>`_
Jinja2 documentation, includes the syntax and semantics of the templates
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,16 @@
.. _templating_now:
The now function: get the current time
======================================
.. versionadded:: 2.8
The ``now()`` Jinja2 function retrieves a Python datetime object or a string representation for the current time.
The ``now()`` function supports 2 arguments:
utc
Specify ``True`` to get the current time in UTC. Defaults to ``False``.
fmt
Accepts a `strftime <https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior>`_ string that returns a formatted date time string.

@ -0,0 +1,524 @@
.. _playbooks_tests:
*****
Tests
*****
`Tests <https://jinja.palletsprojects.com/en/latest/templates/#tests>`_ in Jinja are a way of evaluating template expressions and returning True or False. Jinja ships with many of these. See `builtin tests`_ in the official Jinja template documentation.
The main difference between tests and filters are that Jinja tests are used for comparisons, whereas filters are used for data manipulation, and have different applications in jinja. Tests can also be used in list processing filters, like ``map()`` and ``select()`` to choose items in the list.
Like all templating, tests always execute on the Ansible controller, **not** on the target of a task, as they test local data.
In addition to those Jinja2 tests, Ansible supplies a few more and users can easily create their own.
.. contents::
:local:
.. _test_syntax:
Test syntax
===========
`Test syntax <https://jinja.palletsprojects.com/en/latest/templates/#tests>`_ varies from `filter syntax <https://jinja.palletsprojects.com/en/latest/templates/#filters>`_ (``variable | filter``). Historically Ansible has registered tests as both jinja tests and jinja filters, allowing for them to be referenced using filter syntax.
As of Ansible 2.5, using a jinja test as a filter will generate a deprecation warning. As of Ansible 2.9+ using jinja test syntax is required.
The syntax for using a jinja test is as follows
.. code-block:: console
variable is test_name
Such as
.. code-block:: console
result is failed
.. _testing_strings:
Testing strings
===============
To match strings against a substring or a regular expression, use the ``match``, ``search`` or ``regex`` tests
.. code-block:: yaml
vars:
url: "https://example.com/users/foo/resources/bar"
tasks:
- debug:
msg: "matched pattern 1"
when: url is match("https://example.com/users/.*/resources")
- debug:
msg: "matched pattern 2"
when: url is search("users/.*/resources/.*")
- debug:
msg: "matched pattern 3"
when: url is search("users")
- debug:
msg: "matched pattern 4"
when: url is regex("example\.com/\w+/foo")
``match`` succeeds if it finds the pattern at the beginning of the string, while ``search`` succeeds if it finds the pattern anywhere within string. By default, ``regex`` works like ``search``, but ``regex`` can be configured to perform other tests as well, by passing the ``match_type`` keyword argument. In particular, ``match_type`` determines the ``re`` method that gets used to perform the search. The full list can be found in the relevant Python documentation `here <https://docs.python.org/3/library/re.html#regular-expression-objects>`_.
All of the string tests also take optional ``ignorecase`` and ``multiline`` arguments. These correspond to ``re.I`` and ``re.M`` from Python's ``re`` library, respectively.
.. _testing_vault:
Vault
=====
.. versionadded:: 2.10
You can test whether a variable is an inline single vault encrypted value using the ``vault_encrypted`` test.
.. code-block:: yaml
vars:
variable: !vault |
$ANSIBLE_VAULT;1.2;AES256;dev
61323931353866666336306139373937316366366138656131323863373866376666353364373761
3539633234313836346435323766306164626134376564330a373530313635343535343133316133
36643666306434616266376434363239346433643238336464643566386135356334303736353136
6565633133366366360a326566323363363936613664616364623437336130623133343530333739
3039
tasks:
- debug:
msg: '{{ (variable is vault_encrypted) | ternary("Vault encrypted", "Not vault encrypted") }}'
.. _testing_truthiness:
Testing truthiness
==================
.. versionadded:: 2.10
As of Ansible 2.10, you can now perform Python like truthy and falsy checks.
.. code-block:: yaml
- debug:
msg: "Truthy"
when: value is truthy
vars:
value: "some string"
- debug:
msg: "Falsy"
when: value is falsy
vars:
value: ""
Additionally, the ``truthy`` and ``falsy`` tests accept an optional parameter called ``convert_bool`` that will attempt
to convert boolean indicators to actual booleans.
.. code-block:: yaml
- debug:
msg: "Truthy"
when: value is truthy(convert_bool=True)
vars:
value: "yes"
- debug:
msg: "Falsy"
when: value is falsy(convert_bool=True)
vars:
value: "off"
.. _testing_versions:
Comparing versions
==================
.. versionadded:: 1.6
.. note:: In 2.5 ``version_compare`` was renamed to ``version``
To compare a version number, such as checking if the ``ansible_facts['distribution_version']``
version is greater than or equal to '12.04', you can use the ``version`` test.
The ``version`` test can also be used to evaluate the ``ansible_facts['distribution_version']``
.. code-block:: yaml+jinja
{{ ansible_facts['distribution_version'] is version('12.04', '>=') }}
If ``ansible_facts['distribution_version']`` is greater than or equal to 12.04, this test returns True, otherwise False.
The ``version`` test accepts the following operators
.. code-block:: console
<, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne
This test also accepts a 3rd parameter, ``strict`` which defines if strict version parsing as defined by ``ansible.module_utils.compat.version.StrictVersion`` should be used. The default is ``False`` (using ``ansible.module_utils.compat.version.LooseVersion``), ``True`` enables strict version parsing
.. code-block:: yaml+jinja
{{ sample_version_var is version('1.0', operator='lt', strict=True) }}
As of Ansible 2.11 the ``version`` test accepts a ``version_type`` parameter which is mutually exclusive with ``strict``, and accepts the following values
.. code-block:: console
loose, strict, semver, semantic
Using ``version_type`` to compare a semantic version would be achieved like the following
.. code-block:: yaml+jinja
{{ sample_semver_var is version('2.0.0-rc.1+build.123', 'lt', version_type='semver') }}
When using ``version`` in a playbook or role, don't use ``{{ }}`` as described in the `FAQ <https://docs.ansible.com/ansible/latest/reference_appendices/faq.html#when-should-i-use-also-how-to-interpolate-variables-or-dynamic-variable-names>`_
.. code-block:: yaml
vars:
my_version: 1.2.3
tasks:
- debug:
msg: "my_version is higher than 1.0.0"
when: my_version is version('1.0.0', '>')
.. _math_tests:
Set theory tests
================
.. versionadded:: 2.1
.. note:: In 2.5 ``issubset`` and ``issuperset`` were renamed to ``subset`` and ``superset``
To see if a list includes or is included by another list, you can use 'subset' and 'superset'
.. code-block:: yaml
vars:
a: [1,2,3,4,5]
b: [2,3]
tasks:
- debug:
msg: "A includes B"
when: a is superset(b)
- debug:
msg: "B is included in A"
when: b is subset(a)
.. _contains_test:
Testing if a list contains a value
==================================
.. versionadded:: 2.8
Ansible includes a ``contains`` test which operates similarly, but in reverse of the Jinja2 provided ``in`` test.
The ``contains`` test is designed to work with the ``select``, ``reject``, ``selectattr``, and ``rejectattr`` filters
.. code-block:: yaml
vars:
lacp_groups:
- master: lacp0
network: 10.65.100.0/24
gateway: 10.65.100.1
dns4:
- 10.65.100.10
- 10.65.100.11
interfaces:
- em1
- em2
- master: lacp1
network: 10.65.120.0/24
gateway: 10.65.120.1
dns4:
- 10.65.100.10
- 10.65.100.11
interfaces:
- em3
- em4
tasks:
- debug:
msg: "{{ (lacp_groups|selectattr('interfaces', 'contains', 'em1')|first).master }}"
Testing if a list value is True
===============================
.. versionadded:: 2.4
You can use `any` and `all` to check if any or all elements in a list are true or not
.. code-block:: yaml
vars:
mylist:
- 1
- "{{ 3 == 3 }}"
- True
myotherlist:
- False
- True
tasks:
- debug:
msg: "all are true!"
when: mylist is all
- debug:
msg: "at least one is true"
when: myotherlist is any
.. _path_tests:
Testing paths
=============
.. note:: In 2.5 the following tests were renamed to remove the ``is_`` prefix
The following tests can provide information about a path on the controller
.. code-block:: yaml
- debug:
msg: "path is a directory"
when: mypath is directory
- debug:
msg: "path is a file"
when: mypath is file
- debug:
msg: "path is a symlink"
when: mypath is link
- debug:
msg: "path already exists"
when: mypath is exists
- debug:
msg: "path is {{ (mypath is abs)|ternary('absolute','relative')}}"
- debug:
msg: "path is the same file as path2"
when: mypath is same_file(path2)
- debug:
msg: "path is a mount"
when: mypath is mount
- debug:
msg: "path is a directory"
when: mypath is directory
vars:
mypath: /my/patth
- debug:
msg: "path is a file"
when: "'/my/path' is file"
Testing size formats
====================
The ``human_readable`` and ``human_to_bytes`` functions let you test your
playbooks to make sure you are using the right size format in your tasks, and that
you provide Byte format to computers and human-readable format to people.
Human readable
--------------
Asserts whether the given string is human readable or not.
For example
.. code-block:: yaml+jinja
- name: "Human Readable"
assert:
that:
- '"1.00 Bytes" == 1|human_readable'
- '"1.00 bits" == 1|human_readable(isbits=True)'
- '"10.00 KB" == 10240|human_readable'
- '"97.66 MB" == 102400000|human_readable'
- '"0.10 GB" == 102400000|human_readable(unit="G")'
- '"0.10 Gb" == 102400000|human_readable(isbits=True, unit="G")'
This would result in
.. code-block:: json
{ "changed": false, "msg": "All assertions passed" }
Human to bytes
--------------
Returns the given string in the Bytes format.
For example
.. code-block:: yaml+jinja
- name: "Human to Bytes"
assert:
that:
- "{{'0'|human_to_bytes}} == 0"
- "{{'0.1'|human_to_bytes}} == 0"
- "{{'0.9'|human_to_bytes}} == 1"
- "{{'1'|human_to_bytes}} == 1"
- "{{'10.00 KB'|human_to_bytes}} == 10240"
- "{{ '11 MB'|human_to_bytes}} == 11534336"
- "{{ '1.1 GB'|human_to_bytes}} == 1181116006"
- "{{'10.00 Kb'|human_to_bytes(isbits=True)}} == 10240"
This would result in
.. code-block:: json
{ "changed": false, "msg": "All assertions passed" }
.. _test_task_results:
Testing task results
====================
The following tasks are illustrative of the tests meant to check the status of tasks
.. code-block:: yaml
tasks:
- shell: /usr/bin/foo
register: result
ignore_errors: True
- debug:
msg: "it failed"
when: result is failed
# in most cases you'll want a handler, but if you want to do something right now, this is nice
- debug:
msg: "it changed"
when: result is changed
- debug:
msg: "it succeeded in Ansible >= 2.1"
when: result is succeeded
- debug:
msg: "it succeeded"
when: result is success
- debug:
msg: "it was skipped"
when: result is skipped
.. note:: From 2.1, you can also use success, failure, change, and skip so that the grammar matches, for those who need to be strict about it.
.. _type_tests:
Type Tests
==========
When looking to determine types, it may be tempting to use the ``type_debug`` filter and compare that to the string name of that type, however, you should instead use type test comparisons, such as:
.. code-block:: yaml
tasks:
- name: "String interpretation"
vars:
a_string: "A string"
a_dictionary: {"a": "dictionary"}
a_list: ["a", "list"]
assert:
that:
# Note that a string is classed as also being "iterable", "sequence" and "mapping"
- a_string is string
# Note that a dictionary is classed as not being a "string", but is "iterable", "sequence" and "mapping"
- a_dictionary is not string and a_dictionary is mapping
# Note that a list is classed as not being a "string" or "mapping" but is "iterable" and "sequence"
- a_list is not string and a_list is not mapping and a_list is iterable
- name: "Number interpretation"
vars:
a_float: 1.01
a_float_as_string: "1.01"
an_integer: 1
an_integer_as_string: "1"
assert:
that:
# Both a_float and an_integer are "number", but each has their own type as well
- a_float is number and a_float is float
- an_integer is number and an_integer is integer
# Both a_float_as_string and an_integer_as_string are not numbers
- a_float_as_string is not number and a_float_as_string is string
- an_integer_as_string is not number and a_float_as_string is string
# a_float or a_float_as_string when cast to a float and then to a string should match the same value cast only to a string
- a_float | float | string == a_float | string
- a_float_as_string | float | string == a_float_as_string | string
# Likewise an_integer and an_integer_as_string when cast to an integer and then to a string should match the same value cast only to an integer
- an_integer | int | string == an_integer | string
- an_integer_as_string | int | string == an_integer_as_string | string
# However, a_float or a_float_as_string cast as an integer and then a string does not match the same value cast to a string
- a_float | int | string != a_float | string
- a_float_as_string | int | string != a_float_as_string | string
# Again, Likewise an_integer and an_integer_as_string cast as a float and then a string does not match the same value cast to a string
- an_integer | float | string != an_integer | string
- an_integer_as_string | float | string != an_integer_as_string | string
- name: "Native Boolean interpretation"
loop:
- yes
- true
- True
- TRUE
- no
- No
- NO
- false
- False
- FALSE
assert:
that:
# Note that while other values may be cast to boolean values, these are the only ones which are natively considered boolean
# Note also that `yes` is the only case sensitive variant of these values.
- item is boolean
.. _builtin tests: https://jinja.palletsprojects.com/en/latest/templates/#builtin-tests
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_variables`
All about variables
:ref:`playbooks_loops`
Looping in playbooks
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`tips_and_tricks`
Tips and tricks for playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,512 @@
.. _playbooks_variables:
***************
Using Variables
***************
Ansible uses variables to manage differences between systems. With Ansible, you can execute tasks and playbooks on multiple different systems with a single command. To represent the variations among those different systems, you can create variables with standard YAML syntax, including lists and dictionaries. You can define these variables in your playbooks, in your :ref:`inventory <intro_inventory>`, in re-usable :ref:`files <playbooks_reuse>` or :ref:`roles <playbooks_reuse_roles>`, or at the command line. You can also create variables during a playbook run by registering the return value or values of a task as a new variable.
After you create variables, either by defining them in a file, passing them at the command line, or registering the return value or values of a task as a new variable, you can use those variables in module arguments, in :ref:`conditional "when" statements <playbooks_conditionals>`, in :ref:`templates <playbooks_templating>`, and in :ref:`loops <playbooks_loops>`. The `ansible-examples github repository <https://github.com/ansible/ansible-examples>`_ contains many examples of using variables in Ansible.
Once you understand the concepts and examples on this page, read about :ref:`Ansible facts <vars_and_facts>`, which are variables you retrieve from remote systems.
.. contents::
:local:
.. _valid_variable_names:
Creating valid variable names
=============================
Not all strings are valid Ansible variable names. A variable name can only include letters, numbers, and underscores. `Python keywords`_ or :ref:`playbook keywords<playbook_keywords>` are not valid variable names. A variable name cannot begin with a number.
Variable names can begin with an underscore. In many programming languages, variables that begin with an underscore are private. This is not true in Ansible. Variables that begin with an underscore are treated exactly the same as any other variable. Do not rely on this convention for privacy or security.
This table gives examples of valid and invalid variable names:
.. table::
:class: documentation-table
====================== ====================================================================
Valid variable names Not valid
====================== ====================================================================
``foo`` ``*foo``, `Python keywords`_ such as ``async`` and ``lambda``
``foo_env`` :ref:`playbook keywords<playbook_keywords>` such as ``environment``
``foo_port`` ``foo-port``, ``foo port``, ``foo.port``
``foo5``, ``_foo`` ``5foo``, ``12``
====================== ====================================================================
.. _Python keywords: https://docs.python.org/3/reference/lexical_analysis.html#keywords
Simple variables
================
Simple variables combine a variable name with a single value. You can use this syntax (and the syntax for lists and dictionaries shown below) in a variety of places. For details about setting variables in inventory, in playbooks, in reusable files, in roles, or at the command line, see :ref:`setting_variables`.
Defining simple variables
-------------------------
You can define a simple variable using standard YAML syntax. For example:
.. code-block:: text
remote_install_path: /opt/my_app_config
Referencing simple variables
----------------------------
After you define a variable, use Jinja2 syntax to reference it. Jinja2 variables use double curly braces. For example, the expression ``My amp goes to {{ max_amp_value }}`` demonstrates the most basic form of variable substitution. You can use Jinja2 syntax in playbooks. For example:
.. code-block:: yaml+jinja
ansible.builtin.template:
src: foo.cfg.j2
dest: '{{ remote_install_path }}/foo.cfg'
In this example, the variable defines the location of a file, which can vary from one system to another.
.. note::
Ansible allows Jinja2 loops and conditionals in :ref:`templates <playbooks_templating>` but not in playbooks. You cannot create a loop of tasks. Ansible playbooks are pure machine-parseable YAML.
.. _yaml_gotchas:
When to quote variables (a YAML gotcha)
=======================================
If you start a value with ``{{ foo }}``, you must quote the whole expression to create valid YAML syntax. If you do not quote the whole expression, the YAML parser cannot interpret the syntax - it might be a variable or it might be the start of a YAML dictionary. For guidance on writing YAML, see the :ref:`yaml_syntax` documentation.
If you use a variable without quotes like this:
.. code-block:: yaml+jinja
- hosts: app_servers
vars:
app_path: {{ base_path }}/22
You will see: ``ERROR! Syntax Error while loading YAML.`` If you add quotes, Ansible works correctly:
.. code-block:: yaml+jinja
- hosts: app_servers
vars:
app_path: "{{ base_path }}/22"
.. _list_variables:
List variables
==============
A list variable combines a variable name with multiple values. The multiple values can be stored as an itemized list or in square brackets ``[]``, separated with commas.
Defining variables as lists
---------------------------
You can define variables with multiple values using YAML lists. For example:
.. code-block:: yaml
region:
- northeast
- southeast
- midwest
Referencing list variables
--------------------------
When you use variables defined as a list (also called an array), you can use individual, specific fields from that list. The first item in a list is item 0, the second item is item 1. For example:
.. code-block:: yaml+jinja
region: "{{ region[0] }}"
The value of this expression would be "northeast".
.. _dictionary_variables:
Dictionary variables
====================
A dictionary stores the data in key-value pairs. Usually, dictionaries are used to store related data, such as the information contained in an ID or a user profile.
Defining variables as key:value dictionaries
--------------------------------------------
You can define more complex variables using YAML dictionaries. A YAML dictionary maps keys to values. For example:
.. code-block:: yaml
foo:
field1: one
field2: two
Referencing key:value dictionary variables
------------------------------------------
When you use variables defined as a key:value dictionary (also called a hash), you can use individual, specific fields from that dictionary using either bracket notation or dot notation:
.. code-block:: yaml
foo['field1']
foo.field1
Both of these examples reference the same value ("one"). Bracket notation always works. Dot notation can cause problems because some keys collide with attributes and methods of python dictionaries. Use bracket notation if you use keys which start and end with two underscores (which are reserved for special meanings in python) or are any of the known public attributes:
``add``, ``append``, ``as_integer_ratio``, ``bit_length``, ``capitalize``, ``center``, ``clear``, ``conjugate``, ``copy``, ``count``, ``decode``, ``denominator``, ``difference``, ``difference_update``, ``discard``, ``encode``, ``endswith``, ``expandtabs``, ``extend``, ``find``, ``format``, ``fromhex``, ``fromkeys``, ``get``, ``has_key``, ``hex``, ``imag``, ``index``, ``insert``, ``intersection``, ``intersection_update``, ``isalnum``, ``isalpha``, ``isdecimal``, ``isdigit``, ``isdisjoint``, ``is_integer``, ``islower``, ``isnumeric``, ``isspace``, ``issubset``, ``issuperset``, ``istitle``, ``isupper``, ``items``, ``iteritems``, ``iterkeys``, ``itervalues``, ``join``, ``keys``, ``ljust``, ``lower``, ``lstrip``, ``numerator``, ``partition``, ``pop``, ``popitem``, ``real``, ``remove``, ``replace``, ``reverse``, ``rfind``, ``rindex``, ``rjust``, ``rpartition``, ``rsplit``, ``rstrip``, ``setdefault``, ``sort``, ``split``, ``splitlines``, ``startswith``, ``strip``, ``swapcase``, ``symmetric_difference``, ``symmetric_difference_update``, ``title``, ``translate``, ``union``, ``update``, ``upper``, ``values``, ``viewitems``, ``viewkeys``, ``viewvalues``, ``zfill``.
.. _registered_variables:
Registering variables
=====================
You can create variables from the output of an Ansible task with the task keyword ``register``. You can use registered variables in any later tasks in your play. For example:
.. code-block:: yaml
- hosts: web_servers
tasks:
- name: Run a shell command and register its output as a variable
ansible.builtin.shell: /usr/bin/foo
register: foo_result
ignore_errors: true
- name: Run a shell command using output of the previous task
ansible.builtin.shell: /usr/bin/bar
when: foo_result.rc == 5
For more examples of using registered variables in conditions on later tasks, see :ref:`playbooks_conditionals`. Registered variables may be simple variables, list variables, dictionary variables, or complex nested data structures. The documentation for each module includes a ``RETURN`` section describing the return values for that module. To see the values for a particular task, run your playbook with ``-v``.
Registered variables are stored in memory. You cannot cache registered variables for use in future plays. Registered variables are only valid on the host for the rest of the current playbook run.
Registered variables are host-level variables. When you register a variable in a task with a loop, the registered variable contains a value for each item in the loop. The data structure placed in the variable during the loop will contain a ``results`` attribute, that is a list of all responses from the module. For a more in-depth example of how this works, see the :ref:`playbooks_loops` section on using register with a loop.
.. note:: If a task fails or is skipped, Ansible still registers a variable with a failure or skipped status, unless the task is skipped based on tags. See :ref:`tags` for information on adding and using tags.
.. _accessing_complex_variable_data:
Referencing nested variables
============================
Many registered variables (and :ref:`facts <vars_and_facts>`) are nested YAML or JSON data structures. You cannot access values from these nested data structures with the simple ``{{ foo }}`` syntax. You must use either bracket notation or dot notation. For example, to reference an IP address from your facts using the bracket notation:
.. code-block:: yaml+jinja
{{ ansible_facts["eth0"]["ipv4"]["address"] }}
To reference an IP address from your facts using the dot notation:
.. code-block:: yaml+jinja
{{ ansible_facts.eth0.ipv4.address }}
.. _about_jinja2:
.. _jinja2_filters:
Transforming variables with Jinja2 filters
==========================================
Jinja2 filters let you transform the value of a variable within a template expression. For example, the ``capitalize`` filter capitalizes any value passed to it; the ``to_yaml`` and ``to_json`` filters change the format of your variable values. Jinja2 includes many `built-in filters <https://jinja.palletsprojects.com/templates/#builtin-filters>`_ and Ansible supplies many more filters. To find more examples of filters, see :ref:`playbooks_filters`.
.. _setting_variables:
Where to set variables
======================
You can define variables in a variety of places, such as in inventory, in playbooks, in reusable files, in roles, and at the command line. Ansible loads every possible variable it finds, then chooses the variable to apply based on :ref:`variable precedence rules <ansible_variable_precedence>`.
.. _define_variables_in_inventory:
Defining variables in inventory
-------------------------------
You can define different variables for each individual host, or set shared variables for a group of hosts in your inventory. For example, if all machines in the ``[Boston]`` group use 'boston.ntp.example.com' as an NTP server, you can set a group variable. The :ref:`intro_inventory` page has details on setting :ref:`host variables <host_variables>` and :ref:`group variables <group_variables>` in inventory.
.. _playbook_variables:
Defining variables in a play
----------------------------
You can define variables directly in a playbook play:
.. code-block:: yaml
- hosts: webservers
vars:
http_port: 80
When you define variables in a play, they are only visible to tasks executed in that play.
.. _included_variables:
.. _variable_file_separation_details:
Defining variables in included files and roles
----------------------------------------------
You can define variables in reusable variables files and/or in reusable roles. When you define variables in reusable variable files, the sensitive variables are separated from playbooks. This separation enables you to store your playbooks in a source control software and even share the playbooks, without the risk of exposing passwords or other sensitive and personal data. For information about creating reusable files and roles, see :ref:`playbooks_reuse`.
This example shows how you can include variables defined in an external file:
.. code-block:: yaml
---
- hosts: all
remote_user: root
vars:
favcolor: blue
vars_files:
- /vars/external_vars.yml
tasks:
- name: This is just a placeholder
ansible.builtin.command: /bin/echo foo
The contents of each variables file is a simple YAML dictionary. For example:
.. code-block:: yaml
---
# in the above example, this would be vars/external_vars.yml
somevar: somevalue
password: magic
.. note::
You can keep per-host and per-group variables in similar files. To learn about organizing your variables, see :ref:`splitting_out_vars`.
.. _passing_variables_on_the_command_line:
Defining variables at runtime
-----------------------------
You can define variables when you run your playbook by passing variables at the command line using the ``--extra-vars`` (or ``-e``) argument. You can also request user input with a ``vars_prompt`` (see :ref:`playbooks_prompts`). When you pass variables at the command line, use a single quoted string, that contains one or more variables, in one of the formats below.
key=value format
^^^^^^^^^^^^^^^^
Values passed in using the ``key=value`` syntax are interpreted as strings. Use the JSON format if you need to pass non-string values such as Booleans, integers, floats, lists, and so on.
.. code-block:: text
ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"
JSON string format
^^^^^^^^^^^^^^^^^^
.. code-block:: shell
ansible-playbook release.yml --extra-vars '{"version":"1.23.45","other_variable":"foo"}'
ansible-playbook arcade.yml --extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}'
When passing variables with ``--extra-vars``, you must escape quotes and other special characters appropriately for both your markup (for example, JSON), and for your shell:
.. code-block:: shell
ansible-playbook arcade.yml --extra-vars "{\"name\":\"Conan O\'Brien\"}"
ansible-playbook arcade.yml --extra-vars '{"name":"Conan O'\\\''Brien"}'
ansible-playbook script.yml --extra-vars "{\"dialog\":\"He said \\\"I just can\'t get enough of those single and double-quotes"\!"\\\"\"}"
If you have a lot of special characters, use a JSON or YAML file containing the variable definitions.
vars from a JSON or YAML file
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: text
ansible-playbook release.yml --extra-vars "@some_file.json"
.. _ansible_variable_precedence:
Variable precedence: Where should I put a variable?
===================================================
You can set multiple variables with the same name in many different places. When you do this, Ansible loads every possible variable it finds, then chooses the variable to apply based on variable precedence. In other words, the different variables will override each other in a certain order.
Teams and projects that agree on guidelines for defining variables (where to define certain types of variables) usually avoid variable precedence concerns. We suggest that you define each variable in one place: figure out where to define a variable, and keep it simple. For examples, see :ref:`variable_examples`.
Some behavioral parameters that you can set in variables you can also set in Ansible configuration, as command-line options, and using playbook keywords. For example, you can define the user Ansible uses to connect to remote devices as a variable with ``ansible_user``, in a configuration file with ``DEFAULT_REMOTE_USER``, as a command-line option with ``-u``, and with the playbook keyword ``remote_user``. If you define the same parameter in a variable and by another method, the variable overrides the other setting. This approach allows host-specific settings to override more general settings. For examples and more details on the precedence of these various settings, see :ref:`general_precedence_rules`.
Understanding variable precedence
---------------------------------
Ansible does apply variable precedence, and you might have a use for it. Here is the order of precedence from least to greatest (the last listed variables override all other variables):
#. command line values (for example, ``-u my_user``, these are not variables)
#. role defaults (defined in role/defaults/main.yml) [1]_
#. inventory file or script group vars [2]_
#. inventory group_vars/all [3]_
#. playbook group_vars/all [3]_
#. inventory group_vars/* [3]_
#. playbook group_vars/* [3]_
#. inventory file or script host vars [2]_
#. inventory host_vars/* [3]_
#. playbook host_vars/* [3]_
#. host facts / cached set_facts [4]_
#. play vars
#. play vars_prompt
#. play vars_files
#. role vars (defined in role/vars/main.yml)
#. block vars (only for tasks in block)
#. task vars (only for the task)
#. include_vars
#. set_facts / registered vars
#. role (and include_role) params
#. include params
#. extra vars (for example, ``-e "user=my_user"``)(always win precedence)
In general, Ansible gives precedence to variables that were defined more recently, more actively, and with more explicit scope. Variables in the defaults folder inside a role are easily overridden. Anything in the vars directory of the role overrides previous versions of that variable in the namespace. Host and/or inventory variables override role defaults, but explicit includes such as the vars directory or an ``include_vars`` task override inventory variables.
Ansible merges different variables set in inventory so that more specific settings override more generic settings. For example, ``ansible_ssh_user`` specified as a group_var is overridden by ``ansible_user`` specified as a host_var. For details about the precedence of variables set in inventory, see :ref:`how_we_merge`.
.. rubric:: Footnotes
.. [1] Tasks in each role see their own role's defaults. Tasks defined outside of a role see the last role's defaults.
.. [2] Variables defined in inventory file or provided by dynamic inventory.
.. [3] Includes vars added by 'vars plugins' as well as host_vars and group_vars which are added by the default vars plugin shipped with Ansible.
.. [4] When created with set_facts's cacheable option, variables have the high precedence in the play,
but are the same as a host facts precedence when they come from the cache.
.. note:: Within any section, redefining a var overrides the previous instance.
If multiple groups have the same variable, the last one loaded wins.
If you define a variable twice in a play's ``vars:`` section, the second one wins.
.. note:: The previous describes the default config ``hash_behaviour=replace``, switch to ``merge`` to only partially overwrite.
.. _variable_scopes:
Scoping variables
-----------------
You can decide where to set a variable based on the scope you want that value to have. Ansible has three main scopes:
* Global: this is set by config, environment variables and the command line
* Play: each play and contained structures, vars entries (vars; vars_files; vars_prompt), role defaults and vars.
* Host: variables directly associated to a host, like inventory, include_vars, facts or registered task outputs
Inside a template, you automatically have access to all variables that are in scope for a host, plus any registered variables, facts, and magic variables.
.. _variable_examples:
Tips on where to set variables
------------------------------
You should choose where to define a variable based on the kind of control you might want over values.
Set variables in inventory that deal with geography or behavior. Since groups are frequently the entity that maps roles onto hosts, you can often set variables on the group instead of defining them on a role. Remember: child groups override parent groups, and host variables override group variables. See :ref:`define_variables_in_inventory` for details on setting host and group variables.
Set common defaults in a ``group_vars/all`` file. See :ref:`splitting_out_vars` for details on how to organize host and group variables in your inventory. Group variables are generally placed alongside your inventory file, but they can also be returned by dynamic inventory (see :ref:`intro_dynamic_inventory`) or defined in AWX or on :ref:`ansible_platform` from the UI or API:
.. code-block:: yaml
---
# file: /etc/ansible/group_vars/all
# this is the site wide default
ntp_server: default-time.example.com
Set location-specific variables in ``group_vars/my_location`` files. All groups are children of the ``all`` group, so variables set here override those set in ``group_vars/all``:
.. code-block:: yaml
---
# file: /etc/ansible/group_vars/boston
ntp_server: boston-time.example.com
If one host used a different NTP server, you could set that in a host_vars file, which would override the group variable:
.. code-block:: yaml
---
# file: /etc/ansible/host_vars/xyz.boston.example.com
ntp_server: override.example.com
Set defaults in roles to avoid undefined-variable errors. If you share your roles, other users can rely on the reasonable defaults you added in the ``roles/x/defaults/main.yml`` file, or they can easily override those values in inventory or at the command line. See :ref:`playbooks_reuse_roles` for more info. For example:
.. code-block:: yaml
---
# file: roles/x/defaults/main.yml
# if no other value is supplied in inventory or as a parameter, this value will be used
http_port: 80
Set variables in roles to ensure a value is used in that role, and is not overridden by inventory variables. If you are not sharing your role with others, you can define app-specific behaviors like ports this way, in ``roles/x/vars/main.yml``. If you are sharing roles with others, putting variables here makes them harder to override, although they still can by passing a parameter to the role or setting a variable with ``-e``:
.. code-block:: yaml
---
# file: roles/x/vars/main.yml
# this will absolutely be used in this role
http_port: 80
Pass variables as parameters when you call roles for maximum clarity, flexibility, and visibility. This approach overrides any defaults that exist for a role. For example:
.. code-block:: yaml
roles:
- role: apache
vars:
http_port: 8080
When you read this playbook it is clear that you have chosen to set a variable or override a default. You can also pass multiple values, which allows you to run the same role multiple times. See :ref:`run_role_twice` for more details. For example:
.. code-block:: yaml
roles:
- role: app_user
vars:
myname: Ian
- role: app_user
vars:
myname: Terry
- role: app_user
vars:
myname: Graham
- role: app_user
vars:
myname: John
Variables set in one role are available to later roles. You can set variables in a ``roles/common_settings/vars/main.yml`` file and use them in other roles and elsewhere in your playbook:
.. code-block:: yaml
roles:
- role: common_settings
- role: something
vars:
foo: 12
- role: something_else
.. note:: There are some protections in place to avoid the need to namespace variables.
In this example, variables defined in 'common_settings' are available to 'something' and 'something_else' tasks, but tasks in 'something' have foo set at 12, even if 'common_settings' sets foo to 20.
Instead of worrying about variable precedence, we encourage you to think about how easily or how often you want to override a variable when deciding where to set it. If you are not sure what other variables are defined, and you need a particular value, use ``--extra-vars`` (``-e``) to override all other variables.
Using advanced variable syntax
==============================
For information about advanced YAML syntax used to declare variables and have more control over the data placed in YAML files used by Ansible, see :ref:`playbooks_advanced_syntax`.
.. seealso::
:ref:`about_playbooks`
An introduction to playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_filters`
Jinja2 filters and their uses
:ref:`playbooks_loops`
Looping in playbooks
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`tips_and_tricks`
Tips and tricks for playbooks
:ref:`special_variables`
List of special variables
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -0,0 +1,714 @@
.. _vars_and_facts:
************************************************
Discovering variables: facts and magic variables
************************************************
With Ansible you can retrieve or discover certain variables containing information about your remote systems or about Ansible itself. Variables related to remote systems are called facts. With facts, you can use the behavior or state of one system as configuration on other systems. For example, you can use the IP address of one system as a configuration value on another system. Variables related to Ansible are called magic variables.
.. contents::
:local:
Ansible facts
=============
Ansible facts are data related to your remote systems, including operating systems, IP addresses, attached filesystems, and more. You can access this data in the ``ansible_facts`` variable. By default, you can also access some Ansible facts as top-level variables with the ``ansible_`` prefix. You can disable this behavior using the :ref:`INJECT_FACTS_AS_VARS` setting. To see all available facts, add this task to a play:
.. code-block:: yaml
- name: Print all available facts
ansible.builtin.debug:
var: ansible_facts
To see the 'raw' information as gathered, run this command at the command line:
.. code-block:: shell
ansible <hostname> -m ansible.builtin.setup
Facts include a large amount of variable data, which may look like this:
.. code-block:: json
{
"ansible_all_ipv4_addresses": [
"REDACTED IP ADDRESS"
],
"ansible_all_ipv6_addresses": [
"REDACTED IPV6 ADDRESS"
],
"ansible_apparmor": {
"status": "disabled"
},
"ansible_architecture": "x86_64",
"ansible_bios_date": "11/28/2013",
"ansible_bios_version": "4.1.5",
"ansible_cmdline": {
"BOOT_IMAGE": "/boot/vmlinuz-3.10.0-862.14.4.el7.x86_64",
"console": "ttyS0,115200",
"no_timer_check": true,
"nofb": true,
"nomodeset": true,
"ro": true,
"root": "LABEL=cloudimg-rootfs",
"vga": "normal"
},
"ansible_date_time": {
"date": "2018-10-25",
"day": "25",
"epoch": "1540469324",
"hour": "12",
"iso8601": "2018-10-25T12:08:44Z",
"iso8601_basic": "20181025T120844109754",
"iso8601_basic_short": "20181025T120844",
"iso8601_micro": "2018-10-25T12:08:44.109968Z",
"minute": "08",
"month": "10",
"second": "44",
"time": "12:08:44",
"tz": "UTC",
"tz_offset": "+0000",
"weekday": "Thursday",
"weekday_number": "4",
"weeknumber": "43",
"year": "2018"
},
"ansible_default_ipv4": {
"address": "REDACTED",
"alias": "eth0",
"broadcast": "REDACTED",
"gateway": "REDACTED",
"interface": "eth0",
"macaddress": "REDACTED",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "REDACTED",
"type": "ether"
},
"ansible_default_ipv6": {},
"ansible_device_links": {
"ids": {},
"labels": {
"xvda1": [
"cloudimg-rootfs"
],
"xvdd": [
"config-2"
]
},
"masters": {},
"uuids": {
"xvda1": [
"cac81d61-d0f8-4b47-84aa-b48798239164"
],
"xvdd": [
"2018-10-25-12-05-57-00"
]
}
},
"ansible_devices": {
"xvda": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"model": null,
"partitions": {
"xvda1": {
"holders": [],
"links": {
"ids": [],
"labels": [
"cloudimg-rootfs"
],
"masters": [],
"uuids": [
"cac81d61-d0f8-4b47-84aa-b48798239164"
]
},
"sectors": "83883999",
"sectorsize": 512,
"size": "40.00 GB",
"start": "2048",
"uuid": "cac81d61-d0f8-4b47-84aa-b48798239164"
}
},
"removable": "0",
"rotational": "0",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "deadline",
"sectors": "83886080",
"sectorsize": "512",
"size": "40.00 GB",
"support_discard": "0",
"vendor": null,
"virtual": 1
},
"xvdd": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [
"config-2"
],
"masters": [],
"uuids": [
"2018-10-25-12-05-57-00"
]
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "0",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "deadline",
"sectors": "131072",
"sectorsize": "512",
"size": "64.00 MB",
"support_discard": "0",
"vendor": null,
"virtual": 1
},
"xvde": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"model": null,
"partitions": {
"xvde1": {
"holders": [],
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"sectors": "167770112",
"sectorsize": 512,
"size": "80.00 GB",
"start": "2048",
"uuid": null
}
},
"removable": "0",
"rotational": "0",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "deadline",
"sectors": "167772160",
"sectorsize": "512",
"size": "80.00 GB",
"support_discard": "0",
"vendor": null,
"virtual": 1
}
},
"ansible_distribution": "CentOS",
"ansible_distribution_file_parsed": true,
"ansible_distribution_file_path": "/etc/redhat-release",
"ansible_distribution_file_variety": "RedHat",
"ansible_distribution_major_version": "7",
"ansible_distribution_release": "Core",
"ansible_distribution_version": "7.5.1804",
"ansible_dns": {
"nameservers": [
"127.0.0.1"
]
},
"ansible_domain": "",
"ansible_effective_group_id": 1000,
"ansible_effective_user_id": 1000,
"ansible_env": {
"HOME": "/home/zuul",
"LANG": "en_US.UTF-8",
"LESSOPEN": "||/usr/bin/lesspipe.sh %s",
"LOGNAME": "zuul",
"MAIL": "/var/mail/zuul",
"PATH": "/usr/local/bin:/usr/bin",
"PWD": "/home/zuul",
"SELINUX_LEVEL_REQUESTED": "",
"SELINUX_ROLE_REQUESTED": "",
"SELINUX_USE_CURRENT_RANGE": "",
"SHELL": "/bin/bash",
"SHLVL": "2",
"SSH_CLIENT": "REDACTED 55672 22",
"SSH_CONNECTION": "REDACTED 55672 REDACTED 22",
"USER": "zuul",
"XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_ID": "1",
"_": "/usr/bin/python2"
},
"ansible_eth0": {
"active": true,
"device": "eth0",
"ipv4": {
"address": "REDACTED",
"broadcast": "REDACTED",
"netmask": "255.255.255.0",
"network": "REDACTED"
},
"ipv6": [
{
"address": "REDACTED",
"prefix": "64",
"scope": "link"
}
],
"macaddress": "REDACTED",
"module": "xen_netfront",
"mtu": 1500,
"pciid": "vif-0",
"promisc": false,
"type": "ether"
},
"ansible_eth1": {
"active": true,
"device": "eth1",
"ipv4": {
"address": "REDACTED",
"broadcast": "REDACTED",
"netmask": "255.255.224.0",
"network": "REDACTED"
},
"ipv6": [
{
"address": "REDACTED",
"prefix": "64",
"scope": "link"
}
],
"macaddress": "REDACTED",
"module": "xen_netfront",
"mtu": 1500,
"pciid": "vif-1",
"promisc": false,
"type": "ether"
},
"ansible_fips": false,
"ansible_form_factor": "Other",
"ansible_fqdn": "centos-7-rax-dfw-0003427354",
"ansible_hostname": "centos-7-rax-dfw-0003427354",
"ansible_interfaces": [
"lo",
"eth1",
"eth0"
],
"ansible_is_chroot": false,
"ansible_kernel": "3.10.0-862.14.4.el7.x86_64",
"ansible_lo": {
"active": true,
"device": "lo",
"ipv4": {
"address": "127.0.0.1",
"broadcast": "host",
"netmask": "255.0.0.0",
"network": "127.0.0.0"
},
"ipv6": [
{
"address": "::1",
"prefix": "128",
"scope": "host"
}
],
"mtu": 65536,
"promisc": false,
"type": "loopback"
},
"ansible_local": {},
"ansible_lsb": {
"codename": "Core",
"description": "CentOS Linux release 7.5.1804 (Core)",
"id": "CentOS",
"major_release": "7",
"release": "7.5.1804"
},
"ansible_machine": "x86_64",
"ansible_machine_id": "2db133253c984c82aef2fafcce6f2bed",
"ansible_memfree_mb": 7709,
"ansible_memory_mb": {
"nocache": {
"free": 7804,
"used": 173
},
"real": {
"free": 7709,
"total": 7977,
"used": 268
},
"swap": {
"cached": 0,
"free": 0,
"total": 0,
"used": 0
}
},
"ansible_memtotal_mb": 7977,
"ansible_mounts": [
{
"block_available": 7220998,
"block_size": 4096,
"block_total": 9817227,
"block_used": 2596229,
"device": "/dev/xvda1",
"fstype": "ext4",
"inode_available": 10052341,
"inode_total": 10419200,
"inode_used": 366859,
"mount": "/",
"options": "rw,seclabel,relatime,data=ordered",
"size_available": 29577207808,
"size_total": 40211361792,
"uuid": "cac81d61-d0f8-4b47-84aa-b48798239164"
},
{
"block_available": 0,
"block_size": 2048,
"block_total": 252,
"block_used": 252,
"device": "/dev/xvdd",
"fstype": "iso9660",
"inode_available": 0,
"inode_total": 0,
"inode_used": 0,
"mount": "/mnt/config",
"options": "ro,relatime,mode=0700",
"size_available": 0,
"size_total": 516096,
"uuid": "2018-10-25-12-05-57-00"
}
],
"ansible_nodename": "centos-7-rax-dfw-0003427354",
"ansible_os_family": "RedHat",
"ansible_pkg_mgr": "yum",
"ansible_processor": [
"0",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"1",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"2",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"3",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"4",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"5",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"6",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"7",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz"
],
"ansible_processor_cores": 8,
"ansible_processor_count": 8,
"ansible_processor_nproc": 8,
"ansible_processor_threads_per_core": 1,
"ansible_processor_vcpus": 8,
"ansible_product_name": "HVM domU",
"ansible_product_serial": "REDACTED",
"ansible_product_uuid": "REDACTED",
"ansible_product_version": "4.1.5",
"ansible_python": {
"executable": "/usr/bin/python2",
"has_sslcontext": true,
"type": "CPython",
"version": {
"major": 2,
"micro": 5,
"minor": 7,
"releaselevel": "final",
"serial": 0
},
"version_info": [
2,
7,
5,
"final",
0
]
},
"ansible_python_version": "2.7.5",
"ansible_real_group_id": 1000,
"ansible_real_user_id": 1000,
"ansible_selinux": {
"config_mode": "enforcing",
"mode": "enforcing",
"policyvers": 31,
"status": "enabled",
"type": "targeted"
},
"ansible_selinux_python_present": true,
"ansible_service_mgr": "systemd",
"ansible_ssh_host_key_ecdsa_public": "REDACTED KEY VALUE",
"ansible_ssh_host_key_ed25519_public": "REDACTED KEY VALUE",
"ansible_ssh_host_key_rsa_public": "REDACTED KEY VALUE",
"ansible_swapfree_mb": 0,
"ansible_swaptotal_mb": 0,
"ansible_system": "Linux",
"ansible_system_capabilities": [
""
],
"ansible_system_capabilities_enforced": "True",
"ansible_system_vendor": "Xen",
"ansible_uptime_seconds": 151,
"ansible_user_dir": "/home/zuul",
"ansible_user_gecos": "",
"ansible_user_gid": 1000,
"ansible_user_id": "zuul",
"ansible_user_shell": "/bin/bash",
"ansible_user_uid": 1000,
"ansible_userspace_architecture": "x86_64",
"ansible_userspace_bits": "64",
"ansible_virtualization_role": "guest",
"ansible_virtualization_type": "xen",
"gather_subset": [
"all"
],
"module_setup": true
}
You can reference the model of the first disk in the facts shown above in a template or playbook as:
.. code-block:: jinja
{{ ansible_facts['devices']['xvda']['model'] }}
To reference the system hostname:
.. code-block:: jinja
{{ ansible_facts['nodename'] }}
You can use facts in conditionals (see :ref:`playbooks_conditionals`) and also in templates. You can also use facts to create dynamic groups of hosts that match particular criteria, see the :ref:`group_by module <group_by_module>` documentation for details.
.. note:: Because ``ansible_date_time`` is created and cached when Ansible gathers facts before each playbook run, it can get stale with long-running playbooks. If your playbook takes a long time to run, use the ``pipe`` filter (for example, ``lookup('pipe', 'date +%Y-%m-%d.%H:%M:%S')``) or :ref:`now() <templating_now>` with a Jinja 2 template instead of ``ansible_date_time``.
.. _fact_requirements:
Package requirements for fact gathering
---------------------------------------
On some distros, you may see missing fact values or facts set to default values because the packages that support gathering those facts are not installed by default. You can install the necessary packages on your remote hosts using the OS package manager. Known dependencies include:
* Linux Network fact gathering - Depends on the ``ip`` binary, commonly included in the ``iproute2`` package.
.. _fact_caching:
Caching facts
-------------
Like registered variables, facts are stored in memory by default. However, unlike registered variables, facts can be gathered independently and cached for repeated use. With cached facts, you can refer to facts from one system when configuring a second system, even if Ansible executes the current play on the second system first. For example:
.. code-block:: jinja
{{ hostvars['asdf.example.com']['ansible_facts']['os_family'] }}
Caching is controlled by the cache plugins. By default, Ansible uses the memory cache plugin, which stores facts in memory for the duration of the current playbook run. To retain Ansible facts for repeated use, select a different cache plugin. See :ref:`cache_plugins` for details.
Fact caching can improve performance. If you manage thousands of hosts, you can configure fact caching to run nightly, then manage configuration on a smaller set of servers periodically throughout the day. With cached facts, you have access to variables and information about all hosts even when you are only managing a small number of servers.
.. _disabling_facts:
Disabling facts
---------------
By default, Ansible gathers facts at the beginning of each play. If you do not need to gather facts (for example, if you know everything about your systems centrally), you can turn off fact gathering at the play level to improve scalability. Disabling facts may particularly improve performance in push mode with very large numbers of systems, or if you are using Ansible on experimental platforms. To disable fact gathering:
.. code-block:: yaml
- hosts: whatever
gather_facts: no
Adding custom facts
-------------------
The setup module in Ansible automatically discovers a standard set of facts about each host. If you want to add custom values to your facts, you can write a custom facts module, set temporary facts with a ``ansible.builtin.set_fact`` task, or provide permanent custom facts using the facts.d directory.
.. _local_facts:
facts.d or local facts
^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 1.3
You can add static custom facts by adding static files to facts.d, or add dynamic facts by adding executable scripts to facts.d. For example, you can add a list of all users on a host to your facts by creating and running a script in facts.d.
To use facts.d, create an ``/etc/ansible/facts.d`` directory on the remote host or hosts. If you prefer a different directory, create it and specify it using the ``fact_path`` play keyword. Add files to the directory to supply your custom facts. All file names must end with ``.fact``. The files can be JSON, INI, or executable files returning JSON.
To add static facts, simply add a file with the ``.fact`` extension. For example, create ``/etc/ansible/facts.d/preferences.fact`` with this content:
.. code-block:: ini
[general]
asdf=1
bar=2
.. note:: Make sure the file is not executable as this will break the ``ansible.builtin.setup`` module.
The next time fact gathering runs, your facts will include a hash variable fact named ``general`` with ``asdf`` and ``bar`` as members. To validate this, run the following:
.. code-block:: shell
ansible <hostname> -m ansible.builtin.setup -a "filter=ansible_local"
And you will see your custom fact added:
.. code-block:: json
{
"ansible_local": {
"preferences": {
"general": {
"asdf" : "1",
"bar" : "2"
}
}
}
}
The ansible_local namespace separates custom facts created by facts.d from system facts or variables defined elsewhere in the playbook, so variables will not override each other. You can access this custom fact in a template or playbook as:
.. code-block:: jinja
{{ ansible_local['preferences']['general']['asdf'] }}
.. note:: The key part in the key=value pairs will be converted into lowercase inside the ansible_local variable. Using the example above, if the ini file contained ``XYZ=3`` in the ``[general]`` section, then you should expect to access it as: ``{{ ansible_local['preferences']['general']['xyz'] }}`` and not ``{{ ansible_local['preferences']['general']['XYZ'] }}``. This is because Ansible uses Python's `ConfigParser`_ which passes all option names through the `optionxform`_ method and this method's default implementation converts option names to lower case.
.. _ConfigParser: https://docs.python.org/3/library/configparser.html
.. _optionxform: https://docs.python.org/3/library/configparser.html#ConfigParser.RawConfigParser.optionxform
You can also use facts.d to execute a script on the remote host, generating dynamic custom facts to the ansible_local namespace. For example, you can generate a list of all users that exist on a remote host as a fact about that host. To generate dynamic custom facts using facts.d:
#. Write and test a script to generate the JSON data you want.
#. Save the script in your facts.d directory.
#. Make sure your script has the ``.fact`` file extension.
#. Make sure your script is executable by the Ansible connection user.
#. Gather facts to execute the script and add the JSON output to ansible_local.
By default, fact gathering runs once at the beginning of each play. If you create a custom fact using facts.d in a playbook, it will be available in the next play that gathers facts. If you want to use it in the same play where you created it, you must explicitly re-run the setup module. For example:
.. code-block:: yaml
- hosts: webservers
tasks:
- name: Create directory for ansible custom facts
ansible.builtin.file:
state: directory
recurse: yes
path: /etc/ansible/facts.d
- name: Install custom ipmi fact
ansible.builtin.copy:
src: ipmi.fact
dest: /etc/ansible/facts.d
- name: Re-read facts after adding custom fact
ansible.builtin.setup:
filter: ansible_local
If you use this pattern frequently, a custom facts module would be more efficient than facts.d.
.. _magic_variables_and_hostvars:
Information about Ansible: magic variables
==========================================
You can access information about Ansible operations, including the python version being used, the hosts and groups in inventory, and the directories for playbooks and roles, using "magic" variables. Like connection variables, magic variables are :ref:`special_variables`. Magic variable names are reserved - do not set variables with these names. The variable ``environment`` is also reserved.
The most commonly used magic variables are ``hostvars``, ``groups``, ``group_names``, and ``inventory_hostname``. With ``hostvars``, you can access variables defined for any host in the play, at any point in a playbook. You can access Ansible facts using the ``hostvars`` variable too, but only after you have gathered (or cached) facts. Note that variables defined at play objects are not defined for specific hosts and therefore are not mapped to hostvars.
If you want to configure your database server using the value of a 'fact' from another node, or the value of an inventory variable assigned to another node, you can use ``hostvars`` in a template or on an action line:
.. code-block:: jinja
{{ hostvars['test.example.com']['ansible_facts']['distribution'] }}
With ``groups``, a list of all the groups (and hosts) in the inventory, you can enumerate all hosts within a group. For example:
.. code-block:: jinja
{% for host in groups['app_servers'] %}
# something that applies to all app servers.
{% endfor %}
You can use ``groups`` and ``hostvars`` together to find all the IP addresses in a group.
.. code-block:: jinja
{% for host in groups['app_servers'] %}
{{ hostvars[host]['ansible_facts']['eth0']['ipv4']['address'] }}
{% endfor %}
You can use this approach to point a frontend proxy server to all the hosts in your app servers group, to set up the correct firewall rules between servers, and so on. You must either cache facts or gather facts for those hosts before the task that fills out the template.
With ``group_names``, a list (array) of all the groups the current host is in, you can create templated files that vary based on the group membership (or role) of the host:
.. code-block:: jinja
{% if 'webserver' in group_names %}
# some part of a configuration file that only applies to webservers
{% endif %}
You can use the magic variable ``inventory_hostname``, the name of the host as configured in your inventory, as an alternative to ``ansible_hostname`` when fact-gathering is disabled. If you have a long FQDN, you can use ``inventory_hostname_short``, which contains the part up to the first period, without the rest of the domain.
Other useful magic variables refer to the current play or playbook. These vars may be useful for filling out templates with multiple hostnames or for injecting the list into the rules for a load balancer.
``ansible_play_hosts`` is the list of all hosts still active in the current play.
``ansible_play_batch`` is a list of hostnames that are in scope for the current 'batch' of the play.
The batch size is defined by ``serial``, when not set it is equivalent to the whole play (making it the same as ``ansible_play_hosts``).
``ansible_playbook_python`` is the path to the python executable used to invoke the Ansible command line tool.
``inventory_dir`` is the pathname of the directory holding Ansible's inventory host file.
``inventory_file`` is the pathname and the filename pointing to the Ansible's inventory host file.
``playbook_dir`` contains the playbook base directory.
``role_path`` contains the current role's pathname and only works inside a role.
``ansible_check_mode`` is a boolean, set to ``True`` if you run Ansible with ``--check``.
.. _ansible_version:
Ansible version
---------------
.. versionadded:: 1.8
To adapt playbook behavior to different versions of Ansible, you can use the variable ``ansible_version``, which has the following structure:
.. code-block:: json
{
"ansible_version": {
"full": "2.10.1",
"major": 2,
"minor": 10,
"revision": 1,
"string": "2.10.1"
}
}

@ -0,0 +1,26 @@
.. code-block:: yaml
roles/
common/ # this hierarchy represents a "role"
tasks/ #
main.yml # <-- tasks file can include smaller files if warranted
handlers/ #
main.yml # <-- handlers file
templates/ # <-- files for use with the template resource
ntp.conf.j2 # <------- templates end in .j2
files/ #
bar.txt # <-- files for use with the copy resource
foo.sh # <-- script files for use with the script resource
vars/ #
main.yml # <-- variables associated with this role
defaults/ #
main.yml # <-- default lower priority variables for this role
meta/ #
main.yml # <-- role dependencies
library/ # roles can also include custom modules
module_utils/ # roles can also include custom module_utils
lookup_plugins/ # or other types of plugins, like lookup in this case
webtier/ # same kind of structure as "common" was above, done for the webtier role
monitoring/ # ""
fooapp/ # ""

@ -0,0 +1,205 @@
In most cases, loops work best with the ``loop`` keyword instead of ``with_X`` style loops. The ``loop`` syntax is usually best expressed using filters instead of more complex use of ``query`` or ``lookup``.
These examples show how to convert many common ``with_`` style loops to ``loop`` and filters.
with_list
---------
``with_list`` is directly replaced by ``loop``.
.. code-block:: yaml+jinja
- name: with_list
ansible.builtin.debug:
msg: "{{ item }}"
with_list:
- one
- two
- name: with_list -> loop
ansible.builtin.debug:
msg: "{{ item }}"
loop:
- one
- two
with_items
----------
``with_items`` is replaced by ``loop`` and the ``flatten`` filter.
.. code-block:: yaml+jinja
- name: with_items
ansible.builtin.debug:
msg: "{{ item }}"
with_items: "{{ items }}"
- name: with_items -> loop
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ items|flatten(levels=1) }}"
with_indexed_items
------------------
``with_indexed_items`` is replaced by ``loop``, the ``flatten`` filter and ``loop_control.index_var``.
.. code-block:: yaml+jinja
- name: with_indexed_items
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }}"
with_indexed_items: "{{ items }}"
- name: with_indexed_items -> loop
ansible.builtin.debug:
msg: "{{ index }} - {{ item }}"
loop: "{{ items|flatten(levels=1) }}"
loop_control:
index_var: index
with_flattened
--------------
``with_flattened`` is replaced by ``loop`` and the ``flatten`` filter.
.. code-block:: yaml+jinja
- name: with_flattened
ansible.builtin.debug:
msg: "{{ item }}"
with_flattened: "{{ items }}"
- name: with_flattened -> loop
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ items|flatten }}"
with_together
-------------
``with_together`` is replaced by ``loop`` and the ``zip`` filter.
.. code-block:: yaml+jinja
- name: with_together
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }}"
with_together:
- "{{ list_one }}"
- "{{ list_two }}"
- name: with_together -> loop
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }}"
loop: "{{ list_one|zip(list_two)|list }}"
Another example with complex data
.. code-block:: yaml+jinja
- name: with_together -> loop
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }} - {{ item.2 }}"
loop: "{{ data[0]|zip(*data[1:])|list }}"
vars:
data:
- ['a', 'b', 'c']
- ['d', 'e', 'f']
- ['g', 'h', 'i']
with_dict
---------
``with_dict`` can be substituted by ``loop`` and either the ``dictsort`` or ``dict2items`` filters.
.. code-block:: yaml+jinja
- name: with_dict
ansible.builtin.debug:
msg: "{{ item.key }} - {{ item.value }}"
with_dict: "{{ dictionary }}"
- name: with_dict -> loop (option 1)
ansible.builtin.debug:
msg: "{{ item.key }} - {{ item.value }}"
loop: "{{ dictionary|dict2items }}"
- name: with_dict -> loop (option 2)
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }}"
loop: "{{ dictionary|dictsort }}"
with_sequence
-------------
``with_sequence`` is replaced by ``loop`` and the ``range`` function, and potentially the ``format`` filter.
.. code-block:: yaml+jinja
- name: with_sequence
ansible.builtin.debug:
msg: "{{ item }}"
with_sequence: start=0 end=4 stride=2 format=testuser%02x
- name: with_sequence -> loop
ansible.builtin.debug:
msg: "{{ 'testuser%02x' | format(item) }}"
# range is exclusive of the end point
loop: "{{ range(0, 4 + 1, 2)|list }}"
with_subelements
----------------
``with_subelements`` is replaced by ``loop`` and the ``subelements`` filter.
.. code-block:: yaml+jinja
- name: with_subelements
ansible.builtin.debug:
msg: "{{ item.0.name }} - {{ item.1 }}"
with_subelements:
- "{{ users }}"
- mysql.hosts
- name: with_subelements -> loop
ansible.builtin.debug:
msg: "{{ item.0.name }} - {{ item.1 }}"
loop: "{{ users|subelements('mysql.hosts') }}"
with_nested/with_cartesian
--------------------------
``with_nested`` and ``with_cartesian`` are replaced by loop and the ``product`` filter.
.. code-block:: yaml+jinja
- name: with_nested
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }}"
with_nested:
- "{{ list_one }}"
- "{{ list_two }}"
- name: with_nested -> loop
ansible.builtin.debug:
msg: "{{ item.0 }} - {{ item.1 }}"
loop: "{{ list_one|product(list_two)|list }}"
with_random_choice
------------------
``with_random_choice`` is replaced by just use of the ``random`` filter, without need of ``loop``.
.. code-block:: yaml+jinja
- name: with_random_choice
ansible.builtin.debug:
msg: "{{ item }}"
with_random_choice: "{{ my_list }}"
- name: with_random_choice -> loop (No loop is needed here)
ansible.builtin.debug:
msg: "{{ my_list|random }}"
tags: random

@ -23,6 +23,7 @@ Customize the CLI output
You can change the output from Ansible CLI commands using :ref:`callback_plugins`.
.. _playbook_tips:
Playbook tips
=============
@ -53,6 +54,8 @@ Use comments
Even with task names and explicit state, sometimes a part of a playbook or role (or inventory/variable file) needs more explanation.
Adding a comment (any line starting with '#') helps others (and possibly yourself in future) understand what a play or task (or variable setting) does, how it does it, and why.
.. _inventory_tips:
Inventory tips
==============
@ -103,6 +106,8 @@ There is no limit to the number of variable and vault files or their names.
Note that using this strategy in your inventory still requires *all vault passwords to be available* (for example for ``ansible-playbook`` or `AWX/Ansible Tower <https://github.com/ansible/awx/issues/223#issuecomment-768386089>`_) when run with that inventory.
.. _execution_tips:
Execution tricks
================

@ -1,773 +1,7 @@
.. _become:
:orphan:
******************************************
Understanding privilege escalation: become
******************************************
Ansible uses existing privilege escalation systems to execute tasks with root privileges or with another user's permissions. Because this feature allows you to 'become' another user, different from the user that logged into the machine (remote user), we call it ``become``. The ``become`` keyword uses existing privilege escalation tools like `sudo`, `su`, `pfexec`, `doas`, `pbrun`, `dzdo`, `ksu`, `runas`, `machinectl` and others.
.. contents::
:local:
Using become
============
You can control the use of ``become`` with play or task directives, connection variables, or at the command line. If you set privilege escalation properties in multiple ways, review the :ref:`general precedence rules<general_precedence_rules>` to understand which settings will be used.
A full list of all become plugins that are included in Ansible can be found in the :ref:`become_plugin_list`.
Become directives
-----------------
You can set the directives that control ``become`` at the play or task level. You can override these by setting connection variables, which often differ from one host to another. These variables and directives are independent. For example, setting ``become_user`` does not set ``become``.
become
set to ``yes`` to activate privilege escalation.
become_user
set to user with desired privileges — the user you `become`, NOT the user you login as. Does NOT imply ``become: yes``, to allow it to be set at host level. Default value is ``root``.
become_method
(at play or task level) overrides the default method set in ansible.cfg, set to use any of the :ref:`become_plugins`.
become_flags
(at play or task level) permit the use of specific flags for the tasks or role. One common use is to change the user to nobody when the shell is set to nologin. Added in Ansible 2.2.
For example, to manage a system service (which requires ``root`` privileges) when connected as a non-``root`` user, you can use the default value of ``become_user`` (``root``):
.. code-block:: yaml
- name: Ensure the httpd service is running
service:
name: httpd
state: started
become: yes
To run a command as the ``apache`` user:
.. code-block:: yaml
- name: Run a command as the apache user
command: somecommand
become: yes
become_user: apache
To do something as the ``nobody`` user when the shell is nologin:
.. code-block:: yaml
- name: Run a command as nobody
command: somecommand
become: yes
become_method: su
become_user: nobody
become_flags: '-s /bin/sh'
To specify a password for sudo, run ``ansible-playbook`` with ``--ask-become-pass`` (``-K`` for short).
If you run a playbook utilizing ``become`` and the playbook seems to hang, most likely it is stuck at the privilege escalation prompt. Stop it with `CTRL-c`, then execute the playbook with ``-K`` and the appropriate password.
Become connection variables
---------------------------
You can define different ``become`` options for each managed node or group. You can define these variables in inventory or use them as normal variables.
ansible_become
overrides the ``become`` directive, decides if privilege escalation is used or not.
ansible_become_method
which privilege escalation method should be used
ansible_become_user
set the user you become through privilege escalation; does not imply ``ansible_become: yes``
ansible_become_password
set the privilege escalation password. See :ref:`playbooks_vault` for details on how to avoid having secrets in plain text
ansible_common_remote_group
determines if Ansible should try to ``chgrp`` its temporary files to a group if ``setfacl`` and ``chown`` both fail. See `Risks of becoming an unprivileged user`_ for more information. Added in version 2.10.
For example, if you want to run all tasks as ``root`` on a server named ``webserver``, but you can only connect as the ``manager`` user, you could use an inventory entry like this:
.. code-block:: text
webserver ansible_user=manager ansible_become=yes
.. note::
The variables defined above are generic for all become plugins but plugin specific ones can also be set instead.
Please see the documentation for each plugin for a list of all options the plugin has and how they can be defined.
A full list of become plugins in Ansible can be found at :ref:`become_plugins`.
Become command-line options
---------------------------
--ask-become-pass, -K
ask for privilege escalation password; does not imply become will be used. Note that this password will be used for all hosts.
--become, -b
run operations with become (no password implied)
--become-method=BECOME_METHOD
privilege escalation method to use (default=sudo),
valid choices: [ sudo | su | pbrun | pfexec | doas | dzdo | ksu | runas | machinectl ]
--become-user=BECOME_USER
run operations as this user (default=root), does not imply --become/-b
Risks and limitations of become
===============================
Although privilege escalation is mostly intuitive, there are a few limitations
on how it works. Users should be aware of these to avoid surprises.
Risks of becoming an unprivileged user
--------------------------------------
Ansible modules are executed on the remote machine by first substituting the
parameters into the module file, then copying the file to the remote machine,
and finally executing it there.
Everything is fine if the module file is executed without using ``become``,
when the ``become_user`` is root, or when the connection to the remote machine
is made as root. In these cases Ansible creates the module file with
permissions that only allow reading by the user and root, or only allow reading
by the unprivileged user being switched to.
However, when both the connection user and the ``become_user`` are unprivileged,
the module file is written as the user that Ansible connects as (the
``remote_user``), but the file needs to be readable by the user Ansible is set
to ``become``. The details of how Ansible solves this can vary based on platform.
However, on POSIX systems, Ansible solves this problem in the following way:
First, if :command:`setfacl` is installed and available in the remote ``PATH``,
and the temporary directory on the remote host is mounted with POSIX.1e
filesystem ACL support, Ansible will use POSIX ACLs to share the module file
with the second unprivileged user.
Next, if POSIX ACLs are **not** available or :command:`setfacl` could not be
run, Ansible will attempt to change ownership of the module file using
:command:`chown` for systems which support doing so as an unprivileged user.
New in Ansible 2.11, at this point, Ansible will try :command:`chmod +a` which
is a macOS-specific way of setting ACLs on files.
New in Ansible 2.10, if all of the above fails, Ansible will then check the
value of the configuration setting ``ansible_common_remote_group``. Many
systems will allow a given user to change the group ownership of a file to a
group the user is in. As a result, if the second unprivileged user (the
``become_user``) has a UNIX group in common with the user Ansible is connected
as (the ``remote_user``), and if ``ansible_common_remote_group`` is defined to
be that group, Ansible can try to change the group ownership of the module file
to that group by using :command:`chgrp`, thereby likely making it readable to
the ``become_user``.
At this point, if ``ansible_common_remote_group`` was defined and a
:command:`chgrp` was attempted and returned successfully, Ansible assumes (but,
importantly, does not check) that the new group ownership is enough and does not
fall back further. That is, Ansible **does not check** that the ``become_user``
does in fact share a group with the ``remote_user``; so long as the command
exits successfully, Ansible considers the result successful and does not proceed
to check ``allow_world_readable_tmpfiles`` per below.
If ``ansible_common_remote_group`` is **not** set and the chown above it failed,
or if ``ansible_common_remote_group`` *is* set but the :command:`chgrp` (or
following group-permissions :command:`chmod`) returned a non-successful exit
code, Ansible will lastly check the value of
``allow_world_readable_tmpfiles``. If this is set, Ansible will place the module
file in a world-readable temporary directory, with world-readable permissions to
allow the ``become_user`` (and incidentally any other user on the system) to
read the contents of the file. **If any of the parameters passed to the module
are sensitive in nature, and you do not trust the remote machines, then this is
a potential security risk.**
Once the module is done executing, Ansible deletes the temporary file.
Several ways exist to avoid the above logic flow entirely:
* Use `pipelining`. When pipelining is enabled, Ansible does not save the
module to a temporary file on the client. Instead it pipes the module to
the remote python interpreter's stdin. Pipelining does not work for
python modules involving file transfer (for example: :ref:`copy <copy_module>`,
:ref:`fetch <fetch_module>`, :ref:`template <template_module>`), or for non-python modules.
* Avoid becoming an unprivileged
user. Temporary files are protected by UNIX file permissions when you
``become`` root or do not use ``become``. In Ansible 2.1 and above, UNIX
file permissions are also secure if you make the connection to the managed
machine as root and then use ``become`` to access an unprivileged account.
.. warning:: Although the Solaris ZFS filesystem has filesystem ACLs, the ACLs
are not POSIX.1e filesystem acls (they are NFSv4 ACLs instead). Ansible
cannot use these ACLs to manage its temp file permissions so you may have
to resort to ``allow_world_readable_tmpfiles`` if the remote machines use ZFS.
.. versionchanged:: 2.1
Ansible makes it hard to unknowingly use ``become`` insecurely. Starting in Ansible 2.1,
Ansible defaults to issuing an error if it cannot execute securely with ``become``.
If you cannot use pipelining or POSIX ACLs, must connect as an unprivileged user,
must use ``become`` to execute as a different unprivileged user,
and decide that your managed nodes are secure enough for the
modules you want to run there to be world readable, you can turn on
``allow_world_readable_tmpfiles`` in the :file:`ansible.cfg` file. Setting
``allow_world_readable_tmpfiles`` will change this from an error into
a warning and allow the task to run as it did prior to 2.1.
.. versionchanged:: 2.10
Ansible 2.10 introduces the above-mentioned ``ansible_common_remote_group``
fallback. As mentioned above, if enabled, it is used when ``remote_user`` and
``become_user`` are both unprivileged users. Refer to the text above for details
on when this fallback happens.
.. warning:: As mentioned above, if ``ansible_common_remote_group`` and
``allow_world_readable_tmpfiles`` are both enabled, it is unlikely that the
world-readable fallback will ever trigger, and yet Ansible might still be
unable to access the module file. This is because after the group ownership
change is successful, Ansible does not fall back any further, and also does
not do any check to ensure that the ``become_user`` is actually a member of
the "common group". This is a design decision made by the fact that doing
such a check would require another round-trip connection to the remote
machine, which is a time-expensive operation. Ansible does, however, emit a
warning in this case.
Not supported by all connection plugins
---------------------------------------
Privilege escalation methods must also be supported by the connection plugin
used. Most connection plugins will warn if they do not support become. Some
will just ignore it as they always run as root (jail, chroot, and so on).
Only one method may be enabled per host
---------------------------------------
Methods cannot be chained. You cannot use ``sudo /bin/su -`` to become a user,
you need to have privileges to run the command as that user in sudo or be able
to su directly to it (the same for pbrun, pfexec or other supported methods).
Privilege escalation must be general
------------------------------------
You cannot limit privilege escalation permissions to certain commands.
Ansible does not always
use a specific command to do something but runs modules (code) from
a temporary file name which changes every time. If you have '/sbin/service'
or '/bin/chmod' as the allowed commands this will fail with ansible as those
paths won't match with the temporary file that Ansible creates to run the
module. If you have security rules that constrain your sudo/pbrun/doas environment
to running specific command paths only, use Ansible from a special account that
does not have this constraint, or use AWX or the :ref:`ansible_platform` to manage indirect access to SSH credentials.
May not access environment variables populated by pamd_systemd
--------------------------------------------------------------
For most Linux distributions using ``systemd`` as their init, the default
methods used by ``become`` do not open a new "session", in the sense of
systemd. Because the ``pam_systemd`` module will not fully initialize a new
session, you might have surprises compared to a normal session opened through
ssh: some environment variables set by ``pam_systemd``, most notably
``XDG_RUNTIME_DIR``, are not populated for the new user and instead inherited
or just emptied.
This might cause trouble when trying to invoke systemd commands that depend on
``XDG_RUNTIME_DIR`` to access the bus:
.. code-block:: console
$ echo $XDG_RUNTIME_DIR
$ systemctl --user status
Failed to connect to bus: Permission denied
To force ``become`` to open a new systemd session that goes through
``pam_systemd``, you can use ``become_method: machinectl``.
For more information, see `this systemd issue
<https://github.com/systemd/systemd/issues/825#issuecomment-127917622>`_.
Resolving Temporary File Error Messsages
----------------------------------------
* Failed to set permissions on the temporary files Ansible needs to create when becoming an unprivileged user"
* This error can be resolved by installing the package that provides the ``setfacl`` command. (This is frequently the ``acl`` package but check your OS documentation.)
.. _become_network:
Become and network automation
=============================
As of version 2.6, Ansible supports ``become`` for privilege escalation (entering ``enable`` mode or privileged EXEC mode) on all Ansible-maintained network platforms that support ``enable`` mode. Using ``become`` replaces the ``authorize`` and ``auth_pass`` options in a ``provider`` dictionary.
You must set the connection type to either ``connection: ansible.netcommon.network_cli`` or ``connection: ansible.netcommon.httpapi`` to use ``become`` for privilege escalation on network devices. Check the :ref:`platform_options` documentation for details.
You can use escalated privileges on only the specific tasks that need them, on an entire play, or on all plays. Adding ``become: yes`` and ``become_method: enable`` instructs Ansible to enter ``enable`` mode before executing the task, play, or playbook where those parameters are set.
If you see this error message, the task that generated it requires ``enable`` mode to succeed:
.. code-block:: console
Invalid input (privileged mode required)
To set ``enable`` mode for a specific task, add ``become`` at the task level:
.. code-block:: yaml
- name: Gather facts (eos)
arista.eos.eos_facts:
gather_subset:
- "!hardware"
become: yes
become_method: enable
To set enable mode for all tasks in a single play, add ``become`` at the play level:
.. code-block:: yaml
- hosts: eos-switches
become: yes
become_method: enable
tasks:
- name: Gather facts (eos)
arista.eos.eos_facts:
gather_subset:
- "!hardware"
Setting enable mode for all tasks
---------------------------------
Often you wish for all tasks in all plays to run using privilege mode, that is best achieved by using ``group_vars``:
**group_vars/eos.yml**
.. code-block:: yaml
ansible_connection: ansible.netcommon.network_cli
ansible_network_os: arista.eos.eos
ansible_user: myuser
ansible_become: yes
ansible_become_method: enable
Passwords for enable mode
^^^^^^^^^^^^^^^^^^^^^^^^^
If you need a password to enter ``enable`` mode, you can specify it in one of two ways:
* providing the :option:`--ask-become-pass <ansible-playbook --ask-become-pass>` command line option
* setting the ``ansible_become_password`` connection variable
.. warning::
As a reminder passwords should never be stored in plain text. For information on encrypting your passwords and other secrets with Ansible Vault, see :ref:`vault`.
authorize and auth_pass
-----------------------
Ansible still supports ``enable`` mode with ``connection: local`` for legacy network playbooks. To enter ``enable`` mode with ``connection: local``, use the module options ``authorize`` and ``auth_pass``:
.. code-block:: yaml
- hosts: eos-switches
ansible_connection: local
tasks:
- name: Gather facts (eos)
eos_facts:
gather_subset:
- "!hardware"
provider:
authorize: yes
auth_pass: " {{ secret_auth_pass }}"
We recommend updating your playbooks to use ``become`` for network-device ``enable`` mode consistently. The use of ``authorize`` and of ``provider`` dictionaries will be deprecated in future. Check the :ref:`platform_options` and :ref:`network_modules` documentation for details.
.. _become_windows:
Become and Windows
==================
Since Ansible 2.3, ``become`` can be used on Windows hosts through the
``runas`` method. Become on Windows uses the same inventory setup and
invocation arguments as ``become`` on a non-Windows host, so the setup and
variable names are the same as what is defined in this document.
While ``become`` can be used to assume the identity of another user, there are other uses for
it with Windows hosts. One important use is to bypass some of the
limitations that are imposed when running on WinRM, such as constrained network
delegation or accessing forbidden system calls like the WUA API. You can use
``become`` with the same user as ``ansible_user`` to bypass these limitations
and run commands that are not normally accessible in a WinRM session.
.. Note::
On Windows you cannot connect with an underprivileged account and use become
to elevate your rights. Become can only be used if your connection account
is already an Administrator of the target host.
Administrative rights
---------------------
Many tasks in Windows require administrative privileges to complete. When using
the ``runas`` become method, Ansible will attempt to run the module with the
full privileges that are available to the become user. If it fails to elevate
the user token, it will continue to use the limited token during execution.
A user must have the ``SeDebugPrivilege`` to run a become process with elevated
privileges. This privilege is assigned to Administrators by default. If the
debug privilege is not available, the become process will run with a limited
set of privileges and groups.
To determine the type of token that Ansible was able to get, run the following
task:
.. code-block:: yaml
- Check my user name
ansible.windows.win_whoami:
become: yes
The output will look something similar to the below:
.. code-block:: ansible-output
ok: [windows] => {
"account": {
"account_name": "vagrant-domain",
"domain_name": "DOMAIN",
"sid": "S-1-5-21-3088887838-4058132883-1884671576-1105",
"type": "User"
},
"authentication_package": "Kerberos",
"changed": false,
"dns_domain_name": "DOMAIN.LOCAL",
"groups": [
{
"account_name": "Administrators",
"attributes": [
"Mandatory",
"Enabled by default",
"Enabled",
"Owner"
],
"domain_name": "BUILTIN",
"sid": "S-1-5-32-544",
"type": "Alias"
},
{
"account_name": "INTERACTIVE",
"attributes": [
"Mandatory",
"Enabled by default",
"Enabled"
],
"domain_name": "NT AUTHORITY",
"sid": "S-1-5-4",
"type": "WellKnownGroup"
},
],
"impersonation_level": "SecurityAnonymous",
"label": {
"account_name": "High Mandatory Level",
"domain_name": "Mandatory Label",
"sid": "S-1-16-12288",
"type": "Label"
},
"login_domain": "DOMAIN",
"login_time": "2018-11-18T20:35:01.9696884+00:00",
"logon_id": 114196830,
"logon_server": "DC01",
"logon_type": "Interactive",
"privileges": {
"SeBackupPrivilege": "disabled",
"SeChangeNotifyPrivilege": "enabled-by-default",
"SeCreateGlobalPrivilege": "enabled-by-default",
"SeCreatePagefilePrivilege": "disabled",
"SeCreateSymbolicLinkPrivilege": "disabled",
"SeDebugPrivilege": "enabled",
"SeDelegateSessionUserImpersonatePrivilege": "disabled",
"SeImpersonatePrivilege": "enabled-by-default",
"SeIncreaseBasePriorityPrivilege": "disabled",
"SeIncreaseQuotaPrivilege": "disabled",
"SeIncreaseWorkingSetPrivilege": "disabled",
"SeLoadDriverPrivilege": "disabled",
"SeManageVolumePrivilege": "disabled",
"SeProfileSingleProcessPrivilege": "disabled",
"SeRemoteShutdownPrivilege": "disabled",
"SeRestorePrivilege": "disabled",
"SeSecurityPrivilege": "disabled",
"SeShutdownPrivilege": "disabled",
"SeSystemEnvironmentPrivilege": "disabled",
"SeSystemProfilePrivilege": "disabled",
"SeSystemtimePrivilege": "disabled",
"SeTakeOwnershipPrivilege": "disabled",
"SeTimeZonePrivilege": "disabled",
"SeUndockPrivilege": "disabled"
},
"rights": [
"SeNetworkLogonRight",
"SeBatchLogonRight",
"SeInteractiveLogonRight",
"SeRemoteInteractiveLogonRight"
],
"token_type": "TokenPrimary",
"upn": "vagrant-domain@DOMAIN.LOCAL",
"user_flags": []
}
Under the ``label`` key, the ``account_name`` entry determines whether the user
has Administrative rights. Here are the labels that can be returned and what
they represent:
* ``Medium``: Ansible failed to get an elevated token and ran under a limited
token. Only a subset of the privileges assigned to user are available during
the module execution and the user does not have administrative rights.
* ``High``: An elevated token was used and all the privileges assigned to the
user are available during the module execution.
* ``System``: The ``NT AUTHORITY\System`` account is used and has the highest
level of privileges available.
The output will also show the list of privileges that have been granted to the
user. When the privilege value is ``disabled``, the privilege is assigned to
the logon token but has not been enabled. In most scenarios these privileges
are automatically enabled when required.
If running on a version of Ansible that is older than 2.5 or the normal
``runas`` escalation process fails, an elevated token can be retrieved by:
* Set the ``become_user`` to ``System`` which has full control over the
operating system.
* Grant ``SeTcbPrivilege`` to the user Ansible connects with on
WinRM. ``SeTcbPrivilege`` is a high-level privilege that grants
full control over the operating system. No user is given this privilege by
default, and care should be taken if you grant this privilege to a user or group.
For more information on this privilege, please see
`Act as part of the operating system <https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/dn221957(v=ws.11)>`_.
You can use the below task to set this privilege on a Windows host:
.. code-block:: yaml
- name: grant the ansible user the SeTcbPrivilege right
ansible.windows.win_user_right:
name: SeTcbPrivilege
users: '{{ansible_user}}'
action: add
* Turn UAC off on the host and reboot before trying to become the user. UAC is
a security protocol that is designed to run accounts with the
``least privilege`` principle. You can turn UAC off by running the following
tasks:
.. code-block:: yaml
- name: turn UAC off
win_regedit:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\policies\system
name: EnableLUA
data: 0
type: dword
state: present
register: uac_result
- name: reboot after disabling UAC
win_reboot:
when: uac_result is changed
.. Note:: Granting the ``SeTcbPrivilege`` or turning UAC off can cause Windows
security vulnerabilities and care should be given if these steps are taken.
Local service accounts
----------------------
Prior to Ansible version 2.5, ``become`` only worked on Windows with a local or domain
user account. Local service accounts like ``System`` or ``NetworkService``
could not be used as ``become_user`` in these older versions. This restriction
has been lifted since the 2.5 release of Ansible. The three service accounts
that can be set under ``become_user`` are:
* System
* NetworkService
* LocalService
Because local service accounts do not have passwords, the
``ansible_become_password`` parameter is not required and is ignored if
specified.
Become without setting a password
---------------------------------
As of Ansible 2.8, ``become`` can be used to become a Windows local or domain account
without requiring a password for that account. For this method to work, the
following requirements must be met:
* The connection user has the ``SeDebugPrivilege`` privilege assigned
* The connection user is part of the ``BUILTIN\Administrators`` group
* The ``become_user`` has either the ``SeBatchLogonRight`` or ``SeNetworkLogonRight`` user right
Using become without a password is achieved in one of two different methods:
* Duplicating an existing logon session's token if the account is already logged on
* Using S4U to generate a logon token that is valid on the remote host only
In the first scenario, the become process is spawned from another logon of that
user account. This could be an existing RDP logon, console logon, but this is
not guaranteed to occur all the time. This is similar to the
``Run only when user is logged on`` option for a Scheduled Task.
In the case where another logon of the become account does not exist, S4U is
used to create a new logon and run the module through that. This is similar to
the ``Run whether user is logged on or not`` with the ``Do not store password``
option for a Scheduled Task. In this scenario, the become process will not be
able to access any network resources like a normal WinRM process.
To make a distinction between using become with no password and becoming an
account that has no password make sure to keep ``ansible_become_password`` as
undefined or set ``ansible_become_password:``.
.. Note:: Because there are no guarantees an existing token will exist for a
user when Ansible runs, there's a high change the become process will only
have access to local resources. Use become with a password if the task needs
to access network resources
Accounts without a password
---------------------------
.. Warning:: As a general security best practice, you should avoid allowing accounts without passwords.
Ansible can be used to become a Windows account that does not have a password (like the
``Guest`` account). To become an account without a password, set up the
variables like normal but set ``ansible_become_password: ''``.
Before become can work on an account like this, the local policy
`Accounts: Limit local account use of blank passwords to console logon only <https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/jj852174(v=ws.11)>`_
must be disabled. This can either be done through a Group Policy Object (GPO)
or with this Ansible task:
.. code-block:: yaml
- name: allow blank password on become
ansible.windows.win_regedit:
path: HKLM:\SYSTEM\CurrentControlSet\Control\Lsa
name: LimitBlankPasswordUse
data: 0
type: dword
state: present
.. Note:: This is only for accounts that do not have a password. You still need
to set the account's password under ``ansible_become_password`` if the
become_user has a password.
Become flags for Windows
------------------------
Ansible 2.5 added the ``become_flags`` parameter to the ``runas`` become method.
This parameter can be set using the ``become_flags`` task directive or set in
Ansible's configuration using ``ansible_become_flags``. The two valid values
that are initially supported for this parameter are ``logon_type`` and
``logon_flags``.
.. Note:: These flags should only be set when becoming a normal user account, not a local service account like LocalSystem.
The key ``logon_type`` sets the type of logon operation to perform. The value
can be set to one of the following:
* ``interactive``: The default logon type. The process will be run under a
context that is the same as when running a process locally. This bypasses all
WinRM restrictions and is the recommended method to use.
* ``batch``: Runs the process under a batch context that is similar to a
scheduled task with a password set. This should bypass most WinRM
restrictions and is useful if the ``become_user`` is not allowed to log on
interactively.
* ``new_credentials``: Runs under the same credentials as the calling user, but
outbound connections are run under the context of the ``become_user`` and
``become_password``, similar to ``runas.exe /netonly``. The ``logon_flags``
flag should also be set to ``netcredentials_only``. Use this flag if
the process needs to access a network resource (like an SMB share) using a
different set of credentials.
* ``network``: Runs the process under a network context without any cached
credentials. This results in the same type of logon session as running a
normal WinRM process without credential delegation, and operates under the same
restrictions.
* ``network_cleartext``: Like the ``network`` logon type, but instead caches
the credentials so it can access network resources. This is the same type of
logon session as running a normal WinRM process with credential delegation.
For more information, see
`dwLogonType <https://docs.microsoft.com/en-gb/windows/desktop/api/winbase/nf-winbase-logonusera>`_.
The ``logon_flags`` key specifies how Windows will log the user on when creating
the new process. The value can be set to none or multiple of the following:
* ``with_profile``: The default logon flag set. The process will load the
user's profile in the ``HKEY_USERS`` registry key to ``HKEY_CURRENT_USER``.
* ``netcredentials_only``: The process will use the same token as the caller
but will use the ``become_user`` and ``become_password`` when accessing a remote
resource. This is useful in inter-domain scenarios where there is no trust
relationship, and should be used with the ``new_credentials`` ``logon_type``.
By default ``logon_flags=with_profile`` is set, if the profile should not be
loaded set ``logon_flags=`` or if the profile should be loaded with
``netcredentials_only``, set ``logon_flags=with_profile,netcredentials_only``.
For more information, see `dwLogonFlags <https://docs.microsoft.com/en-gb/windows/desktop/api/winbase/nf-winbase-createprocesswithtokenw>`_.
Here are some examples of how to use ``become_flags`` with Windows tasks:
.. code-block:: yaml
- name: copy a file from a fileshare with custom credentials
ansible.windows.win_copy:
src: \\server\share\data\file.txt
dest: C:\temp\file.txt
remote_src: yes
vars:
ansible_become: yes
ansible_become_method: runas
ansible_become_user: DOMAIN\user
ansible_become_password: Password01
ansible_become_flags: logon_type=new_credentials logon_flags=netcredentials_only
- name: run a command under a batch logon
ansible.windows.win_whoami:
become: yes
become_flags: logon_type=batch
- name: run a command and not load the user profile
ansible.windows.win_whomai:
become: yes
become_flags: logon_flags=
Limitations of become on Windows
--------------------------------
* Running a task with ``async`` and ``become`` on Windows Server 2008, 2008 R2
and Windows 7 only works when using Ansible 2.7 or newer.
* By default, the become user logs on with an interactive session, so it must
have the right to do so on the Windows host. If it does not inherit the
``SeAllowLogOnLocally`` privilege or inherits the ``SeDenyLogOnLocally``
privilege, the become process will fail. Either add the privilege or set the
``logon_type`` flag to change the logon type used.
* Prior to Ansible version 2.3, become only worked when
``ansible_winrm_transport`` was either ``basic`` or ``credssp``. This
restriction has been lifted since the 2.4 release of Ansible for all hosts
except Windows Server 2008 (non R2 version).
* The Secondary Logon service ``seclogon`` must be running to use ``ansible_become_method: runas``
* The connection user must already be an Administrator on the Windows host to
use ``runas``. The target become user does not need to be an Administrator
though.
.. seealso::
`Mailing List <https://groups.google.com/forum/#!forum/ansible-project>`_
Questions? Help? Ideas? Stop by the list on Google Groups
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_privilege_escalation`.

@ -13,24 +13,6 @@ User Guide
Welcome to the Ansible User Guide! This guide covers how to work with Ansible, including using the command line, working with inventory, interacting with data, writing tasks, plays, and playbooks; executing playbooks, and reference materials.
Quickly find answers in the following sections or expand the table of contents below to scroll through all resources.
Writing tasks, plays, and playbooks
===================================
* I have a specific use case for a task or play:
* Executing tasks with elevated privileges or as a different user with :ref:`become <become>`
* Repeating a task once for each item in a list with :ref:`loops <playbooks_loops>`
* Executing tasks on a different machine with :ref:`delegation <playbooks_delegation>`
* Running tasks only when certain conditions apply with :ref:`conditionals <playbooks_conditionals>` and evaluating conditions with :ref:`tests <playbooks_tests>`
* Grouping a set of tasks together with :ref:`blocks <playbooks_blocks>`
* Running tasks only when something has changed with :ref:`handlers <handlers>`
* Changing the way Ansible :ref:`handles failures <playbooks_error_handling>`
* Setting remote :ref:`environment values <playbooks_environment>`
* I want to take advantage of the power of re-usable Ansible artifacts. How do I create re-usable :ref:`files <playbooks_reuse>` and :ref:`roles <playbooks_reuse_roles>`?
* I need to incorporate one file or playbook inside another. What is the difference between :ref:`including and importing <dynamic_vs_static>`?
* I want to run selected parts of my playbook. How do I add and use :ref:`tags <tags>`?
Working with inventory
======================
@ -41,32 +23,14 @@ Working with inventory
Interacting with data
=====================
* I want to use a single playbook against multiple systems with different attributes. How do I use :ref:`variables <playbooks_variables>` to handle the differences?
* I want to retrieve data about my systems. How do I access :ref:`Ansible facts <vars_and_facts>`?
* I need to access sensitive data like passwords with Ansible. How can I protect that data with :ref:`Ansible vault <vault>`?
* I want to change the data I have, so I can use it in a task. How do I use :ref:`filters <playbooks_filters>` to transform my data?
* I need to retrieve data from an external datastore. How do I use :ref:`lookups <playbooks_lookups>` to access databases and APIs?
* I want to ask playbook users to supply data. How do I get user input with :ref:`prompts <playbooks_prompts>`?
* I use certain modules frequently. How do I streamline my inventory and playbooks by :ref:`setting default values for module parameters <module_defaults>`?
Executing playbooks
===================
Once your playbook is ready to run, you may need to use these topics:
* Executing "dry run" playbooks with :ref:`check mode and diff <check_mode_dry>`
* Running playbooks while troubleshooting with :ref:`start and step <playbooks_start_and_step>`
* Correcting tasks during execution with the :ref:`Ansible debugger <playbook_debugger>`
* Controlling how my playbook executes with :ref:`strategies and more <playbooks_strategies>`
* Running tasks, plays, and playbooks :ref:`asynchronously <playbooks_async>`
Advanced features and reference
===============================
* Using :ref:`advanced syntax <playbooks_advanced_syntax>`
* Manipulating :ref:`complex data <complex_data_manipulation>`
* Using :ref:`plugins <plugins_lookup>`
* Using :ref:`playbook keywords <playbook_keywords>`
* Using :ref:`command-line tools <command_line_tools>`
* Rejecting :ref:`specific modules <plugin_filtering_config>`
* Module :ref:`maintenance <modules_support>`
@ -81,44 +45,16 @@ Here is the complete list of resources in the Ansible User Guide:
intro_adhoc
cheatsheet
playbooks_intro
playbooks
become
playbooks_loops
playbooks_delegation
playbooks_conditionals
playbooks_tests
playbooks_blocks
playbooks_handlers
playbooks_error_handling
playbooks_environment
playbooks_reuse
playbooks_reuse_roles
playbooks_reuse_includes
playbooks_tags
intro_inventory
intro_dynamic_inventory
intro_patterns
connection_details
command_line_tools
playbooks_variables
playbooks_vars_facts
vault
playbooks_filters
playbooks_lookups
playbooks_prompts
playbooks_module_defaults
playbooks_checkmode
playbooks_startnstep
playbooks_debugger
playbooks_strategies
playbooks_async
playbooks_advanced_syntax
complex_data_manipulation
plugin_filtering_config
sample_setup
modules
../plugins/plugins
../reference_appendices/playbooks_keywords
intro_bsd
collections_using

@ -1,21 +1,6 @@
.. _working_with_playbooks:
:orphan:
Working with playbooks
======================
Playbooks record and execute Ansible's configuration, deployment, and orchestration functions. They can describe a policy you want your remote systems to enforce, or a set of steps in a general IT process.
If Ansible modules are the tools in your workshop, playbooks are your instruction manuals, and your inventory of hosts are your raw material.
At a basic level, playbooks can be used to manage configurations of and deployments to remote machines. At a more advanced level, they can sequence multi-tier rollouts involving rolling updates, and can delegate actions to other hosts, interacting with monitoring servers and load balancers along the way.
Playbooks are designed to be human-readable and are developed in a basic text language. There are multiple ways to organize playbooks and the files they include, and we'll offer up some suggestions on that and making the most out of Ansible.
You should look at `Example Playbooks <https://github.com/ansible/ansible-examples>`_ while reading along with the playbook documentation. These illustrate best practices as well as how to put many of the various concepts together.
.. toctree::
:maxdepth: 2
playbooks_templating
playbooks_special_topics
guide_rolling_upgrade
This page has moved to :ref:`working_with_playbooks`.

@ -1,124 +1,7 @@
.. _playbooks_advanced_syntax:
:orphan:
***************
Advanced Syntax
***************
The advanced YAML syntax examples on this page give you more control over the data placed in YAML files used by Ansible. You can find additional information about Python-specific YAML in the official `PyYAML Documentation <https://pyyaml.org/wiki/PyYAMLDocumentation#YAMLtagsandPythontypes>`_.
.. contents::
:local:
.. _unsafe_strings:
Unsafe or raw strings
=====================
When handling values returned by lookup plugins, Ansible uses a data type called ``unsafe`` to block templating. Marking data as unsafe prevents malicious users from abusing Jinja2 templates to execute arbitrary code on target machines. The Ansible implementation ensures that unsafe values are never templated. It is more comprehensive than escaping Jinja2 with ``{% raw %} ... {% endraw %}`` tags.
You can use the same ``unsafe`` data type in variables you define, to prevent templating errors and information disclosure. You can mark values supplied by :ref:`vars_prompts<unsafe_prompts>` as unsafe. You can also use ``unsafe`` in playbooks. The most common use cases include passwords that allow special characters like ``{`` or ``%``, and JSON arguments that look like templates but should not be templated. For example:
.. code-block:: yaml
---
mypassword: !unsafe 234%234{435lkj{{lkjsdf
In a playbook:
.. code-block:: yaml
---
hosts: all
vars:
my_unsafe_variable: !unsafe 'unsafe % value'
tasks:
...
For complex variables such as hashes or arrays, use ``!unsafe`` on the individual elements:
.. code-block:: yaml
---
my_unsafe_array:
- !unsafe 'unsafe element'
- 'safe element'
my_unsafe_hash:
unsafe_key: !unsafe 'unsafe value'
.. _anchors_and_aliases:
YAML anchors and aliases: sharing variable values
=================================================
`YAML anchors and aliases <https://yaml.org/spec/1.2/spec.html#id2765878>`_ help you define, maintain, and use shared variable values in a flexible way.
You define an anchor with ``&``, then refer to it using an alias, denoted with ``*``. Here's an example that sets three values with an anchor, uses two of those values with an alias, and overrides the third value:
.. code-block:: yaml
---
...
vars:
app1:
jvm: &jvm_opts
opts: '-Xms1G -Xmx2G'
port: 1000
path: /usr/lib/app1
app2:
jvm:
<<: *jvm_opts
path: /usr/lib/app2
...
Here, ``app1`` and ``app2`` share the values for ``opts`` and ``port`` using the anchor ``&jvm_opts`` and the alias ``*jvm_opts``.
The value for ``path`` is merged by ``<<`` or `merge operator <https://yaml.org/type/merge.html>`_.
Anchors and aliases also let you share complex sets of variable values, including nested variables. If you have one variable value that includes another variable value, you can define them separately:
.. code-block:: yaml
vars:
webapp_version: 1.0
webapp_custom_name: ToDo_App-1.0
This is inefficient and, at scale, means more maintenance. To incorporate the version value in the name, you can use an anchor in ``app_version`` and an alias in ``custom_name``:
.. code-block:: yaml
vars:
webapp:
version: &my_version 1.0
custom_name:
- "ToDo_App"
- *my_version
Now, you can re-use the value of ``app_version`` within the value of ``custom_name`` and use the output in a template:
.. code-block:: yaml
---
- name: Using values nested inside dictionary
hosts: localhost
vars:
webapp:
version: &my_version 1.0
custom_name:
- "ToDo_App"
- *my_version
tasks:
- name: Using Anchor value
ansible.builtin.debug:
msg: My app is called "{{ webapp.custom_name | join('-') }}".
You've anchored the value of ``version`` with the ``&my_version`` anchor, and re-used it with the ``*my_version`` alias. Anchors and aliases let you access nested values inside dictionaries.
.. seealso::
:ref:`playbooks_variables`
All about variables
:ref:`complex_data_manipulation`
Doing complex data manipulation in Ansible
`User Mailing List <https://groups.google.com/group/ansible-project>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_advanced_syntax`.

@ -1,189 +1,6 @@
.. _playbooks_async:
:orphan:
Asynchronous actions and polling
================================
By default Ansible runs tasks synchronously, holding the connection to the remote node open until the action is completed. This means within a playbook, each task blocks the next task by default, meaning subsequent tasks will not run until the current task completes. This behavior can create challenges. For example, a task may take longer to complete than the SSH session allows for, causing a timeout. Or you may want a long-running process to execute in the background while you perform other tasks concurrently. Asynchronous mode lets you control how long-running tasks execute.
.. contents::
:local:
Asynchronous ad hoc tasks
-------------------------
You can execute long-running operations in the background with :ref:`ad hoc tasks <intro_adhoc>`. For example, to execute ``long_running_operation`` asynchronously in the background, with a timeout (``-B``) of 3600 seconds, and without polling (``-P``):
.. code-block:: bash
$ ansible all -B 3600 -P 0 -a "/usr/bin/long_running_operation --do-stuff"
To check on the job status later, use the ``async_status`` module, passing it the job ID that was returned when you ran the original job in the background:
.. code-block:: bash
$ ansible web1.example.com -m async_status -a "jid=488359678239.2844"
Ansible can also check on the status of your long-running job automatically with polling. In most cases, Ansible will keep the connection to your remote node open between polls. To run for 30 minutes and poll for status every 60 seconds:
.. code-block:: bash
$ ansible all -B 1800 -P 60 -a "/usr/bin/long_running_operation --do-stuff"
Poll mode is smart so all jobs will be started before polling begins on any machine. Be sure to use a high enough ``--forks`` value if you want to get all of your jobs started very quickly. After the time limit (in seconds) runs out (``-B``), the process on the remote nodes will be terminated.
Asynchronous mode is best suited to long-running shell commands or software upgrades. Running the copy module asynchronously, for example, does not do a background file transfer.
Asynchronous playbook tasks
---------------------------
:ref:`Playbooks <working_with_playbooks>` also support asynchronous mode and polling, with a simplified syntax. You can use asynchronous mode in playbooks to avoid connection timeouts or to avoid blocking subsequent tasks. The behavior of asynchronous mode in a playbook depends on the value of `poll`.
Avoid connection timeouts: poll > 0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you want to set a longer timeout limit for a certain task in your playbook, use ``async`` with ``poll`` set to a positive value. Ansible will still block the next task in your playbook, waiting until the async task either completes, fails or times out. However, the task will only time out if it exceeds the timeout limit you set with the ``async`` parameter.
To avoid timeouts on a task, specify its maximum runtime and how frequently you would like to poll for status:
.. code-block:: yaml
---
- hosts: all
remote_user: root
tasks:
- name: Simulate long running op (15 sec), wait for up to 45 sec, poll every 5 sec
ansible.builtin.command: /bin/sleep 15
async: 45
poll: 5
.. note::
The default poll value is set by the :ref:`DEFAULT_POLL_INTERVAL` setting.
There is no default for the async time limit. If you leave off the
'async' keyword, the task runs synchronously, which is Ansible's
default.
.. note::
As of Ansible 2.3, async does not support check mode and will fail the
task when run in check mode. See :ref:`check_mode_dry` on how to
skip a task in check mode.
.. note::
When an async task completes with polling enabled, the temporary async job cache
file (by default in ~/.ansible_async/) is automatically removed.
Run tasks concurrently: poll = 0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you want to run multiple tasks in a playbook concurrently, use ``async`` with ``poll`` set to 0. When you set ``poll: 0``, Ansible starts the task and immediately moves on to the next task without waiting for a result. Each async task runs until it either completes, fails or times out (runs longer than its ``async`` value). The playbook run ends without checking back on async tasks.
To run a playbook task asynchronously:
.. code-block:: yaml
---
- hosts: all
remote_user: root
tasks:
- name: Simulate long running op, allow to run for 45 sec, fire and forget
ansible.builtin.command: /bin/sleep 15
async: 45
poll: 0
.. note::
Do not specify a poll value of 0 with operations that require exclusive locks (such as yum transactions) if you expect to run other commands later in the playbook against those same resources.
.. note::
Using a higher value for ``--forks`` will result in kicking off asynchronous tasks even faster. This also increases the efficiency of polling.
.. note::
When running with ``poll: 0``, Ansible will not automatically cleanup the async job cache file.
You will need to manually clean this up with the :ref:`async_status <async_status_module>` module
with ``mode: cleanup``.
If you need a synchronization point with an async task, you can register it to obtain its job ID and use the :ref:`async_status <async_status_module>` module to observe it in a later task. For example:
.. code-block:: yaml+jinja
- name: Run an async task
ansible.builtin.yum:
name: docker-io
state: present
async: 1000
poll: 0
register: yum_sleeper
- name: Check on an async task
async_status:
jid: "{{ yum_sleeper.ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 100
delay: 10
.. note::
If the value of ``async:`` is not high enough, this will cause the
"check on it later" task to fail because the temporary status file that
the ``async_status:`` is looking for will not have been written or no longer exist
.. note::
Asynchronous playbook tasks always return changed. If the task is using a module
that requires the user to annotate changes with ``changed_when``, ``creates``, and so on,
then those should be added to the following ``async_status`` task.
To run multiple asynchronous tasks while limiting the number of tasks running concurrently:
.. code-block:: yaml+jinja
#####################
# main.yml
#####################
- name: Run items asynchronously in batch of two items
vars:
sleep_durations:
- 1
- 2
- 3
- 4
- 5
durations: "{{ item }}"
include_tasks: execute_batch.yml
loop: "{{ sleep_durations | batch(2) | list }}"
#####################
# execute_batch.yml
#####################
- name: Async sleeping for batched_items
ansible.builtin.command: sleep {{ async_item }}
async: 45
poll: 0
loop: "{{ durations }}"
loop_control:
loop_var: "async_item"
register: async_results
- name: Check sync status
async_status:
jid: "{{ async_result_item.ansible_job_id }}"
loop: "{{ async_results.results }}"
loop_control:
loop_var: "async_result_item"
register: async_poll_results
until: async_poll_results.finished
retries: 30
.. seealso::
:ref:`playbooks_strategies`
Options for controlling playbook execution
:ref:`playbooks_intro`
An introduction to playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_async`.

@ -1,189 +1,7 @@
.. _playbooks_blocks:
:orphan:
******
Blocks
******
Blocks create logical groups of tasks. Blocks also offer ways to handle task errors, similar to exception handling in many programming languages.
.. contents::
:local:
Grouping tasks with blocks
==========================
All tasks in a block inherit directives applied at the block level. Most of what you can apply to a single task (with the exception of loops) can be applied at the block level, so blocks make it much easier to set data or directives common to the tasks. The directive does not affect the block itself, it is only inherited by the tasks enclosed by a block. For example, a `when` statement is applied to the tasks within a block, not to the block itself.
.. code-block:: YAML
:emphasize-lines: 3
:caption: Block example with named tasks inside the block
tasks:
- name: Install, configure, and start Apache
block:
- name: Install httpd and memcached
ansible.builtin.yum:
name:
- httpd
- memcached
state: present
- name: Apply the foo config template
ansible.builtin.template:
src: templates/src.j2
dest: /etc/foo.conf
- name: Start service bar and enable it
ansible.builtin.service:
name: bar
state: started
enabled: True
when: ansible_facts['distribution'] == 'CentOS'
become: true
become_user: root
ignore_errors: yes
In the example above, the 'when' condition will be evaluated before Ansible runs each of the three tasks in the block. All three tasks also inherit the privilege escalation directives, running as the root user. Finally, ``ignore_errors: yes`` ensures that Ansible continues to execute the playbook even if some of the tasks fail.
Names for blocks have been available since Ansible 2.3. We recommend using names in all tasks, within blocks or elsewhere, for better visibility into the tasks being executed when you run the playbook.
.. _block_error_handling:
Handling errors with blocks
===========================
You can control how Ansible responds to task errors using blocks with ``rescue`` and ``always`` sections.
Rescue blocks specify tasks to run when an earlier task in a block fails. This approach is similar to exception handling in many programming languages. Ansible only runs rescue blocks after a task returns a 'failed' state. Bad task definitions and unreachable hosts will not trigger the rescue block.
.. _block_rescue:
.. code-block:: YAML
:emphasize-lines: 3,14
:caption: Block error handling example
tasks:
- name: Handle the error
block:
- name: Print a message
ansible.builtin.debug:
msg: 'I execute normally'
- name: Force a failure
ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I never execute, due to the above task failing, :-('
rescue:
- name: Print when errors
ansible.builtin.debug:
msg: 'I caught an error, can do stuff here to fix it, :-)'
You can also add an ``always`` section to a block. Tasks in the ``always`` section run no matter what the task status of the previous block is.
.. _block_always:
.. code-block:: YAML
:emphasize-lines: 2,13
:caption: Block with always section
- name: Always do X
block:
- name: Print a message
ansible.builtin.debug:
msg: 'I execute normally'
- name: Force a failure
ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I never execute :-('
always:
- name: Always do this
ansible.builtin.debug:
msg: "This always executes, :-)"
Together, these elements offer complex error handling.
.. code-block:: YAML
:emphasize-lines: 2,13,24
:caption: Block with all sections
- name: Attempt and graceful roll back demo
block:
- name: Print a message
ansible.builtin.debug:
msg: 'I execute normally'
- name: Force a failure
ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I never execute, due to the above task failing, :-('
rescue:
- name: Print when errors
ansible.builtin.debug:
msg: 'I caught an error'
- name: Force a failure in middle of recovery! >:-)
ansible.builtin.command: /bin/false
- name: Never print this
ansible.builtin.debug:
msg: 'I also never execute :-('
always:
- name: Always do this
ansible.builtin.debug:
msg: "This always executes"
The tasks in the ``block`` execute normally. If any tasks in the block return ``failed``, the ``rescue`` section executes tasks to recover from the error. The ``always`` section runs regardless of the results of the ``block`` and ``rescue`` sections.
If an error occurs in the block and the rescue task succeeds, Ansible reverts the failed status of the original task for the run and continues to run the play as if the original task had succeeded. The rescued task is considered successful, and does not trigger ``max_fail_percentage`` or ``any_errors_fatal`` configurations. However, Ansible still reports a failure in the playbook statistics.
You can use blocks with ``flush_handlers`` in a rescue task to ensure that all handlers run even if an error occurs:
.. code-block:: YAML
:emphasize-lines: 3,12
:caption: Block run handlers in error handling
tasks:
- name: Attempt and graceful roll back demo
block:
- name: Print a message
ansible.builtin.debug:
msg: 'I execute normally'
changed_when: yes
notify: run me even after an error
- name: Force a failure
ansible.builtin.command: /bin/false
rescue:
- name: Make sure all handlers run
meta: flush_handlers
handlers:
- name: Run me even after an error
ansible.builtin.debug:
msg: 'This handler runs even on error'
.. versionadded:: 2.1
Ansible provides a couple of variables for tasks in the ``rescue`` portion of a block:
ansible_failed_task
The task that returned 'failed' and triggered the rescue. For example, to get the name use ``ansible_failed_task.name``.
ansible_failed_result
The captured return result of the failed task that triggered the rescue. This would equate to having used this var in the ``register`` keyword.
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_reuse_roles`
Playbook organization by roles
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_blocks`.

@ -1,107 +1,7 @@
.. _check_mode_dry:
:orphan:
******************************************
Validating tasks: check mode and diff mode
******************************************
Ansible provides two modes of execution that validate tasks: check mode and diff mode. These modes can be used separately or together. They are useful when you are creating or editing a playbook or role and you want to know what it will do. In check mode, Ansible runs without making any changes on remote systems. Modules that support check mode report the changes they would have made. Modules that do not support check mode report nothing and do nothing. In diff mode, Ansible provides before-and-after comparisons. Modules that support diff mode display detailed information. You can combine check mode and diff mode for detailed validation of your playbook or role.
.. contents::
:local:
Using check mode
================
Check mode is just a simulation. It will not generate output for tasks that use :ref:`conditionals based on registered variables <conditionals_registered_vars>` (results of prior tasks). However, it is great for validating configuration management playbooks that run on one node at a time. To run a playbook in check mode:
.. code-block:: console
ansible-playbook foo.yml --check
.. _forcing_to_run_in_check_mode:
Enforcing or preventing check mode on tasks
-------------------------------------------
.. versionadded:: 2.2
If you want certain tasks to run in check mode always, or never, regardless of whether you run the playbook with or without ``--check``, you can add the ``check_mode`` option to those tasks:
- To force a task to run in check mode, even when the playbook is called without ``--check``, set ``check_mode: yes``.
- To force a task to run in normal mode and make changes to the system, even when the playbook is called with ``--check``, set ``check_mode: no``.
For example:
.. code-block:: yaml
tasks:
- name: This task will always make changes to the system
ansible.builtin.command: /something/to/run --even-in-check-mode
check_mode: no
- name: This task will never make changes to the system
ansible.builtin.lineinfile:
line: "important config"
dest: /path/to/myconfig.conf
state: present
check_mode: yes
register: changes_to_important_config
Running single tasks with ``check_mode: yes`` can be useful for testing Ansible modules, either to test the module itself or to test the conditions under which a module would make changes. You can register variables (see :ref:`playbooks_conditionals`) on these tasks for even more detail on the potential changes.
.. note:: Prior to version 2.2 only the equivalent of ``check_mode: no`` existed. The notation for that was ``always_run: yes``.
Skipping tasks or ignoring errors in check mode
-----------------------------------------------
.. versionadded:: 2.1
If you want to skip a task or ignore errors on a task when you run Ansible in check mode, you can use a boolean magic variable ``ansible_check_mode``, which is set to ``True`` when Ansible runs in check mode. For example:
.. code-block:: yaml
tasks:
- name: This task will be skipped in check mode
ansible.builtin.git:
repo: ssh://git@github.com/mylogin/hello.git
dest: /home/mylogin/hello
when: not ansible_check_mode
- name: This task will ignore errors in check mode
ansible.builtin.git:
repo: ssh://git@github.com/mylogin/hello.git
dest: /home/mylogin/hello
ignore_errors: "{{ ansible_check_mode }}"
.. _diff_mode:
Using diff mode
===============
The ``--diff`` option for ansible-playbook can be used alone or with ``--check``. When you run in diff mode, any module that supports diff mode reports the changes made or, if used with ``--check``, the changes that would have been made. Diff mode is most common in modules that manipulate files (for example, the template module) but other modules might also show 'before and after' information (for example, the user module).
Diff mode produces a large amount of output, so it is best used when checking a single host at a time. For example:
.. code-block:: console
ansible-playbook foo.yml --check --diff --limit foo.example.com
.. versionadded:: 2.4
Enforcing or preventing diff mode on tasks
------------------------------------------
Because the ``--diff`` option can reveal sensitive information, you can disable it for a task by specifying ``diff: no``. For example:
.. code-block:: yaml
tasks:
- name: This task will not report a diff when the file changes
ansible.builtin.template:
src: secret.conf.j2
dest: /etc/secret.conf
owner: root
group: root
mode: '0600'
diff: no
This page has moved to :ref:`check_mode_dry`.

@ -1,551 +1,7 @@
.. _playbooks_conditionals:
:orphan:
************
Conditionals
************
In a playbook, you may want to execute different tasks, or have different goals, depending on the value of a fact (data about the remote system), a variable, or the result of a previous task. You may want the value of some variables to depend on the value of other variables. Or you may want to create additional groups of hosts based on whether the hosts match other criteria. You can do all of these things with conditionals.
Ansible uses Jinja2 :ref:`tests <playbooks_tests>` and :ref:`filters <playbooks_filters>` in conditionals. Ansible supports all the standard tests and filters, and adds some unique ones as well.
.. note::
There are many options to control execution flow in Ansible. You can find more examples of supported conditionals at `<https://jinja.palletsprojects.com/en/latest/templates/#comparisons>`_.
.. contents::
:local:
.. _the_when_statement:
Basic conditionals with ``when``
================================
The simplest conditional statement applies to a single task. Create the task, then add a ``when`` statement that applies a test. The ``when`` clause is a raw Jinja2 expression without double curly braces (see :ref:`group_by_module`). When you run the task or playbook, Ansible evaluates the test for all hosts. On any host where the test passes (returns a value of True), Ansible runs that task. For example, if you are installing mysql on multiple machines, some of which have SELinux enabled, you might have a task to configure SELinux to allow mysql to run. You would only want that task to run on machines that have SELinux enabled:
.. code-block:: yaml
tasks:
- name: Configure SELinux to start mysql on any port
ansible.posix.seboolean:
name: mysql_connect_any
state: true
persistent: yes
when: ansible_selinux.status == "enabled"
# all variables can be used directly in conditionals without double curly braces
Conditionals based on ansible_facts
-----------------------------------
Often you want to execute or skip a task based on facts. Facts are attributes of individual hosts, including IP address, operating system, the status of a filesystem, and many more. With conditionals based on facts:
- You can install a certain package only when the operating system is a particular version.
- You can skip configuring a firewall on hosts with internal IP addresses.
- You can perform cleanup tasks only when a filesystem is getting full.
See :ref:`commonly_used_facts` for a list of facts that frequently appear in conditional statements. Not all facts exist for all hosts. For example, the 'lsb_major_release' fact used in an example below only exists when the lsb_release package is installed on the target host. To see what facts are available on your systems, add a debug task to your playbook:
.. code-block:: yaml
- name: Show facts available on the system
ansible.builtin.debug:
var: ansible_facts
Here is a sample conditional based on a fact:
.. code-block:: yaml
tasks:
- name: Shut down Debian flavored systems
ansible.builtin.command: /sbin/shutdown -t now
when: ansible_facts['os_family'] == "Debian"
If you have multiple conditions, you can group them with parentheses:
.. code-block:: yaml
tasks:
- name: Shut down CentOS 6 and Debian 7 systems
ansible.builtin.command: /sbin/shutdown -t now
when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
(ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")
You can use `logical operators <https://jinja.palletsprojects.com/en/latest/templates/#logic>`_ to combine conditions. When you have multiple conditions that all need to be true (that is, a logical ``and``), you can specify them as a list:
.. code-block:: yaml
tasks:
- name: Shut down CentOS 6 systems
ansible.builtin.command: /sbin/shutdown -t now
when:
- ansible_facts['distribution'] == "CentOS"
- ansible_facts['distribution_major_version'] == "6"
If a fact or variable is a string, and you need to run a mathematical comparison on it, use a filter to ensure that Ansible reads the value as an integer:
.. code-block:: yaml
tasks:
- ansible.builtin.shell: echo "only on Red Hat 6, derivatives, and later"
when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release'] | int >= 6
.. _conditionals_registered_vars:
Conditions based on registered variables
----------------------------------------
Often in a playbook you want to execute or skip a task based on the outcome of an earlier task. For example, you might want to configure a service after it is upgraded by an earlier task. To create a conditional based on a registered variable:
#. Register the outcome of the earlier task as a variable.
#. Create a conditional test based on the registered variable.
You create the name of the registered variable using the ``register`` keyword. A registered variable always contains the status of the task that created it as well as any output that task generated. You can use registered variables in templates and action lines as well as in conditional ``when`` statements. You can access the string contents of the registered variable using ``variable.stdout``. For example:
.. code-block:: yaml
- name: Test play
hosts: all
tasks:
- name: Register a variable
ansible.builtin.shell: cat /etc/motd
register: motd_contents
- name: Use the variable in conditional statement
ansible.builtin.shell: echo "motd contains the word hi"
when: motd_contents.stdout.find('hi') != -1
You can use registered results in the loop of a task if the variable is a list. If the variable is not a list, you can convert it into a list, with either ``stdout_lines`` or with ``variable.stdout.split()``. You can also split the lines by other fields:
.. code-block:: yaml
- name: Registered variable usage as a loop list
hosts: all
tasks:
- name: Retrieve the list of home directories
ansible.builtin.command: ls /home
register: home_dirs
- name: Add home dirs to the backup spooler
ansible.builtin.file:
path: /mnt/bkspool/{{ item }}
src: /home/{{ item }}
state: link
loop: "{{ home_dirs.stdout_lines }}"
# same as loop: "{{ home_dirs.stdout.split() }}"
The string content of a registered variable can be empty. If you want to run another task only on hosts where the stdout of your registered variable is empty, check the registered variable's string contents for emptiness:
.. code-block:: yaml
- name: check registered variable for emptiness
hosts: all
tasks:
- name: List contents of directory
ansible.builtin.command: ls mydir
register: contents
- name: Check contents for emptiness
ansible.builtin.debug:
msg: "Directory is empty"
when: contents.stdout == ""
Ansible always registers something in a registered variable for every host, even on hosts where a task fails or Ansible skips a task because a condition is not met. To run a follow-up task on these hosts, query the registered variable for ``is skipped`` (not for "undefined" or "default"). See :ref:`registered_variables` for more information. Here are sample conditionals based on the success or failure of a task. Remember to ignore errors if you want Ansible to continue executing on a host when a failure occurs:
.. code-block:: yaml
tasks:
- name: Register a variable, ignore errors and continue
ansible.builtin.command: /bin/false
register: result
ignore_errors: true
- name: Run only if the task that registered the "result" variable fails
ansible.builtin.command: /bin/something
when: result is failed
- name: Run only if the task that registered the "result" variable succeeds
ansible.builtin.command: /bin/something_else
when: result is succeeded
- name: Run only if the task that registered the "result" variable is skipped
ansible.builtin.command: /bin/still/something_else
when: result is skipped
.. note:: Older versions of Ansible used ``success`` and ``fail``, but ``succeeded`` and ``failed`` use the correct tense. All of these options are now valid.
Conditionals based on variables
-------------------------------
You can also create conditionals based on variables defined in the playbooks or inventory. Because conditionals require boolean input (a test must evaluate as True to trigger the condition), you must apply the ``| bool`` filter to non boolean variables, such as string variables with content like 'yes', 'on', '1', or 'true'. You can define variables like this:
.. code-block:: yaml
vars:
epic: true
monumental: "yes"
With the variables above, Ansible would run one of these tasks and skip the other:
.. code-block:: yaml
tasks:
- name: Run the command if "epic" or "monumental" is true
ansible.builtin.shell: echo "This certainly is epic!"
when: epic or monumental | bool
- name: Run the command if "epic" is false
ansible.builtin.shell: echo "This certainly isn't epic!"
when: not epic
If a required variable has not been set, you can skip or fail using Jinja2's `defined` test. For example:
.. code-block:: yaml
tasks:
- name: Run the command if "foo" is defined
ansible.builtin.shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
when: foo is defined
- name: Fail if "bar" is undefined
ansible.builtin.fail: msg="Bailing out. This play requires 'bar'"
when: bar is undefined
This is especially useful in combination with the conditional import of vars files (see below).
As the examples show, you do not need to use `{{ }}` to use variables inside conditionals, as these are already implied.
.. _loops_and_conditionals:
Using conditionals in loops
---------------------------
If you combine a ``when`` statement with a :ref:`loop <playbooks_loops>`, Ansible processes the condition separately for each item. This is by design, so you can execute the task on some items in the loop and skip it on other items. For example:
.. code-block:: yaml
tasks:
- name: Run with items greater than 5
ansible.builtin.command: echo {{ item }}
loop: [ 0, 2, 4, 6, 8, 10 ]
when: item > 5
If you need to skip the whole task when the loop variable is undefined, use the `|default` filter to provide an empty iterator. For example, when looping over a list:
.. code-block:: yaml
- name: Skip the whole task when a loop variable is undefined
ansible.builtin.command: echo {{ item }}
loop: "{{ mylist|default([]) }}"
when: item > 5
You can do the same thing when looping over a dict:
.. code-block:: yaml
- name: The same as above using a dict
ansible.builtin.command: echo {{ item.key }}
loop: "{{ query('dict', mydict|default({})) }}"
when: item.value > 5
.. _loading_in_custom_facts:
Loading custom facts
--------------------
You can provide your own facts, as described in :ref:`developing_modules`. To run them, just make a call to your own custom fact gathering module at the top of your list of tasks, and variables returned there will be accessible to future tasks:
.. code-block:: yaml
tasks:
- name: Gather site specific fact data
action: site_facts
- name: Use a custom fact
ansible.builtin.command: /usr/bin/thingy
when: my_custom_fact_just_retrieved_from_the_remote_system == '1234'
.. _when_with_reuse:
Conditionals with re-use
------------------------
You can use conditionals with re-usable tasks files, playbooks, or roles. Ansible executes these conditional statements differently for dynamic re-use (includes) and for static re-use (imports). See :ref:`playbooks_reuse` for more information on re-use in Ansible.
.. _conditional_imports:
Conditionals with imports
^^^^^^^^^^^^^^^^^^^^^^^^^
When you add a conditional to an import statement, Ansible applies the condition to all tasks within the imported file. This behavior is the equivalent of :ref:`tag_inheritance`. Ansible applies the condition to every task, and evaluates each task separately. For example, if you want to define and then display a variable that was not previously defined, you might have a playbook called ``main.yml`` and a tasks file called ``other_tasks.yml``:
.. code-block:: yaml
# all tasks within an imported file inherit the condition from the import statement
# main.yml
- hosts: all
tasks:
- import_tasks: other_tasks.yml # note "import"
when: x is not defined
# other_tasks.yml
- name: Set a variable
ansible.builtin.set_fact:
x: foo
- name: Print a variable
ansible.builtin.debug:
var: x
Ansible expands this at execution time to the equivalent of:
.. code-block:: yaml
- name: Set a variable if not defined
ansible.builtin.set_fact:
x: foo
when: x is not defined
# this task sets a value for x
- name: Do the task if "x" is not defined
ansible.builtin.debug:
var: x
when: x is not defined
# Ansible skips this task, because x is now defined
If ``x`` is initially defined, both tasks are skipped as intended. But if ``x`` is initially undefined, the debug task will be skipped since the conditional is evaluated for every imported task. The conditional will evaluate to ``true`` for the ``set_fact`` task, which will define the variable and cause the ``debug`` conditional to evaluate to ``false``.
If this is not the behavior you want, use an ``include_*`` statement to apply a condition only to that statement itself.
.. code-block:: yaml
# using a conditional on include_* only applies to the include task itself
# main.yml
- hosts: all
tasks:
- include_tasks: other_tasks.yml # note "include"
when: x is not defined
Now if ``x`` is initially undefined, the debug task will not be skipped because the conditional is evaluated at the time of the include and does not apply to the individual tasks.
You can apply conditions to ``import_playbook`` as well as to the other ``import_*`` statements. When you use this approach, Ansible returns a 'skipped' message for every task on every host that does not match the criteria, creating repetitive output. In many cases the :ref:`group_by module <group_by_module>` can be a more streamlined way to accomplish the same objective; see :ref:`os_variance`.
.. _conditional_includes:
Conditionals with includes
^^^^^^^^^^^^^^^^^^^^^^^^^^
When you use a conditional on an ``include_*`` statement, the condition is applied only to the include task itself and not to any other tasks within the included file(s). To contrast with the example used for conditionals on imports above, look at the same playbook and tasks file, but using an include instead of an import:
.. code-block:: yaml
# Includes let you re-use a file to define a variable when it is not already defined
# main.yml
- include_tasks: other_tasks.yml
when: x is not defined
# other_tasks.yml
- name: Set a variable
ansible.builtin.set_fact:
x: foo
- name: Print a variable
ansible.builtin.debug:
var: x
Ansible expands this at execution time to the equivalent of:
.. code-block:: yaml
# main.yml
- include_tasks: other_tasks.yml
when: x is not defined
# if condition is met, Ansible includes other_tasks.yml
# other_tasks.yml
- name: Set a variable
ansible.builtin.set_fact:
x: foo
# no condition applied to this task, Ansible sets the value of x to foo
- name: Print a variable
ansible.builtin.debug:
var: x
# no condition applied to this task, Ansible prints the debug statement
By using ``include_tasks`` instead of ``import_tasks``, both tasks from ``other_tasks.yml`` will be executed as expected. For more information on the differences between ``include`` v ``import`` see :ref:`playbooks_reuse`.
Conditionals with roles
^^^^^^^^^^^^^^^^^^^^^^^
There are three ways to apply conditions to roles:
- Add the same condition or conditions to all tasks in the role by placing your ``when`` statement under the ``roles`` keyword. See the example in this section.
- Add the same condition or conditions to all tasks in the role by placing your ``when`` statement on a static ``import_role`` in your playbook.
- Add a condition or conditions to individual tasks or blocks within the role itself. This is the only approach that allows you to select or skip some tasks within the role based on your ``when`` statement. To select or skip tasks within the role, you must have conditions set on individual tasks or blocks, use the dynamic ``include_role`` in your playbook, and add the condition or conditions to the include. When you use this approach, Ansible applies the condition to the include itself plus any tasks in the role that also have that ``when`` statement.
When you incorporate a role in your playbook statically with the ``roles`` keyword, Ansible adds the conditions you define to all the tasks in the role. For example:
.. code-block:: yaml
- hosts: webservers
roles:
- role: debian_stock_config
when: ansible_facts['os_family'] == 'Debian'
.. _conditional_variable_and_files:
Selecting variables, files, or templates based on facts
-------------------------------------------------------
Sometimes the facts about a host determine the values you want to use for certain variables or even the file or template you want to select for that host. For example, the names of packages are different on CentOS and on Debian. The configuration files for common services are also different on different OS flavors and versions. To load different variables file, templates, or other files based on a fact about the hosts:
1) name your vars files, templates, or files to match the Ansible fact that differentiates them
2) select the correct vars file, template, or file for each host with a variable based on that Ansible fact
Ansible separates variables from tasks, keeping your playbooks from turning into arbitrary code with nested conditionals. This approach results in more streamlined and auditable configuration rules because there are fewer decision points to track.
Selecting variables files based on facts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can create a playbook that works on multiple platforms and OS versions with a minimum of syntax by placing your variable values in vars files and conditionally importing them. If you want to install Apache on some CentOS and some Debian servers, create variables files with YAML keys and values. For example:
.. code-block:: yaml
---
# for vars/RedHat.yml
apache: httpd
somethingelse: 42
Then import those variables files based on the facts you gather on the hosts in your playbook:
.. code-block:: yaml
---
- hosts: webservers
remote_user: root
vars_files:
- "vars/common.yml"
- [ "vars/{{ ansible_facts['os_family'] }}.yml", "vars/os_defaults.yml" ]
tasks:
- name: Make sure apache is started
ansible.builtin.service:
name: '{{ apache }}'
state: started
Ansible gathers facts on the hosts in the webservers group, then interpolates the variable "ansible_facts['os_family']" into a list of filenames. If you have hosts with Red Hat operating systems (CentOS, for example), Ansible looks for 'vars/RedHat.yml'. If that file does not exist, Ansible attempts to load 'vars/os_defaults.yml'. For Debian hosts, Ansible first looks for 'vars/Debian.yml', before falling back on 'vars/os_defaults.yml'. If no files in the list are found, Ansible raises an error.
Selecting files and templates based on facts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
You can use the same approach when different OS flavors or versions require different configuration files or templates. Select the appropriate file or template based on the variables assigned to each host. This approach is often much cleaner than putting a lot of conditionals into a single template to cover multiple OS or package versions.
For example, you can template out a configuration file that is very different between, say, CentOS and Debian:
.. code-block:: yaml
- name: Template a file
ansible.builtin.template:
src: "{{ item }}"
dest: /etc/myapp/foo.conf
loop: "{{ query('first_found', { 'files': myfiles, 'paths': mypaths}) }}"
vars:
myfiles:
- "{{ ansible_facts['distribution'] }}.conf"
- default.conf
mypaths: ['search_location_one/somedir/', '/opt/other_location/somedir/']
.. _commonly_used_facts:
Commonly-used facts
===================
The following Ansible facts are frequently used in conditionals.
.. _ansible_distribution:
ansible_facts['distribution']
-----------------------------
Possible values (sample, not complete list):
.. code-block:: text
Alpine
Altlinux
Amazon
Archlinux
ClearLinux
Coreos
CentOS
Debian
Fedora
Gentoo
Mandriva
NA
OpenWrt
OracleLinux
RedHat
Slackware
SLES
SMGL
SUSE
Ubuntu
VMwareESX
.. See `OSDIST_LIST`
.. _ansible_distribution_major_version:
ansible_facts['distribution_major_version']
-------------------------------------------
The major version of the operating system. For example, the value is `16` for Ubuntu 16.04.
.. _ansible_os_family:
ansible_facts['os_family']
--------------------------
Possible values (sample, not complete list):
.. code-block:: text
AIX
Alpine
Altlinux
Archlinux
Darwin
Debian
FreeBSD
Gentoo
HP-UX
Mandrake
RedHat
SGML
Slackware
Solaris
Suse
Windows
.. Ansible checks `OS_FAMILY_MAP`; if there's no match, it returns the value of `platform.system()`.
.. seealso::
:ref:`working_with_playbooks`
An introduction to playbooks
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`tips_and_tricks`
Tips and tricks for playbooks
:ref:`playbooks_variables`
All about variables
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_conditionals`.

@ -1,342 +1,7 @@
.. _playbook_debugger:
:orphan:
***************
Debugging tasks
***************
Ansible offers a task debugger so you can fix errors during execution instead of editing your playbook and running it again to see if your change worked. You have access to all of the features of the debugger in the context of the task. You can check or set the value of variables, update module arguments, and re-run the task with the new variables and arguments. The debugger lets you resolve the cause of the failure and continue with playbook execution.
.. contents::
:local:
Enabling the debugger
=====================
The debugger is not enabled by default. If you want to invoke the debugger during playbook execution, you must enable it first.
Use one of these three methods to enable the debugger:
* with the debugger keyword
* in configuration or an environment variable, or
* as a strategy
Enabling the debugger with the ``debugger`` keyword
---------------------------------------------------
.. versionadded:: 2.5
You can use the ``debugger`` keyword to enable (or disable) the debugger for a specific play, role, block, or task. This option is especially useful when developing or extending playbooks, plays, and roles. You can enable the debugger on new or updated tasks. If they fail, you can fix the errors efficiently. The ``debugger`` keyword accepts five values:
.. table::
:class: documentation-table
========================= ======================================================
Value Result
========================= ======================================================
always Always invoke the debugger, regardless of the outcome
never Never invoke the debugger, regardless of the outcome
on_failed Only invoke the debugger if a task fails
on_unreachable Only invoke the debugger if a host is unreachable
on_skipped Only invoke the debugger if the task is skipped
========================= ======================================================
When you use the ``debugger`` keyword, the value you specify overrides any global configuration to enable or disable the debugger. If you define ``debugger`` at multiple levels, such as in a role and in a task, Ansible honors the most granular definition. The definition at the play or role level applies to all blocks and tasks within that play or role, unless they specify a different value. The definition at the block level overrides the definition at the play or role level, and applies to all tasks within that block, unless they specify a different value. The definition at the task level always applies to the task; it overrides the definitions at the block, play, or role level.
Examples of using the ``debugger`` keyword
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Example of setting the ``debugger`` keyword on a task:
.. code-block:: yaml
- name: Execute a command
ansible.builtin.command: "false"
debugger: on_failed
Example of setting the ``debugger`` keyword on a play:
.. code-block:: yaml
- name: My play
hosts: all
debugger: on_skipped
tasks:
- name: Execute a command
ansible.builtin.command: "true"
when: False
Example of setting the ``debugger`` keyword at multiple levels:
.. code-block:: yaml
- name: Play
hosts: all
debugger: never
tasks:
- name: Execute a command
ansible.builtin.command: "false"
debugger: on_failed
In this example, the debugger is set to ``never`` at the play level and to ``on_failed`` at the task level. If the task fails, Ansible invokes the debugger, because the definition on the task overrides the definition on its parent play.
Enabling the debugger in configuration or an environment variable
-----------------------------------------------------------------
.. versionadded:: 2.5
You can enable the task debugger globally with a setting in ansible.cfg or with an environment variable. The only options are ``True`` or ``False``. If you set the configuration option or environment variable to ``True``, Ansible runs the debugger on failed tasks by default.
To enable the task debugger from ansible.cfg, add this setting to the defaults section:
.. code-block:: yaml
[defaults]
enable_task_debugger = True
To enable the task debugger with an environment variable, pass the variable when you run your playbook:
.. code-block:: shell
ANSIBLE_ENABLE_TASK_DEBUGGER=True ansible-playbook -i hosts site.yml
When you enable the debugger globally, every failed task invokes the debugger, unless the role, play, block, or task explicitly disables the debugger. If you need more granular control over what conditions trigger the debugger, use the ``debugger`` keyword.
Enabling the debugger as a strategy
-----------------------------------
If you are running legacy playbooks or roles, you may see the debugger enabled as a :ref:`strategy <strategy_plugins>`. You can do this at the play level, in ansible.cfg, or with the environment variable ``ANSIBLE_STRATEGY=debug``. For example:
.. code-block:: yaml
- hosts: test
strategy: debug
tasks:
...
Or in ansible.cfg:
.. code-block:: ini
[defaults]
strategy = debug
.. note::
This backwards-compatible method, which matches Ansible versions before 2.5, may be removed in a future release.
Resolving errors in the debugger
================================
After Ansible invokes the debugger, you can use the seven :ref:`debugger commands <available_commands>` to resolve the error that Ansible encountered. Consider this example playbook, which defines the ``var1`` variable but uses the undefined ``wrong_var`` variable in a task by mistake.
.. code-block:: yaml
- hosts: test
debugger: on_failed
gather_facts: no
vars:
var1: value1
tasks:
- name: Use a wrong variable
ansible.builtin.ping: data={{ wrong_var }}
If you run this playbook, Ansible invokes the debugger when the task fails. From the debug prompt, you can change the module arguments or the variables and run the task again.
.. code-block:: ansible-output
PLAY ***************************************************************************
TASK [wrong variable] **********************************************************
fatal: [192.0.2.10]: FAILED! => {"failed": true, "msg": "ERROR! 'wrong_var' is undefined"}
Debugger invoked
[192.0.2.10] TASK: wrong variable (debug)> p result._result
{'failed': True,
'msg': 'The task includes an option with an undefined variable. The error '
"was: 'wrong_var' is undefined\n"
'\n'
'The error appears to have been in '
"'playbooks/debugger.yml': line 7, "
'column 7, but may\n'
'be elsewhere in the file depending on the exact syntax problem.\n'
'\n'
'The offending line appears to be:\n'
'\n'
' tasks:\n'
' - name: wrong variable\n'
' ^ here\n'}
[192.0.2.10] TASK: wrong variable (debug)> p task.args
{u'data': u'{{ wrong_var }}'}
[192.0.2.10] TASK: wrong variable (debug)> task.args['data'] = '{{ var1 }}'
[192.0.2.10] TASK: wrong variable (debug)> p task.args
{u'data': '{{ var1 }}'}
[192.0.2.10] TASK: wrong variable (debug)> redo
ok: [192.0.2.10]
PLAY RECAP *********************************************************************
192.0.2.10 : ok=1 changed=0 unreachable=0 failed=0
Changing the task arguments in the debugger to use ``var1`` instead of ``wrong_var`` makes the task run successfully.
.. _available_commands:
Available debug commands
========================
You can use these seven commands at the debug prompt:
.. table::
:class: documentation-table
========================== ============ =========================================================
Command Shortcut Action
========================== ============ =========================================================
print p Print information about the task
task.args[*key*] = *value* no shortcut Update module arguments
task_vars[*key*] = *value* no shortcut Update task variables (you must ``update_task`` next)
update_task u Recreate a task with updated task variables
redo r Run the task again
continue c Continue executing, starting with the next task
quit q Quit the debugger
========================== ============ =========================================================
For more details, see the individual descriptions and examples below.
.. _pprint_command:
Print command
-------------
``print *task/task.args/task_vars/host/result*`` prints information about the task.
.. code-block:: ansible-output
[192.0.2.10] TASK: install package (debug)> p task
TASK: install package
[192.0.2.10] TASK: install package (debug)> p task.args
{u'name': u'{{ pkg_name }}'}
[192.0.2.10] TASK: install package (debug)> p task_vars
{u'ansible_all_ipv4_addresses': [u'192.0.2.10'],
u'ansible_architecture': u'x86_64',
...
}
[192.0.2.10] TASK: install package (debug)> p task_vars['pkg_name']
u'bash'
[192.0.2.10] TASK: install package (debug)> p host
192.0.2.10
[192.0.2.10] TASK: install package (debug)> p result._result
{'_ansible_no_log': False,
'changed': False,
u'failed': True,
...
u'msg': u"No package matching 'not_exist' is available"}
.. _update_args_command:
Update args command
-------------------
``task.args[*key*] = *value*`` updates a module argument. This sample playbook has an invalid package name.
.. code-block:: yaml
- hosts: test
strategy: debug
gather_facts: yes
vars:
pkg_name: not_exist
tasks:
- name: Install a package
ansible.builtin.apt: name={{ pkg_name }}
When you run the playbook, the invalid package name triggers an error, and Ansible invokes the debugger. You can fix the package name by viewing, then updating the module argument.
.. code-block:: ansible-output
[192.0.2.10] TASK: install package (debug)> p task.args
{u'name': u'{{ pkg_name }}'}
[192.0.2.10] TASK: install package (debug)> task.args['name'] = 'bash'
[192.0.2.10] TASK: install package (debug)> p task.args
{u'name': 'bash'}
[192.0.2.10] TASK: install package (debug)> redo
After you update the module argument, use ``redo`` to run the task again with the new args.
.. _update_vars_command:
Update vars command
-------------------
``task_vars[*key*] = *value*`` updates the ``task_vars``. You could fix the playbook above by viewing, then updating the task variables instead of the module args.
.. code-block:: ansible-output
[192.0.2.10] TASK: install package (debug)> p task_vars['pkg_name']
u'not_exist'
[192.0.2.10] TASK: install package (debug)> task_vars['pkg_name'] = 'bash'
[192.0.2.10] TASK: install package (debug)> p task_vars['pkg_name']
'bash'
[192.0.2.10] TASK: install package (debug)> update_task
[192.0.2.10] TASK: install package (debug)> redo
After you update the task variables, you must use ``update_task`` to load the new variables before using ``redo`` to run the task again.
.. note::
In 2.5 this was updated from ``vars`` to ``task_vars`` to avoid conflicts with the ``vars()`` python function.
.. _update_task_command:
Update task command
-------------------
.. versionadded:: 2.8
``u`` or ``update_task`` recreates the task from the original task data structure and templates with updated task variables. See the entry :ref:`update_vars_command` for an example of use.
.. _redo_command:
Redo command
------------
``r`` or ``redo`` runs the task again.
.. _continue_command:
Continue command
----------------
``c`` or ``continue`` continues executing, starting with the next task.
.. _quit_command:
Quit command
------------
``q`` or ``quit`` quits the debugger. The playbook execution is aborted.
How the debugger interacts with the free strategy
=================================================
With the default ``linear`` strategy enabled, Ansible halts execution while the debugger is active, and runs the debugged task immediately after you enter the ``redo`` command. With the ``free`` strategy enabled, however, Ansible does not wait for all hosts, and may queue later tasks on one host before a task fails on another host. With the ``free`` strategy, Ansible does not queue or execute any tasks while the debugger is active. However, all queued tasks remain in the queue and run as soon as you exit the debugger. If you use ``redo`` to reschedule a task from the debugger, other queued tasks may execute before your rescheduled task. For more information about strategies, see :ref:`playbooks_strategies`.
.. seealso::
:ref:`playbooks_start_and_step`
Running playbooks while debugging or testing
:ref:`playbooks_intro`
An introduction to playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbook_debugger`.

@ -1,174 +1,6 @@
.. _playbooks_delegation:
:orphan:
Controlling where tasks run: delegation and local actions
=========================================================
By default Ansible gathers facts and executes all tasks on the machines that match the ``hosts`` line of your playbook. This page shows you how to delegate tasks to a different machine or group, delegate facts to specific machines or groups, or run an entire playbook locally. Using these approaches, you can manage inter-related environments precisely and efficiently. For example, when updating your webservers, you might need to remove them from a load-balanced pool temporarily. You cannot perform this task on the webservers themselves. By delegating the task to localhost, you keep all the tasks within the same play.
.. contents::
:local:
Tasks that cannot be delegated
------------------------------
Some tasks always execute on the controller. These tasks, including ``include``, ``add_host``, and ``debug``, cannot be delegated.
.. _delegation:
Delegating tasks
----------------
If you want to perform a task on one host with reference to other hosts, use the ``delegate_to`` keyword on a task. This is ideal for managing nodes in a load balanced pool or for controlling outage windows. You can use delegation with the :ref:`serial <rolling_update_batch_size>` keyword to control the number of hosts executing at one time:
.. code-block:: yaml
---
- hosts: webservers
serial: 5
tasks:
- name: Take out of load balancer pool
ansible.builtin.command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
- name: Actual steps would go here
ansible.builtin.yum:
name: acme-web-stack
state: latest
- name: Add back to load balancer pool
ansible.builtin.command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1
The first and third tasks in this play run on 127.0.0.1, which is the machine running Ansible. There is also a shorthand syntax that you can use on a per-task basis: ``local_action``. Here is the same playbook as above, but using the shorthand syntax for delegating to 127.0.0.1:
.. code-block:: yaml
---
# ...
tasks:
- name: Take out of load balancer pool
local_action: ansible.builtin.command /usr/bin/take_out_of_pool {{ inventory_hostname }}
# ...
- name: Add back to load balancer pool
local_action: ansible.builtin.command /usr/bin/add_back_to_pool {{ inventory_hostname }}
You can use a local action to call 'rsync' to recursively copy files to the managed servers:
.. code-block:: yaml
---
# ...
tasks:
- name: Recursively copy files from management server to target
local_action: ansible.builtin.command rsync -a /path/to/files {{ inventory_hostname }}:/path/to/target/
Note that you must have passphrase-less SSH keys or an ssh-agent configured for this to work, otherwise rsync asks for a passphrase.
To specify more arguments, use the following syntax:
.. code-block:: yaml
---
# ...
tasks:
- name: Send summary mail
local_action:
module: community.general.mail
subject: "Summary Mail"
to: "{{ mail_recipient }}"
body: "{{ mail_body }}"
run_once: True
.. note::
- The `ansible_host` variable and other connection variables, if present, reflects information about the host a task is delegated to, not the inventory_hostname.
.. warning::
Although you can ``delegate_to`` a host that does not exist in inventory (by adding IP address, DNS name or whatever requirement the connection plugin has), doing so does not add the host to your inventory and might cause issues. Hosts delegated to in this way do not inherit variables from the "all" group', so variables like connection user and key are missing. If you must ``delegate_to`` a non-inventory host, use the :ref:`add host module <add_host_module>`.
.. _delegate_parallel:
Delegation and parallel execution
---------------------------------
By default Ansible tasks are executed in parallel. Delegating a task does not change this and does not handle concurrency issues (multiple forks writing to the same file).
Most commonly, users are affected by this when updating a single file on a single delegated to host for all hosts (using the ``copy``, ``template``, or ``lineinfile`` modules, for example). They will still operate in parallel forks (default 5) and overwrite each other.
This can be handled in several ways:
.. code-block:: yaml
- name: "handle concurrency with a loop on the hosts with `run_once: true`"
lineinfile: "<options here>"
run_once: true
loop: '{{ ansible_play_hosts_all }}'
By using an intermediate play with `serial: 1` or using `throttle: 1` at task level, for more detail see :ref:`playbooks_strategies`
.. _delegate_facts:
Delegating facts
----------------
Delegating Ansible tasks is like delegating tasks in the real world - your groceries belong to you, even if someone else delivers them to your home. Similarly, any facts gathered by a delegated task are assigned by default to the `inventory_hostname` (the current host), not to the host which produced the facts (the delegated to host). To assign gathered facts to the delegated host instead of the current host, set ``delegate_facts`` to ``true``:
.. code-block:: yaml
---
- hosts: app_servers
tasks:
- name: Gather facts from db servers
ansible.builtin.setup:
delegate_to: "{{ item }}"
delegate_facts: true
loop: "{{ groups['dbservers'] }}"
This task gathers facts for the machines in the dbservers group and assigns the facts to those machines, even though the play targets the app_servers group. This way you can lookup `hostvars['dbhost1']['ansible_default_ipv4']['address']` even though dbservers were not part of the play, or left out by using `--limit`.
.. _local_playbooks:
Local playbooks
---------------
It may be useful to use a playbook locally on a remote host, rather than by connecting over SSH. This can be useful for assuring the configuration of a system by putting a playbook in a crontab. This may also be used
to run a playbook inside an OS installer, such as an Anaconda kickstart.
To run an entire playbook locally, just set the ``hosts:`` line to ``hosts: 127.0.0.1`` and then run the playbook like so:
.. code-block:: shell
ansible-playbook playbook.yml --connection=local
Alternatively, a local connection can be used in a single playbook play, even if other plays in the playbook
use the default remote connection type:
.. code-block:: yaml
---
- hosts: 127.0.0.1
connection: local
.. note::
If you set the connection to local and there is no ansible_python_interpreter set, modules will run under /usr/bin/python and not
under {{ ansible_playbook_python }}. Be sure to set ansible_python_interpreter: "{{ ansible_playbook_python }}" in
host_vars/localhost.yml, for example. You can avoid this issue by using ``local_action`` or ``delegate_to: localhost`` instead.
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_strategies`
More ways to control how and where Ansible executes
`Ansible Examples on GitHub <https://github.com/ansible/ansible-examples>`_
Many examples of full-stack deployments
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_delegation`.

@ -1,153 +1,6 @@
.. _playbooks_environment:
:orphan:
Setting the remote environment
==============================
.. versionadded:: 1.1
You can use the ``environment`` keyword at the play, block, or task level to set an environment variable for an action on a remote host. With this keyword, you can enable using a proxy for a task that does http requests, set the required environment variables for language-specific version managers, and more.
When you set a value with ``environment:`` at the play or block level, it is available only to tasks within the play or block that are executed by the same user. The ``environment:`` keyword does not affect Ansible itself, Ansible configuration settings, the environment for other users, or the execution of other plugins like lookups and filters. Variables set with ``environment:`` do not automatically become Ansible facts, even when you set them at the play level. You must include an explicit ``gather_facts`` task in your playbook and set the ``environment`` keyword on that task to turn these values into Ansible facts.
.. contents::
:local:
Setting the remote environment in a task
----------------------------------------
You can set the environment directly at the task level.
.. code-block:: yaml
- hosts: all
remote_user: root
tasks:
- name: Install cobbler
ansible.builtin.package:
name: cobbler
state: present
environment:
http_proxy: http://proxy.example.com:8080
You can re-use environment settings by defining them as variables in your play and accessing them in a task as you would access any stored Ansible variable.
.. code-block:: yaml
- hosts: all
remote_user: root
# create a variable named "proxy_env" that is a dictionary
vars:
proxy_env:
http_proxy: http://proxy.example.com:8080
tasks:
- name: Install cobbler
ansible.builtin.package:
name: cobbler
state: present
environment: "{{ proxy_env }}"
You can store environment settings for re-use in multiple playbooks by defining them in a group_vars file.
.. code-block:: yaml
---
# file: group_vars/boston
ntp_server: ntp.bos.example.com
backup: bak.bos.example.com
proxy_env:
http_proxy: http://proxy.bos.example.com:8080
https_proxy: http://proxy.bos.example.com:8080
You can set the remote environment at the play level.
.. code-block:: yaml
- hosts: testing
roles:
- php
- nginx
environment:
http_proxy: http://proxy.example.com:8080
These examples show proxy settings, but you can provide any number of settings this way.
Working with language-specific version managers
===============================================
Some language-specific version managers (such as rbenv and nvm) require you to set environment variables while these tools are in use. When using these tools manually, you usually source some environment variables from a script or from lines added to your shell configuration file. In Ansible, you can do this with the environment keyword at the play level.
.. code-block:: yaml+jinja
---
### A playbook demonstrating a common npm workflow:
# - Check for package.json in the application directory
# - If package.json exists:
# * Run npm prune
# * Run npm install
- hosts: application
become: false
vars:
node_app_dir: /var/local/my_node_app
environment:
NVM_DIR: /var/local/nvm
PATH: /var/local/nvm/versions/node/v4.2.1/bin:{{ ansible_env.PATH }}
tasks:
- name: Check for package.json
ansible.builtin.stat:
path: '{{ node_app_dir }}/package.json'
register: packagejson
- name: Run npm prune
ansible.builtin.command: npm prune
args:
chdir: '{{ node_app_dir }}'
when: packagejson.stat.exists
- name: Run npm install
community.general.npm:
path: '{{ node_app_dir }}'
when: packagejson.stat.exists
.. note::
The example above uses ``ansible_env`` as part of the PATH. Basing variables on ``ansible_env`` is risky. Ansible populates ``ansible_env`` values by gathering facts, so the value of the variables depends on the remote_user or become_user Ansible used when gathering those facts. If you change remote_user/become_user the values in ``ansible_env`` may not be the ones you expect.
.. warning::
Environment variables are normally passed in clear text (shell plugin dependent) so they are not a recommended way of passing secrets to the module being executed.
You can also specify the environment at the task level.
.. code-block:: yaml+jinja
---
- name: Install ruby 2.3.1
ansible.builtin.command: rbenv install {{ rbenv_ruby_version }}
args:
creates: '{{ rbenv_root }}/versions/{{ rbenv_ruby_version }}/bin/ruby'
vars:
rbenv_root: /usr/local/rbenv
rbenv_ruby_version: 2.3.1
environment:
CONFIGURE_OPTS: '--disable-install-doc'
RBENV_ROOT: '{{ rbenv_root }}'
PATH: '{{ rbenv_root }}/bin:{{ rbenv_root }}/shims:{{ rbenv_plugins }}/ruby-build/bin:{{ ansible_env.PATH }}'
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_environment`.

@ -1,279 +1,7 @@
.. _playbooks_error_handling:
:orphan:
***************************
Error handling in playbooks
***************************
When Ansible receives a non-zero return code from a command or a failure from a module, by default it stops executing on that host and continues on other hosts. However, in some circumstances you may want different behavior. Sometimes a non-zero return code indicates success. Sometimes you want a failure on one host to stop execution on all hosts. Ansible provides tools and settings to handle these situations and help you get the behavior, output, and reporting you want.
.. contents::
:local:
.. _ignoring_failed_commands:
Ignoring failed commands
========================
By default Ansible stops executing tasks on a host when a task fails on that host. You can use ``ignore_errors`` to continue on in spite of the failure.
.. code-block:: yaml
- name: Do not count this as a failure
ansible.builtin.command: /bin/false
ignore_errors: yes
The ``ignore_errors`` directive only works when the task is able to run and returns a value of 'failed'. It does not make Ansible ignore undefined variable errors, connection failures, execution issues (for example, missing packages), or syntax errors.
.. _ignore_unreachable:
Ignoring unreachable host errors
================================
.. versionadded:: 2.7
You can ignore a task failure due to the host instance being 'UNREACHABLE' with the ``ignore_unreachable`` keyword. Ansible ignores the task errors, but continues to execute future tasks against the unreachable host. For example, at the task level:
.. code-block:: yaml
- name: This executes, fails, and the failure is ignored
ansible.builtin.command: /bin/true
ignore_unreachable: yes
- name: This executes, fails, and ends the play for this host
ansible.builtin.command: /bin/true
And at the playbook level:
.. code-block:: yaml
- hosts: all
ignore_unreachable: yes
tasks:
- name: This executes, fails, and the failure is ignored
ansible.builtin.command: /bin/true
- name: This executes, fails, and ends the play for this host
ansible.builtin.command: /bin/true
ignore_unreachable: no
.. _resetting_unreachable:
Resetting unreachable hosts
===========================
If Ansible cannot connect to a host, it marks that host as 'UNREACHABLE' and removes it from the list of active hosts for the run. You can use `meta: clear_host_errors` to reactivate all hosts, so subsequent tasks can try to reach them again.
.. _handlers_and_failure:
Handlers and failure
====================
Ansible runs :ref:`handlers <handlers>` at the end of each play. If a task notifies a handler but
another task fails later in the play, by default the handler does *not* run on that host,
which may leave the host in an unexpected state. For example, a task could update
a configuration file and notify a handler to restart some service. If a
task later in the same play fails, the configuration file might be changed but
the service will not be restarted.
You can change this behavior with the ``--force-handlers`` command-line option,
by including ``force_handlers: True`` in a play, or by adding ``force_handlers = True``
to ansible.cfg. When handlers are forced, Ansible will run all notified handlers on
all hosts, even hosts with failed tasks. (Note that certain errors could still prevent
the handler from running, such as a host becoming unreachable.)
.. _controlling_what_defines_failure:
Defining failure
================
Ansible lets you define what "failure" means in each task using the ``failed_when`` conditional. As with all conditionals in Ansible, lists of multiple ``failed_when`` conditions are joined with an implicit ``and``, meaning the task only fails when *all* conditions are met. If you want to trigger a failure when any of the conditions is met, you must define the conditions in a string with an explicit ``or`` operator.
You may check for failure by searching for a word or phrase in the output of a command
.. code-block:: yaml
- name: Fail task when the command error output prints FAILED
ansible.builtin.command: /usr/bin/example-command -x -y -z
register: command_result
failed_when: "'FAILED' in command_result.stderr"
or based on the return code
.. code-block:: yaml
- name: Fail task when both files are identical
ansible.builtin.raw: diff foo/file1 bar/file2
register: diff_cmd
failed_when: diff_cmd.rc == 0 or diff_cmd.rc >= 2
You can also combine multiple conditions for failure. This task will fail if both conditions are true:
.. code-block:: yaml
- name: Check if a file exists in temp and fail task if it does
ansible.builtin.command: ls /tmp/this_should_not_be_here
register: result
failed_when:
- result.rc == 0
- '"No such" not in result.stdout'
If you want the task to fail when only one condition is satisfied, change the ``failed_when`` definition to
.. code-block:: yaml
failed_when: result.rc == 0 or "No such" not in result.stdout
If you have too many conditions to fit neatly into one line, you can split it into a multi-line YAML value with ``>``.
.. code-block:: yaml
- name: example of many failed_when conditions with OR
ansible.builtin.shell: "./myBinary"
register: ret
failed_when: >
("No such file or directory" in ret.stdout) or
(ret.stderr != '') or
(ret.rc == 10)
.. _override_the_changed_result:
Defining "changed"
==================
Ansible lets you define when a particular task has "changed" a remote node using the ``changed_when`` conditional. This lets you determine, based on return codes or output, whether a change should be reported in Ansible statistics and whether a handler should be triggered or not. As with all conditionals in Ansible, lists of multiple ``changed_when`` conditions are joined with an implicit ``and``, meaning the task only reports a change when *all* conditions are met. If you want to report a change when any of the conditions is met, you must define the conditions in a string with an explicit ``or`` operator. For example:
.. code-block:: yaml
tasks:
- name: Report 'changed' when the return code is not equal to 2
ansible.builtin.shell: /usr/bin/billybass --mode="take me to the river"
register: bass_result
changed_when: "bass_result.rc != 2"
- name: This will never report 'changed' status
ansible.builtin.shell: wall 'beep'
changed_when: False
You can also combine multiple conditions to override "changed" result.
.. code-block:: yaml
- name: Combine multiple conditions to override 'changed' result
ansible.builtin.command: /bin/fake_command
register: result
ignore_errors: True
changed_when:
- '"ERROR" in result.stderr'
- result.rc == 2
.. note::
Just like ``when`` these two conditionals do not require templating delimiters (``{{ }}``) as they are implied.
See :ref:`controlling_what_defines_failure` for more conditional syntax examples.
Ensuring success for command and shell
======================================
The :ref:`command <command_module>` and :ref:`shell <shell_module>` modules care about return codes, so if you have a command whose successful exit code is not zero, you can do this:
.. code-block:: yaml
tasks:
- name: Run this command and ignore the result
ansible.builtin.shell: /usr/bin/somecommand || /bin/true
Aborting a play on all hosts
============================
Sometimes you want a failure on a single host, or failures on a certain percentage of hosts, to abort the entire play on all hosts. You can stop play execution after the first failure happens with ``any_errors_fatal``. For finer-grained control, you can use ``max_fail_percentage`` to abort the run after a given percentage of hosts has failed.
Aborting on the first error: any_errors_fatal
---------------------------------------------
If you set ``any_errors_fatal`` and a task returns an error, Ansible finishes the fatal task on all hosts in the current batch, then stops executing the play on all hosts. Subsequent tasks and plays are not executed. You can recover from fatal errors by adding a :ref:`rescue section <block_error_handling>` to the block. You can set ``any_errors_fatal`` at the play or block level.
.. code-block:: yaml
- hosts: somehosts
any_errors_fatal: true
roles:
- myrole
- hosts: somehosts
tasks:
- block:
- include_tasks: mytasks.yml
any_errors_fatal: true
You can use this feature when all tasks must be 100% successful to continue playbook execution. For example, if you run a service on machines in multiple data centers with load balancers to pass traffic from users to the service, you want all load balancers to be disabled before you stop the service for maintenance. To ensure that any failure in the task that disables the load balancers will stop all other tasks:
.. code-block:: yaml
---
- hosts: load_balancers_dc_a
any_errors_fatal: true
tasks:
- name: Shut down datacenter 'A'
ansible.builtin.command: /usr/bin/disable-dc
- hosts: frontends_dc_a
tasks:
- name: Stop service
ansible.builtin.command: /usr/bin/stop-software
- name: Update software
ansible.builtin.command: /usr/bin/upgrade-software
- hosts: load_balancers_dc_a
tasks:
- name: Start datacenter 'A'
ansible.builtin.command: /usr/bin/enable-dc
In this example Ansible starts the software upgrade on the front ends only if all of the load balancers are successfully disabled.
.. _maximum_failure_percentage:
Setting a maximum failure percentage
------------------------------------
By default, Ansible continues to execute tasks as long as there are hosts that have not yet failed. In some situations, such as when executing a rolling update, you may want to abort the play when a certain threshold of failures has been reached. To achieve this, you can set a maximum failure percentage on a play:
.. code-block:: yaml
---
- hosts: webservers
max_fail_percentage: 30
serial: 10
The ``max_fail_percentage`` setting applies to each batch when you use it with :ref:`serial <rolling_update_batch_size>`. In the example above, if more than 3 of the 10 servers in the first (or any) batch of servers failed, the rest of the play would be aborted.
.. note::
The percentage set must be exceeded, not equaled. For example, if serial were set to 4 and you wanted the task to abort the play when 2 of the systems failed, set the max_fail_percentage at 49 rather than 50.
Controlling errors in blocks
============================
You can also use blocks to define responses to task errors. This approach is similar to exception handling in many programming languages. See :ref:`block_error_handling` for details and examples.
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`tips_and_tricks`
Tips and tricks for playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_variables`
All about variables
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_error_handling`.

File diff suppressed because it is too large Load Diff

@ -1,190 +1,6 @@
.. _handlers:
:orphan:
Handlers: running operations on change
======================================
Sometimes you want a task to run only when a change is made on a machine. For example, you may want to restart a service if a task updates the configuration of that service, but not if the configuration is unchanged. Ansible uses handlers to address this use case. Handlers are tasks that only run when notified.
.. contents::
:local:
Handler example
---------------
This playbook, ``verify-apache.yml``, contains a single play with a handler.
.. code-block:: yaml
---
- name: Verify apache installation
hosts: webservers
vars:
http_port: 80
max_clients: 200
remote_user: root
tasks:
- name: Ensure apache is at the latest version
ansible.builtin.yum:
name: httpd
state: latest
- name: Write the apache config file
ansible.builtin.template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
notify:
- Restart apache
- name: Ensure apache is running
ansible.builtin.service:
name: httpd
state: started
handlers:
- name: Restart apache
ansible.builtin.service:
name: httpd
state: restarted
In this example playbook, the Apache server is restarted by the handler after all tasks complete in the play.
Notifying handlers
------------------
Tasks can instruct one or more handlers to execute using the ``notify`` keyword. The ``notify`` keyword can be applied to a task and accepts a list of handler names that are notified on a task change. Alternately, a string containing a single handler name can be supplied as well. The following example demonstrates how multiple handlers can be notified by a single task:
.. code-block:: yaml
tasks:
- name: Template configuration file
ansible.builtin.template:
src: template.j2
dest: /etc/foo.conf
notify:
- Restart apache
- Restart memcached
handlers:
- name: Restart memcached
ansible.builtin.service:
name: memcached
state: restarted
- name: Restart apache
ansible.builtin.service:
name: apache
state: restarted
In the above example the handlers are executed on task change in the following order: ``Restart memcached``, ``Restart apache``. Handlers are executed in the order they are defined in the ``handlers`` section, not in the order listed in the ``notify`` statement. Notifying the same handler multiple times will result in executing the handler only once regardless of how many tasks notify it. For example, if multiple tasks update a configuration file and notify a handler to restart Apache, Ansible only bounces Apache once to avoid unnecessary restarts.
Naming handlers
---------------
Handlers must be named in order for tasks to be able to notify them using the ``notify`` keyword.
Alternately, handlers can utilize the ``listen`` keyword. Using this handler keyword, handlers can listen on topics that can group multiple handlers as follows:
.. code-block:: yaml
tasks:
- name: Restart everything
command: echo "this task will restart the web services"
notify: "restart web services"
handlers:
- name: Restart memcached
service:
name: memcached
state: restarted
listen: "restart web services"
- name: Restart apache
service:
name: apache
state: restarted
listen: "restart web services"
Notifying the ``restart web services`` topic results in executing all handlers listening to that topic regardless of how those handlers are named.
This use makes it much easier to trigger multiple handlers. It also decouples handlers from their names, making it easier to share handlers among playbooks and roles (especially when using third-party roles from a shared source such as Ansible Galaxy).
Each handler should have a globally unique name. If multiple handlers are defined with the same name, only the last one defined is notified with ``notify``, effectively shadowing all of the previous handlers with the same name. Alternately handlers sharing the same name can all be notified and executed if they listen on the same topic by notifying that topic.
There is only one global scope for handlers (handler names and listen topics) regardless of where the handlers are defined. This also includes handlers defined in roles.
Controlling when handlers run
-----------------------------
By default, handlers run after all the tasks in a particular play have been completed. Notified handlers are executed automatically after each of the following sections, in the following order: ``pre_tasks``, ``roles``/``tasks`` and ``post_tasks``. This approach is efficient, because the handler only runs once, regardless of how many tasks notify it. For example, if multiple tasks update a configuration file and notify a handler to restart Apache, Ansible only bounces Apache once to avoid unnecessary restarts.
If you need handlers to run before the end of the play, add a task to flush them using the :ref:`meta module <meta_module>`, which executes Ansible actions:
.. code-block:: yaml
tasks:
- name: Some tasks go here
ansible.builtin.shell: ...
- name: Flush handlers
meta: flush_handlers
- name: Some other tasks
ansible.builtin.shell: ...
The ``meta: flush_handlers`` task triggers any handlers that have been notified at that point in the play.
Once handlers are executed, either automatically after each mentioned section or manually by the ``flush_handlers`` meta task, they can be notified and run again in later sections of the play.
Using variables with handlers
-----------------------------
You may want your Ansible handlers to use variables. For example, if the name of a service varies slightly by distribution, you want your output to show the exact name of the restarted service for each target machine. Avoid placing variables in the name of the handler. Since handler names are templated early on, Ansible may not have a value available for a handler name like this:
.. code-block:: yaml+jinja
handlers:
# This handler name may cause your play to fail!
- name: Restart "{{ web_service_name }}"
If the variable used in the handler name is not available, the entire play fails. Changing that variable mid-play **will not** result in newly created handler.
Instead, place variables in the task parameters of your handler. You can load the values using ``include_vars`` like this:
.. code-block:: yaml+jinja
tasks:
- name: Set host variables based on distribution
include_vars: "{{ ansible_facts.distribution }}.yml"
handlers:
- name: Restart web service
ansible.builtin.service:
name: "{{ web_service_name | default('httpd') }}"
state: restarted
While handler names can contain a template, ``listen`` topics cannot.
Handlers in roles
-----------------
Handlers from roles are not just contained in their roles but rather inserted into global scope with all other handlers from a play. As such they can be used outside of the role they are defined in. It also means that their name can conflict with handlers from outside the role. To ensure that a handler from a role is notified as opposed to one from outside the role with the same name, notify the handler by using its name in the following form: ``role_name : handler_name``.
Handlers notified within the ``roles`` section are automatically flushed at the end of the ``tasks`` section, but before any ``tasks`` handlers.
Includes and imports in handlers
--------------------------------
Notifying a dynamic include such as ``include_task`` as a handler results in executing all tasks from within the include. It is not possible to notify a handler defined inside a dynamic include.
Having a static include such as ``import_task`` as a handler results in that handler being effectively rewritten by handlers from within that import before the play execution. A static include itself cannot be notified, the tasks from withing that include, on the other hand, can be notified individually.
Limitations
-----------
A handler cannot run ``import_role`` or ``include_role``.
This page has moved to :ref:`handlers`.

@ -1,159 +1,7 @@
.. _about_playbooks:
.. _playbooks_intro:
:orphan:
******************
Intro to playbooks
******************
Ansible Playbooks offer a repeatable, re-usable, simple configuration management and multi-machine deployment system, one that is well suited to deploying complex applications. If you need to execute a task with Ansible more than once, write a playbook and put it under source control. Then you can use the playbook to push out new configuration or confirm the configuration of remote systems. The playbooks in the `ansible-examples repository <https://github.com/ansible/ansible-examples>`_ illustrate many useful techniques. You may want to look at these in another tab as you read the documentation.
Playbooks can:
* declare configurations
* orchestrate steps of any manual ordered process, on multiple sets of machines, in a defined order
* launch tasks synchronously or :ref:`asynchronously <playbooks_async>`
.. contents::
:local:
.. _playbook_language_example:
Playbook syntax
===============
Playbooks are expressed in YAML format with a minimum of syntax. If you are not familiar with YAML, look at our overview of :ref:`yaml_syntax` and consider installing an add-on for your text editor (see :ref:`other_tools_and_programs`) to help you write clean YAML syntax in your playbooks.
A playbook is composed of one or more 'plays' in an ordered list. The terms 'playbook' and 'play' are sports analogies. Each play executes part of the overall goal of the playbook, running one or more tasks. Each task calls an Ansible module.
Playbook execution
==================
A playbook runs in order from top to bottom. Within each play, tasks also run in order from top to bottom. Playbooks with multiple 'plays' can orchestrate multi-machine deployments, running one play on your webservers, then another play on your database servers, then a third play on your network infrastructure, and so on. At a minimum, each play defines two things:
* the managed nodes to target, using a :ref:`pattern <intro_patterns>`
* at least one task to execute
.. note::
In Ansible 2.10 and later, we recommend you use the fully-qualified collection name in your playbooks to ensure the correct module is selected, because multiple collections can contain modules with the same name (for example, ``user``). See :ref:`collections_using_playbook`.
In this example, the first play targets the web servers; the second play targets the database servers.
.. code-block:: yaml
---
- name: Update web servers
hosts: webservers
remote_user: root
tasks:
- name: Ensure apache is at the latest version
ansible.builtin.yum:
name: httpd
state: latest
- name: Write the apache config file
ansible.builtin.template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
- name: Update db servers
hosts: databases
remote_user: root
tasks:
- name: Ensure postgresql is at the latest version
ansible.builtin.yum:
name: postgresql
state: latest
- name: Ensure that postgresql is started
ansible.builtin.service:
name: postgresql
state: started
Your playbook can include more than just a hosts line and tasks. For example, the playbook above sets a ``remote_user`` for each play. This is the user account for the SSH connection. You can add other :ref:`playbook_keywords` at the playbook, play, or task level to influence how Ansible behaves. Playbook keywords can control the :ref:`connection plugin <connection_plugins>`, whether to use :ref:`privilege escalation <become>`, how to handle errors, and more. To support a variety of environments, Ansible lets you set many of these parameters as command-line flags, in your Ansible configuration, or in your inventory. Learning the :ref:`precedence rules <general_precedence_rules>` for these sources of data will help you as you expand your Ansible ecosystem.
.. _tasks_list:
Task execution
--------------
By default, Ansible executes each task in order, one at a time, against all machines matched by the host pattern. Each task executes a module with specific arguments. When a task has executed on all target machines, Ansible moves on to the next task. You can use :ref:`strategies <playbooks_strategies>` to change this default behavior. Within each play, Ansible applies the same task directives to all hosts. If a task fails on a host, Ansible takes that host out of the rotation for the rest of the playbook.
When you run a playbook, Ansible returns information about connections, the ``name`` lines of all your plays and tasks, whether each task has succeeded or failed on each machine, and whether each task has made a change on each machine. At the bottom of the playbook execution, Ansible provides a summary of the nodes that were targeted and how they performed. General failures and fatal "unreachable" communication attempts are kept separate in the counts.
.. _idempotency:
Desired state and 'idempotency'
-------------------------------
Most Ansible modules check whether the desired final state has already been achieved, and exit without performing any actions if that state has been achieved, so that repeating the task does not change the final state. Modules that behave this way are often called 'idempotent.' Whether you run a playbook once, or multiple times, the outcome should be the same. However, not all playbooks and not all modules behave this way. If you are unsure, test your playbooks in a sandbox environment before running them multiple times in production.
.. _executing_a_playbook:
Running playbooks
-----------------
To run your playbook, use the :ref:`ansible-playbook` command.
.. code-block:: bash
ansible-playbook playbook.yml -f 10
Use the ``--verbose`` flag when running your playbook to see detailed output from successful modules as well as unsuccessful ones.
.. _playbook_ansible-pull:
Ansible-Pull
============
Should you want to invert the architecture of Ansible, so that nodes check in to a central location, instead
of pushing configuration out to them, you can.
The ``ansible-pull`` is a small script that will checkout a repo of configuration instructions from git, and then
run ``ansible-playbook`` against that content.
Assuming you load balance your checkout location, ``ansible-pull`` scales essentially infinitely.
Run ``ansible-pull --help`` for details.
There's also a `clever playbook <https://github.com/ansible/ansible-examples/blob/master/language_features/ansible_pull.yml>`_ available to configure ``ansible-pull`` via a crontab from push mode.
Verifying playbooks
===================
You may want to verify your playbooks to catch syntax errors and other problems before you run them. The :ref:`ansible-playbook` command offers several options for verification, including ``--check``, ``--diff``, ``--list-hosts``, ``--list-tasks``, and ``--syntax-check``. The :ref:`validate-playbook-tools` describes other tools for validating and testing playbooks.
.. _linting_playbooks:
ansible-lint
------------
You can use `ansible-lint <https://docs.ansible.com/ansible-lint/index.html>`_ for detailed, Ansible-specific feedback on your playbooks before you execute them. For example, if you run ``ansible-lint`` on the playbook called ``verify-apache.yml`` near the top of this page, you should get the following results:
.. code-block:: bash
$ ansible-lint verify-apache.yml
[403] Package installs should not use latest
verify-apache.yml:8
Task/Handler: ensure apache is at the latest version
The `ansible-lint default rules <https://docs.ansible.com/ansible-lint/rules/default_rules.html>`_ page describes each error. For ``[403]``, the recommended fix is to change ``state: latest`` to ``state: present`` in the playbook.
.. seealso::
`ansible-lint <https://docs.ansible.com/ansible-lint/index.html>`_
Learn how to test Ansible Playbooks syntax
:ref:`yaml_syntax`
Learn about YAML syntax
:ref:`tips_and_tricks`
Tips for managing playbooks in the real world
:ref:`list_of_collections`
Browse existing collections, modules, and plugins
:ref:`developing_modules`
Learn to extend Ansible by writing your own modules
:ref:`intro_patterns`
Learn about how to select hosts
`GitHub examples directory <https://github.com/ansible/ansible-examples>`_
Complete end-to-end playbook examples
`Mailing List <https://groups.google.com/group/ansible-project>`_
Questions? Help? Ideas? Stop by the list on Google Groups
This page has moved to :ref:`playbooks_intro`.

@ -1,39 +1,7 @@
.. _playbooks_lookups:
:orphan:
*******
Lookups
*******
Lookup plugins retrieve data from outside sources such as files, databases, key/value stores, APIs, and other services. Like all templating, lookups execute and are evaluated on the Ansible control machine. Ansible makes the data returned by a lookup plugin available using the standard templating system. Before Ansible 2.5, lookups were mostly used indirectly in ``with_<lookup>`` constructs for looping. Starting with Ansible 2.5, lookups are used more explicitly as part of Jinja2 expressions fed into the ``loop`` keyword.
.. _lookups_and_variables:
Using lookups in variables
==========================
You can populate variables using lookups. Ansible evaluates the value each time it is executed in a task (or template).
.. code-block:: yaml+jinja
vars:
motd_value: "{{ lookup('file', '/etc/motd') }}"
tasks:
- debug:
msg: "motd value is {{ motd_value }}"
For more details and a list of lookup plugins in ansible-core, see :ref:`plugins_lookup`. You may also find lookup plugins in collections. You can review a list of lookup plugins installed on your control machine with the command ``ansible-doc -l -t lookup``.
.. seealso::
:ref:`working_with_playbooks`
An introduction to playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_variables`
All about variables
:ref:`playbooks_loops`
Looping in playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_lookups`.

@ -1,499 +1,7 @@
.. _playbooks_loops:
:orphan:
*****
Loops
*****
Ansible offers the ``loop``, ``with_<lookup>``, and ``until`` keywords to execute a task multiple times. Examples of commonly-used loops include changing ownership on several files and/or directories with the :ref:`file module <file_module>`, creating multiple users with the :ref:`user module <user_module>`, and
repeating a polling step until a certain result is reached.
.. note::
* We added ``loop`` in Ansible 2.5. It is not yet a full replacement for ``with_<lookup>``, but we recommend it for most use cases.
* We have not deprecated the use of ``with_<lookup>`` - that syntax will still be valid for the foreseeable future.
* We are looking to improve ``loop`` syntax - watch this page and the `changelog <https://github.com/ansible/ansible/tree/devel/changelogs>`_ for updates.
.. contents::
:local:
Comparing ``loop`` and ``with_*``
=================================
* The ``with_<lookup>`` keywords rely on :ref:`lookup_plugins` - even ``items`` is a lookup.
* The ``loop`` keyword is equivalent to ``with_list``, and is the best choice for simple loops.
* The ``loop`` keyword will not accept a string as input, see :ref:`query_vs_lookup`.
* Generally speaking, any use of ``with_*`` covered in :ref:`migrating_to_loop` can be updated to use ``loop``.
* Be careful when changing ``with_items`` to ``loop``, as ``with_items`` performed implicit single-level flattening. You may need to use ``flatten(1)`` with ``loop`` to match the exact outcome. For example, to get the same output as:
.. code-block:: yaml
with_items:
- 1
- [2,3]
- 4
you would need
.. code-block:: yaml+jinja
loop: "{{ [1, [2, 3], 4] | flatten(1) }}"
* Any ``with_*`` statement that requires using ``lookup`` within a loop should not be converted to use the ``loop`` keyword. For example, instead of doing:
.. code-block:: yaml+jinja
loop: "{{ lookup('fileglob', '*.txt', wantlist=True) }}"
it's cleaner to keep
.. code-block:: yaml
with_fileglob: '*.txt'
.. _standard_loops:
Standard loops
==============
Iterating over a simple list
----------------------------
Repeated tasks can be written as standard loops over a simple list of strings. You can define the list directly in the task.
.. code-block:: yaml+jinja
- name: Add several users
ansible.builtin.user:
name: "{{ item }}"
state: present
groups: "wheel"
loop:
- testuser1
- testuser2
You can define the list in a variables file, or in the 'vars' section of your play, then refer to the name of the list in the task.
.. code-block:: yaml+jinja
loop: "{{ somelist }}"
Either of these examples would be the equivalent of
.. code-block:: yaml
- name: Add user testuser1
ansible.builtin.user:
name: "testuser1"
state: present
groups: "wheel"
- name: Add user testuser2
ansible.builtin.user:
name: "testuser2"
state: present
groups: "wheel"
You can pass a list directly to a parameter for some plugins. Most of the packaging modules, like :ref:`yum <yum_module>` and :ref:`apt <apt_module>`, have this capability. When available, passing the list to a parameter is better than looping over the task. For example
.. code-block:: yaml+jinja
- name: Optimal yum
ansible.builtin.yum:
name: "{{ list_of_packages }}"
state: present
- name: Non-optimal yum, slower and may cause issues with interdependencies
ansible.builtin.yum:
name: "{{ item }}"
state: present
loop: "{{ list_of_packages }}"
Check the :ref:`module documentation <modules_by_category>` to see if you can pass a list to any particular module's parameter(s).
Iterating over a list of hashes
-------------------------------
If you have a list of hashes, you can reference subkeys in a loop. For example:
.. code-block:: yaml+jinja
- name: Add several users
ansible.builtin.user:
name: "{{ item.name }}"
state: present
groups: "{{ item.groups }}"
loop:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }
When combining :ref:`conditionals <playbooks_conditionals>` with a loop, the ``when:`` statement is processed separately for each item.
See :ref:`the_when_statement` for examples.
Iterating over a dictionary
---------------------------
To loop over a dict, use the :ref:`dict2items <dict_filter>`:
.. code-block:: yaml+jinja
- name: Using dict2items
ansible.builtin.debug:
msg: "{{ item.key }} - {{ item.value }}"
loop: "{{ tag_data | dict2items }}"
vars:
tag_data:
Environment: dev
Application: payment
Here, we are iterating over `tag_data` and printing the key and the value from it.
Registering variables with a loop
=================================
You can register the output of a loop as a variable. For example
.. code-block:: yaml+jinja
- name: Register loop output as a variable
ansible.builtin.shell: "echo {{ item }}"
loop:
- "one"
- "two"
register: echo
When you use ``register`` with a loop, the data structure placed in the variable will contain a ``results`` attribute that is a list of all responses from the module. This differs from the data structure returned when using ``register`` without a loop.
.. code-block:: json
{
"changed": true,
"msg": "All items completed",
"results": [
{
"changed": true,
"cmd": "echo \"one\" ",
"delta": "0:00:00.003110",
"end": "2013-12-19 12:00:05.187153",
"invocation": {
"module_args": "echo \"one\"",
"module_name": "shell"
},
"item": "one",
"rc": 0,
"start": "2013-12-19 12:00:05.184043",
"stderr": "",
"stdout": "one"
},
{
"changed": true,
"cmd": "echo \"two\" ",
"delta": "0:00:00.002920",
"end": "2013-12-19 12:00:05.245502",
"invocation": {
"module_args": "echo \"two\"",
"module_name": "shell"
},
"item": "two",
"rc": 0,
"start": "2013-12-19 12:00:05.242582",
"stderr": "",
"stdout": "two"
}
]
}
Subsequent loops over the registered variable to inspect the results may look like
.. code-block:: yaml+jinja
- name: Fail if return code is not 0
ansible.builtin.fail:
msg: "The command ({{ item.cmd }}) did not have a 0 return code"
when: item.rc != 0
loop: "{{ echo.results }}"
During iteration, the result of the current item will be placed in the variable.
.. code-block:: yaml+jinja
- name: Place the result of the current item in the variable
ansible.builtin.shell: echo "{{ item }}"
loop:
- one
- two
register: echo
changed_when: echo.stdout != "one"
.. _complex_loops:
Complex loops
=============
Iterating over nested lists
---------------------------
You can use Jinja2 expressions to iterate over complex lists. For example, a loop can combine nested lists.
.. code-block:: yaml+jinja
- name: Give users access to multiple databases
community.mysql.mysql_user:
name: "{{ item[0] }}"
priv: "{{ item[1] }}.*:ALL"
append_privs: yes
password: "foo"
loop: "{{ ['alice', 'bob'] | product(['clientdb', 'employeedb', 'providerdb']) | list }}"
.. _do_until_loops:
Retrying a task until a condition is met
----------------------------------------
.. versionadded:: 1.4
You can use the ``until`` keyword to retry a task until a certain condition is met. Here's an example:
.. code-block:: yaml
- name: Retry a task until a certain condition is met
ansible.builtin.shell: /usr/bin/foo
register: result
until: result.stdout.find("all systems go") != -1
retries: 5
delay: 10
This task runs up to 5 times with a delay of 10 seconds between each attempt. If the result of any attempt has "all systems go" in its stdout, the task succeeds. The default value for "retries" is 3 and "delay" is 5.
To see the results of individual retries, run the play with ``-vv``.
When you run a task with ``until`` and register the result as a variable, the registered variable will include a key called "attempts", which records the number of the retries for the task.
.. note:: You must set the ``until`` parameter if you want a task to retry. If ``until`` is not defined, the value for the ``retries`` parameter is forced to 1.
Looping over inventory
----------------------
To loop over your inventory, or just a subset of it, you can use a regular ``loop`` with the ``ansible_play_batch`` or ``groups`` variables.
.. code-block:: yaml+jinja
- name: Show all the hosts in the inventory
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ groups['all'] }}"
- name: Show all the hosts in the current play
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ ansible_play_batch }}"
There is also a specific lookup plugin ``inventory_hostnames`` that can be used like this
.. code-block:: yaml+jinja
- name: Show all the hosts in the inventory
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ query('inventory_hostnames', 'all') }}"
- name: Show all the hosts matching the pattern, ie all but the group www
ansible.builtin.debug:
msg: "{{ item }}"
loop: "{{ query('inventory_hostnames', 'all:!www') }}"
More information on the patterns can be found in :ref:`intro_patterns`.
.. _query_vs_lookup:
Ensuring list input for ``loop``: using ``query`` rather than ``lookup``
========================================================================
The ``loop`` keyword requires a list as input, but the ``lookup`` keyword returns a string of comma-separated values by default. Ansible 2.5 introduced a new Jinja2 function named :ref:`query <query>` that always returns a list, offering a simpler interface and more predictable output from lookup plugins when using the ``loop`` keyword.
You can force ``lookup`` to return a list to ``loop`` by using ``wantlist=True``, or you can use ``query`` instead.
The following two examples do the same thing.
.. code-block:: yaml+jinja
loop: "{{ query('inventory_hostnames', 'all') }}"
loop: "{{ lookup('inventory_hostnames', 'all', wantlist=True) }}"
.. _loop_control:
Adding controls to loops
========================
.. versionadded:: 2.1
The ``loop_control`` keyword lets you manage your loops in useful ways.
Limiting loop output with ``label``
-----------------------------------
.. versionadded:: 2.2
When looping over complex data structures, the console output of your task can be enormous. To limit the displayed output, use the ``label`` directive with ``loop_control``.
.. code-block:: yaml+jinja
- name: Create servers
digital_ocean:
name: "{{ item.name }}"
state: present
loop:
- name: server1
disks: 3gb
ram: 15Gb
network:
nic01: 100Gb
nic02: 10Gb
...
loop_control:
label: "{{ item.name }}"
The output of this task will display just the ``name`` field for each ``item`` instead of the entire contents of the multi-line ``{{ item }}`` variable.
.. note:: This is for making console output more readable, not protecting sensitive data. If there is sensitive data in ``loop``, set ``no_log: yes`` on the task to prevent disclosure.
Pausing within a loop
---------------------
.. versionadded:: 2.2
To control the time (in seconds) between the execution of each item in a task loop, use the ``pause`` directive with ``loop_control``.
.. code-block:: yaml+jinja
# main.yml
- name: Create servers, pause 3s before creating next
community.digitalocean.digital_ocean:
name: "{{ item }}"
state: present
loop:
- server1
- server2
loop_control:
pause: 3
Tracking progress through a loop with ``index_var``
---------------------------------------------------
.. versionadded:: 2.5
To keep track of where you are in a loop, use the ``index_var`` directive with ``loop_control``. This directive specifies a variable name to contain the current loop index.
.. code-block:: yaml+jinja
- name: Count our fruit
ansible.builtin.debug:
msg: "{{ item }} with index {{ my_idx }}"
loop:
- apple
- banana
- pear
loop_control:
index_var: my_idx
.. note:: `index_var` is 0 indexed.
Defining inner and outer variable names with ``loop_var``
---------------------------------------------------------
.. versionadded:: 2.1
You can nest two looping tasks using ``include_tasks``. However, by default Ansible sets the loop variable ``item`` for each loop. This means the inner, nested loop will overwrite the value of ``item`` from the outer loop.
You can specify the name of the variable for each loop using ``loop_var`` with ``loop_control``.
.. code-block:: yaml+jinja
# main.yml
- include_tasks: inner.yml
loop:
- 1
- 2
- 3
loop_control:
loop_var: outer_item
# inner.yml
- name: Print outer and inner items
ansible.builtin.debug:
msg: "outer item={{ outer_item }} inner item={{ item }}"
loop:
- a
- b
- c
.. note:: If Ansible detects that the current loop is using a variable which has already been defined, it will raise an error to fail the task.
Extended loop variables
-----------------------
.. versionadded:: 2.8
As of Ansible 2.8 you can get extended loop information using the ``extended`` option to loop control. This option will expose the following information.
========================== ===========
Variable Description
-------------------------- -----------
``ansible_loop.allitems`` The list of all items in the loop
``ansible_loop.index`` The current iteration of the loop. (1 indexed)
``ansible_loop.index0`` The current iteration of the loop. (0 indexed)
``ansible_loop.revindex`` The number of iterations from the end of the loop (1 indexed)
``ansible_loop.revindex0`` The number of iterations from the end of the loop (0 indexed)
``ansible_loop.first`` ``True`` if first iteration
``ansible_loop.last`` ``True`` if last iteration
``ansible_loop.length`` The number of items in the loop
``ansible_loop.previtem`` The item from the previous iteration of the loop. Undefined during the first iteration.
``ansible_loop.nextitem`` The item from the following iteration of the loop. Undefined during the last iteration.
========================== ===========
::
loop_control:
extended: yes
.. note:: When using ``loop_control.extended`` more memory will be utilized on the control node. This is a result of ``ansible_loop.allitems`` containing a reference to the full loop data for every loop. When serializing the results for display in callback plugins within the main ansible process, these references may be dereferenced causing memory usage to increase.
.. versionadded:: 2.14
To disable the ``ansible_loop.allitems`` item, to reduce memory consumption, set ``loop_control.extended_allitems: no``.
::
loop_control:
extended: yes
extended_allitems: no
Accessing the name of your loop_var
-----------------------------------
.. versionadded:: 2.8
As of Ansible 2.8 you can get the name of the value provided to ``loop_control.loop_var`` using the ``ansible_loop_var`` variable
For role authors, writing roles that allow loops, instead of dictating the required ``loop_var`` value, you can gather the value via the following
.. code-block:: yaml+jinja
"{{ lookup('vars', ansible_loop_var) }}"
.. _migrating_to_loop:
Migrating from with_X to loop
=============================
.. include:: shared_snippets/with2loop.txt
.. seealso::
:ref:`about_playbooks`
An introduction to playbooks
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`tips_and_tricks`
Tips and tricks for playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_variables`
All about variables
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_loops`.

@ -1,175 +1,6 @@
.. _module_defaults:
:orphan:
Module defaults
===============
If you frequently call the same module with the same arguments, it can be useful to define default arguments for that particular module using the ``module_defaults`` keyword.
Here is a basic example:
.. code-block:: YAML
- hosts: localhost
module_defaults:
ansible.builtin.file:
owner: root
group: root
mode: 0755
tasks:
- name: Create file1
ansible.builtin.file:
state: touch
path: /tmp/file1
- name: Create file2
ansible.builtin.file:
state: touch
path: /tmp/file2
- name: Create file3
ansible.builtin.file:
state: touch
path: /tmp/file3
The ``module_defaults`` keyword can be used at the play, block, and task level. Any module arguments explicitly specified in a task will override any established default for that module argument.
.. code-block:: YAML
- block:
- name: Print a message
ansible.builtin.debug:
msg: "Different message"
module_defaults:
ansible.builtin.debug:
msg: "Default message"
You can remove any previously established defaults for a module by specifying an empty dict.
.. code-block:: YAML
- name: Create file1
ansible.builtin.file:
state: touch
path: /tmp/file1
module_defaults:
file: {}
.. note::
Any module defaults set at the play level (and block/task level when using ``include_role`` or ``import_role``) will apply to any roles used, which may cause unexpected behavior in the role.
Here are some more realistic use cases for this feature.
Interacting with an API that requires auth.
.. code-block:: YAML
- hosts: localhost
module_defaults:
ansible.builtin.uri:
force_basic_auth: true
user: some_user
password: some_password
tasks:
- name: Interact with a web service
ansible.builtin.uri:
url: http://some.api.host/v1/whatever1
- name: Interact with a web service
ansible.builtin.uri:
url: http://some.api.host/v1/whatever2
- name: Interact with a web service
ansible.builtin.uri:
url: http://some.api.host/v1/whatever3
Setting a default AWS region for specific EC2-related modules.
.. code-block:: YAML
- hosts: localhost
vars:
my_region: us-west-2
module_defaults:
amazon.aws.ec2:
region: '{{ my_region }}'
community.aws.ec2_instance_info:
region: '{{ my_region }}'
amazon.aws.ec2_vpc_net_info:
region: '{{ my_region }}'
.. _module_defaults_groups:
Module defaults groups
----------------------
.. versionadded:: 2.7
Ansible 2.7 adds a preview-status feature to group together modules that share common sets of parameters. This makes it easier to author playbooks making heavy use of API-based modules such as cloud modules.
+---------+---------------------------+-----------------+
| Group | Purpose | Ansible Version |
+=========+===========================+=================+
| aws | Amazon Web Services | 2.7 |
+---------+---------------------------+-----------------+
| azure | Azure | 2.7 |
+---------+---------------------------+-----------------+
| gcp | Google Cloud Platform | 2.7 |
+---------+---------------------------+-----------------+
| k8s | Kubernetes | 2.8 |
+---------+---------------------------+-----------------+
| os | OpenStack | 2.8 |
+---------+---------------------------+-----------------+
| acme | ACME | 2.10 |
+---------+---------------------------+-----------------+
| docker* | Docker | 2.10 |
+---------+---------------------------+-----------------+
| ovirt | oVirt | 2.10 |
+---------+---------------------------+-----------------+
| vmware | VMware | 2.10 |
+---------+---------------------------+-----------------+
* The `docker_stack <docker_stack_module>`_ module is not included in the ``docker`` defaults group.
Use the groups with ``module_defaults`` by prefixing the group name with ``group/`` - for example ``group/aws``.
In a playbook, you can set module defaults for whole groups of modules, such as setting a common AWS region.
.. code-block:: YAML
# example_play.yml
- hosts: localhost
module_defaults:
group/aws:
region: us-west-2
tasks:
- name: Get info
aws_s3_bucket_info:
# now the region is shared between both info modules
- name: Get info
ec2_ami_info:
filters:
name: 'RHEL*7.5*'
In ansible-core 2.12, collections can define their own groups in the ``meta/runtime.yml`` file. ``module_defaults`` does not take the ``collections`` keyword into account, so the fully qualified group name must be used for new groups in ``module_defaults``.
Here is an example ``runtime.yml`` file for a collection and a sample playbook using the group.
.. code-block:: YAML
# collections/ansible_collections/ns/coll/meta/runtime.yml
action_groups:
groupname:
- module
- another.collection.module
.. code-block:: YAML
- hosts: localhost
module_defaults:
group/ns.coll.groupname:
option_name: option_value
tasks:
- ns.coll.module:
- another.collection.module
This page has moved to :ref:`module_defaults`.

@ -1,124 +1,7 @@
.. _playbooks_prompts:
:orphan:
**************************
Interactive input: prompts
**************************
If you want your playbook to prompt the user for certain input, add a 'vars_prompt' section. Prompting the user for variables lets you avoid recording sensitive data like passwords. In addition to security, prompts support flexibility. For example, if you use one playbook across multiple software releases, you could prompt for the particular release version.
.. contents::
:local:
Here is a most basic example:
.. code-block:: yaml
---
- hosts: all
vars_prompt:
- name: username
prompt: What is your username?
private: no
- name: password
prompt: What is your password?
tasks:
- name: Print a message
ansible.builtin.debug:
msg: 'Logging in as {{ username }}'
The user input is hidden by default but it can be made visible by setting ``private: no``.
.. note::
Prompts for individual ``vars_prompt`` variables will be skipped for any variable that is already defined through the command line ``--extra-vars`` option, or when running from a non-interactive session (such as cron or Ansible AWX). See :ref:`passing_variables_on_the_command_line`.
If you have a variable that changes infrequently, you can provide a default value that can be overridden.
.. code-block:: yaml
vars_prompt:
- name: release_version
prompt: Product release version
default: "1.0"
Hashing values supplied by ``vars_prompt``
------------------------------------------
You can hash the entered value so you can use it, for instance, with the user module to define a password:
.. code-block:: yaml
vars_prompt:
- name: my_password2
prompt: Enter password2
private: yes
encrypt: sha512_crypt
confirm: yes
salt_size: 7
If you have `Passlib <https://passlib.readthedocs.io/en/stable/>`_ installed, you can use any crypt scheme the library supports:
- *des_crypt* - DES Crypt
- *bsdi_crypt* - BSDi Crypt
- *bigcrypt* - BigCrypt
- *crypt16* - Crypt16
- *md5_crypt* - MD5 Crypt
- *bcrypt* - BCrypt
- *sha1_crypt* - SHA-1 Crypt
- *sun_md5_crypt* - Sun MD5 Crypt
- *sha256_crypt* - SHA-256 Crypt
- *sha512_crypt* - SHA-512 Crypt
- *apr_md5_crypt* - Apache's MD5-Crypt variant
- *phpass* - PHPass' Portable Hash
- *pbkdf2_digest* - Generic PBKDF2 Hashes
- *cta_pbkdf2_sha1* - Cryptacular's PBKDF2 hash
- *dlitz_pbkdf2_sha1* - Dwayne Litzenberger's PBKDF2 hash
- *scram* - SCRAM Hash
- *bsd_nthash* - FreeBSD's MCF-compatible nthash encoding
The only parameters accepted are 'salt' or 'salt_size'. You can use your own salt by defining
'salt', or have one generated automatically using 'salt_size'. By default Ansible generates a salt
of size 8.
.. versionadded:: 2.7
If you do not have Passlib installed, Ansible uses the `crypt <https://docs.python.org/3/library/crypt.html>`_ library as a fallback. Ansible supports at most four crypt schemes, depending on your platform at most the following crypt schemes are supported:
- *bcrypt* - BCrypt
- *md5_crypt* - MD5 Crypt
- *sha256_crypt* - SHA-256 Crypt
- *sha512_crypt* - SHA-512 Crypt
.. versionadded:: 2.8
.. _unsafe_prompts:
Allowing special characters in ``vars_prompt`` values
-----------------------------------------------------
Some special characters, such as ``{`` and ``%`` can create templating errors. If you need to accept special characters, use the ``unsafe`` option:
.. code-block:: yaml
vars_prompt:
- name: my_password_with_weird_chars
prompt: Enter password
unsafe: yes
private: yes
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_variables`
All about variables
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_prompts`.

@ -1,226 +1,7 @@
.. _playbooks_reuse:
:orphan:
**************************
Re-using Ansible artifacts
**************************
You can write a simple playbook in one very large file, and most users learn the one-file approach first. However, breaking your automation work up into smaller files is an excellent way to organize complex sets of tasks and reuse them. Smaller, more distributed artifacts let you re-use the same variables, tasks, and plays in multiple playbooks to address different use cases. You can use distributed artifacts across multiple parent playbooks or even multiple times within one playbook. For example, you might want to update your customer database as part of several different playbooks. If you put all the tasks related to updating your database in a tasks file or a role, you can re-use them in many playbooks while only maintaining them in one place.
.. contents::
:local:
Creating re-usable files and roles
==================================
Ansible offers four distributed, re-usable artifacts: variables files, task files, playbooks, and roles.
- A variables file contains only variables.
- A task file contains only tasks.
- A playbook contains at least one play, and may contain variables, tasks, and other content. You can re-use tightly focused playbooks, but you can only re-use them statically, not dynamically.
- A role contains a set of related tasks, variables, defaults, handlers, and even modules or other plugins in a defined file-tree. Unlike variables files, task files, or playbooks, roles can be easily uploaded and shared via Ansible Galaxy. See :ref:`playbooks_reuse_roles` for details about creating and using roles.
.. versionadded:: 2.4
Re-using playbooks
==================
You can incorporate multiple playbooks into a main playbook. However, you can only use imports to re-use playbooks. For example:
.. code-block:: yaml
- import_playbook: webservers.yml
- import_playbook: databases.yml
Importing incorporates playbooks in other playbooks statically. Ansible runs the plays and tasks in each imported playbook in the order they are listed, just as if they had been defined directly in the main playbook.
You can select which playbook you want to import at runtime by defining your imported playbook filename with a variable, then passing the variable with either ``--extra-vars`` or the ``vars`` keyword. For example:
.. code-block:: yaml
- import_playbook: "/path/to/{{ import_from_extra_var }}"
- import_playbook: "{{ import_from_vars }}"
vars:
import_from_vars: /path/to/one_playbook.yml
If you run this playbook with ``ansible-playbook my_playbook -e import_from_extra_var=other_playbook.yml``, Ansible imports both one_playbook.yml and other_playbook.yml.
When to turn a playbook into a role
===================================
For some use cases, simple playbooks work well. However, starting at a certain level of complexity, roles work better than playbooks. A role lets you store your defaults, handlers, variables, and tasks in separate directories, instead of in a single long document. Roles are easy to share on Ansible Galaxy. For complex use cases, most users find roles easier to read, understand, and maintain than all-in-one playbooks.
Re-using files and roles
========================
Ansible offers two ways to re-use files and roles in a playbook: dynamic and static.
- For dynamic re-use, add an ``include_*`` task in the tasks section of a play:
- :ref:`include_role <include_role_module>`
- :ref:`include_tasks <include_tasks_module>`
- :ref:`include_vars <include_vars_module>`
- For static re-use, add an ``import_*`` task in the tasks section of a play:
- :ref:`import_role <import_role_module>`
- :ref:`import_tasks <import_tasks_module>`
Task include and import statements can be used at arbitrary depth.
You can still use the bare :ref:`roles <roles_keyword>` keyword at the play level to incorporate a role in a playbook statically. However, the bare :ref:`include <include_module>` keyword, once used for both task files and playbook-level includes, is now deprecated.
Includes: dynamic re-use
------------------------
Including roles, tasks, or variables adds them to a playbook dynamically. Ansible processes included files and roles as they come up in a playbook, so included tasks can be affected by the results of earlier tasks within the top-level playbook. Included roles and tasks are similar to handlers - they may or may not run, depending on the results of other tasks in the top-level playbook.
The primary advantage of using ``include_*`` statements is looping. When a loop is used with an include, the included tasks or role will be executed once for each item in the loop.
The filenames for included roles, tasks, and vars are templated before inclusion.
You can pass variables into includes. See :ref:`ansible_variable_precedence` for more details on variable inheritance and precedence.
Imports: static re-use
----------------------
Importing roles, tasks, or playbooks adds them to a playbook statically. Ansible pre-processes imported files and roles before it runs any tasks in a playbook, so imported content is never affected by other tasks within the top-level playbook.
The filenames for imported roles and tasks support templating, but the variables must be available when Ansible is pre-processing the imports. This can be done with the ``vars`` keyword or by using ``--extra-vars``.
You can pass variables to imports. You must pass variables if you want to run an imported file more than once in a playbook. For example:
.. code-block:: yaml
tasks:
- import_tasks: wordpress.yml
vars:
wp_user: timmy
- import_tasks: wordpress.yml
vars:
wp_user: alice
- import_tasks: wordpress.yml
vars:
wp_user: bob
See :ref:`ansible_variable_precedence` for more details on variable inheritance and precedence.
.. _dynamic_vs_static:
Comparing includes and imports: dynamic and static re-use
------------------------------------------------------------
Each approach to re-using distributed Ansible artifacts has advantages and limitations. You may choose dynamic re-use for some playbooks and static re-use for others. Although you can use both dynamic and static re-use in a single playbook, it is best to select one approach per playbook. Mixing static and dynamic re-use can introduce difficult-to-diagnose bugs into your playbooks. This table summarizes the main differences so you can choose the best approach for each playbook you create.
.. table::
:class: documentation-table
========================= ======================================== ========================================
.. Include_* Import_*
========================= ======================================== ========================================
Type of re-use Dynamic Static
When processed At runtime, when encountered Pre-processed during playbook parsing
Task or play All includes are tasks ``import_playbook`` cannot be a task
Task options Apply only to include task itself Apply to all child tasks in import
Calling from loops Executed once for each loop item Cannot be used in a loop
Using ``--list-tags`` Tags within includes not listed All tags appear with ``--list-tags``
Using ``--list-tasks`` Tasks within includes not listed All tasks appear with ``--list-tasks``
Notifying handlers Cannot trigger handlers within includes Can trigger individual imported handlers
Using ``--start-at-task`` Cannot start at tasks within includes Can start at imported tasks
Using inventory variables Can ``include_*: {{ inventory_var }}`` Cannot ``import_*: {{ inventory_var }}``
With playbooks No ``include_playbook`` Can import full playbooks
With variables files Can include variables files Use ``vars_files:`` to import variables
========================= ======================================== ========================================
.. note::
* There are also big differences in resource consumption and performance, imports are quite lean and fast, while includes require a lot of management
and accounting.
Re-using tasks as handlers
==========================
You can also use includes and imports in the :ref:`handlers` section of a playbook. For instance, if you want to define how to restart Apache, you only have to do that once for all of your playbooks. You might make a ``restarts.yml`` file that looks like:
.. code-block:: yaml
# restarts.yml
- name: Restart apache
ansible.builtin.service:
name: apache
state: restarted
- name: Restart mysql
ansible.builtin.service:
name: mysql
state: restarted
You can trigger handlers from either an import or an include, but the procedure is different for each method of re-use. If you include the file, you must notify the include itself, which triggers all the tasks in ``restarts.yml``. If you import the file, you must notify the individual task(s) within ``restarts.yml``. You can mix direct tasks and handlers with included or imported tasks and handlers.
Triggering included (dynamic) handlers
--------------------------------------
Includes are executed at run-time, so the name of the include exists during play execution, but the included tasks do not exist until the include itself is triggered. To use the ``Restart apache`` task with dynamic re-use, refer to the name of the include itself. This approach triggers all tasks in the included file as handlers. For example, with the task file shown above:
.. code-block:: yaml
- name: Trigger an included (dynamic) handler
hosts: localhost
handlers:
- name: Restart services
include_tasks: restarts.yml
tasks:
- command: "true"
notify: Restart services
Triggering imported (static) handlers
-------------------------------------
Imports are processed before the play begins, so the name of the import no longer exists during play execution, but the names of the individual imported tasks do exist. To use the ``Restart apache`` task with static re-use, refer to the name of each task or tasks within the imported file. For example, with the task file shown above:
.. code-block:: yaml
- name: Trigger an imported (static) handler
hosts: localhost
handlers:
- name: Restart services
import_tasks: restarts.yml
tasks:
- command: "true"
notify: Restart apache
- command: "true"
notify: Restart mysql
.. seealso::
:ref:`utilities_modules`
Documentation of the ``include*`` and ``import*`` modules discussed here.
:ref:`working_with_playbooks`
Review the basic Playbook language features
:ref:`playbooks_variables`
All about variables in playbooks
:ref:`playbooks_conditionals`
Conditionals in playbooks
:ref:`playbooks_loops`
Loops in playbooks
:ref:`tips_and_tricks`
Tips and tricks for playbooks
:ref:`ansible_galaxy`
How to share roles on galaxy, role management
`GitHub Ansible examples <https://github.com/ansible/ansible-examples>`_
Complete playbook files from the GitHub project source
`Mailing List <https://groups.google.com/group/ansible-project>`_
Questions? Help? Ideas? Stop by the list on Google Groups
This page has moved to :ref:`playbooks_reuse`.

@ -1,32 +1,6 @@
:orphan:
.. _playbooks_reuse_includes:
Including and importing
=======================
The content on this page has been moved to :ref:`playbooks_reuse`.
.. seealso::
:ref:`yaml_syntax`
Learn about YAML syntax
:ref:`working_with_playbooks`
Review the basic Playbook language features
:ref:`tips_and_tricks`
Tips and tricks for playbooks
:ref:`playbooks_variables`
All about variables in playbooks
:ref:`playbooks_conditionals`
Conditionals in playbooks
:ref:`playbooks_loops`
Loops in playbooks
:ref:`list_of_collections`
Browse existing collections, modules, and plugins
:ref:`developing_modules`
Learn how to extend Ansible by writing your own modules
`GitHub Ansible examples <https://github.com/ansible/ansible-examples>`_
Complete playbook files from the GitHub project source
`Mailing List <https://groups.google.com/group/ansible-project>`_
Questions? Help? Ideas? Stop by the list on Google Groups

@ -1,611 +1,7 @@
.. _playbooks_reuse_roles:
:orphan:
*****
Roles
*****
Roles let you automatically load related vars, files, tasks, handlers, and other Ansible artifacts based on a known file structure. After you group your content in roles, you can easily reuse them and share them with other users.
.. contents::
:local:
Role directory structure
========================
An Ansible role has a defined directory structure with eight main standard directories. You must include at least one of these directories in each role. You can omit any directories the role does not use. For example:
.. code-block:: text
# playbooks
site.yml
webservers.yml
fooservers.yml
.. include:: shared_snippets/role_directory.txt
By default Ansible will look in each directory within a role for a ``main.yml`` file for relevant content (also ``main.yaml`` and ``main``):
- ``tasks/main.yml`` - the main list of tasks that the role executes.
- ``handlers/main.yml`` - handlers, which may be used within or outside this role.
- ``library/my_module.py`` - modules, which may be used within this role (see :ref:`embedding_modules_and_plugins_in_roles` for more information).
- ``defaults/main.yml`` - default variables for the role (see :ref:`playbooks_variables` for more information). These variables have the lowest priority of any variables available, and can be easily overridden by any other variable, including inventory variables.
- ``vars/main.yml`` - other variables for the role (see :ref:`playbooks_variables` for more information).
- ``files/main.yml`` - files that the role deploys.
- ``templates/main.yml`` - templates that the role deploys.
- ``meta/main.yml`` - metadata for the role, including role dependencies.
You can add other YAML files in some directories. For example, you can place platform-specific tasks in separate files and refer to them in the ``tasks/main.yml`` file:
.. code-block:: yaml
# roles/example/tasks/main.yml
- name: Install the correct web server for RHEL
import_tasks: redhat.yml
when: ansible_facts['os_family']|lower == 'redhat'
- name: Install the correct web server for Debian
import_tasks: debian.yml
when: ansible_facts['os_family']|lower == 'debian'
# roles/example/tasks/redhat.yml
- name: Install web server
ansible.builtin.yum:
name: "httpd"
state: present
# roles/example/tasks/debian.yml
- name: Install web server
ansible.builtin.apt:
name: "apache2"
state: present
Roles may also include modules and other plugin types in a directory called ``library``. For more information, please refer to :ref:`embedding_modules_and_plugins_in_roles` below.
.. _role_search_path:
Storing and finding roles
=========================
By default, Ansible looks for roles in the following locations:
- in collections, if you are using them
- in a directory called ``roles/``, relative to the playbook file
- in the configured :ref:`roles_path <DEFAULT_ROLES_PATH>`. The default search path is ``~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles``.
- in the directory where the playbook file is located
If you store your roles in a different location, set the :ref:`roles_path <DEFAULT_ROLES_PATH>` configuration option so Ansible can find your roles. Checking shared roles into a single location makes them easier to use in multiple playbooks. See :ref:`intro_configuration` for details about managing settings in ansible.cfg.
Alternatively, you can call a role with a fully qualified path:
.. code-block:: yaml
---
- hosts: webservers
roles:
- role: '/path/to/my/roles/common'
Using roles
===========
You can use roles in three ways:
- at the play level with the ``roles`` option: This is the classic way of using roles in a play.
- at the tasks level with ``include_role``: You can reuse roles dynamically anywhere in the ``tasks`` section of a play using ``include_role``.
- at the tasks level with ``import_role``: You can reuse roles statically anywhere in the ``tasks`` section of a play using ``import_role``.
.. _roles_keyword:
Using roles at the play level
-----------------------------
The classic (original) way to use roles is with the ``roles`` option for a given play:
.. code-block:: yaml
---
- hosts: webservers
roles:
- common
- webservers
When you use the ``roles`` option at the play level, for each role 'x':
- If roles/x/tasks/main.yml exists, Ansible adds the tasks in that file to the play.
- If roles/x/handlers/main.yml exists, Ansible adds the handlers in that file to the play.
- If roles/x/vars/main.yml exists, Ansible adds the variables in that file to the play.
- If roles/x/defaults/main.yml exists, Ansible adds the variables in that file to the play.
- If roles/x/meta/main.yml exists, Ansible adds any role dependencies in that file to the list of roles.
- Any copy, script, template or include tasks (in the role) can reference files in roles/x/{files,templates,tasks}/ (dir depends on task) without having to path them relatively or absolutely.
When you use the ``roles`` option at the play level, Ansible treats the roles as static imports and processes them during playbook parsing. Ansible executes each play in this order:
- Any ``pre_tasks`` defined in the play.
- Any handlers triggered by pre_tasks.
- Each role listed in ``roles:``, in the order listed. Any role dependencies defined in the role's ``meta/main.yml`` run first, subject to tag filtering and conditionals. See :ref:`role_dependencies` for more details.
- Any ``tasks`` defined in the play.
- Any handlers triggered by the roles or tasks.
- Any ``post_tasks`` defined in the play.
- Any handlers triggered by post_tasks.
.. note::
If using tags with tasks in a role, be sure to also tag your pre_tasks, post_tasks, and role dependencies and pass those along as well, especially if the pre/post tasks and role dependencies are used for monitoring outage window control or load balancing. See :ref:`tags` for details on adding and using tags.
You can pass other keywords to the ``roles`` option:
.. code-block:: yaml
---
- hosts: webservers
roles:
- common
- role: foo_app_instance
vars:
dir: '/opt/a'
app_port: 5000
tags: typeA
- role: foo_app_instance
vars:
dir: '/opt/b'
app_port: 5001
tags: typeB
When you add a tag to the ``role`` option, Ansible applies the tag to ALL tasks within the role.
When using ``vars:`` within the ``roles:`` section of a playbook, the variables are added to the play variables, making them available to all tasks within the play before and after the role. This behavior can be changed by :ref:`DEFAULT_PRIVATE_ROLE_VARS`.
Including roles: dynamic reuse
------------------------------
You can reuse roles dynamically anywhere in the ``tasks`` section of a play using ``include_role``. While roles added in a ``roles`` section run before any other tasks in a play, included roles run in the order they are defined. If there are other tasks before an ``include_role`` task, the other tasks will run first.
To include a role:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Print a message
ansible.builtin.debug:
msg: "this task runs before the example role"
- name: Include the example role
include_role:
name: example
- name: Print a message
ansible.builtin.debug:
msg: "this task runs after the example role"
You can pass other keywords, including variables and tags, when including roles:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Include the foo_app_instance role
include_role:
name: foo_app_instance
vars:
dir: '/opt/a'
app_port: 5000
tags: typeA
...
When you add a :ref:`tag <tags>` to an ``include_role`` task, Ansible applies the tag `only` to the include itself. This means you can pass ``--tags`` to run only selected tasks from the role, if those tasks themselves have the same tag as the include statement. See :ref:`selective_reuse` for details.
You can conditionally include a role:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Include the some_role role
include_role:
name: some_role
when: "ansible_facts['os_family'] == 'RedHat'"
Importing roles: static reuse
-----------------------------
You can reuse roles statically anywhere in the ``tasks`` section of a play using ``import_role``. The behavior is the same as using the ``roles`` keyword. For example:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Print a message
ansible.builtin.debug:
msg: "before we run our role"
- name: Import the example role
import_role:
name: example
- name: Print a message
ansible.builtin.debug:
msg: "after we ran our role"
You can pass other keywords, including variables and tags, when importing roles:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Import the foo_app_instance role
import_role:
name: foo_app_instance
vars:
dir: '/opt/a'
app_port: 5000
...
When you add a tag to an ``import_role`` statement, Ansible applies the tag to `all` tasks within the role. See :ref:`tag_inheritance` for details.
Role argument validation
========================
Beginning with version 2.11, you may choose to enable role argument validation based on an argument
specification. This specification is defined in the ``meta/argument_specs.yml`` file (or with the ``.yaml``
file extension). When this argument specification is defined, a new task is inserted at the beginning of role execution
that will validate the parameters supplied for the role against the specification. If the parameters fail
validation, the role will fail execution.
.. note::
Ansible also supports role specifications defined in the role ``meta/main.yml`` file, as well. However,
any role that defines the specs within this file will not work on versions below 2.11. For this reason,
we recommend using the ``meta/argument_specs.yml`` file to maintain backward compatibility.
.. note::
When role argument validation is used on a role that has defined :ref:`dependencies <role_dependencies>`,
then validation on those dependencies will run before the dependent role, even if argument validation fails
for the dependent role.
Specification format
--------------------
The role argument specification must be defined in a top-level ``argument_specs`` block within the
role ``meta/argument_specs.yml`` file. All fields are lower-case.
:entry-point-name:
* The name of the role entry point.
* This should be ``main`` in the case of an unspecified entry point.
* This will be the base name of the tasks file to execute, with no ``.yml`` or ``.yaml`` file extension.
:short_description:
* A short, one-line description of the entry point.
* The ``short_description`` is displayed by ``ansible-doc -t role -l``.
:description:
* A longer description that may contain multiple lines.
:author:
* Name of the entry point authors.
* Use a multi-line list if there is more than one author.
:options:
* Options are often called "parameters" or "arguments". This section defines those options.
* For each role option (argument), you may include:
:option-name:
* The name of the option/argument.
:description:
* Detailed explanation of what this option does. It should be written in full sentences.
:type:
* The data type of the option. See :ref:`Argument spec <argument_spec>` for allowed values for ``type``. Default is ``str``.
* If an option is of type ``list``, ``elements`` should be specified.
:required:
* Only needed if ``true``.
* If missing, the option is not required.
:default:
* If ``required`` is false/missing, ``default`` may be specified (assumed 'null' if missing).
* Ensure that the default value in the docs matches the default value in the code. The actual
default for the role variable will always come from ``defaults/main.yml``.
* The default field must not be listed as part of the description, unless it requires additional information or conditions.
* If the option is a boolean value, you can use any of the boolean values recognized by Ansible:
(such as true/false or yes/no). Choose the one that reads better in the context of the option.
:choices:
* List of option values.
* Should be absent if empty.
:elements:
* Specifies the data type for list elements when type is ``list``.
:options:
* If this option takes a dict or list of dicts, you can define the structure here.
Sample specification
--------------------
.. code-block:: yaml
# roles/myapp/meta/argument_specs.yml
---
argument_specs:
# roles/myapp/tasks/main.yml entry point
main:
short_description: The main entry point for the myapp role.
options:
myapp_int:
type: "int"
required: false
default: 42
description: "The integer value, defaulting to 42."
myapp_str:
type: "str"
required: true
description: "The string value"
# roles/maypp/tasks/alternate.yml entry point
alternate:
short_description: The alternate entry point for the myapp role.
options:
myapp_int:
type: "int"
required: false
default: 1024
description: "The integer value, defaulting to 1024."
.. _run_role_twice:
Running a role multiple times in one play
=========================================
Ansible only executes each role once in a play, even if you define it multiple times, unless the parameters defined on the role are different for each definition. For example, Ansible only runs the role ``foo`` once in a play like this:
.. code-block:: yaml
---
- hosts: webservers
roles:
- foo
- bar
- foo
You have two options to force Ansible to run a role more than once.
Passing different parameters
----------------------------
If you pass different parameters in each role definition, Ansible runs the role more than once. Providing different variable values is not the same as passing different role parameters. You must use the ``roles`` keyword for this behavior, since ``import_role`` and ``include_role`` do not accept role parameters.
This play runs the ``foo`` role twice:
.. code-block:: yaml
---
- hosts: webservers
roles:
- { role: foo, message: "first" }
- { role: foo, message: "second" }
This syntax also runs the ``foo`` role twice;
.. code-block:: yaml
---
- hosts: webservers
roles:
- role: foo
message: "first"
- role: foo
message: "second"
In these examples, Ansible runs ``foo`` twice because each role definition has different parameters.
Using ``allow_duplicates: true``
--------------------------------
Add ``allow_duplicates: true`` to the ``meta/main.yml`` file for the role:
.. code-block:: yaml
# playbook.yml
---
- hosts: webservers
roles:
- foo
- foo
# roles/foo/meta/main.yml
---
allow_duplicates: true
In this example, Ansible runs ``foo`` twice because we have explicitly enabled it to do so.
.. _role_dependencies:
Using role dependencies
=======================
Role dependencies let you automatically pull in other roles when using a role.
Role dependencies are prerequisites, not true dependencies. The roles do not have a parent/child relationship. Ansible loads all listed roles, runs the roles listed under ``dependencies`` first, then runs the role that lists them. The play object is the parent of all roles, including roles called by a ``dependencies`` list.
Role dependencies are stored in the ``meta/main.yml`` file within the role directory. This file should contain a list of roles and parameters to insert before the specified role. For example:
.. code-block:: yaml
# roles/myapp/meta/main.yml
---
dependencies:
- role: common
vars:
some_parameter: 3
- role: apache
vars:
apache_port: 80
- role: postgres
vars:
dbname: blarg
other_parameter: 12
Ansible always executes roles listed in ``dependencies`` before the role that lists them. Ansible executes this pattern recursively when you use the ``roles`` keyword. For example, if you list role ``foo`` under ``roles:``, role ``foo`` lists role ``bar`` under ``dependencies`` in its meta/main.yml file, and role ``bar`` lists role ``baz`` under ``dependencies`` in its meta/main.yml, Ansible executes ``baz``, then ``bar``, then ``foo``.
Running role dependencies multiple times in one play
----------------------------------------------------
Ansible treats duplicate role dependencies like duplicate roles listed under ``roles:``: Ansible only executes role dependencies once, even if defined multiple times, unless the parameters, tags, or when clause defined on the role are different for each definition. If two roles in a play both list a third role as a dependency, Ansible only runs that role dependency once, unless you pass different parameters, tags, when clause, or use ``allow_duplicates: true`` in the role you want to run multiple times. See :ref:`Galaxy role dependencies <galaxy_dependencies>` for more details.
.. note::
Role deduplication does not consult the invocation signature of parent roles. Additionally, when using ``vars:`` instead of role params, there is a side effect of changing variable scoping. Using ``vars:`` results in those variables being scoped at the play level. In the below example, using ``vars:`` would cause ``n`` to be defined as ``4`` through the entire play, including roles called before it.
In addition to the above, users should be aware that role de-duplication occurs before variable evaluation. This means that :term:`Lazy Evaluation` may make seemingly different role invocations equivalently the same, preventing the role from running more than once.
For example, a role named ``car`` depends on a role named ``wheel`` as follows:
.. code-block:: yaml
---
dependencies:
- role: wheel
n: 1
- role: wheel
n: 2
- role: wheel
n: 3
- role: wheel
n: 4
And the ``wheel`` role depends on two roles: ``tire`` and ``brake``. The ``meta/main.yml`` for wheel would then contain the following:
.. code-block:: yaml
---
dependencies:
- role: tire
- role: brake
And the ``meta/main.yml`` for ``tire`` and ``brake`` would contain the following:
.. code-block:: yaml
---
allow_duplicates: true
The resulting order of execution would be as follows:
.. code-block:: text
tire(n=1)
brake(n=1)
wheel(n=1)
tire(n=2)
brake(n=2)
wheel(n=2)
...
car
To use ``allow_duplicates: true`` with role dependencies, you must specify it for the role listed under ``dependencies``, not for the role that lists it. In the example above, ``allow_duplicates: true`` appears in the ``meta/main.yml`` of the ``tire`` and ``brake`` roles. The ``wheel`` role does not require ``allow_duplicates: true``, because each instance defined by ``car`` uses different parameter values.
.. note::
See :ref:`playbooks_variables` for details on how Ansible chooses among variable values defined in different places (variable inheritance and scope).
Also deduplication happens ONLY at the play level, so multiple plays in the same playbook may rerun the roles.
.. _embedding_modules_and_plugins_in_roles:
Embedding modules and plugins in roles
======================================
If you write a custom module (see :ref:`developing_modules`) or a plugin (see :ref:`developing_plugins`), you might wish to distribute it as part of a role. For example, if you write a module that helps configure your company's internal software, and you want other people in your organization to use this module, but you do not want to tell everyone how to configure their Ansible library path, you can include the module in your internal_config role.
To add a module or a plugin to a role:
Alongside the 'tasks' and 'handlers' structure of a role, add a directory named 'library' and then include the module directly inside the 'library' directory.
Assuming you had this:
.. code-block:: text
roles/
my_custom_modules/
library/
module1
module2
The module will be usable in the role itself, as well as any roles that are called *after* this role, as follows:
.. code-block:: yaml
---
- hosts: webservers
roles:
- my_custom_modules
- some_other_role_using_my_custom_modules
- yet_another_role_using_my_custom_modules
If necessary, you can also embed a module in a role to modify a module in Ansible's core distribution. For example, you can use the development version of a particular module before it is released in production releases by copying the module and embedding the copy in a role. Use this approach with caution, as API signatures may change in core components, and this workaround is not guaranteed to work.
The same mechanism can be used to embed and distribute plugins in a role, using the same schema. For example, for a filter plugin:
.. code-block:: text
roles/
my_custom_filter/
filter_plugins
filter1
filter2
These filters can then be used in a Jinja template in any role called after 'my_custom_filter'.
Sharing roles: Ansible Galaxy
=============================
`Ansible Galaxy <https://galaxy.ansible.com>`_ is a free site for finding, downloading, rating, and reviewing all kinds of community-developed Ansible roles and can be a great way to get a jumpstart on your automation projects.
The client ``ansible-galaxy`` is included in Ansible. The Galaxy client allows you to download roles from Ansible Galaxy, and also provides an excellent default framework for creating your own roles.
Read the `Ansible Galaxy documentation <https://galaxy.ansible.com/docs/>`_ page for more information
.. seealso::
:ref:`ansible_galaxy`
How to create new roles, share roles on Galaxy, role management
:ref:`yaml_syntax`
Learn about YAML syntax
:ref:`working_with_playbooks`
Review the basic Playbook language features
:ref:`tips_and_tricks`
Tips and tricks for playbooks
:ref:`playbooks_variables`
Variables in playbooks
:ref:`playbooks_conditionals`
Conditionals in playbooks
:ref:`playbooks_loops`
Loops in playbooks
:ref:`tags`
Using tags to select or skip roles/tasks in long playbooks
:ref:`list_of_collections`
Browse existing collections, modules, and plugins
:ref:`developing_modules`
Extending Ansible by writing your own modules
`GitHub Ansible examples <https://github.com/ansible/ansible-examples>`_
Complete playbook files from the GitHub project source
`Mailing List <https://groups.google.com/group/ansible-project>`_
Questions? Help? Ideas? Stop by the list on Google Groups
This page has moved to :ref:`playbooks_reuse_roles`.

@ -1,46 +1,7 @@
.. _playbooks_start_and_step:
:orphan:
***************************************
Executing playbooks for troubleshooting
***************************************
When you are testing new plays or debugging playbooks, you may need to run the same play multiple times. To make this more efficient, Ansible offers two alternative ways to execute a playbook: start-at-task and step mode.
.. _start_at_task:
start-at-task
-------------
To start executing your playbook at a particular task (usually the task that failed on the previous run), use the ``--start-at-task`` option.
.. code-block:: shell
ansible-playbook playbook.yml --start-at-task="install packages"
In this example, Ansible starts executing your playbook at a task named "install packages". This feature does not work with tasks inside dynamically re-used roles or tasks (``include_*``), see :ref:`dynamic_vs_static`.
.. _step:
Step mode
---------
To execute a playbook interactively, use ``--step``.
.. code-block:: shell
ansible-playbook playbook.yml --step
With this option, Ansible stops on each task, and asks if it should execute that task. For example, if you have a task called "configure ssh", the playbook run will stop and ask.
.. code-block:: shell
Perform task: configure ssh (y/n/c):
Answer "y" to execute the task, answer "n" to skip the task, and answer "c" to exit step mode, executing all remaining tasks without asking.
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbook_debugger`
Using the Ansible debugger
This page has moved to :ref:`playbooks_start_and_step`.

@ -1,254 +1,6 @@
.. _playbooks_strategies:
:orphan:
Controlling playbook execution: strategies and more
===================================================
By default, Ansible runs each task on all hosts affected by a play before starting the next task on any host, using 5 forks. If you want to change this default behavior, you can use a different strategy plugin, change the number of forks, or apply one of several keywords like ``serial``.
.. contents::
:local:
Selecting a strategy
--------------------
The default behavior described above is the :ref:`linear strategy<linear_strategy>`. Ansible offers other strategies, including the :ref:`debug strategy<debug_strategy>` (see also :ref:`playbook_debugger`) and the :ref:`free strategy<free_strategy>`, which allows each host to run until the end of the play as fast as it can:
.. code-block:: yaml
- hosts: all
strategy: free
tasks:
# ...
You can select a different strategy for each play as shown above, or set your preferred strategy globally in ``ansible.cfg``, under the ``defaults`` stanza:
.. code-block:: ini
[defaults]
strategy = free
All strategies are implemented as :ref:`strategy plugins<strategy_plugins>`. Please review the documentation for each strategy plugin for details on how it works.
Setting the number of forks
---------------------------
If you have the processing power available and want to use more forks, you can set the number in ``ansible.cfg``:
.. code-block:: ini
[defaults]
forks = 30
or pass it on the command line: `ansible-playbook -f 30 my_playbook.yml`.
Using keywords to control execution
-----------------------------------
In addition to strategies, several :ref:`keywords<playbook_keywords>` also affect play execution. You can set a number, a percentage, or a list of numbers of hosts you want to manage at a time with ``serial``. Ansible completes the play on the specified number or percentage of hosts before starting the next batch of hosts. You can restrict the number of workers allotted to a block or task with ``throttle``. You can control how Ansible selects the next host in a group to execute against with ``order``. You can run a task on a single host with ``run_once``. These keywords are not strategies. They are directives or options applied to a play, block, or task.
Other keywords that affect play execution include ``ignore_errors``, ``ignore_unreachable``, and ``any_errors_fatal``. These options are documented in :ref:`playbooks_error_handling`.
.. _rolling_update_batch_size:
Setting the batch size with ``serial``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, Ansible runs in parallel against all the hosts in the :ref:`pattern <intro_patterns>` you set in the ``hosts:`` field of each play. If you want to manage only a few machines at a time, for example during a rolling update, you can define how many hosts Ansible should manage at a single time using the ``serial`` keyword:
.. code-block:: yaml
---
- name: test play
hosts: webservers
serial: 3
gather_facts: False
tasks:
- name: first task
command: hostname
- name: second task
command: hostname
In the above example, if we had 6 hosts in the group 'webservers', Ansible would execute the play completely (both tasks) on 3 of the hosts before moving on to the next 3 hosts:
.. code-block:: ansible-output
PLAY [webservers] ****************************************
TASK [first task] ****************************************
changed: [web3]
changed: [web2]
changed: [web1]
TASK [second task] ***************************************
changed: [web1]
changed: [web2]
changed: [web3]
PLAY [webservers] ****************************************
TASK [first task] ****************************************
changed: [web4]
changed: [web5]
changed: [web6]
TASK [second task] ***************************************
changed: [web4]
changed: [web5]
changed: [web6]
PLAY RECAP ***********************************************
web1 : ok=2 changed=2 unreachable=0 failed=0
web2 : ok=2 changed=2 unreachable=0 failed=0
web3 : ok=2 changed=2 unreachable=0 failed=0
web4 : ok=2 changed=2 unreachable=0 failed=0
web5 : ok=2 changed=2 unreachable=0 failed=0
web6 : ok=2 changed=2 unreachable=0 failed=0
.. note::
Setting the batch size with ``serial`` changes the scope of the Ansible failures to the batch size, not the entire host list. You can use :ref:`ignore_unreachable <ignore_unreachable>` or :ref:`max_fail_percentage <maximum_failure_percentage>` to modify this behavior.
You can also specify a percentage with the ``serial`` keyword. Ansible applies the percentage to the total number of hosts in a play to determine the number of hosts per pass:
.. code-block:: yaml
---
- name: test play
hosts: webservers
serial: "30%"
If the number of hosts does not divide equally into the number of passes, the final pass contains the remainder. In this example, if you had 20 hosts in the webservers group, the first batch would contain 6 hosts, the second batch would contain 6 hosts, the third batch would contain 6 hosts, and the last batch would contain 2 hosts.
You can also specify batch sizes as a list. For example:
.. code-block:: yaml
---
- name: test play
hosts: webservers
serial:
- 1
- 5
- 10
In the above example, the first batch would contain a single host, the next would contain 5 hosts, and (if there are any hosts left), every following batch would contain either 10 hosts or all the remaining hosts, if fewer than 10 hosts remained.
You can list multiple batch sizes as percentages:
.. code-block:: yaml
---
- name: test play
hosts: webservers
serial:
- "10%"
- "20%"
- "100%"
You can also mix and match the values:
.. code-block:: yaml
---
- name: test play
hosts: webservers
serial:
- 1
- 5
- "20%"
.. note::
No matter how small the percentage, the number of hosts per pass will always be 1 or greater.
Restricting execution with ``throttle``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``throttle`` keyword limits the number of workers for a particular task. It can be set at the block and task level. Use ``throttle`` to restrict tasks that may be CPU-intensive or interact with a rate-limiting API:
.. code-block:: yaml
tasks:
- command: /path/to/cpu_intensive_command
throttle: 1
If you have already restricted the number of forks or the number of machines to execute against in parallel, you can reduce the number of workers with ``throttle``, but you cannot increase it. In other words, to have an effect, your ``throttle`` setting must be lower than your ``forks`` or ``serial`` setting if you are using them together.
Ordering execution based on inventory
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``order`` keyword controls the order in which hosts are run. Possible values for order are:
inventory:
(default) The order provided by the inventory for the selection requested (see note below)
reverse_inventory:
The same as above, but reversing the returned list
sorted:
Sorted alphabetically sorted by name
reverse_sorted:
Sorted by name in reverse alphabetical order
shuffle:
Randomly ordered on each run
.. note::
the 'inventory' order does not equate to the order in which hosts/groups are defined in the inventory source file, but the 'order in which a selection is returned from the compiled inventory'. This is a backwards compatible option and while reproducible it is not normally predictable. Due to the nature of inventory, host patterns, limits, inventory plugins and the ability to allow multiple sources it is almost impossible to return such an order. For simple cases this might happen to match the file definition order, but that is not guaranteed.
.. _run_once:
Running on a single machine with ``run_once``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you want a task to run only on the first host in your batch of hosts, set ``run_once`` to true on that task:
.. code-block:: yaml
---
# ...
tasks:
# ...
- command: /opt/application/upgrade_db.py
run_once: true
# ...
Ansible executes this task on the first host in the current batch and applies all results and facts to all the hosts in the same batch. This approach is similar to applying a conditional to a task such as:
.. code-block:: yaml
- command: /opt/application/upgrade_db.py
when: inventory_hostname == webservers[0]
However, with ``run_once``, the results are applied to all the hosts. To run the task on a specific host, instead of the first host in the batch, delegate the task:
.. code-block:: yaml
- command: /opt/application/upgrade_db.py
run_once: true
delegate_to: web01.example.org
As always with :ref:`delegation <playbooks_delegation>`, the action will be executed on the delegated host, but the information is still that of the original host in the task.
.. note::
When used together with ``serial``, tasks marked as ``run_once`` will be run on one host in *each* serial batch. If the task must run only once regardless of ``serial`` mode, use
:code:`when: inventory_hostname == ansible_play_hosts_all[0]` construct.
.. note::
Any conditional (in other words, `when:`) will use the variables of the 'first host' to decide if the task runs or not, no other hosts will be tested.
.. note::
If you want to avoid the default behavior of setting the fact for all hosts, set ``delegate_facts: True`` for the specific task or block.
.. seealso::
:ref:`about_playbooks`
An introduction to playbooks
:ref:`playbooks_delegation`
Running tasks on or assigning facts to specific machines
:ref:`playbooks_reuse_roles`
Playbook organization by roles
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_strategies`.

@ -1,432 +1,7 @@
.. _tags:
:orphan:
****
Tags
****
If you have a large playbook, it may be useful to run only specific parts of it instead of running the entire playbook. You can do this with Ansible tags. Using tags to execute or skip selected tasks is a two-step process:
#. Add tags to your tasks, either individually or with tag inheritance from a block, play, role, or import.
#. Select or skip tags when you run your playbook.
.. contents::
:local:
Adding tags with the tags keyword
=================================
You can add tags to a single task or include. You can also add tags to multiple tasks by defining them at the level of a block, play, role, or import. The keyword ``tags`` addresses all these use cases. The ``tags`` keyword always defines tags and adds them to tasks; it does not select or skip tasks for execution. You can only select or skip tasks based on tags at the command line when you run a playbook. See :ref:`using_tags` for more details.
Adding tags to individual tasks
-------------------------------
At the simplest level, you can apply one or more tags to an individual task. You can add tags to tasks in playbooks, in task files, or within a role. Here is an example that tags two tasks with different tags:
.. code-block:: yaml
tasks:
- name: Install the servers
ansible.builtin.yum:
name:
- httpd
- memcached
state: present
tags:
- packages
- webservers
- name: Configure the service
ansible.builtin.template:
src: templates/src.j2
dest: /etc/foo.conf
tags:
- configuration
You can apply the same tag to more than one individual task. This example tags several tasks with the same tag, "ntp":
.. code-block:: yaml
---
# file: roles/common/tasks/main.yml
- name: Install ntp
ansible.builtin.yum:
name: ntp
state: present
tags: ntp
- name: Configure ntp
ansible.builtin.template:
src: ntp.conf.j2
dest: /etc/ntp.conf
notify:
- restart ntpd
tags: ntp
- name: Enable and run ntpd
ansible.builtin.service:
name: ntpd
state: started
enabled: yes
tags: ntp
- name: Install NFS utils
ansible.builtin.yum:
name:
- nfs-utils
- nfs-util-lib
state: present
tags: filesharing
If you ran these four tasks in a playbook with ``--tags ntp``, Ansible would run the three tasks tagged ``ntp`` and skip the one task that does not have that tag.
.. _tags_on_includes:
Adding tags to includes
-----------------------
You can apply tags to dynamic includes in a playbook. As with tags on an individual task, tags on an ``include_*`` task apply only to the include itself, not to any tasks within the included file or role. If you add ``mytag`` to a dynamic include, then run that playbook with ``--tags mytag``, Ansible runs the include itself, runs any tasks within the included file or role tagged with ``mytag``, and skips any tasks within the included file or role without that tag. See :ref:`selective_reuse` for more details.
You add tags to includes the same way you add tags to any other task:
.. code-block:: yaml
---
# file: roles/common/tasks/main.yml
- name: Dynamic re-use of database tasks
include_tasks: db.yml
tags: db
You can add a tag only to the dynamic include of a role. In this example, the ``foo`` tag will `not` apply to tasks inside the ``bar`` role:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Include the bar role
include_role:
name: bar
tags:
- foo
With plays, blocks, the ``role`` keyword, and static imports, Ansible applies tag inheritance, adding the tags you define to every task inside the play, block, role, or imported file. However, tag inheritance does *not* apply to dynamic re-use with ``include_role`` and ``include_tasks``. With dynamic re-use (includes), the tags you define apply only to the include itself. If you need tag inheritance, use a static import. If you cannot use an import because the rest of your playbook uses includes, see :ref:`apply_keyword` for ways to work around this behavior.
.. _tag_inheritance:
Tag inheritance: adding tags to multiple tasks
----------------------------------------------
If you want to apply the same tag or tags to multiple tasks without adding a ``tags`` line to every task, you can define the tags at the level of your play or block, or when you add a role or import a file. Ansible applies the tags down the dependency chain to all child tasks. With roles and imports, Ansible appends the tags set by the ``roles`` section or import to any tags set on individual tasks or blocks within the role or imported file. This is called tag inheritance. Tag inheritance is convenient, because you do not have to tag every task. However, the tags still apply to the tasks individually.
Adding tags to blocks
^^^^^^^^^^^^^^^^^^^^^
If you want to apply a tag to many, but not all, of the tasks in your play, use a :ref:`block <playbooks_blocks>` and define the tags at that level. For example, we could edit the NTP example shown above to use a block:
.. code-block:: yaml
# myrole/tasks/main.yml
- name: ntp tasks
tags: ntp
block:
- name: Install ntp
ansible.builtin.yum:
name: ntp
state: present
- name: Configure ntp
ansible.builtin.template:
src: ntp.conf.j2
dest: /etc/ntp.conf
notify:
- restart ntpd
- name: Enable and run ntpd
ansible.builtin.service:
name: ntpd
state: started
enabled: yes
- name: Install NFS utils
ansible.builtin.yum:
name:
- nfs-utils
- nfs-util-lib
state: present
tags: filesharing
Adding tags to plays
^^^^^^^^^^^^^^^^^^^^
If all the tasks in a play should get the same tag, you can add the tag at the level of the play. For example, if you had a play with only the NTP tasks, you could tag the entire play:
.. code-block:: yaml
- hosts: all
tags: ntp
tasks:
- name: Install ntp
ansible.builtin.yum:
name: ntp
state: present
- name: Configure ntp
ansible.builtin.template:
src: ntp.conf.j2
dest: /etc/ntp.conf
notify:
- restart ntpd
- name: Enable and run ntpd
ansible.builtin.service:
name: ntpd
state: started
enabled: yes
- hosts: fileservers
tags: filesharing
tasks:
...
Adding tags to roles
^^^^^^^^^^^^^^^^^^^^
There are three ways to add tags to roles:
#. Add the same tag or tags to all tasks in the role by setting tags under ``roles``. See examples in this section.
#. Add the same tag or tags to all tasks in the role by setting tags on a static ``import_role`` in your playbook. See examples in :ref:`tags_on_imports`.
#. Add a tag or tags to individual tasks or blocks within the role itself. This is the only approach that allows you to select or skip some tasks within the role. To select or skip tasks within the role, you must have tags set on individual tasks or blocks, use the dynamic ``include_role`` in your playbook, and add the same tag or tags to the include. When you use this approach, and then run your playbook with ``--tags foo``, Ansible runs the include itself plus any tasks in the role that also have the tag ``foo``. See :ref:`tags_on_includes` for details.
When you incorporate a role in your playbook statically with the ``roles`` keyword, Ansible adds any tags you define to all the tasks in the role. For example:
.. code-block:: yaml
roles:
- role: webserver
vars:
port: 5000
tags: [ web, foo ]
or:
.. code-block:: yaml
---
- hosts: webservers
roles:
- role: foo
tags:
- bar
- baz
# using YAML shorthand, this is equivalent to:
# - { role: foo, tags: ["bar", "baz"] }
.. _tags_on_imports:
Adding tags to imports
^^^^^^^^^^^^^^^^^^^^^^
You can also apply a tag or tags to all the tasks imported by the static ``import_role`` and ``import_tasks`` statements:
.. code-block:: yaml
---
- hosts: webservers
tasks:
- name: Import the foo role
import_role:
name: foo
tags:
- bar
- baz
- name: Import tasks from foo.yml
import_tasks: foo.yml
tags: [ web, foo ]
.. _apply_keyword:
Tag inheritance for includes: blocks and the ``apply`` keyword
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, Ansible does not apply :ref:`tag inheritance <tag_inheritance>` to dynamic re-use with ``include_role`` and ``include_tasks``. If you add tags to an include, they apply only to the include itself, not to any tasks in the included file or role. This allows you to execute selected tasks within a role or task file - see :ref:`selective_reuse` when you run your playbook.
If you want tag inheritance, you probably want to use imports. However, using both includes and imports in a single playbook can lead to difficult-to-diagnose bugs. For this reason, if your playbook uses ``include_*`` to re-use roles or tasks, and you need tag inheritance on one include, Ansible offers two workarounds. You can use the ``apply`` keyword:
.. code-block:: yaml
- name: Apply the db tag to the include and to all tasks in db.yaml
include_tasks:
file: db.yml
# adds 'db' tag to tasks within db.yml
apply:
tags: db
# adds 'db' tag to this 'include_tasks' itself
tags: db
Or you can use a block:
.. code-block:: yaml
- block:
- name: Include tasks from db.yml
include_tasks: db.yml
tags: db
.. _special_tags:
Special tags: always and never
==============================
Ansible reserves two tag names for special behavior: always and never. If you assign the ``always`` tag to a task or play, Ansible will always run that task or play, unless you specifically skip it (``--skip-tags always``).
For example:
.. code-block:: yaml
tasks:
- name: Print a message
ansible.builtin.debug:
msg: "Always runs"
tags:
- always
- name: Print a message
ansible.builtin.debug:
msg: "runs when you use tag1"
tags:
- tag1
.. warning::
* Fact gathering is tagged with 'always' by default. It is only skipped if
you apply a tag and then use a different tag in ``--tags`` or the same
tag in ``--skip-tags``.
.. warning::
* The role argument specification validation task is tagged with 'always' by default. This validation
will be skipped if you use ``--skip-tags always``.
.. versionadded:: 2.5
If you assign the ``never`` tag to a task or play, Ansible will skip that task or play unless you specifically request it (``--tags never``).
For example:
.. code-block:: yaml
tasks:
- name: Run the rarely-used debug task
ansible.builtin.debug:
msg: '{{ showmevar }}'
tags: [ never, debug ]
The rarely-used debug task in the example above only runs when you specifically request the ``debug`` or ``never`` tags.
.. _using_tags:
Selecting or skipping tags when you run a playbook
==================================================
Once you have added tags to your tasks, includes, blocks, plays, roles, and imports, you can selectively execute or skip tasks based on their tags when you run :ref:`ansible-playbook`. Ansible runs or skips all tasks with tags that match the tags you pass at the command line. If you have added a tag at the block or play level, with ``roles``, or with an import, that tag applies to every task within the block, play, role, or imported role or file. If you have a role with lots of tags and you want to call subsets of the role at different times, either :ref:`use it with dynamic includes <selective_reuse>`, or split the role into multiple roles.
:ref:`ansible-playbook` offers five tag-related command-line options:
* ``--tags all`` - run all tasks, ignore tags (default behavior)
* ``--tags [tag1, tag2]`` - run only tasks with either the tag ``tag1`` or the tag ``tag2``
* ``--skip-tags [tag3, tag4]`` - run all tasks except those with either the tag ``tag3`` or the tag ``tag4``
* ``--tags tagged`` - run only tasks with at least one tag
* ``--tags untagged`` - run only tasks with no tags
For example, to run only tasks and blocks tagged ``configuration`` and ``packages`` in a very long playbook:
.. code-block:: bash
ansible-playbook example.yml --tags "configuration,packages"
To run all tasks except those tagged ``packages``:
.. code-block:: bash
ansible-playbook example.yml --skip-tags "packages"
Previewing the results of using tags
------------------------------------
When you run a role or playbook, you might not know or remember which tasks have which tags, or which tags exist at all. Ansible offers two command-line flags for :ref:`ansible-playbook` that help you manage tagged playbooks:
* ``--list-tags`` - generate a list of available tags
* ``--list-tasks`` - when used with ``--tags tagname`` or ``--skip-tags tagname``, generate a preview of tagged tasks
For example, if you do not know whether the tag for configuration tasks is ``config`` or ``conf`` in a playbook, role, or tasks file, you can display all available tags without running any tasks:
.. code-block:: bash
ansible-playbook example.yml --list-tags
If you do not know which tasks have the tags ``configuration`` and ``packages``, you can pass those tags and add ``--list-tasks``. Ansible lists the tasks but does not execute any of them.
.. code-block:: bash
ansible-playbook example.yml --tags "configuration,packages" --list-tasks
These command-line flags have one limitation: they cannot show tags or tasks within dynamically included files or roles. See :ref:`dynamic_vs_static` for more information on differences between static imports and dynamic includes.
.. _selective_reuse:
Selectively running tagged tasks in re-usable files
---------------------------------------------------
If you have a role or a tasks file with tags defined at the task or block level, you can selectively run or skip those tagged tasks in a playbook if you use a dynamic include instead of a static import. You must use the same tag on the included tasks and on the include statement itself. For example you might create a file with some tagged and some untagged tasks:
.. code-block:: yaml
# mixed.yml
tasks:
- name: Run the task with no tags
ansible.builtin.debug:
msg: this task has no tags
- name: Run the tagged task
ansible.builtin.debug:
msg: this task is tagged with mytag
tags: mytag
- block:
- name: Run the first block task with mytag
...
- name: Run the second block task with mytag
...
tags:
- mytag
And you might include the tasks file above in a playbook:
.. code-block:: yaml
# myplaybook.yml
- hosts: all
tasks:
- name: Run tasks from mixed.yml
include_tasks:
name: mixed.yml
tags: mytag
When you run the playbook with ``ansible-playbook -i hosts myplaybook.yml --tags "mytag"``, Ansible skips the task with no tags, runs the tagged individual task, and runs the two tasks in the block.
Configuring tags globally
-------------------------
If you run or skip certain tags by default, you can use the :ref:`TAGS_RUN` and :ref:`TAGS_SKIP` options in Ansible configuration to set those defaults.
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_reuse_roles`
Playbook organization by roles
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`tags`.

@ -1,61 +0,0 @@
.. _playbooks_templating:
*******************
Templating (Jinja2)
*******************
Ansible uses Jinja2 templating to enable dynamic expressions and access to :ref:`variables <playbooks_variables>` and :ref:`facts <vars_and_facts>`. You can use templating with the :ref:`template module <template_module>`. For example, you can create a template for a configuration file, then deploy that configuration file to multiple environments and supply the correct data (IP address, hostname, version) for each environment. You can also use templating in playbooks directly, by templating task names and more. You can use all the :ref:`standard filters and tests <jinja2:builtin-filters>` included in Jinja2. Ansible includes additional specialized filters for selecting and transforming data, tests for evaluating template expressions, and :ref:`lookup_plugins` for retrieving data from external sources such as files, APIs, and databases for use in templating.
All templating happens on the Ansible controller **before** the task is sent and executed on the target machine. This approach minimizes the package requirements on the target (jinja2 is only required on the controller). It also limits the amount of data Ansible passes to the target machine. Ansible parses templates on the controller and passes only the information needed for each task to the target machine, instead of passing all the data on the controller and parsing it on the target.
.. note::
Files and data used by the :ref:`template module <template_module>` must be utf-8 encoded.
.. contents::
:local:
.. toctree::
:maxdepth: 2
playbooks_filters
playbooks_tests
playbooks_lookups
playbooks_python_version
.. _templating_now:
Get the current time
====================
.. versionadded:: 2.8
The ``now()`` Jinja2 function retrieves a Python datetime object or a string representation for the current time.
The ``now()`` function supports 2 arguments:
utc
Specify ``True`` to get the current time in UTC. Defaults to ``False``.
fmt
Accepts a `strftime <https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior>`_ string that returns a formatted date time string.
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_loops`
Looping in playbooks
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`tips_and_tricks`
Tips and tricks for playbooks
`Jinja2 Docs <https://jinja.palletsprojects.com/en/latest/templates/>`_
Jinja2 documentation, includes the syntax and semantics of the templates
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels

@ -1,524 +1,7 @@
.. _playbooks_tests:
:orphan:
*****
Tests
*****
`Tests <https://jinja.palletsprojects.com/en/latest/templates/#tests>`_ in Jinja are a way of evaluating template expressions and returning True or False. Jinja ships with many of these. See `builtin tests`_ in the official Jinja template documentation.
The main difference between tests and filters are that Jinja tests are used for comparisons, whereas filters are used for data manipulation, and have different applications in jinja. Tests can also be used in list processing filters, like ``map()`` and ``select()`` to choose items in the list.
Like all templating, tests always execute on the Ansible controller, **not** on the target of a task, as they test local data.
In addition to those Jinja2 tests, Ansible supplies a few more and users can easily create their own.
.. contents::
:local:
.. _test_syntax:
Test syntax
===========
`Test syntax <https://jinja.palletsprojects.com/en/latest/templates/#tests>`_ varies from `filter syntax <https://jinja.palletsprojects.com/en/latest/templates/#filters>`_ (``variable | filter``). Historically Ansible has registered tests as both jinja tests and jinja filters, allowing for them to be referenced using filter syntax.
As of Ansible 2.5, using a jinja test as a filter will generate a deprecation warning. As of Ansible 2.9+ using jinja test syntax is required.
The syntax for using a jinja test is as follows
.. code-block:: console
variable is test_name
Such as
.. code-block:: console
result is failed
.. _testing_strings:
Testing strings
===============
To match strings against a substring or a regular expression, use the ``match``, ``search`` or ``regex`` tests
.. code-block:: yaml
vars:
url: "https://example.com/users/foo/resources/bar"
tasks:
- debug:
msg: "matched pattern 1"
when: url is match("https://example.com/users/.*/resources")
- debug:
msg: "matched pattern 2"
when: url is search("users/.*/resources/.*")
- debug:
msg: "matched pattern 3"
when: url is search("users")
- debug:
msg: "matched pattern 4"
when: url is regex("example\.com/\w+/foo")
``match`` succeeds if it finds the pattern at the beginning of the string, while ``search`` succeeds if it finds the pattern anywhere within string. By default, ``regex`` works like ``search``, but ``regex`` can be configured to perform other tests as well, by passing the ``match_type`` keyword argument. In particular, ``match_type`` determines the ``re`` method that gets used to perform the search. The full list can be found in the relevant Python documentation `here <https://docs.python.org/3/library/re.html#regular-expression-objects>`_.
All of the string tests also take optional ``ignorecase`` and ``multiline`` arguments. These correspond to ``re.I`` and ``re.M`` from Python's ``re`` library, respectively.
.. _testing_vault:
Vault
=====
.. versionadded:: 2.10
You can test whether a variable is an inline single vault encrypted value using the ``vault_encrypted`` test.
.. code-block:: yaml
vars:
variable: !vault |
$ANSIBLE_VAULT;1.2;AES256;dev
61323931353866666336306139373937316366366138656131323863373866376666353364373761
3539633234313836346435323766306164626134376564330a373530313635343535343133316133
36643666306434616266376434363239346433643238336464643566386135356334303736353136
6565633133366366360a326566323363363936613664616364623437336130623133343530333739
3039
tasks:
- debug:
msg: '{{ (variable is vault_encrypted) | ternary("Vault encrypted", "Not vault encrypted") }}'
.. _testing_truthiness:
Testing truthiness
==================
.. versionadded:: 2.10
As of Ansible 2.10, you can now perform Python like truthy and falsy checks.
.. code-block:: yaml
- debug:
msg: "Truthy"
when: value is truthy
vars:
value: "some string"
- debug:
msg: "Falsy"
when: value is falsy
vars:
value: ""
Additionally, the ``truthy`` and ``falsy`` tests accept an optional parameter called ``convert_bool`` that will attempt
to convert boolean indicators to actual booleans.
.. code-block:: yaml
- debug:
msg: "Truthy"
when: value is truthy(convert_bool=True)
vars:
value: "yes"
- debug:
msg: "Falsy"
when: value is falsy(convert_bool=True)
vars:
value: "off"
.. _testing_versions:
Comparing versions
==================
.. versionadded:: 1.6
.. note:: In 2.5 ``version_compare`` was renamed to ``version``
To compare a version number, such as checking if the ``ansible_facts['distribution_version']``
version is greater than or equal to '12.04', you can use the ``version`` test.
The ``version`` test can also be used to evaluate the ``ansible_facts['distribution_version']``
.. code-block:: yaml+jinja
{{ ansible_facts['distribution_version'] is version('12.04', '>=') }}
If ``ansible_facts['distribution_version']`` is greater than or equal to 12.04, this test returns True, otherwise False.
The ``version`` test accepts the following operators
.. code-block:: console
<, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne
This test also accepts a 3rd parameter, ``strict`` which defines if strict version parsing as defined by ``ansible.module_utils.compat.version.StrictVersion`` should be used. The default is ``False`` (using ``ansible.module_utils.compat.version.LooseVersion``), ``True`` enables strict version parsing
.. code-block:: yaml+jinja
{{ sample_version_var is version('1.0', operator='lt', strict=True) }}
As of Ansible 2.11 the ``version`` test accepts a ``version_type`` parameter which is mutually exclusive with ``strict``, and accepts the following values
.. code-block:: console
loose, strict, semver, semantic
Using ``version_type`` to compare a semantic version would be achieved like the following
.. code-block:: yaml+jinja
{{ sample_semver_var is version('2.0.0-rc.1+build.123', 'lt', version_type='semver') }}
When using ``version`` in a playbook or role, don't use ``{{ }}`` as described in the `FAQ <https://docs.ansible.com/ansible/latest/reference_appendices/faq.html#when-should-i-use-also-how-to-interpolate-variables-or-dynamic-variable-names>`_
.. code-block:: yaml
vars:
my_version: 1.2.3
tasks:
- debug:
msg: "my_version is higher than 1.0.0"
when: my_version is version('1.0.0', '>')
.. _math_tests:
Set theory tests
================
.. versionadded:: 2.1
.. note:: In 2.5 ``issubset`` and ``issuperset`` were renamed to ``subset`` and ``superset``
To see if a list includes or is included by another list, you can use 'subset' and 'superset'
.. code-block:: yaml
vars:
a: [1,2,3,4,5]
b: [2,3]
tasks:
- debug:
msg: "A includes B"
when: a is superset(b)
- debug:
msg: "B is included in A"
when: b is subset(a)
.. _contains_test:
Testing if a list contains a value
==================================
.. versionadded:: 2.8
Ansible includes a ``contains`` test which operates similarly, but in reverse of the Jinja2 provided ``in`` test.
The ``contains`` test is designed to work with the ``select``, ``reject``, ``selectattr``, and ``rejectattr`` filters
.. code-block:: yaml
vars:
lacp_groups:
- master: lacp0
network: 10.65.100.0/24
gateway: 10.65.100.1
dns4:
- 10.65.100.10
- 10.65.100.11
interfaces:
- em1
- em2
- master: lacp1
network: 10.65.120.0/24
gateway: 10.65.120.1
dns4:
- 10.65.100.10
- 10.65.100.11
interfaces:
- em3
- em4
tasks:
- debug:
msg: "{{ (lacp_groups|selectattr('interfaces', 'contains', 'em1')|first).master }}"
Testing if a list value is True
===============================
.. versionadded:: 2.4
You can use `any` and `all` to check if any or all elements in a list are true or not
.. code-block:: yaml
vars:
mylist:
- 1
- "{{ 3 == 3 }}"
- True
myotherlist:
- False
- True
tasks:
- debug:
msg: "all are true!"
when: mylist is all
- debug:
msg: "at least one is true"
when: myotherlist is any
.. _path_tests:
Testing paths
=============
.. note:: In 2.5 the following tests were renamed to remove the ``is_`` prefix
The following tests can provide information about a path on the controller
.. code-block:: yaml
- debug:
msg: "path is a directory"
when: mypath is directory
- debug:
msg: "path is a file"
when: mypath is file
- debug:
msg: "path is a symlink"
when: mypath is link
- debug:
msg: "path already exists"
when: mypath is exists
- debug:
msg: "path is {{ (mypath is abs)|ternary('absolute','relative')}}"
- debug:
msg: "path is the same file as path2"
when: mypath is same_file(path2)
- debug:
msg: "path is a mount"
when: mypath is mount
- debug:
msg: "path is a directory"
when: mypath is directory
vars:
mypath: /my/patth
- debug:
msg: "path is a file"
when: "'/my/path' is file"
Testing size formats
====================
The ``human_readable`` and ``human_to_bytes`` functions let you test your
playbooks to make sure you are using the right size format in your tasks, and that
you provide Byte format to computers and human-readable format to people.
Human readable
--------------
Asserts whether the given string is human readable or not.
For example
.. code-block:: yaml+jinja
- name: "Human Readable"
assert:
that:
- '"1.00 Bytes" == 1|human_readable'
- '"1.00 bits" == 1|human_readable(isbits=True)'
- '"10.00 KB" == 10240|human_readable'
- '"97.66 MB" == 102400000|human_readable'
- '"0.10 GB" == 102400000|human_readable(unit="G")'
- '"0.10 Gb" == 102400000|human_readable(isbits=True, unit="G")'
This would result in
.. code-block:: json
{ "changed": false, "msg": "All assertions passed" }
Human to bytes
--------------
Returns the given string in the Bytes format.
For example
.. code-block:: yaml+jinja
- name: "Human to Bytes"
assert:
that:
- "{{'0'|human_to_bytes}} == 0"
- "{{'0.1'|human_to_bytes}} == 0"
- "{{'0.9'|human_to_bytes}} == 1"
- "{{'1'|human_to_bytes}} == 1"
- "{{'10.00 KB'|human_to_bytes}} == 10240"
- "{{ '11 MB'|human_to_bytes}} == 11534336"
- "{{ '1.1 GB'|human_to_bytes}} == 1181116006"
- "{{'10.00 Kb'|human_to_bytes(isbits=True)}} == 10240"
This would result in
.. code-block:: json
{ "changed": false, "msg": "All assertions passed" }
.. _test_task_results:
Testing task results
====================
The following tasks are illustrative of the tests meant to check the status of tasks
.. code-block:: yaml
tasks:
- shell: /usr/bin/foo
register: result
ignore_errors: True
- debug:
msg: "it failed"
when: result is failed
# in most cases you'll want a handler, but if you want to do something right now, this is nice
- debug:
msg: "it changed"
when: result is changed
- debug:
msg: "it succeeded in Ansible >= 2.1"
when: result is succeeded
- debug:
msg: "it succeeded"
when: result is success
- debug:
msg: "it was skipped"
when: result is skipped
.. note:: From 2.1, you can also use success, failure, change, and skip so that the grammar matches, for those who need to be strict about it.
.. _type_tests:
Type Tests
==========
When looking to determine types, it may be tempting to use the ``type_debug`` filter and compare that to the string name of that type, however, you should instead use type test comparisons, such as:
.. code-block:: yaml
tasks:
- name: "String interpretation"
vars:
a_string: "A string"
a_dictionary: {"a": "dictionary"}
a_list: ["a", "list"]
assert:
that:
# Note that a string is classed as also being "iterable", "sequence" and "mapping"
- a_string is string
# Note that a dictionary is classed as not being a "string", but is "iterable", "sequence" and "mapping"
- a_dictionary is not string and a_dictionary is mapping
# Note that a list is classed as not being a "string" or "mapping" but is "iterable" and "sequence"
- a_list is not string and a_list is not mapping and a_list is iterable
- name: "Number interpretation"
vars:
a_float: 1.01
a_float_as_string: "1.01"
an_integer: 1
an_integer_as_string: "1"
assert:
that:
# Both a_float and an_integer are "number", but each has their own type as well
- a_float is number and a_float is float
- an_integer is number and an_integer is integer
# Both a_float_as_string and an_integer_as_string are not numbers
- a_float_as_string is not number and a_float_as_string is string
- an_integer_as_string is not number and a_float_as_string is string
# a_float or a_float_as_string when cast to a float and then to a string should match the same value cast only to a string
- a_float | float | string == a_float | string
- a_float_as_string | float | string == a_float_as_string | string
# Likewise an_integer and an_integer_as_string when cast to an integer and then to a string should match the same value cast only to an integer
- an_integer | int | string == an_integer | string
- an_integer_as_string | int | string == an_integer_as_string | string
# However, a_float or a_float_as_string cast as an integer and then a string does not match the same value cast to a string
- a_float | int | string != a_float | string
- a_float_as_string | int | string != a_float_as_string | string
# Again, Likewise an_integer and an_integer_as_string cast as a float and then a string does not match the same value cast to a string
- an_integer | float | string != an_integer | string
- an_integer_as_string | float | string != an_integer_as_string | string
- name: "Native Boolean interpretation"
loop:
- yes
- true
- True
- TRUE
- no
- No
- NO
- false
- False
- FALSE
assert:
that:
# Note that while other values may be cast to boolean values, these are the only ones which are natively considered boolean
# Note also that `yes` is the only case sensitive variant of these values.
- item is boolean
.. _builtin tests: https://jinja.palletsprojects.com/en/latest/templates/#builtin-tests
.. seealso::
:ref:`playbooks_intro`
An introduction to playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_variables`
All about variables
:ref:`playbooks_loops`
Looping in playbooks
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`tips_and_tricks`
Tips and tricks for playbooks
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_tests`.

@ -1,512 +1,7 @@
.. _playbooks_variables:
:orphan:
***************
Using Variables
***************
Ansible uses variables to manage differences between systems. With Ansible, you can execute tasks and playbooks on multiple different systems with a single command. To represent the variations among those different systems, you can create variables with standard YAML syntax, including lists and dictionaries. You can define these variables in your playbooks, in your :ref:`inventory <intro_inventory>`, in re-usable :ref:`files <playbooks_reuse>` or :ref:`roles <playbooks_reuse_roles>`, or at the command line. You can also create variables during a playbook run by registering the return value or values of a task as a new variable.
After you create variables, either by defining them in a file, passing them at the command line, or registering the return value or values of a task as a new variable, you can use those variables in module arguments, in :ref:`conditional "when" statements <playbooks_conditionals>`, in :ref:`templates <playbooks_templating>`, and in :ref:`loops <playbooks_loops>`. The `ansible-examples github repository <https://github.com/ansible/ansible-examples>`_ contains many examples of using variables in Ansible.
Once you understand the concepts and examples on this page, read about :ref:`Ansible facts <vars_and_facts>`, which are variables you retrieve from remote systems.
.. contents::
:local:
.. _valid_variable_names:
Creating valid variable names
=============================
Not all strings are valid Ansible variable names. A variable name can only include letters, numbers, and underscores. `Python keywords`_ or :ref:`playbook keywords<playbook_keywords>` are not valid variable names. A variable name cannot begin with a number.
Variable names can begin with an underscore. In many programming languages, variables that begin with an underscore are private. This is not true in Ansible. Variables that begin with an underscore are treated exactly the same as any other variable. Do not rely on this convention for privacy or security.
This table gives examples of valid and invalid variable names:
.. table::
:class: documentation-table
====================== ====================================================================
Valid variable names Not valid
====================== ====================================================================
``foo`` ``*foo``, `Python keywords`_ such as ``async`` and ``lambda``
``foo_env`` :ref:`playbook keywords<playbook_keywords>` such as ``environment``
``foo_port`` ``foo-port``, ``foo port``, ``foo.port``
``foo5``, ``_foo`` ``5foo``, ``12``
====================== ====================================================================
.. _Python keywords: https://docs.python.org/3/reference/lexical_analysis.html#keywords
Simple variables
================
Simple variables combine a variable name with a single value. You can use this syntax (and the syntax for lists and dictionaries shown below) in a variety of places. For details about setting variables in inventory, in playbooks, in reusable files, in roles, or at the command line, see :ref:`setting_variables`.
Defining simple variables
-------------------------
You can define a simple variable using standard YAML syntax. For example:
.. code-block:: text
remote_install_path: /opt/my_app_config
Referencing simple variables
----------------------------
After you define a variable, use Jinja2 syntax to reference it. Jinja2 variables use double curly braces. For example, the expression ``My amp goes to {{ max_amp_value }}`` demonstrates the most basic form of variable substitution. You can use Jinja2 syntax in playbooks. For example:
.. code-block:: yaml+jinja
ansible.builtin.template:
src: foo.cfg.j2
dest: '{{ remote_install_path }}/foo.cfg'
In this example, the variable defines the location of a file, which can vary from one system to another.
.. note::
Ansible allows Jinja2 loops and conditionals in :ref:`templates <playbooks_templating>` but not in playbooks. You cannot create a loop of tasks. Ansible playbooks are pure machine-parseable YAML.
.. _yaml_gotchas:
When to quote variables (a YAML gotcha)
=======================================
If you start a value with ``{{ foo }}``, you must quote the whole expression to create valid YAML syntax. If you do not quote the whole expression, the YAML parser cannot interpret the syntax - it might be a variable or it might be the start of a YAML dictionary. For guidance on writing YAML, see the :ref:`yaml_syntax` documentation.
If you use a variable without quotes like this:
.. code-block:: yaml+jinja
- hosts: app_servers
vars:
app_path: {{ base_path }}/22
You will see: ``ERROR! Syntax Error while loading YAML.`` If you add quotes, Ansible works correctly:
.. code-block:: yaml+jinja
- hosts: app_servers
vars:
app_path: "{{ base_path }}/22"
.. _list_variables:
List variables
==============
A list variable combines a variable name with multiple values. The multiple values can be stored as an itemized list or in square brackets ``[]``, separated with commas.
Defining variables as lists
---------------------------
You can define variables with multiple values using YAML lists. For example:
.. code-block:: yaml
region:
- northeast
- southeast
- midwest
Referencing list variables
--------------------------
When you use variables defined as a list (also called an array), you can use individual, specific fields from that list. The first item in a list is item 0, the second item is item 1. For example:
.. code-block:: yaml+jinja
region: "{{ region[0] }}"
The value of this expression would be "northeast".
.. _dictionary_variables:
Dictionary variables
====================
A dictionary stores the data in key-value pairs. Usually, dictionaries are used to store related data, such as the information contained in an ID or a user profile.
Defining variables as key:value dictionaries
--------------------------------------------
You can define more complex variables using YAML dictionaries. A YAML dictionary maps keys to values. For example:
.. code-block:: yaml
foo:
field1: one
field2: two
Referencing key:value dictionary variables
------------------------------------------
When you use variables defined as a key:value dictionary (also called a hash), you can use individual, specific fields from that dictionary using either bracket notation or dot notation:
.. code-block:: yaml
foo['field1']
foo.field1
Both of these examples reference the same value ("one"). Bracket notation always works. Dot notation can cause problems because some keys collide with attributes and methods of python dictionaries. Use bracket notation if you use keys which start and end with two underscores (which are reserved for special meanings in python) or are any of the known public attributes:
``add``, ``append``, ``as_integer_ratio``, ``bit_length``, ``capitalize``, ``center``, ``clear``, ``conjugate``, ``copy``, ``count``, ``decode``, ``denominator``, ``difference``, ``difference_update``, ``discard``, ``encode``, ``endswith``, ``expandtabs``, ``extend``, ``find``, ``format``, ``fromhex``, ``fromkeys``, ``get``, ``has_key``, ``hex``, ``imag``, ``index``, ``insert``, ``intersection``, ``intersection_update``, ``isalnum``, ``isalpha``, ``isdecimal``, ``isdigit``, ``isdisjoint``, ``is_integer``, ``islower``, ``isnumeric``, ``isspace``, ``issubset``, ``issuperset``, ``istitle``, ``isupper``, ``items``, ``iteritems``, ``iterkeys``, ``itervalues``, ``join``, ``keys``, ``ljust``, ``lower``, ``lstrip``, ``numerator``, ``partition``, ``pop``, ``popitem``, ``real``, ``remove``, ``replace``, ``reverse``, ``rfind``, ``rindex``, ``rjust``, ``rpartition``, ``rsplit``, ``rstrip``, ``setdefault``, ``sort``, ``split``, ``splitlines``, ``startswith``, ``strip``, ``swapcase``, ``symmetric_difference``, ``symmetric_difference_update``, ``title``, ``translate``, ``union``, ``update``, ``upper``, ``values``, ``viewitems``, ``viewkeys``, ``viewvalues``, ``zfill``.
.. _registered_variables:
Registering variables
=====================
You can create variables from the output of an Ansible task with the task keyword ``register``. You can use registered variables in any later tasks in your play. For example:
.. code-block:: yaml
- hosts: web_servers
tasks:
- name: Run a shell command and register its output as a variable
ansible.builtin.shell: /usr/bin/foo
register: foo_result
ignore_errors: true
- name: Run a shell command using output of the previous task
ansible.builtin.shell: /usr/bin/bar
when: foo_result.rc == 5
For more examples of using registered variables in conditions on later tasks, see :ref:`playbooks_conditionals`. Registered variables may be simple variables, list variables, dictionary variables, or complex nested data structures. The documentation for each module includes a ``RETURN`` section describing the return values for that module. To see the values for a particular task, run your playbook with ``-v``.
Registered variables are stored in memory. You cannot cache registered variables for use in future plays. Registered variables are only valid on the host for the rest of the current playbook run.
Registered variables are host-level variables. When you register a variable in a task with a loop, the registered variable contains a value for each item in the loop. The data structure placed in the variable during the loop will contain a ``results`` attribute, that is a list of all responses from the module. For a more in-depth example of how this works, see the :ref:`playbooks_loops` section on using register with a loop.
.. note:: If a task fails or is skipped, Ansible still registers a variable with a failure or skipped status, unless the task is skipped based on tags. See :ref:`tags` for information on adding and using tags.
.. _accessing_complex_variable_data:
Referencing nested variables
============================
Many registered variables (and :ref:`facts <vars_and_facts>`) are nested YAML or JSON data structures. You cannot access values from these nested data structures with the simple ``{{ foo }}`` syntax. You must use either bracket notation or dot notation. For example, to reference an IP address from your facts using the bracket notation:
.. code-block:: yaml+jinja
{{ ansible_facts["eth0"]["ipv4"]["address"] }}
To reference an IP address from your facts using the dot notation:
.. code-block:: yaml+jinja
{{ ansible_facts.eth0.ipv4.address }}
.. _about_jinja2:
.. _jinja2_filters:
Transforming variables with Jinja2 filters
==========================================
Jinja2 filters let you transform the value of a variable within a template expression. For example, the ``capitalize`` filter capitalizes any value passed to it; the ``to_yaml`` and ``to_json`` filters change the format of your variable values. Jinja2 includes many `built-in filters <https://jinja.palletsprojects.com/templates/#builtin-filters>`_ and Ansible supplies many more filters. To find more examples of filters, see :ref:`playbooks_filters`.
.. _setting_variables:
Where to set variables
======================
You can define variables in a variety of places, such as in inventory, in playbooks, in reusable files, in roles, and at the command line. Ansible loads every possible variable it finds, then chooses the variable to apply based on :ref:`variable precedence rules <ansible_variable_precedence>`.
.. _define_variables_in_inventory:
Defining variables in inventory
-------------------------------
You can define different variables for each individual host, or set shared variables for a group of hosts in your inventory. For example, if all machines in the ``[Boston]`` group use 'boston.ntp.example.com' as an NTP server, you can set a group variable. The :ref:`intro_inventory` page has details on setting :ref:`host variables <host_variables>` and :ref:`group variables <group_variables>` in inventory.
.. _playbook_variables:
Defining variables in a play
----------------------------
You can define variables directly in a playbook play:
.. code-block:: yaml
- hosts: webservers
vars:
http_port: 80
When you define variables in a play, they are only visible to tasks executed in that play.
.. _included_variables:
.. _variable_file_separation_details:
Defining variables in included files and roles
----------------------------------------------
You can define variables in reusable variables files and/or in reusable roles. When you define variables in reusable variable files, the sensitive variables are separated from playbooks. This separation enables you to store your playbooks in a source control software and even share the playbooks, without the risk of exposing passwords or other sensitive and personal data. For information about creating reusable files and roles, see :ref:`playbooks_reuse`.
This example shows how you can include variables defined in an external file:
.. code-block:: yaml
---
- hosts: all
remote_user: root
vars:
favcolor: blue
vars_files:
- /vars/external_vars.yml
tasks:
- name: This is just a placeholder
ansible.builtin.command: /bin/echo foo
The contents of each variables file is a simple YAML dictionary. For example:
.. code-block:: yaml
---
# in the above example, this would be vars/external_vars.yml
somevar: somevalue
password: magic
.. note::
You can keep per-host and per-group variables in similar files. To learn about organizing your variables, see :ref:`splitting_out_vars`.
.. _passing_variables_on_the_command_line:
Defining variables at runtime
-----------------------------
You can define variables when you run your playbook by passing variables at the command line using the ``--extra-vars`` (or ``-e``) argument. You can also request user input with a ``vars_prompt`` (see :ref:`playbooks_prompts`). When you pass variables at the command line, use a single quoted string, that contains one or more variables, in one of the formats below.
key=value format
^^^^^^^^^^^^^^^^
Values passed in using the ``key=value`` syntax are interpreted as strings. Use the JSON format if you need to pass non-string values such as Booleans, integers, floats, lists, and so on.
.. code-block:: text
ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"
JSON string format
^^^^^^^^^^^^^^^^^^
.. code-block:: shell
ansible-playbook release.yml --extra-vars '{"version":"1.23.45","other_variable":"foo"}'
ansible-playbook arcade.yml --extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}'
When passing variables with ``--extra-vars``, you must escape quotes and other special characters appropriately for both your markup (for example, JSON), and for your shell:
.. code-block:: shell
ansible-playbook arcade.yml --extra-vars "{\"name\":\"Conan O\'Brien\"}"
ansible-playbook arcade.yml --extra-vars '{"name":"Conan O'\\\''Brien"}'
ansible-playbook script.yml --extra-vars "{\"dialog\":\"He said \\\"I just can\'t get enough of those single and double-quotes"\!"\\\"\"}"
If you have a lot of special characters, use a JSON or YAML file containing the variable definitions.
vars from a JSON or YAML file
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: text
ansible-playbook release.yml --extra-vars "@some_file.json"
.. _ansible_variable_precedence:
Variable precedence: Where should I put a variable?
===================================================
You can set multiple variables with the same name in many different places. When you do this, Ansible loads every possible variable it finds, then chooses the variable to apply based on variable precedence. In other words, the different variables will override each other in a certain order.
Teams and projects that agree on guidelines for defining variables (where to define certain types of variables) usually avoid variable precedence concerns. We suggest that you define each variable in one place: figure out where to define a variable, and keep it simple. For examples, see :ref:`variable_examples`.
Some behavioral parameters that you can set in variables you can also set in Ansible configuration, as command-line options, and using playbook keywords. For example, you can define the user Ansible uses to connect to remote devices as a variable with ``ansible_user``, in a configuration file with ``DEFAULT_REMOTE_USER``, as a command-line option with ``-u``, and with the playbook keyword ``remote_user``. If you define the same parameter in a variable and by another method, the variable overrides the other setting. This approach allows host-specific settings to override more general settings. For examples and more details on the precedence of these various settings, see :ref:`general_precedence_rules`.
Understanding variable precedence
---------------------------------
Ansible does apply variable precedence, and you might have a use for it. Here is the order of precedence from least to greatest (the last listed variables override all other variables):
#. command line values (for example, ``-u my_user``, these are not variables)
#. role defaults (defined in role/defaults/main.yml) [1]_
#. inventory file or script group vars [2]_
#. inventory group_vars/all [3]_
#. playbook group_vars/all [3]_
#. inventory group_vars/* [3]_
#. playbook group_vars/* [3]_
#. inventory file or script host vars [2]_
#. inventory host_vars/* [3]_
#. playbook host_vars/* [3]_
#. host facts / cached set_facts [4]_
#. play vars
#. play vars_prompt
#. play vars_files
#. role vars (defined in role/vars/main.yml)
#. block vars (only for tasks in block)
#. task vars (only for the task)
#. include_vars
#. set_facts / registered vars
#. role (and include_role) params
#. include params
#. extra vars (for example, ``-e "user=my_user"``)(always win precedence)
In general, Ansible gives precedence to variables that were defined more recently, more actively, and with more explicit scope. Variables in the defaults folder inside a role are easily overridden. Anything in the vars directory of the role overrides previous versions of that variable in the namespace. Host and/or inventory variables override role defaults, but explicit includes such as the vars directory or an ``include_vars`` task override inventory variables.
Ansible merges different variables set in inventory so that more specific settings override more generic settings. For example, ``ansible_ssh_user`` specified as a group_var is overridden by ``ansible_user`` specified as a host_var. For details about the precedence of variables set in inventory, see :ref:`how_we_merge`.
.. rubric:: Footnotes
.. [1] Tasks in each role see their own role's defaults. Tasks defined outside of a role see the last role's defaults.
.. [2] Variables defined in inventory file or provided by dynamic inventory.
.. [3] Includes vars added by 'vars plugins' as well as host_vars and group_vars which are added by the default vars plugin shipped with Ansible.
.. [4] When created with set_facts's cacheable option, variables have the high precedence in the play,
but are the same as a host facts precedence when they come from the cache.
.. note:: Within any section, redefining a var overrides the previous instance.
If multiple groups have the same variable, the last one loaded wins.
If you define a variable twice in a play's ``vars:`` section, the second one wins.
.. note:: The previous describes the default config ``hash_behaviour=replace``, switch to ``merge`` to only partially overwrite.
.. _variable_scopes:
Scoping variables
-----------------
You can decide where to set a variable based on the scope you want that value to have. Ansible has three main scopes:
* Global: this is set by config, environment variables and the command line
* Play: each play and contained structures, vars entries (vars; vars_files; vars_prompt), role defaults and vars.
* Host: variables directly associated to a host, like inventory, include_vars, facts or registered task outputs
Inside a template, you automatically have access to all variables that are in scope for a host, plus any registered variables, facts, and magic variables.
.. _variable_examples:
Tips on where to set variables
------------------------------
You should choose where to define a variable based on the kind of control you might want over values.
Set variables in inventory that deal with geography or behavior. Since groups are frequently the entity that maps roles onto hosts, you can often set variables on the group instead of defining them on a role. Remember: child groups override parent groups, and host variables override group variables. See :ref:`define_variables_in_inventory` for details on setting host and group variables.
Set common defaults in a ``group_vars/all`` file. See :ref:`splitting_out_vars` for details on how to organize host and group variables in your inventory. Group variables are generally placed alongside your inventory file, but they can also be returned by dynamic inventory (see :ref:`intro_dynamic_inventory`) or defined in AWX or on :ref:`ansible_platform` from the UI or API:
.. code-block:: yaml
---
# file: /etc/ansible/group_vars/all
# this is the site wide default
ntp_server: default-time.example.com
Set location-specific variables in ``group_vars/my_location`` files. All groups are children of the ``all`` group, so variables set here override those set in ``group_vars/all``:
.. code-block:: yaml
---
# file: /etc/ansible/group_vars/boston
ntp_server: boston-time.example.com
If one host used a different NTP server, you could set that in a host_vars file, which would override the group variable:
.. code-block:: yaml
---
# file: /etc/ansible/host_vars/xyz.boston.example.com
ntp_server: override.example.com
Set defaults in roles to avoid undefined-variable errors. If you share your roles, other users can rely on the reasonable defaults you added in the ``roles/x/defaults/main.yml`` file, or they can easily override those values in inventory or at the command line. See :ref:`playbooks_reuse_roles` for more info. For example:
.. code-block:: yaml
---
# file: roles/x/defaults/main.yml
# if no other value is supplied in inventory or as a parameter, this value will be used
http_port: 80
Set variables in roles to ensure a value is used in that role, and is not overridden by inventory variables. If you are not sharing your role with others, you can define app-specific behaviors like ports this way, in ``roles/x/vars/main.yml``. If you are sharing roles with others, putting variables here makes them harder to override, although they still can by passing a parameter to the role or setting a variable with ``-e``:
.. code-block:: yaml
---
# file: roles/x/vars/main.yml
# this will absolutely be used in this role
http_port: 80
Pass variables as parameters when you call roles for maximum clarity, flexibility, and visibility. This approach overrides any defaults that exist for a role. For example:
.. code-block:: yaml
roles:
- role: apache
vars:
http_port: 8080
When you read this playbook it is clear that you have chosen to set a variable or override a default. You can also pass multiple values, which allows you to run the same role multiple times. See :ref:`run_role_twice` for more details. For example:
.. code-block:: yaml
roles:
- role: app_user
vars:
myname: Ian
- role: app_user
vars:
myname: Terry
- role: app_user
vars:
myname: Graham
- role: app_user
vars:
myname: John
Variables set in one role are available to later roles. You can set variables in a ``roles/common_settings/vars/main.yml`` file and use them in other roles and elsewhere in your playbook:
.. code-block:: yaml
roles:
- role: common_settings
- role: something
vars:
foo: 12
- role: something_else
.. note:: There are some protections in place to avoid the need to namespace variables.
In this example, variables defined in 'common_settings' are available to 'something' and 'something_else' tasks, but tasks in 'something' have foo set at 12, even if 'common_settings' sets foo to 20.
Instead of worrying about variable precedence, we encourage you to think about how easily or how often you want to override a variable when deciding where to set it. If you are not sure what other variables are defined, and you need a particular value, use ``--extra-vars`` (``-e``) to override all other variables.
Using advanced variable syntax
==============================
For information about advanced YAML syntax used to declare variables and have more control over the data placed in YAML files used by Ansible, see :ref:`playbooks_advanced_syntax`.
.. seealso::
:ref:`about_playbooks`
An introduction to playbooks
:ref:`playbooks_conditionals`
Conditional statements in playbooks
:ref:`playbooks_filters`
Jinja2 filters and their uses
:ref:`playbooks_loops`
Looping in playbooks
:ref:`playbooks_reuse_roles`
Playbook organization by roles
:ref:`tips_and_tricks`
Tips and tricks for playbooks
:ref:`special_variables`
List of special variables
`User Mailing List <https://groups.google.com/group/ansible-devel>`_
Have a question? Stop by the google group!
:ref:`communication_irc`
How to join Ansible chat channels
This page has moved to :ref:`playbooks_variables`.

@ -1,714 +1,7 @@
.. _vars_and_facts:
:orphan:
************************************************
Discovering variables: facts and magic variables
************************************************
With Ansible you can retrieve or discover certain variables containing information about your remote systems or about Ansible itself. Variables related to remote systems are called facts. With facts, you can use the behavior or state of one system as configuration on other systems. For example, you can use the IP address of one system as a configuration value on another system. Variables related to Ansible are called magic variables.
.. contents::
:local:
Ansible facts
=============
Ansible facts are data related to your remote systems, including operating systems, IP addresses, attached filesystems, and more. You can access this data in the ``ansible_facts`` variable. By default, you can also access some Ansible facts as top-level variables with the ``ansible_`` prefix. You can disable this behavior using the :ref:`INJECT_FACTS_AS_VARS` setting. To see all available facts, add this task to a play:
.. code-block:: yaml
- name: Print all available facts
ansible.builtin.debug:
var: ansible_facts
To see the 'raw' information as gathered, run this command at the command line:
.. code-block:: shell
ansible <hostname> -m ansible.builtin.setup
Facts include a large amount of variable data, which may look like this:
.. code-block:: json
{
"ansible_all_ipv4_addresses": [
"REDACTED IP ADDRESS"
],
"ansible_all_ipv6_addresses": [
"REDACTED IPV6 ADDRESS"
],
"ansible_apparmor": {
"status": "disabled"
},
"ansible_architecture": "x86_64",
"ansible_bios_date": "11/28/2013",
"ansible_bios_version": "4.1.5",
"ansible_cmdline": {
"BOOT_IMAGE": "/boot/vmlinuz-3.10.0-862.14.4.el7.x86_64",
"console": "ttyS0,115200",
"no_timer_check": true,
"nofb": true,
"nomodeset": true,
"ro": true,
"root": "LABEL=cloudimg-rootfs",
"vga": "normal"
},
"ansible_date_time": {
"date": "2018-10-25",
"day": "25",
"epoch": "1540469324",
"hour": "12",
"iso8601": "2018-10-25T12:08:44Z",
"iso8601_basic": "20181025T120844109754",
"iso8601_basic_short": "20181025T120844",
"iso8601_micro": "2018-10-25T12:08:44.109968Z",
"minute": "08",
"month": "10",
"second": "44",
"time": "12:08:44",
"tz": "UTC",
"tz_offset": "+0000",
"weekday": "Thursday",
"weekday_number": "4",
"weeknumber": "43",
"year": "2018"
},
"ansible_default_ipv4": {
"address": "REDACTED",
"alias": "eth0",
"broadcast": "REDACTED",
"gateway": "REDACTED",
"interface": "eth0",
"macaddress": "REDACTED",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "REDACTED",
"type": "ether"
},
"ansible_default_ipv6": {},
"ansible_device_links": {
"ids": {},
"labels": {
"xvda1": [
"cloudimg-rootfs"
],
"xvdd": [
"config-2"
]
},
"masters": {},
"uuids": {
"xvda1": [
"cac81d61-d0f8-4b47-84aa-b48798239164"
],
"xvdd": [
"2018-10-25-12-05-57-00"
]
}
},
"ansible_devices": {
"xvda": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"model": null,
"partitions": {
"xvda1": {
"holders": [],
"links": {
"ids": [],
"labels": [
"cloudimg-rootfs"
],
"masters": [],
"uuids": [
"cac81d61-d0f8-4b47-84aa-b48798239164"
]
},
"sectors": "83883999",
"sectorsize": 512,
"size": "40.00 GB",
"start": "2048",
"uuid": "cac81d61-d0f8-4b47-84aa-b48798239164"
}
},
"removable": "0",
"rotational": "0",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "deadline",
"sectors": "83886080",
"sectorsize": "512",
"size": "40.00 GB",
"support_discard": "0",
"vendor": null,
"virtual": 1
},
"xvdd": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [
"config-2"
],
"masters": [],
"uuids": [
"2018-10-25-12-05-57-00"
]
},
"model": null,
"partitions": {},
"removable": "0",
"rotational": "0",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "deadline",
"sectors": "131072",
"sectorsize": "512",
"size": "64.00 MB",
"support_discard": "0",
"vendor": null,
"virtual": 1
},
"xvde": {
"holders": [],
"host": "",
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"model": null,
"partitions": {
"xvde1": {
"holders": [],
"links": {
"ids": [],
"labels": [],
"masters": [],
"uuids": []
},
"sectors": "167770112",
"sectorsize": 512,
"size": "80.00 GB",
"start": "2048",
"uuid": null
}
},
"removable": "0",
"rotational": "0",
"sas_address": null,
"sas_device_handle": null,
"scheduler_mode": "deadline",
"sectors": "167772160",
"sectorsize": "512",
"size": "80.00 GB",
"support_discard": "0",
"vendor": null,
"virtual": 1
}
},
"ansible_distribution": "CentOS",
"ansible_distribution_file_parsed": true,
"ansible_distribution_file_path": "/etc/redhat-release",
"ansible_distribution_file_variety": "RedHat",
"ansible_distribution_major_version": "7",
"ansible_distribution_release": "Core",
"ansible_distribution_version": "7.5.1804",
"ansible_dns": {
"nameservers": [
"127.0.0.1"
]
},
"ansible_domain": "",
"ansible_effective_group_id": 1000,
"ansible_effective_user_id": 1000,
"ansible_env": {
"HOME": "/home/zuul",
"LANG": "en_US.UTF-8",
"LESSOPEN": "||/usr/bin/lesspipe.sh %s",
"LOGNAME": "zuul",
"MAIL": "/var/mail/zuul",
"PATH": "/usr/local/bin:/usr/bin",
"PWD": "/home/zuul",
"SELINUX_LEVEL_REQUESTED": "",
"SELINUX_ROLE_REQUESTED": "",
"SELINUX_USE_CURRENT_RANGE": "",
"SHELL": "/bin/bash",
"SHLVL": "2",
"SSH_CLIENT": "REDACTED 55672 22",
"SSH_CONNECTION": "REDACTED 55672 REDACTED 22",
"USER": "zuul",
"XDG_RUNTIME_DIR": "/run/user/1000",
"XDG_SESSION_ID": "1",
"_": "/usr/bin/python2"
},
"ansible_eth0": {
"active": true,
"device": "eth0",
"ipv4": {
"address": "REDACTED",
"broadcast": "REDACTED",
"netmask": "255.255.255.0",
"network": "REDACTED"
},
"ipv6": [
{
"address": "REDACTED",
"prefix": "64",
"scope": "link"
}
],
"macaddress": "REDACTED",
"module": "xen_netfront",
"mtu": 1500,
"pciid": "vif-0",
"promisc": false,
"type": "ether"
},
"ansible_eth1": {
"active": true,
"device": "eth1",
"ipv4": {
"address": "REDACTED",
"broadcast": "REDACTED",
"netmask": "255.255.224.0",
"network": "REDACTED"
},
"ipv6": [
{
"address": "REDACTED",
"prefix": "64",
"scope": "link"
}
],
"macaddress": "REDACTED",
"module": "xen_netfront",
"mtu": 1500,
"pciid": "vif-1",
"promisc": false,
"type": "ether"
},
"ansible_fips": false,
"ansible_form_factor": "Other",
"ansible_fqdn": "centos-7-rax-dfw-0003427354",
"ansible_hostname": "centos-7-rax-dfw-0003427354",
"ansible_interfaces": [
"lo",
"eth1",
"eth0"
],
"ansible_is_chroot": false,
"ansible_kernel": "3.10.0-862.14.4.el7.x86_64",
"ansible_lo": {
"active": true,
"device": "lo",
"ipv4": {
"address": "127.0.0.1",
"broadcast": "host",
"netmask": "255.0.0.0",
"network": "127.0.0.0"
},
"ipv6": [
{
"address": "::1",
"prefix": "128",
"scope": "host"
}
],
"mtu": 65536,
"promisc": false,
"type": "loopback"
},
"ansible_local": {},
"ansible_lsb": {
"codename": "Core",
"description": "CentOS Linux release 7.5.1804 (Core)",
"id": "CentOS",
"major_release": "7",
"release": "7.5.1804"
},
"ansible_machine": "x86_64",
"ansible_machine_id": "2db133253c984c82aef2fafcce6f2bed",
"ansible_memfree_mb": 7709,
"ansible_memory_mb": {
"nocache": {
"free": 7804,
"used": 173
},
"real": {
"free": 7709,
"total": 7977,
"used": 268
},
"swap": {
"cached": 0,
"free": 0,
"total": 0,
"used": 0
}
},
"ansible_memtotal_mb": 7977,
"ansible_mounts": [
{
"block_available": 7220998,
"block_size": 4096,
"block_total": 9817227,
"block_used": 2596229,
"device": "/dev/xvda1",
"fstype": "ext4",
"inode_available": 10052341,
"inode_total": 10419200,
"inode_used": 366859,
"mount": "/",
"options": "rw,seclabel,relatime,data=ordered",
"size_available": 29577207808,
"size_total": 40211361792,
"uuid": "cac81d61-d0f8-4b47-84aa-b48798239164"
},
{
"block_available": 0,
"block_size": 2048,
"block_total": 252,
"block_used": 252,
"device": "/dev/xvdd",
"fstype": "iso9660",
"inode_available": 0,
"inode_total": 0,
"inode_used": 0,
"mount": "/mnt/config",
"options": "ro,relatime,mode=0700",
"size_available": 0,
"size_total": 516096,
"uuid": "2018-10-25-12-05-57-00"
}
],
"ansible_nodename": "centos-7-rax-dfw-0003427354",
"ansible_os_family": "RedHat",
"ansible_pkg_mgr": "yum",
"ansible_processor": [
"0",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"1",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"2",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"3",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"4",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"5",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"6",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz",
"7",
"GenuineIntel",
"Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz"
],
"ansible_processor_cores": 8,
"ansible_processor_count": 8,
"ansible_processor_nproc": 8,
"ansible_processor_threads_per_core": 1,
"ansible_processor_vcpus": 8,
"ansible_product_name": "HVM domU",
"ansible_product_serial": "REDACTED",
"ansible_product_uuid": "REDACTED",
"ansible_product_version": "4.1.5",
"ansible_python": {
"executable": "/usr/bin/python2",
"has_sslcontext": true,
"type": "CPython",
"version": {
"major": 2,
"micro": 5,
"minor": 7,
"releaselevel": "final",
"serial": 0
},
"version_info": [
2,
7,
5,
"final",
0
]
},
"ansible_python_version": "2.7.5",
"ansible_real_group_id": 1000,
"ansible_real_user_id": 1000,
"ansible_selinux": {
"config_mode": "enforcing",
"mode": "enforcing",
"policyvers": 31,
"status": "enabled",
"type": "targeted"
},
"ansible_selinux_python_present": true,
"ansible_service_mgr": "systemd",
"ansible_ssh_host_key_ecdsa_public": "REDACTED KEY VALUE",
"ansible_ssh_host_key_ed25519_public": "REDACTED KEY VALUE",
"ansible_ssh_host_key_rsa_public": "REDACTED KEY VALUE",
"ansible_swapfree_mb": 0,
"ansible_swaptotal_mb": 0,
"ansible_system": "Linux",
"ansible_system_capabilities": [
""
],
"ansible_system_capabilities_enforced": "True",
"ansible_system_vendor": "Xen",
"ansible_uptime_seconds": 151,
"ansible_user_dir": "/home/zuul",
"ansible_user_gecos": "",
"ansible_user_gid": 1000,
"ansible_user_id": "zuul",
"ansible_user_shell": "/bin/bash",
"ansible_user_uid": 1000,
"ansible_userspace_architecture": "x86_64",
"ansible_userspace_bits": "64",
"ansible_virtualization_role": "guest",
"ansible_virtualization_type": "xen",
"gather_subset": [
"all"
],
"module_setup": true
}
You can reference the model of the first disk in the facts shown above in a template or playbook as:
.. code-block:: jinja
{{ ansible_facts['devices']['xvda']['model'] }}
To reference the system hostname:
.. code-block:: jinja
{{ ansible_facts['nodename'] }}
You can use facts in conditionals (see :ref:`playbooks_conditionals`) and also in templates. You can also use facts to create dynamic groups of hosts that match particular criteria, see the :ref:`group_by module <group_by_module>` documentation for details.
.. note:: Because ``ansible_date_time`` is created and cached when Ansible gathers facts before each playbook run, it can get stale with long-running playbooks. If your playbook takes a long time to run, use the ``pipe`` filter (for example, ``lookup('pipe', 'date +%Y-%m-%d.%H:%M:%S')``) or :ref:`now() <templating_now>` with a Jinja 2 template instead of ``ansible_date_time``.
.. _fact_requirements:
Package requirements for fact gathering
---------------------------------------
On some distros, you may see missing fact values or facts set to default values because the packages that support gathering those facts are not installed by default. You can install the necessary packages on your remote hosts using the OS package manager. Known dependencies include:
* Linux Network fact gathering - Depends on the ``ip`` binary, commonly included in the ``iproute2`` package.
.. _fact_caching:
Caching facts
-------------
Like registered variables, facts are stored in memory by default. However, unlike registered variables, facts can be gathered independently and cached for repeated use. With cached facts, you can refer to facts from one system when configuring a second system, even if Ansible executes the current play on the second system first. For example:
.. code-block:: jinja
{{ hostvars['asdf.example.com']['ansible_facts']['os_family'] }}
Caching is controlled by the cache plugins. By default, Ansible uses the memory cache plugin, which stores facts in memory for the duration of the current playbook run. To retain Ansible facts for repeated use, select a different cache plugin. See :ref:`cache_plugins` for details.
Fact caching can improve performance. If you manage thousands of hosts, you can configure fact caching to run nightly, then manage configuration on a smaller set of servers periodically throughout the day. With cached facts, you have access to variables and information about all hosts even when you are only managing a small number of servers.
.. _disabling_facts:
Disabling facts
---------------
By default, Ansible gathers facts at the beginning of each play. If you do not need to gather facts (for example, if you know everything about your systems centrally), you can turn off fact gathering at the play level to improve scalability. Disabling facts may particularly improve performance in push mode with very large numbers of systems, or if you are using Ansible on experimental platforms. To disable fact gathering:
.. code-block:: yaml
- hosts: whatever
gather_facts: no
Adding custom facts
-------------------
The setup module in Ansible automatically discovers a standard set of facts about each host. If you want to add custom values to your facts, you can write a custom facts module, set temporary facts with a ``ansible.builtin.set_fact`` task, or provide permanent custom facts using the facts.d directory.
.. _local_facts:
facts.d or local facts
^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 1.3
You can add static custom facts by adding static files to facts.d, or add dynamic facts by adding executable scripts to facts.d. For example, you can add a list of all users on a host to your facts by creating and running a script in facts.d.
To use facts.d, create an ``/etc/ansible/facts.d`` directory on the remote host or hosts. If you prefer a different directory, create it and specify it using the ``fact_path`` play keyword. Add files to the directory to supply your custom facts. All file names must end with ``.fact``. The files can be JSON, INI, or executable files returning JSON.
To add static facts, simply add a file with the ``.fact`` extension. For example, create ``/etc/ansible/facts.d/preferences.fact`` with this content:
.. code-block:: ini
[general]
asdf=1
bar=2
.. note:: Make sure the file is not executable as this will break the ``ansible.builtin.setup`` module.
The next time fact gathering runs, your facts will include a hash variable fact named ``general`` with ``asdf`` and ``bar`` as members. To validate this, run the following:
.. code-block:: shell
ansible <hostname> -m ansible.builtin.setup -a "filter=ansible_local"
And you will see your custom fact added:
.. code-block:: json
{
"ansible_local": {
"preferences": {
"general": {
"asdf" : "1",
"bar" : "2"
}
}
}
}
The ansible_local namespace separates custom facts created by facts.d from system facts or variables defined elsewhere in the playbook, so variables will not override each other. You can access this custom fact in a template or playbook as:
.. code-block:: jinja
{{ ansible_local['preferences']['general']['asdf'] }}
.. note:: The key part in the key=value pairs will be converted into lowercase inside the ansible_local variable. Using the example above, if the ini file contained ``XYZ=3`` in the ``[general]`` section, then you should expect to access it as: ``{{ ansible_local['preferences']['general']['xyz'] }}`` and not ``{{ ansible_local['preferences']['general']['XYZ'] }}``. This is because Ansible uses Python's `ConfigParser`_ which passes all option names through the `optionxform`_ method and this method's default implementation converts option names to lower case.
.. _ConfigParser: https://docs.python.org/3/library/configparser.html
.. _optionxform: https://docs.python.org/3/library/configparser.html#ConfigParser.RawConfigParser.optionxform
You can also use facts.d to execute a script on the remote host, generating dynamic custom facts to the ansible_local namespace. For example, you can generate a list of all users that exist on a remote host as a fact about that host. To generate dynamic custom facts using facts.d:
#. Write and test a script to generate the JSON data you want.
#. Save the script in your facts.d directory.
#. Make sure your script has the ``.fact`` file extension.
#. Make sure your script is executable by the Ansible connection user.
#. Gather facts to execute the script and add the JSON output to ansible_local.
By default, fact gathering runs once at the beginning of each play. If you create a custom fact using facts.d in a playbook, it will be available in the next play that gathers facts. If you want to use it in the same play where you created it, you must explicitly re-run the setup module. For example:
.. code-block:: yaml
- hosts: webservers
tasks:
- name: Create directory for ansible custom facts
ansible.builtin.file:
state: directory
recurse: yes
path: /etc/ansible/facts.d
- name: Install custom ipmi fact
ansible.builtin.copy:
src: ipmi.fact
dest: /etc/ansible/facts.d
- name: Re-read facts after adding custom fact
ansible.builtin.setup:
filter: ansible_local
If you use this pattern frequently, a custom facts module would be more efficient than facts.d.
.. _magic_variables_and_hostvars:
Information about Ansible: magic variables
==========================================
You can access information about Ansible operations, including the python version being used, the hosts and groups in inventory, and the directories for playbooks and roles, using "magic" variables. Like connection variables, magic variables are :ref:`special_variables`. Magic variable names are reserved - do not set variables with these names. The variable ``environment`` is also reserved.
The most commonly used magic variables are ``hostvars``, ``groups``, ``group_names``, and ``inventory_hostname``. With ``hostvars``, you can access variables defined for any host in the play, at any point in a playbook. You can access Ansible facts using the ``hostvars`` variable too, but only after you have gathered (or cached) facts. Note that variables defined at play objects are not defined for specific hosts and therefore are not mapped to hostvars.
If you want to configure your database server using the value of a 'fact' from another node, or the value of an inventory variable assigned to another node, you can use ``hostvars`` in a template or on an action line:
.. code-block:: jinja
{{ hostvars['test.example.com']['ansible_facts']['distribution'] }}
With ``groups``, a list of all the groups (and hosts) in the inventory, you can enumerate all hosts within a group. For example:
.. code-block:: jinja
{% for host in groups['app_servers'] %}
# something that applies to all app servers.
{% endfor %}
You can use ``groups`` and ``hostvars`` together to find all the IP addresses in a group.
.. code-block:: jinja
{% for host in groups['app_servers'] %}
{{ hostvars[host]['ansible_facts']['eth0']['ipv4']['address'] }}
{% endfor %}
You can use this approach to point a frontend proxy server to all the hosts in your app servers group, to set up the correct firewall rules between servers, and so on. You must either cache facts or gather facts for those hosts before the task that fills out the template.
With ``group_names``, a list (array) of all the groups the current host is in, you can create templated files that vary based on the group membership (or role) of the host:
.. code-block:: jinja
{% if 'webserver' in group_names %}
# some part of a configuration file that only applies to webservers
{% endif %}
You can use the magic variable ``inventory_hostname``, the name of the host as configured in your inventory, as an alternative to ``ansible_hostname`` when fact-gathering is disabled. If you have a long FQDN, you can use ``inventory_hostname_short``, which contains the part up to the first period, without the rest of the domain.
Other useful magic variables refer to the current play or playbook. These vars may be useful for filling out templates with multiple hostnames or for injecting the list into the rules for a load balancer.
``ansible_play_hosts`` is the list of all hosts still active in the current play.
``ansible_play_batch`` is a list of hostnames that are in scope for the current 'batch' of the play.
The batch size is defined by ``serial``, when not set it is equivalent to the whole play (making it the same as ``ansible_play_hosts``).
``ansible_playbook_python`` is the path to the python executable used to invoke the Ansible command line tool.
``inventory_dir`` is the pathname of the directory holding Ansible's inventory host file.
``inventory_file`` is the pathname and the filename pointing to the Ansible's inventory host file.
``playbook_dir`` contains the playbook base directory.
``role_path`` contains the current role's pathname and only works inside a role.
``ansible_check_mode`` is a boolean, set to ``True`` if you run Ansible with ``--check``.
.. _ansible_version:
Ansible version
---------------
.. versionadded:: 1.8
To adapt playbook behavior to different versions of Ansible, you can use the variable ``ansible_version``, which has the following structure:
.. code-block:: json
{
"ansible_version": {
"full": "2.10.1",
"major": 2,
"minor": 10,
"revision": 1,
"string": "2.10.1"
}
}
This page has moved to :ref:`vars_and_facts`.
Loading…
Cancel
Save