summaryrefslogtreecommitdiffstats
path: root/docs/docsite/rst/playbook_guide
diff options
context:
space:
mode:
Diffstat (limited to 'docs/docsite/rst/playbook_guide')
-rw-r--r--docs/docsite/rst/playbook_guide/complex_data_manipulation.rst314
-rw-r--r--docs/docsite/rst/playbook_guide/guide_rolling_upgrade.rst324
-rw-r--r--docs/docsite/rst/playbook_guide/index.rst32
-rw-r--r--docs/docsite/rst/playbook_guide/playbook_pathing.rst45
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks.rst42
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_advanced_syntax.rst122
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_async.rst189
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_blocks.rst193
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_checkmode.rst107
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_conditionals.rst551
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_debugger.rst342
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_delegation.rst174
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_environment.rst153
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_error_handling.rst279
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_execution.rst22
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_filters.rst2203
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_handlers.rst196
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_intro.rst159
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_lookups.rst39
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_loops.rst499
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_module_defaults.rst175
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_privilege_escalation.rst776
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_prompts.rst124
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_python_version.rst68
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_reuse.rst226
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_reuse_roles.rst615
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_roles.rst19
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_special_topics.rst8
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_startnstep.rst46
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_strategies.rst254
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_tags.rst432
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_templating.rst34
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_templating_now.rst16
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_tests.rst542
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_variables.rst534
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_vars_facts.rst714
-rw-r--r--docs/docsite/rst/playbook_guide/playbooks_vault.rst6
-rw-r--r--docs/docsite/rst/playbook_guide/shared_snippets/role_directory.txt26
-rw-r--r--docs/docsite/rst/playbook_guide/shared_snippets/with2loop.txt206
39 files changed, 10806 insertions, 0 deletions
diff --git a/docs/docsite/rst/playbook_guide/complex_data_manipulation.rst b/docs/docsite/rst/playbook_guide/complex_data_manipulation.rst
new file mode 100644
index 0000000..11ed3c3
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/complex_data_manipulation.rst
@@ -0,0 +1,314 @@
+.. _complex_data_manipulation:
+
+Manipulating data
+#################
+
+In many cases, you need to do some complex operation with your variables, while Ansible is not recommended as a data processing/manipulation tool, you can use the existing Jinja2 templating in conjunction with the many added Ansible filters, lookups and tests to do some very complex transformations.
+
+Let's start with a quick definition of each type of plugin:
+ - lookups: Mainly used to query 'external data', in Ansible these were the primary part of loops using the ``with_<lookup>`` construct, but they can be used independently to return data for processing. They normally return a list due to their primary function in loops as mentioned previously. Used with the ``lookup`` or ``query`` Jinja2 operators.
+ - filters: used to change/transform data, used with the ``|`` Jinja2 operator.
+ - tests: used to validate data, used with the ``is`` Jinja2 operator.
+
+.. _note:
+ * Some tests and filters are provided directly by Jinja2, so their availability depends on the Jinja2 version, not Ansible.
+
+.. _for_loops_or_list_comprehensions:
+
+Loops and list comprehensions
+=============================
+
+Most programming languages have loops (``for``, ``while``, and so on) and list comprehensions to do transformations on lists including lists of objects. Jinja2 has a few filters that provide this functionality: ``map``, ``select``, ``reject``, ``selectattr``, ``rejectattr``.
+
+- map: this is a basic for loop that just allows you to change every item in a list, using the 'attribute' keyword you can do the transformation based on attributes of the list elements.
+- select/reject: this is a for loop with a condition, that allows you to create a subset of a list that matches (or not) based on the result of the condition.
+- selectattr/rejectattr: very similar to the above but it uses a specific attribute of the list elements for the conditional statement.
+
+
+.. _exponential_backoff:
+
+Use a loop to create exponential backoff for retries/until.
+
+.. code-block:: yaml
+
+ - name: retry ping 10 times with exponential backoff delay
+ ping:
+ retries: 10
+ delay: '{{item|int}}'
+ loop: '{{ range(1, 10)|map("pow", 2) }}'
+
+
+.. _keys_from_dict_matching_list:
+
+Extract keys from a dictionary matching elements from a list
+------------------------------------------------------------
+
+The Python equivalent code would be:
+
+.. code-block:: python
+
+ chains = [1, 2]
+ for chain in chains:
+ for config in chains_config[chain]['configs']:
+ print(config['type'])
+
+There are several ways to do it in Ansible, this is just one example:
+
+.. code-block:: YAML+Jinja
+ :emphasize-lines: 4
+ :caption: Way to extract matching keys from a list of dictionaries
+
+ tasks:
+ - name: Show extracted list of keys from a list of dictionaries
+ ansible.builtin.debug:
+ msg: "{{ chains | map('extract', chains_config) | map(attribute='configs') | flatten | map(attribute='type') | flatten }}"
+ vars:
+ chains: [1, 2]
+ chains_config:
+ 1:
+ foo: bar
+ configs:
+ - type: routed
+ version: 0.1
+ - type: bridged
+ version: 0.2
+ 2:
+ foo: baz
+ configs:
+ - type: routed
+ version: 1.0
+ - type: bridged
+ version: 1.1
+
+
+.. code-block:: ansible-output
+ :caption: Results of debug task, a list with the extracted keys
+
+ ok: [localhost] => {
+ "msg": [
+ "routed",
+ "bridged",
+ "routed",
+ "bridged"
+ ]
+ }
+
+
+.. code-block:: YAML+Jinja
+ :caption: Get the unique list of values of a variable that vary per host
+
+ vars:
+ unique_value_list: "{{ groups['all'] | map ('extract', hostvars, 'varname') | list | unique}}"
+
+
+.. _find_mount_point:
+
+Find mount point
+----------------
+
+In this case, we want to find the mount point for a given path across our machines, since we already collect mount facts, we can use the following:
+
+.. code-block:: YAML+Jinja
+ :caption: Use selectattr to filter mounts into list I can then sort and select the last from
+ :emphasize-lines: 8
+
+ - hosts: all
+ gather_facts: True
+ vars:
+ path: /var/lib/cache
+ tasks:
+ - name: The mount point for {{path}}, found using the Ansible mount facts, [-1] is the same as the 'last' filter
+ ansible.builtin.debug:
+ msg: "{{(ansible_facts.mounts | selectattr('mount', 'in', path) | list | sort(attribute='mount'))[-1]['mount']}}"
+
+
+.. _omit_elements_from_list:
+
+Omit elements from a list
+-------------------------
+
+The special ``omit`` variable ONLY works with module options, but we can still use it in other ways as an identifier to tailor a list of elements:
+
+.. code-block:: YAML+Jinja
+ :caption: Inline list filtering when feeding a module option
+ :emphasize-lines: 3, 6
+
+ - name: Enable a list of Windows features, by name
+ ansible.builtin.set_fact:
+ win_feature_list: "{{ namestuff | reject('equalto', omit) | list }}"
+ vars:
+ namestuff:
+ - "{{ (fs_installed_smb_v1 | default(False)) | ternary(omit, 'FS-SMB1') }}"
+ - "foo"
+ - "bar"
+
+
+Another way is to avoid adding elements to the list in the first place, so you can just use it directly:
+
+.. code-block:: YAML+Jinja
+ :caption: Using set_fact in a loop to increment a list conditionally
+ :emphasize-lines: 3, 4, 6
+
+ - name: Build unique list with some items conditionally omitted
+ ansible.builtin.set_fact:
+ namestuff: ' {{ (namestuff | default([])) | union([item]) }}'
+ when: item != omit
+ loop:
+ - "{{ (fs_installed_smb_v1 | default(False)) | ternary(omit, 'FS-SMB1') }}"
+ - "foo"
+ - "bar"
+
+
+
+.. _combine_optional_values:
+
+Combine values from same list of dicts
+---------------------------------------
+Combining positive and negative filters from examples above, you can get a 'value when it exists' and a 'fallback' when it doesn't.
+
+.. code-block:: YAML+Jinja
+ :caption: Use selectattr and rejectattr to get the ansible_host or inventory_hostname as needed
+
+ - hosts: localhost
+ tasks:
+ - name: Check hosts in inventory that respond to ssh port
+ wait_for:
+ host: "{{ item }}"
+ port: 22
+ loop: '{{ has_ah + no_ah }}'
+ vars:
+ has_ah: '{{ hostvars|dictsort|selectattr("1.ansible_host", "defined")|map(attribute="1.ansible_host")|list }}'
+ no_ah: '{{ hostvars|dictsort|rejectattr("1.ansible_host", "defined")|map(attribute="0")|list }}'
+
+
+.. _custom_fileglob_variable:
+
+Custom Fileglob Based on a Variable
+-----------------------------------
+
+This example uses `Python argument list unpacking <https://docs.python.org/3/tutorial/controlflow.html#unpacking-argument-lists>`_ to create a custom list of fileglobs based on a variable.
+
+.. code-block:: YAML+Jinja
+ :caption: Using fileglob with a list based on a variable.
+
+ - hosts: all
+ vars:
+ mygroups:
+ - prod
+ - web
+ tasks:
+ - name: Copy a glob of files based on a list of groups
+ copy:
+ src: "{{ item }}"
+ dest: "/tmp/{{ item }}"
+ loop: '{{ q("fileglob", *globlist) }}'
+ vars:
+ globlist: '{{ mygroups | map("regex_replace", "^(.*)$", "files/\1/*.conf") | list }}'
+
+
+.. _complex_type_transformations:
+
+Complex Type transformations
+=============================
+
+Jinja provides filters for simple data type transformations (``int``, ``bool``, and so on), but when you want to transform data structures things are not as easy.
+You can use loops and list comprehensions as shown above to help, also other filters and lookups can be chained and used to achieve more complex transformations.
+
+
+.. _create_dictionary_from_list:
+
+Create dictionary from list
+---------------------------
+
+In most languages it is easy to create a dictionary (a.k.a. map/associative array/hash and so on) from a list of pairs, in Ansible there are a couple of ways to do it and the best one for you might depend on the source of your data.
+
+
+These example produces ``{"a": "b", "c": "d"}``
+
+.. code-block:: YAML+Jinja
+ :caption: Simple list to dict by assuming the list is [key, value , key, value, ...]
+
+ vars:
+ single_list: [ 'a', 'b', 'c', 'd' ]
+ mydict: "{{ dict(single_list | slice(2)) }}"
+
+
+.. code-block:: YAML+Jinja
+ :caption: It is simpler when we have a list of pairs:
+
+ vars:
+ list_of_pairs: [ ['a', 'b'], ['c', 'd'] ]
+ mydict: "{{ dict(list_of_pairs) }}"
+
+Both end up being the same thing, with ``slice(2)`` transforming ``single_list`` to a ``list_of_pairs`` generator.
+
+
+
+A bit more complex, using ``set_fact`` and a ``loop`` to create/update a dictionary with key value pairs from 2 lists:
+
+.. code-block:: YAML+Jinja
+ :caption: Using set_fact to create a dictionary from a set of lists
+ :emphasize-lines: 3, 4
+
+ - name: Uses 'combine' to update the dictionary and 'zip' to make pairs of both lists
+ ansible.builtin.set_fact:
+ mydict: "{{ mydict | default({}) | combine({item[0]: item[1]}) }}"
+ loop: "{{ (keys | zip(values)) | list }}"
+ vars:
+ keys:
+ - foo
+ - var
+ - bar
+ values:
+ - a
+ - b
+ - c
+
+This results in ``{"foo": "a", "var": "b", "bar": "c"}``.
+
+
+You can even combine these simple examples with other filters and lookups to create a dictionary dynamically by matching patterns to variable names:
+
+.. code-block:: YAML+Jinja
+ :caption: Using 'vars' to define dictionary from a set of lists without needing a task
+
+ vars:
+ xyz_stuff: 1234
+ xyz_morestuff: 567
+ myvarnames: "{{ q('varnames', '^xyz_') }}"
+ mydict: "{{ dict(myvarnames|map('regex_replace', '^xyz_', '')|list | zip(q('vars', *myvarnames))) }}"
+
+A quick explanation, since there is a lot to unpack from these two lines:
+
+ - The ``varnames`` lookup returns a list of variables that match "begin with ``xyz_``".
+ - Then feeding the list from the previous step into the ``vars`` lookup to get the list of values.
+ The ``*`` is used to 'dereference the list' (a pythonism that works in Jinja), otherwise it would take the list as a single argument.
+ - Both lists get passed to the ``zip`` filter to pair them off into a unified list (key, value, key2, value2, ...).
+ - The dict function then takes this 'list of pairs' to create the dictionary.
+
+
+An example on how to use facts to find a host's data that meets condition X:
+
+
+.. code-block:: YAML+Jinja
+
+ vars:
+ uptime_of_host_most_recently_rebooted: "{{ansible_play_hosts_all | map('extract', hostvars, 'ansible_uptime_seconds') | sort | first}}"
+
+An example to show a host uptime in days/hours/minutes/seconds (assumes facts were gathered).
+
+.. code-block:: YAML+Jinja
+
+ - name: Show the uptime in days/hours/minutes/seconds
+ ansible.builtin.debug:
+ msg: Uptime {{ now().replace(microsecond=0) - now().fromtimestamp(now(fmt='%s') | int - ansible_uptime_seconds) }}
+
+
+.. seealso::
+
+ :ref:`playbooks_filters`
+ Jinja2 filters included with Ansible
+ :ref:`playbooks_tests`
+ Jinja2 tests included with Ansible
+ `Jinja2 Docs <https://jinja.palletsprojects.com/>`_
+ Jinja2 documentation, includes lists for core filters and tests
diff --git a/docs/docsite/rst/playbook_guide/guide_rolling_upgrade.rst b/docs/docsite/rst/playbook_guide/guide_rolling_upgrade.rst
new file mode 100644
index 0000000..3031bf7
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/guide_rolling_upgrade.rst
@@ -0,0 +1,324 @@
+**********************************************************
+Playbook Example: Continuous Delivery and Rolling Upgrades
+**********************************************************
+
+.. contents::
+ :local:
+
+.. _lamp_introduction:
+
+What is continuous delivery?
+============================
+
+Continuous delivery (CD) means frequently delivering updates to your software application.
+
+The idea is that by updating more often, you do not have to wait for a specific timed period, and your organization
+gets better at the process of responding to change.
+
+Some Ansible users are deploying updates to their end users on an hourly or even more frequent basis -- sometimes every time
+there is an approved code change. To achieve this, you need tools to be able to quickly apply those updates in a zero-downtime way.
+
+This document describes in detail how to achieve this goal, using one of Ansible's most complete example
+playbooks as a template: lamp_haproxy. This example uses a lot of Ansible features: roles, templates,
+and group variables, and it also comes with an orchestration playbook that can do zero-downtime
+rolling upgrades of the web application stack.
+
+.. note::
+
+ `Click here for the latest playbooks for this example
+ <https://github.com/ansible/ansible-examples/tree/master/lamp_haproxy>`_.
+
+The playbooks deploy Apache, PHP, MySQL, Nagios, and HAProxy to a CentOS-based set of servers.
+
+We're not going to cover how to run these playbooks here. Read the included README in the github project along with the
+example for that information. Instead, we're going to take a close look at every part of the playbook and describe what it does.
+
+.. _lamp_deployment:
+
+Site deployment
+===============
+
+Let's start with ``site.yml``. This is our site-wide deployment playbook. It can be used to initially deploy the site, as well
+as push updates to all of the servers:
+
+.. code-block:: yaml
+
+ ---
+ # This playbook deploys the whole application stack in this site.
+
+ # Apply common configuration to all hosts
+ - hosts: all
+
+ roles:
+ - common
+
+ # Configure and deploy database servers.
+ - hosts: dbservers
+
+ roles:
+ - db
+
+ # Configure and deploy the web servers. Note that we include two roles
+ # here, the 'base-apache' role which simply sets up Apache, and 'web'
+ # which includes our example web application.
+
+ - hosts: webservers
+
+ roles:
+ - base-apache
+ - web
+
+ # Configure and deploy the load balancer(s).
+ - hosts: lbservers
+
+ roles:
+ - haproxy
+
+ # Configure and deploy the Nagios monitoring node(s).
+ - hosts: monitoring
+
+ roles:
+ - base-apache
+ - nagios
+
+.. note::
+
+ If you're not familiar with terms like playbooks and plays, you should review :ref:`working_with_playbooks`.
+
+In this playbook we have 5 plays. The first one targets ``all`` hosts and applies the ``common`` role to all of the hosts.
+This is for site-wide things like yum repository configuration, firewall configuration, and anything else that needs to apply to all of the servers.
+
+The next four plays run against specific host groups and apply specific roles to those servers.
+Along with the roles for Nagios monitoring, the database, and the web application, we've implemented a
+``base-apache`` role that installs and configures a basic Apache setup. This is used by both the
+sample web application and the Nagios hosts.
+
+.. _lamp_roles:
+
+Reusable content: roles
+=======================
+
+By now you should have a bit of understanding about roles and how they work in Ansible. Roles are a way to organize
+content: tasks, handlers, templates, and files, into reusable components.
+
+This example has six roles: ``common``, ``base-apache``, ``db``, ``haproxy``, ``nagios``, and ``web``. How you organize
+your roles is up to you and your application, but most sites will have one or more common roles that are applied to
+all systems, and then a series of application-specific roles that install and configure particular parts of the site.
+
+Roles can have variables and dependencies, and you can pass in parameters to roles to modify their behavior.
+You can read more about roles in the :ref:`playbooks_reuse_roles` section.
+
+.. _lamp_group_variables:
+
+Configuration: group variables
+==============================
+
+Group variables are variables that are applied to groups of servers. They can be used in templates and in
+playbooks to customize behavior and to provide easily-changed settings and parameters. They are stored in
+a directory called ``group_vars`` in the same location as your inventory.
+Here is lamp_haproxy's ``group_vars/all`` file. As you might expect, these variables are applied to all of the machines in your inventory:
+
+.. code-block:: yaml
+
+ ---
+ httpd_port: 80
+ ntpserver: 192.0.2.23
+
+This is a YAML file, and you can create lists and dictionaries for more complex variable structures.
+In this case, we are just setting two variables, one for the port for the web server, and one for the
+NTP server that our machines should use for time synchronization.
+
+Here's another group variables file. This is ``group_vars/dbservers`` which applies to the hosts in the ``dbservers`` group:
+
+.. code-block:: yaml
+
+ ---
+ mysqlservice: mysqld
+ mysql_port: 3306
+ dbuser: root
+ dbname: foodb
+ upassword: usersecret
+
+If you look in the example, there are group variables for the ``webservers`` group and the ``lbservers`` group, similarly.
+
+These variables are used in a variety of places. You can use them in playbooks, like this, in ``roles/db/tasks/main.yml``:
+
+.. code-block:: yaml
+
+ - name: Create Application Database
+ mysql_db:
+ name: "{{ dbname }}"
+ state: present
+
+ - name: Create Application DB User
+ mysql_user:
+ name: "{{ dbuser }}"
+ password: "{{ upassword }}"
+ priv: "*.*:ALL"
+ host: '%'
+ state: present
+
+You can also use these variables in templates, like this, in ``roles/common/templates/ntp.conf.j2``:
+
+.. code-block:: text
+
+ driftfile /var/lib/ntp/drift
+
+ restrict 127.0.0.1
+ restrict -6 ::1
+
+ server {{ ntpserver }}
+
+ includefile /etc/ntp/crypto/pw
+
+ keys /etc/ntp/keys
+
+You can see that the variable substitution syntax of {{ and }} is the same for both templates and variables. The syntax
+inside the curly braces is Jinja2, and you can do all sorts of operations and apply different filters to the
+data inside. In templates, you can also use for loops and if statements to handle more complex situations,
+like this, in ``roles/common/templates/iptables.j2``:
+
+.. code-block:: jinja
+
+ {% if inventory_hostname in groups['dbservers'] %}
+ -A INPUT -p tcp --dport 3306 -j ACCEPT
+ {% endif %}
+
+This is testing to see if the inventory name of the machine we're currently operating on (``inventory_hostname``)
+exists in the inventory group ``dbservers``. If so, that machine will get an iptables ACCEPT line for port 3306.
+
+Here's another example, from the same template:
+
+.. code-block:: jinja
+
+ {% for host in groups['monitoring'] %}
+ -A INPUT -p tcp -s {{ hostvars[host].ansible_default_ipv4.address }} --dport 5666 -j ACCEPT
+ {% endfor %}
+
+This loops over all of the hosts in the group called ``monitoring``, and adds an ACCEPT line for
+each monitoring hosts' default IPv4 address to the current machine's iptables configuration, so that Nagios can monitor those hosts.
+
+You can learn a lot more about Jinja2 and its capabilities `here <https://jinja.palletsprojects.com/>`_, and you
+can read more about Ansible variables in general in the :ref:`playbooks_variables` section.
+
+.. _lamp_rolling_upgrade:
+
+The rolling upgrade
+===================
+
+Now you have a fully-deployed site with web servers, a load balancer, and monitoring. How do you update it? This is where Ansible's
+orchestration features come into play. While some applications use the term 'orchestration' to mean basic ordering or command-blasting, Ansible
+refers to orchestration as 'conducting machines like an orchestra', and has a pretty sophisticated engine for it.
+
+Ansible has the capability to do operations on multi-tier applications in a coordinated way, making it easy to orchestrate a sophisticated zero-downtime rolling upgrade of our web application. This is implemented in a separate playbook, called ``rolling_update.yml``.
+
+Looking at the playbook, you can see it is made up of two plays. The first play is very simple and looks like this:
+
+.. code-block:: yaml
+
+ - hosts: monitoring
+ tasks: []
+
+What's going on here, and why are there no tasks? You might know that Ansible gathers "facts" from the servers before operating upon them. These facts are useful for all sorts of things: networking information, OS/distribution versions, and so on. In our case, we need to know something about all of the monitoring servers in our environment before we perform the update, so this simple play forces a fact-gathering step on our monitoring servers. You will see this pattern sometimes, and it's a useful trick to know.
+
+The next part is the update play. The first part looks like this:
+
+.. code-block:: yaml
+
+ - hosts: webservers
+ user: root
+ serial: 1
+
+This is just a normal play definition, operating on the ``webservers`` group. The ``serial`` keyword tells Ansible how many servers to operate on at once. If it's not specified, Ansible will parallelize these operations up to the default "forks" limit specified in the configuration file. But for a zero-downtime rolling upgrade, you may not want to operate on that many hosts at once. If you had just a handful of webservers, you may want to set ``serial`` to 1, for one host at a time. If you have 100, maybe you could set ``serial`` to 10, for ten at a time.
+
+Here is the next part of the update play:
+
+.. code-block:: yaml
+
+ pre_tasks:
+ - name: disable nagios alerts for this host webserver service
+ nagios:
+ action: disable_alerts
+ host: "{{ inventory_hostname }}"
+ services: webserver
+ delegate_to: "{{ item }}"
+ loop: "{{ groups.monitoring }}"
+
+ - name: disable the server in haproxy
+ shell: echo "disable server myapplb/{{ inventory_hostname }}" | socat stdio /var/lib/haproxy/stats
+ delegate_to: "{{ item }}"
+ loop: "{{ groups.lbservers }}"
+
+.. note::
+ - The ``serial`` keyword forces the play to be executed in 'batches'. Each batch counts as a full play with a subselection of hosts.
+ This has some consequences on play behavior. For example, if all hosts in a batch fails, the play fails, which in turn fails the entire run. You should consider this when combining with ``max_fail_percentage``.
+
+The ``pre_tasks`` keyword just lets you list tasks to run before the roles are called. This will make more sense in a minute. If you look at the names of these tasks, you can see that we are disabling Nagios alerts and then removing the webserver that we are currently updating from the HAProxy load balancing pool.
+
+The ``delegate_to`` and ``loop`` arguments, used together, cause Ansible to loop over each monitoring server and load balancer, and perform that operation (delegate that operation) on the monitoring or load balancing server, "on behalf" of the webserver. In programming terms, the outer loop is the list of web servers, and the inner loop is the list of monitoring servers.
+
+Note that the HAProxy step looks a little complicated. We're using HAProxy in this example because it's freely available, though if you have (for instance) an F5 or Netscaler in your infrastructure (or maybe you have an AWS Elastic IP setup?), you can use Ansible modules to communicate with them instead. You might also wish to use other monitoring modules instead of nagios, but this just shows the main goal of the 'pre tasks' section -- take the server out of monitoring, and take it out of rotation.
+
+The next step simply re-applies the proper roles to the web servers. This will cause any configuration management declarations in ``web`` and ``base-apache`` roles to be applied to the web servers, including an update of the web application code itself. We don't have to do it this way--we could instead just purely update the web application, but this is a good example of how roles can be used to reuse tasks:
+
+.. code-block:: yaml
+
+ roles:
+ - common
+ - base-apache
+ - web
+
+Finally, in the ``post_tasks`` section, we reverse the changes to the Nagios configuration and put the web server back in the load balancing pool:
+
+.. code-block:: yaml
+
+ post_tasks:
+ - name: Enable the server in haproxy
+ shell: echo "enable server myapplb/{{ inventory_hostname }}" | socat stdio /var/lib/haproxy/stats
+ delegate_to: "{{ item }}"
+ loop: "{{ groups.lbservers }}"
+
+ - name: re-enable nagios alerts
+ nagios:
+ action: enable_alerts
+ host: "{{ inventory_hostname }}"
+ services: webserver
+ delegate_to: "{{ item }}"
+ loop: "{{ groups.monitoring }}"
+
+Again, if you were using a Netscaler or F5 or Elastic Load Balancer, you would just substitute in the appropriate modules instead.
+
+.. _lamp_end_notes:
+
+Managing other load balancers
+=============================
+
+In this example, we use the simple HAProxy load balancer to front-end the web servers. It's easy to configure and easy to manage. As we have mentioned, Ansible has support for a variety of other load balancers like Citrix NetScaler, F5 BigIP, Amazon Elastic Load Balancers, and more. See the :ref:`working_with_modules` documentation for more information.
+
+For other load balancers, you may need to send shell commands to them (like we do for HAProxy above), or call an API, if your load balancer exposes one. For the load balancers for which Ansible has modules, you may want to run them as a ``local_action`` if they contact an API. You can read more about local actions in the :ref:`playbooks_delegation` section. Should you develop anything interesting for some hardware where there is not a module, it might make for a good contribution!
+
+.. _lamp_end_to_end:
+
+Continuous delivery end-to-end
+==============================
+
+Now that you have an automated way to deploy updates to your application, how do you tie it all together? A lot of organizations use a continuous integration tool like `Jenkins <https://jenkins.io/>`_ or `Atlassian Bamboo <https://www.atlassian.com/software/bamboo>`_ to tie the development, test, release, and deploy steps together. You may also want to use a tool like `Gerrit <https://www.gerritcodereview.com/>`_ to add a code review step to commits to either the application code itself, or to your Ansible playbooks, or both.
+
+Depending on your environment, you might be deploying continuously to a test environment, running an integration test battery against that environment, and then deploying automatically into production. Or you could keep it simple and just use the rolling-update for on-demand deployment into test or production specifically. This is all up to you.
+
+For integration with Continuous Integration systems, you can easily trigger playbook runs using the ``ansible-playbook`` command line tool, or, if you're using AWX, the ``tower-cli`` command or the built-in REST API. (The tower-cli command 'joblaunch' will spawn a remote job over the REST API and is pretty slick).
+
+This should give you a good idea of how to structure a multi-tier application with Ansible, and orchestrate operations upon that app, with the eventual goal of continuous delivery to your customers. You could extend the idea of the rolling upgrade to lots of different parts of the app; maybe add front-end web servers along with application servers, for instance, or replace the SQL database with something like MongoDB or Riak. Ansible gives you the capability to easily manage complicated environments and automate common operations.
+
+.. seealso::
+
+ `lamp_haproxy example <https://github.com/ansible/ansible-examples/tree/master/lamp_haproxy>`_
+ The lamp_haproxy example discussed here.
+ :ref:`working_with_playbooks`
+ An introduction to playbooks
+ :ref:`playbooks_reuse_roles`
+ An introduction to playbook roles
+ :ref:`playbooks_variables`
+ An introduction to Ansible variables
+ `Ansible.com: Continuous Delivery <https://www.ansible.com/use-cases/continuous-delivery>`_
+ An introduction to Continuous Delivery with Ansible
diff --git a/docs/docsite/rst/playbook_guide/index.rst b/docs/docsite/rst/playbook_guide/index.rst
new file mode 100644
index 0000000..56f4d2e
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/index.rst
@@ -0,0 +1,32 @@
+.. _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
+ complex_data_manipulation \ No newline at end of file
diff --git a/docs/docsite/rst/playbook_guide/playbook_pathing.rst b/docs/docsite/rst/playbook_guide/playbook_pathing.rst
new file mode 100644
index 0000000..a049ef0
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbook_pathing.rst
@@ -0,0 +1,45 @@
+:orphan:
+
+***********************
+Search paths in Ansible
+***********************
+
+You can control the paths Ansible searches to find resources on your control node (including configuration, modules, roles, ssh keys, and more) as well as resources on the remote nodes you are managing. Use absolute paths to tell Ansible where to find resources whenever you can. However, absolute paths are not always practical. This page covers how Ansible interprets relative search paths, along with ways to troubleshoot when Ansible cannot find the resource you need.
+
+.. contents::
+ :local:
+
+Config paths
+============
+
+By default these should be relative to the config file, some are specifically relative to the current working directory or the playbook and should have this noted in their description. Things like ssh keys are left to use the current working directory because it mirrors how the underlying tools would use it.
+
+.. _playbook_task_paths:
+
+Task paths
+==========
+
+Relative paths used in a task typically refer to remote files and directories on the managed nodes. However, paths passed to lookup plugins and some paths used in action plugins such as the "src" path for the :ref:`template <ansible_collections.ansible.builtin.template_module>` and :ref:`copy <ansible_collections.ansible.builtin.copy_module>` modules refer to local files and directories on the control node.
+
+Resolving local relative paths
+------------------------------
+
+When you specify a relative path for a local file, Ansible will try to find that file first in the current task's role, then in other roles that included or depend on the current role, then relative to the file in which the task is defined, and finally relative to the current play. It will take the first matching file that it finds. This way, if multiple files with the same filename exist, Ansible will find the file that is closest to the current task and that is most likely to be file you wanted.
+
+Specifically, Ansible tries to find the file
+
+1. In the current role.
+
+ 1. In its appropriate subdirectory—"files", "vars", "templates" or "tasks", depending on the kind of file Ansible is searching for.
+ 2. Directly in its directory.
+
+2. Like 1, in the parent role that called into this current role with `include_role`, `import_role`, or with a role dependency. If the parent role has its own parent role, Ansible will repeat this step with that role.
+3. Like 1, in the current task file's directory.
+4. Like 1, in the current play file's directory.
+
+Ansible does not search the current working directory. (The directory you're in when you execute Ansible.) Also, Ansible will only search within a role if you actually included it with an `include_role` or `import_role` task or a dependency. If you instead use `include`, `include_task` or `import_task` to include just the tasks from a specific file but not the full role, Ansible will not search that role in steps 1 and 2.
+
+When you execute Ansible, the variable `ansible_search_path` will contain the paths searched, in the order they were searched in but without listing their subdirectories. If you run Ansible in verbosity level 5 by passing the `-vvvvv` argument, Ansible will report each directory as it searches, except when it searches for a tasks file.
+
+
+.. note:: The current working directory might vary depending on the connection plugin and if the action is local or remote. For the remote it is normally the directory on which the login shell puts the user. For local it is either the directory you executed ansible from or in some cases the playbook directory.
diff --git a/docs/docsite/rst/playbook_guide/playbooks.rst b/docs/docsite/rst/playbook_guide/playbooks.rst
new file mode 100644
index 0000000..48ea92f
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks.rst
@@ -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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_advanced_syntax.rst b/docs/docsite/rst/playbook_guide/playbooks_advanced_syntax.rst
new file mode 100644
index 0000000..ee6ff67
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_advanced_syntax.rst
@@ -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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_async.rst b/docs/docsite/rst/playbook_guide/playbooks_async.rst
new file mode 100644
index 0000000..2f56dbf
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_async.rst
@@ -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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_blocks.rst b/docs/docsite/rst/playbook_guide/playbooks_blocks.rst
new file mode 100644
index 0000000..805045d
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_blocks.rst
@@ -0,0 +1,193 @@
+.. _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: true
+
+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: true`` 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: true
+ 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.
+
+.. note::
+
+ In ``ansible-core`` 2.14 or later, both variables are propagated from an inner block to an outer ``rescue`` portion of a block.
+
+.. 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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_checkmode.rst b/docs/docsite/rst/playbook_guide/playbooks_checkmode.rst
new file mode 100644
index 0000000..4b21f7c
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_checkmode.rst
@@ -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: false
+
+ - 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: true
+ 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: false
diff --git a/docs/docsite/rst/playbook_guide/playbooks_conditionals.rst b/docs/docsite/rst/playbook_guide/playbooks_conditionals.rst
new file mode 100644
index 0000000..f920fd7
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_conditionals.rst
@@ -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: true
+ 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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_debugger.rst b/docs/docsite/rst/playbook_guide/playbooks_debugger.rst
new file mode 100644
index 0000000..79d7c31
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_debugger.rst
@@ -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: false
+ 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: true
+ 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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_delegation.rst b/docs/docsite/rst/playbook_guide/playbooks_delegation.rst
new file mode 100644
index 0000000..faf7c5b
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_delegation.rst
@@ -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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_environment.rst b/docs/docsite/rst/playbook_guide/playbooks_environment.rst
new file mode 100644
index 0000000..d347812
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_environment.rst
@@ -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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_error_handling.rst b/docs/docsite/rst/playbook_guide/playbooks_error_handling.rst
new file mode 100644
index 0000000..d94fc1e
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_error_handling.rst
@@ -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: true
+
+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: true
+
+ - 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: true
+ 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: false
+
+.. _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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_execution.rst b/docs/docsite/rst/playbook_guide/playbooks_execution.rst
new file mode 100644
index 0000000..b1377cd
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_execution.rst
@@ -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 \ No newline at end of file
diff --git a/docs/docsite/rst/playbook_guide/playbooks_filters.rst b/docs/docsite/rst/playbook_guide/playbooks_filters.rst
new file mode 100644
index 0000000..52fb861
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_filters.rst
@@ -0,0 +1,2203 @@
+.. _playbooks_filters:
+
+********************************
+Using filters to manipulate data
+********************************
+
+Filters let you transform JSON data into YAML data, split a URL to extract the hostname, get the SHA1 hash of a string, add or multiply integers, and much more. You can use the Ansible-specific filters documented here to manipulate your data, or use any of the standard filters shipped with Jinja2 - see the list of :ref:`built-in filters <jinja2:builtin-filters>` in the official Jinja2 template documentation. You can also use :ref:`Python methods <jinja2:python-methods>` to transform data. You can :ref:`create custom Ansible filters as plugins <developing_filter_plugins>`, though we generally welcome new filters into the ansible-core repo so everyone can use them.
+
+Because templating happens on the Ansible controller, **not** on the target host, filters execute on the controller and transform data locally.
+
+.. contents::
+ :local:
+
+Handling undefined variables
+============================
+
+Filters can help you manage missing or undefined variables by providing defaults or making some variables optional. If you configure Ansible to ignore most undefined variables, you can mark some variables as requiring values with the ``mandatory`` filter.
+
+.. _defaulting_undefined_variables:
+
+Providing default values
+------------------------
+
+You can provide default values for variables directly in your templates using the Jinja2 'default' filter. This is often a better approach than failing if a variable is not defined:
+
+.. code-block:: yaml+jinja
+
+ {{ some_variable | default(5) }}
+
+In the above example, if the variable 'some_variable' is not defined, Ansible uses the default value 5, rather than raising an "undefined variable" error and failing. If you are working within a role, you can also add a ``defaults/main.yml`` to define the default values for variables in your role.
+
+Beginning in version 2.8, attempting to access an attribute of an Undefined value in Jinja will return another Undefined value, rather than throwing an error immediately. This means that you can now simply use
+a default with a value in a nested data structure (in other words, :code:`{{ foo.bar.baz | default('DEFAULT') }}`) when you do not know if the intermediate values are defined.
+
+If you want to use the default value when variables evaluate to false or an empty string you have to set the second parameter to ``true``:
+
+.. code-block:: yaml+jinja
+
+ {{ lookup('env', 'MY_USER') | default('admin', true) }}
+
+.. _omitting_undefined_variables:
+
+Making variables optional
+-------------------------
+
+By default Ansible requires values for all variables in a templated expression. However, you can make specific variables optional. For example, you might want to use a system default for some items and control the value for others. To make a variable optional, set the default value to the special variable ``omit``:
+
+.. code-block:: yaml+jinja
+
+ - name: Touch files with an optional mode
+ ansible.builtin.file:
+ dest: "{{ item.path }}"
+ state: touch
+ mode: "{{ item.mode | default(omit) }}"
+ loop:
+ - path: /tmp/foo
+ - path: /tmp/bar
+ - path: /tmp/baz
+ mode: "0444"
+
+In this example, the default mode for the files ``/tmp/foo`` and ``/tmp/bar`` is determined by the umask of the system. Ansible does not send a value for ``mode``. Only the third file, ``/tmp/baz``, receives the `mode=0444` option.
+
+.. note:: If you are "chaining" additional filters after the ``default(omit)`` filter, you should instead do something like this:
+ ``"{{ foo | default(None) | some_filter or omit }}"``. In this example, the default ``None`` (Python null) value will cause the later filters to fail, which will trigger the ``or omit`` portion of the logic. Using ``omit`` in this manner is very specific to the later filters you are chaining though, so be prepared for some trial and error if you do this.
+
+.. _forcing_variables_to_be_defined:
+
+Defining mandatory values
+-------------------------
+
+If you configure Ansible to ignore undefined variables, you may want to define some values as mandatory. By default, Ansible fails if a variable in your playbook or command is undefined. You can configure Ansible to allow undefined variables by setting :ref:`DEFAULT_UNDEFINED_VAR_BEHAVIOR` to ``false``. In that case, you may want to require some variables to be defined. You can do this with:
+
+.. code-block:: yaml+jinja
+
+ {{ variable | mandatory }}
+
+The variable value will be used as is, but the template evaluation will raise an error if it is undefined.
+
+A convenient way of requiring a variable to be overridden is to give it an undefined value using the ``undef`` keyword. This can be useful in a role's defaults.
+
+.. code-block:: yaml+jinja
+
+ galaxy_url: "https://galaxy.ansible.com"
+ galaxy_api_key: "{{ undef(hint='You must specify your Galaxy API key') }}"
+
+Defining different values for true/false/null (ternary)
+=======================================================
+
+You can create a test, then define one value to use when the test returns true and another when the test returns false (new in version 1.9):
+
+.. code-block:: yaml+jinja
+
+ {{ (status == 'needs_restart') | ternary('restart', 'continue') }}
+
+In addition, you can define a one value to use on true, one value on false and a third value on null (new in version 2.8):
+
+.. code-block:: yaml+jinja
+
+ {{ enabled | ternary('no shutdown', 'shutdown', omit) }}
+
+Managing data types
+===================
+
+You might need to know, change, or set the data type on a variable. For example, a registered variable might contain a dictionary when your next task needs a list, or a user :ref:`prompt <playbooks_prompts>` might return a string when your playbook needs a boolean value. Use the ``type_debug``, ``dict2items``, and ``items2dict`` filters to manage data types. You can also use the data type itself to cast a value as a specific data type.
+
+Discovering the data type
+-------------------------
+
+.. versionadded:: 2.3
+
+If you are unsure of the underlying Python type of a variable, you can use the ``type_debug`` filter to display it. This is useful in debugging when you need a particular type of variable:
+
+.. code-block:: yaml+jinja
+
+ {{ myvar | type_debug }}
+
+You should note that, while this may seem like a useful filter for checking that you have the right type of data in a variable, you should often prefer :ref:`type tests <type_tests>`, which will allow you to test for specific data types.
+
+.. _dict_filter:
+
+Transforming dictionaries into lists
+------------------------------------
+
+.. versionadded:: 2.6
+
+
+Use the ``dict2items`` filter to transform a dictionary into a list of items suitable for :ref:`looping <playbooks_loops>`:
+
+.. code-block:: yaml+jinja
+
+ {{ dict | dict2items }}
+
+Dictionary data (before applying the ``dict2items`` filter):
+
+.. code-block:: yaml
+
+ tags:
+ Application: payment
+ Environment: dev
+
+List data (after applying the ``dict2items`` filter):
+
+.. code-block:: yaml
+
+ - key: Application
+ value: payment
+ - key: Environment
+ value: dev
+
+.. versionadded:: 2.8
+
+The ``dict2items`` filter is the reverse of the ``items2dict`` filter.
+
+If you want to configure the names of the keys, the ``dict2items`` filter accepts 2 keyword arguments. Pass the ``key_name`` and ``value_name`` arguments to configure the names of the keys in the list output:
+
+.. code-block:: yaml+jinja
+
+ {{ files | dict2items(key_name='file', value_name='path') }}
+
+Dictionary data (before applying the ``dict2items`` filter):
+
+.. code-block:: yaml
+
+ files:
+ users: /etc/passwd
+ groups: /etc/group
+
+List data (after applying the ``dict2items`` filter):
+
+.. code-block:: yaml
+
+ - file: users
+ path: /etc/passwd
+ - file: groups
+ path: /etc/group
+
+
+Transforming lists into dictionaries
+------------------------------------
+
+.. versionadded:: 2.7
+
+Use the ``items2dict`` filter to transform a list into a dictionary, mapping the content into ``key: value`` pairs:
+
+.. code-block:: yaml+jinja
+
+ {{ tags | items2dict }}
+
+List data (before applying the ``items2dict`` filter):
+
+.. code-block:: yaml
+
+ tags:
+ - key: Application
+ value: payment
+ - key: Environment
+ value: dev
+
+Dictionary data (after applying the ``items2dict`` filter):
+
+.. code-block:: text
+
+ Application: payment
+ Environment: dev
+
+The ``items2dict`` filter is the reverse of the ``dict2items`` filter.
+
+Not all lists use ``key`` to designate keys and ``value`` to designate values. For example:
+
+.. code-block:: yaml
+
+ fruits:
+ - fruit: apple
+ color: red
+ - fruit: pear
+ color: yellow
+ - fruit: grapefruit
+ color: yellow
+
+In this example, you must pass the ``key_name`` and ``value_name`` arguments to configure the transformation. For example:
+
+.. code-block:: yaml+jinja
+
+ {{ tags | items2dict(key_name='fruit', value_name='color') }}
+
+If you do not pass these arguments, or do not pass the correct values for your list, you will see ``KeyError: key`` or ``KeyError: my_typo``.
+
+Forcing the data type
+---------------------
+
+You can cast values as certain types. For example, if you expect the input "True" from a :ref:`vars_prompt <playbooks_prompts>` and you want Ansible to recognize it as a boolean value instead of a string:
+
+.. code-block:: yaml
+
+ - ansible.builtin.debug:
+ msg: test
+ when: some_string_value | bool
+
+If you want to perform a mathematical comparison on a fact and you want Ansible to recognize it as an integer instead of a string:
+
+.. code-block:: yaml
+
+ - shell: echo "only on Red Hat 6, derivatives, and later"
+ when: ansible_facts['os_family'] == "RedHat" and ansible_facts['lsb']['major_release'] | int >= 6
+
+
+.. versionadded:: 1.6
+
+.. _filters_for_formatting_data:
+
+Formatting data: YAML and JSON
+==============================
+
+You can switch a data structure in a template from or to JSON or YAML format, with options for formatting, indenting, and loading data. The basic filters are occasionally useful for debugging:
+
+.. code-block:: yaml+jinja
+
+ {{ some_variable | to_json }}
+ {{ some_variable | to_yaml }}
+
+For human readable output, you can use:
+
+.. code-block:: yaml+jinja
+
+ {{ some_variable | to_nice_json }}
+ {{ some_variable | to_nice_yaml }}
+
+You can change the indentation of either format:
+
+.. code-block:: yaml+jinja
+
+ {{ some_variable | to_nice_json(indent=2) }}
+ {{ some_variable | to_nice_yaml(indent=8) }}
+
+The ``to_yaml`` and ``to_nice_yaml`` filters use the `PyYAML library`_ which has a default 80 symbol string length limit. That causes unexpected line break after 80th symbol (if there is a space after 80th symbol)
+To avoid such behavior and generate long lines, use the ``width`` option. You must use a hardcoded number to define the width, instead of a construction like ``float("inf")``, because the filter does not support proxying Python functions. For example:
+
+.. code-block:: yaml+jinja
+
+ {{ some_variable | to_yaml(indent=8, width=1337) }}
+ {{ some_variable | to_nice_yaml(indent=8, width=1337) }}
+
+The filter does support passing through other YAML parameters. For a full list, see the `PyYAML documentation`_ for ``dump()``.
+
+If you are reading in some already formatted data:
+
+.. code-block:: yaml+jinja
+
+ {{ some_variable | from_json }}
+ {{ some_variable | from_yaml }}
+
+for example:
+
+.. code-block:: yaml+jinja
+
+ tasks:
+ - name: Register JSON output as a variable
+ ansible.builtin.shell: cat /some/path/to/file.json
+ register: result
+
+ - name: Set a variable
+ ansible.builtin.set_fact:
+ myvar: "{{ result.stdout | from_json }}"
+
+
+Filter `to_json` and Unicode support
+------------------------------------
+
+By default `to_json` and `to_nice_json` will convert data received to ASCII, so:
+
+.. code-block:: yaml+jinja
+
+ {{ 'München'| to_json }}
+
+will return:
+
+.. code-block:: text
+
+ 'M\u00fcnchen'
+
+To keep Unicode characters, pass the parameter `ensure_ascii=False` to the filter:
+
+.. code-block:: yaml+jinja
+
+ {{ 'München'| to_json(ensure_ascii=False) }}
+
+ 'München'
+
+.. versionadded:: 2.7
+
+To parse multi-document YAML strings, the ``from_yaml_all`` filter is provided.
+The ``from_yaml_all`` filter will return a generator of parsed YAML documents.
+
+for example:
+
+.. code-block:: yaml+jinja
+
+ tasks:
+ - name: Register a file content as a variable
+ ansible.builtin.shell: cat /some/path/to/multidoc-file.yaml
+ register: result
+
+ - name: Print the transformed variable
+ ansible.builtin.debug:
+ msg: '{{ item }}'
+ loop: '{{ result.stdout | from_yaml_all | list }}'
+
+Combining and selecting data
+============================
+
+You can combine data from multiple sources and types, and select values from large data structures, giving you precise control over complex data.
+
+.. _zip_filter_example:
+
+Combining items from multiple lists: zip and zip_longest
+--------------------------------------------------------
+
+.. versionadded:: 2.3
+
+To get a list combining the elements of other lists use ``zip``:
+
+.. code-block:: yaml+jinja
+
+ - name: Give me list combo of two lists
+ ansible.builtin.debug:
+ msg: "{{ [1,2,3,4,5,6] | zip(['a','b','c','d','e','f']) | list }}"
+
+ # => [[1, "a"], [2, "b"], [3, "c"], [4, "d"], [5, "e"], [6, "f"]]
+
+ - name: Give me shortest combo of two lists
+ ansible.builtin.debug:
+ msg: "{{ [1,2,3] | zip(['a','b','c','d','e','f']) | list }}"
+
+ # => [[1, "a"], [2, "b"], [3, "c"]]
+
+To always exhaust all lists use ``zip_longest``:
+
+.. code-block:: yaml+jinja
+
+ - name: Give me longest combo of three lists , fill with X
+ ansible.builtin.debug:
+ msg: "{{ [1,2,3] | zip_longest(['a','b','c','d','e','f'], [21, 22, 23], fillvalue='X') | list }}"
+
+ # => [[1, "a", 21], [2, "b", 22], [3, "c", 23], ["X", "d", "X"], ["X", "e", "X"], ["X", "f", "X"]]
+
+Similarly to the output of the ``items2dict`` filter mentioned above, these filters can be used to construct a ``dict``:
+
+.. code-block:: yaml+jinja
+
+ {{ dict(keys_list | zip(values_list)) }}
+
+List data (before applying the ``zip`` filter):
+
+.. code-block:: yaml
+
+ keys_list:
+ - one
+ - two
+ values_list:
+ - apple
+ - orange
+
+Dictionary data (after applying the ``zip`` filter):
+
+.. code-block:: yaml
+
+ one: apple
+ two: orange
+
+Combining objects and subelements
+---------------------------------
+
+.. versionadded:: 2.7
+
+The ``subelements`` filter produces a product of an object and the subelement values of that object, similar to the ``subelements`` lookup. This lets you specify individual subelements to use in a template. For example, this expression:
+
+.. code-block:: yaml+jinja
+
+ {{ users | subelements('groups', skip_missing=True) }}
+
+Data before applying the ``subelements`` filter:
+
+.. code-block:: yaml
+
+ users:
+ - name: alice
+ authorized:
+ - /tmp/alice/onekey.pub
+ - /tmp/alice/twokey.pub
+ groups:
+ - wheel
+ - docker
+ - name: bob
+ authorized:
+ - /tmp/bob/id_rsa.pub
+ groups:
+ - docker
+
+Data after applying the ``subelements`` filter:
+
+.. code-block:: yaml
+
+ -
+ - name: alice
+ groups:
+ - wheel
+ - docker
+ authorized:
+ - /tmp/alice/onekey.pub
+ - /tmp/alice/twokey.pub
+ - wheel
+ -
+ - name: alice
+ groups:
+ - wheel
+ - docker
+ authorized:
+ - /tmp/alice/onekey.pub
+ - /tmp/alice/twokey.pub
+ - docker
+ -
+ - name: bob
+ authorized:
+ - /tmp/bob/id_rsa.pub
+ groups:
+ - docker
+ - docker
+
+You can use the transformed data with ``loop`` to iterate over the same subelement for multiple objects:
+
+.. code-block:: yaml+jinja
+
+ - name: Set authorized ssh key, extracting just that data from 'users'
+ ansible.posix.authorized_key:
+ user: "{{ item.0.name }}"
+ key: "{{ lookup('file', item.1) }}"
+ loop: "{{ users | subelements('authorized') }}"
+
+.. _combine_filter:
+
+Combining hashes/dictionaries
+-----------------------------
+
+.. versionadded:: 2.0
+
+The ``combine`` filter allows hashes to be merged. For example, the following would override keys in one hash:
+
+.. code-block:: yaml+jinja
+
+ {{ {'a':1, 'b':2} | combine({'b':3}) }}
+
+The resulting hash would be:
+
+.. code-block:: text
+
+ {'a':1, 'b':3}
+
+The filter can also take multiple arguments to merge:
+
+.. code-block:: yaml+jinja
+
+ {{ a | combine(b, c, d) }}
+ {{ [a, b, c, d] | combine }}
+
+In this case, keys in ``d`` would override those in ``c``, which would override those in ``b``, and so on.
+
+The filter also accepts two optional parameters: ``recursive`` and ``list_merge``.
+
+recursive
+ Is a boolean, default to ``False``.
+ Should the ``combine`` recursively merge nested hashes.
+ Note: It does **not** depend on the value of the ``hash_behaviour`` setting in ``ansible.cfg``.
+
+list_merge
+ Is a string, its possible values are ``replace`` (default), ``keep``, ``append``, ``prepend``, ``append_rp`` or ``prepend_rp``.
+ It modifies the behaviour of ``combine`` when the hashes to merge contain arrays/lists.
+
+.. code-block:: yaml
+
+ default:
+ a:
+ x: default
+ y: default
+ b: default
+ c: default
+ patch:
+ a:
+ y: patch
+ z: patch
+ b: patch
+
+If ``recursive=False`` (the default), nested hash aren't merged:
+
+.. code-block:: yaml+jinja
+
+ {{ default | combine(patch) }}
+
+This would result in:
+
+.. code-block:: yaml
+
+ a:
+ y: patch
+ z: patch
+ b: patch
+ c: default
+
+If ``recursive=True``, recurse into nested hash and merge their keys:
+
+.. code-block:: yaml+jinja
+
+ {{ default | combine(patch, recursive=True) }}
+
+This would result in:
+
+.. code-block:: yaml
+
+ a:
+ x: default
+ y: patch
+ z: patch
+ b: patch
+ c: default
+
+If ``list_merge='replace'`` (the default), arrays from the right hash will "replace" the ones in the left hash:
+
+.. code-block:: yaml
+
+ default:
+ a:
+ - default
+ patch:
+ a:
+ - patch
+
+.. code-block:: yaml+jinja
+
+ {{ default | combine(patch) }}
+
+This would result in:
+
+.. code-block:: yaml
+
+ a:
+ - patch
+
+If ``list_merge='keep'``, arrays from the left hash will be kept:
+
+.. code-block:: yaml+jinja
+
+ {{ default | combine(patch, list_merge='keep') }}
+
+This would result in:
+
+.. code-block:: yaml
+
+ a:
+ - default
+
+If ``list_merge='append'``, arrays from the right hash will be appended to the ones in the left hash:
+
+.. code-block:: yaml+jinja
+
+ {{ default | combine(patch, list_merge='append') }}
+
+This would result in:
+
+.. code-block:: yaml
+
+ a:
+ - default
+ - patch
+
+If ``list_merge='prepend'``, arrays from the right hash will be prepended to the ones in the left hash:
+
+.. code-block:: yaml+jinja
+
+ {{ default | combine(patch, list_merge='prepend') }}
+
+This would result in:
+
+.. code-block:: yaml
+
+ a:
+ - patch
+ - default
+
+If ``list_merge='append_rp'``, arrays from the right hash will be appended to the ones in the left hash. Elements of arrays in the left hash that are also in the corresponding array of the right hash will be removed ("rp" stands for "remove present"). Duplicate elements that aren't in both hashes are kept:
+
+.. code-block:: yaml
+
+ default:
+ a:
+ - 1
+ - 1
+ - 2
+ - 3
+ patch:
+ a:
+ - 3
+ - 4
+ - 5
+ - 5
+
+.. code-block:: yaml+jinja
+
+ {{ default | combine(patch, list_merge='append_rp') }}
+
+This would result in:
+
+.. code-block:: yaml
+
+ a:
+ - 1
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 5
+
+If ``list_merge='prepend_rp'``, the behavior is similar to the one for ``append_rp``, but elements of arrays in the right hash are prepended:
+
+.. code-block:: yaml+jinja
+
+ {{ default | combine(patch, list_merge='prepend_rp') }}
+
+This would result in:
+
+.. code-block:: yaml
+
+ a:
+ - 3
+ - 4
+ - 5
+ - 5
+ - 1
+ - 1
+ - 2
+
+``recursive`` and ``list_merge`` can be used together:
+
+.. code-block:: yaml
+
+ default:
+ a:
+ a':
+ x: default_value
+ y: default_value
+ list:
+ - default_value
+ b:
+ - 1
+ - 1
+ - 2
+ - 3
+ patch:
+ a:
+ a':
+ y: patch_value
+ z: patch_value
+ list:
+ - patch_value
+ b:
+ - 3
+ - 4
+ - 4
+ - key: value
+
+.. code-block:: yaml+jinja
+
+ {{ default | combine(patch, recursive=True, list_merge='append_rp') }}
+
+This would result in:
+
+.. code-block:: yaml
+
+ a:
+ a':
+ x: default_value
+ y: patch_value
+ z: patch_value
+ list:
+ - default_value
+ - patch_value
+ b:
+ - 1
+ - 1
+ - 2
+ - 3
+ - 4
+ - 4
+ - key: value
+
+
+.. _extract_filter:
+
+Selecting values from arrays or hashtables
+-------------------------------------------
+
+.. versionadded:: 2.1
+
+The `extract` filter is used to map from a list of indices to a list of values from a container (hash or array):
+
+.. code-block:: yaml+jinja
+
+ {{ [0,2] | map('extract', ['x','y','z']) | list }}
+ {{ ['x','y'] | map('extract', {'x': 42, 'y': 31}) | list }}
+
+The results of the above expressions would be:
+
+.. code-block:: none
+
+ ['x', 'z']
+ [42, 31]
+
+The filter can take another argument:
+
+.. code-block:: yaml+jinja
+
+ {{ groups['x'] | map('extract', hostvars, 'ec2_ip_address') | list }}
+
+This takes the list of hosts in group 'x', looks them up in `hostvars`, and then looks up the `ec2_ip_address` of the result. The final result is a list of IP addresses for the hosts in group 'x'.
+
+The third argument to the filter can also be a list, for a recursive lookup inside the container:
+
+.. code-block:: yaml+jinja
+
+ {{ ['a'] | map('extract', b, ['x','y']) | list }}
+
+This would return a list containing the value of `b['a']['x']['y']`.
+
+Combining lists
+---------------
+
+This set of filters returns a list of combined lists.
+
+
+permutations
+^^^^^^^^^^^^
+To get permutations of a list:
+
+.. code-block:: yaml+jinja
+
+ - name: Give me largest permutations (order matters)
+ ansible.builtin.debug:
+ msg: "{{ [1,2,3,4,5] | ansible.builtin.permutations | list }}"
+
+ - name: Give me permutations of sets of three
+ ansible.builtin.debug:
+ msg: "{{ [1,2,3,4,5] | ansible.builtin.permutations(3) | list }}"
+
+combinations
+^^^^^^^^^^^^
+Combinations always require a set size:
+
+.. code-block:: yaml+jinja
+
+ - name: Give me combinations for sets of two
+ ansible.builtin.debug:
+ msg: "{{ [1,2,3,4,5] | ansible.builtin.combinations(2) | list }}"
+
+Also see the :ref:`zip_filter`
+
+products
+^^^^^^^^
+The product filter returns the `cartesian product <https://docs.python.org/3/library/itertools.html#itertools.product>`_ of the input iterables. This is roughly equivalent to nested for-loops in a generator expression.
+
+For example:
+
+.. code-block:: yaml+jinja
+
+ - name: Generate multiple hostnames
+ ansible.builtin.debug:
+ msg: "{{ ['foo', 'bar'] | product(['com']) | map('join', '.') | join(',') }}"
+
+This would result in:
+
+.. code-block:: json
+
+ { "msg": "foo.com,bar.com" }
+
+.. json_query_filter:
+
+Selecting JSON data: JSON queries
+---------------------------------
+
+To select a single element or a data subset from a complex data structure in JSON format (for example, Ansible facts), use the ``json_query`` filter. The ``json_query`` filter lets you query a complex JSON structure and iterate over it using a loop structure.
+
+.. note::
+
+ This filter has migrated to the `community.general <https://galaxy.ansible.com/community/general>`_ collection. Follow the installation instructions to install that collection.
+
+
+.. note:: You must manually install the **jmespath** dependency on the Ansible controller before using this filter. This filter is built upon **jmespath**, and you can use the same syntax. For examples, see `jmespath examples <https://jmespath.org/examples.html>`_.
+
+Consider this data structure:
+
+.. code-block:: json
+
+ {
+ "domain_definition": {
+ "domain": {
+ "cluster": [
+ {
+ "name": "cluster1"
+ },
+ {
+ "name": "cluster2"
+ }
+ ],
+ "server": [
+ {
+ "name": "server11",
+ "cluster": "cluster1",
+ "port": "8080"
+ },
+ {
+ "name": "server12",
+ "cluster": "cluster1",
+ "port": "8090"
+ },
+ {
+ "name": "server21",
+ "cluster": "cluster2",
+ "port": "9080"
+ },
+ {
+ "name": "server22",
+ "cluster": "cluster2",
+ "port": "9090"
+ }
+ ],
+ "library": [
+ {
+ "name": "lib1",
+ "target": "cluster1"
+ },
+ {
+ "name": "lib2",
+ "target": "cluster2"
+ }
+ ]
+ }
+ }
+ }
+
+To extract all clusters from this structure, you can use the following query:
+
+.. code-block:: yaml+jinja
+
+ - name: Display all cluster names
+ ansible.builtin.debug:
+ var: item
+ loop: "{{ domain_definition | community.general.json_query('domain.cluster[*].name') }}"
+
+To extract all server names:
+
+.. code-block:: yaml+jinja
+
+ - name: Display all server names
+ ansible.builtin.debug:
+ var: item
+ loop: "{{ domain_definition | community.general.json_query('domain.server[*].name') }}"
+
+To extract ports from cluster1:
+
+.. code-block:: yaml+jinja
+
+ - name: Display all ports from cluster1
+ ansible.builtin.debug:
+ var: item
+ loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}"
+ vars:
+ server_name_cluster1_query: "domain.server[?cluster=='cluster1'].port"
+
+.. note:: You can use a variable to make the query more readable.
+
+To print out the ports from cluster1 in a comma separated string:
+
+.. code-block:: yaml+jinja
+
+ - name: Display all ports from cluster1 as a string
+ ansible.builtin.debug:
+ msg: "{{ domain_definition | community.general.json_query('domain.server[?cluster==`cluster1`].port') | join(', ') }}"
+
+.. note:: In the example above, quoting literals using backticks avoids escaping quotes and maintains readability.
+
+You can use YAML `single quote escaping <https://yaml.org/spec/current.html#id2534365>`_:
+
+.. code-block:: yaml+jinja
+
+ - name: Display all ports from cluster1
+ ansible.builtin.debug:
+ var: item
+ loop: "{{ domain_definition | community.general.json_query('domain.server[?cluster==''cluster1''].port') }}"
+
+.. note:: Escaping single quotes within single quotes in YAML is done by doubling the single quote.
+
+To get a hash map with all ports and names of a cluster:
+
+.. code-block:: yaml+jinja
+
+ - name: Display all server ports and names from cluster1
+ ansible.builtin.debug:
+ var: item
+ loop: "{{ domain_definition | community.general.json_query(server_name_cluster1_query) }}"
+ vars:
+ server_name_cluster1_query: "domain.server[?cluster=='cluster2'].{name: name, port: port}"
+
+To extract ports from all clusters with name starting with 'server1':
+
+.. code-block:: yaml+jinja
+
+ - name: Display all ports from cluster1
+ ansible.builtin.debug:
+ msg: "{{ domain_definition | to_json | from_json | community.general.json_query(server_name_query) }}"
+ vars:
+ server_name_query: "domain.server[?starts_with(name,'server1')].port"
+
+To extract ports from all clusters with name containing 'server1':
+
+.. code-block:: yaml+jinja
+
+ - name: Display all ports from cluster1
+ ansible.builtin.debug:
+ msg: "{{ domain_definition | to_json | from_json | community.general.json_query(server_name_query) }}"
+ vars:
+ server_name_query: "domain.server[?contains(name,'server1')].port"
+
+.. note:: while using ``starts_with`` and ``contains``, you have to use `` to_json | from_json `` filter for correct parsing of data structure.
+
+
+Randomizing data
+================
+
+When you need a randomly generated value, use one of these filters.
+
+
+.. _random_mac_filter:
+
+Random MAC addresses
+--------------------
+
+.. versionadded:: 2.6
+
+This filter can be used to generate a random MAC address from a string prefix.
+
+.. note::
+
+ This filter has migrated to the `community.general <https://galaxy.ansible.com/community/general>`_ collection. Follow the installation instructions to install that collection.
+
+To get a random MAC address from a string prefix starting with '52:54:00':
+
+.. code-block:: yaml+jinja
+
+ "{{ '52:54:00' | community.general.random_mac }}"
+ # => '52:54:00:ef:1c:03'
+
+Note that if anything is wrong with the prefix string, the filter will issue an error.
+
+ .. versionadded:: 2.9
+
+As of Ansible version 2.9, you can also initialize the random number generator from a seed to create random-but-idempotent MAC addresses:
+
+.. code-block:: yaml+jinja
+
+ "{{ '52:54:00' | community.general.random_mac(seed=inventory_hostname) }}"
+
+
+.. _random_filter_example:
+
+Random items or numbers
+-----------------------
+
+The ``random`` filter in Ansible is an extension of the default Jinja2 random filter, and can be used to return a random item from a sequence of items or to generate a random number based on a range.
+
+To get a random item from a list:
+
+.. code-block:: yaml+jinja
+
+ "{{ ['a','b','c'] | random }}"
+ # => 'c'
+
+To get a random number between 0 (inclusive) and a specified integer (exclusive):
+
+.. code-block:: yaml+jinja
+
+ "{{ 60 | random }} * * * * root /script/from/cron"
+ # => '21 * * * * root /script/from/cron'
+
+To get a random number from 0 to 100 but in steps of 10:
+
+.. code-block:: yaml+jinja
+
+ {{ 101 | random(step=10) }}
+ # => 70
+
+To get a random number from 1 to 100 but in steps of 10:
+
+.. code-block:: yaml+jinja
+
+ {{ 101 | random(1, 10) }}
+ # => 31
+ {{ 101 | random(start=1, step=10) }}
+ # => 51
+
+You can initialize the random number generator from a seed to create random-but-idempotent numbers:
+
+.. code-block:: yaml+jinja
+
+ "{{ 60 | random(seed=inventory_hostname) }} * * * * root /script/from/cron"
+
+Shuffling a list
+----------------
+
+The ``shuffle`` filter randomizes an existing list, giving a different order every invocation.
+
+To get a random list from an existing list:
+
+.. code-block:: yaml+jinja
+
+ {{ ['a','b','c'] | shuffle }}
+ # => ['c','a','b']
+ {{ ['a','b','c'] | shuffle }}
+ # => ['b','c','a']
+
+You can initialize the shuffle generator from a seed to generate a random-but-idempotent order:
+
+.. code-block:: yaml+jinja
+
+ {{ ['a','b','c'] | shuffle(seed=inventory_hostname) }}
+ # => ['b','a','c']
+
+The shuffle filter returns a list whenever possible. If you use it with a non 'listable' item, the filter does nothing.
+
+
+.. _list_filters:
+
+Managing list variables
+=======================
+
+You can search for the minimum or maximum value in a list, or flatten a multi-level list.
+
+To get the minimum value from list of numbers:
+
+.. code-block:: yaml+jinja
+
+ {{ list1 | min }}
+
+.. versionadded:: 2.11
+
+To get the minimum value in a list of objects:
+
+.. code-block:: yaml+jinja
+
+ {{ [{'val': 1}, {'val': 2}] | min(attribute='val') }}
+
+To get the maximum value from a list of numbers:
+
+.. code-block:: yaml+jinja
+
+ {{ [3, 4, 2] | max }}
+
+.. versionadded:: 2.11
+
+To get the maximum value in a list of objects:
+
+.. code-block:: yaml+jinja
+
+ {{ [{'val': 1}, {'val': 2}] | max(attribute='val') }}
+
+.. versionadded:: 2.5
+
+Flatten a list (same thing the `flatten` lookup does):
+
+.. code-block:: yaml+jinja
+
+ {{ [3, [4, 2] ] | flatten }}
+ # => [3, 4, 2]
+
+Flatten only the first level of a list (akin to the `items` lookup):
+
+.. code-block:: yaml+jinja
+
+ {{ [3, [4, [2]] ] | flatten(levels=1) }}
+ # => [3, 4, [2]]
+
+
+.. versionadded:: 2.11
+
+Preserve nulls in a list, by default flatten removes them. :
+
+.. code-block:: yaml+jinja
+
+ {{ [3, None, [4, [2]] ] | flatten(levels=1, skip_nulls=False) }}
+ # => [3, None, 4, [2]]
+
+
+.. _set_theory_filters:
+
+Selecting from sets or lists (set theory)
+=========================================
+
+You can select or combine items from sets or lists.
+
+.. versionadded:: 1.4
+
+To get a unique set from a list:
+
+.. code-block:: yaml+jinja
+
+ # list1: [1, 2, 5, 1, 3, 4, 10]
+ {{ list1 | unique }}
+ # => [1, 2, 5, 3, 4, 10]
+
+To get a union of two lists:
+
+.. code-block:: yaml+jinja
+
+ # list1: [1, 2, 5, 1, 3, 4, 10]
+ # list2: [1, 2, 3, 4, 5, 11, 99]
+ {{ list1 | union(list2) }}
+ # => [1, 2, 5, 1, 3, 4, 10, 11, 99]
+
+To get the intersection of 2 lists (unique list of all items in both):
+
+.. code-block:: yaml+jinja
+
+ # list1: [1, 2, 5, 3, 4, 10]
+ # list2: [1, 2, 3, 4, 5, 11, 99]
+ {{ list1 | intersect(list2) }}
+ # => [1, 2, 5, 3, 4]
+
+To get the difference of 2 lists (items in 1 that don't exist in 2):
+
+.. code-block:: yaml+jinja
+
+ # list1: [1, 2, 5, 1, 3, 4, 10]
+ # list2: [1, 2, 3, 4, 5, 11, 99]
+ {{ list1 | difference(list2) }}
+ # => [10]
+
+To get the symmetric difference of 2 lists (items exclusive to each list):
+
+.. code-block:: yaml+jinja
+
+ # list1: [1, 2, 5, 1, 3, 4, 10]
+ # list2: [1, 2, 3, 4, 5, 11, 99]
+ {{ list1 | symmetric_difference(list2) }}
+ # => [10, 11, 99]
+
+.. _math_stuff:
+
+Calculating numbers (math)
+==========================
+
+.. versionadded:: 1.9
+
+You can calculate logs, powers, and roots of numbers with Ansible filters. Jinja2 provides other mathematical functions like abs() and round().
+
+Get the logarithm (default is e):
+
+.. code-block:: yaml+jinja
+
+ {{ 8 | log }}
+ # => 2.0794415416798357
+
+Get the base 10 logarithm:
+
+.. code-block:: yaml+jinja
+
+ {{ 8 | log(10) }}
+ # => 0.9030899869919435
+
+Give me the power of 2! (or 5):
+
+.. code-block:: yaml+jinja
+
+ {{ 8 | pow(5) }}
+ # => 32768.0
+
+Square root, or the 5th:
+
+.. code-block:: yaml+jinja
+
+ {{ 8 | root }}
+ # => 2.8284271247461903
+
+ {{ 8 | root(5) }}
+ # => 1.5157165665103982
+
+
+Managing network interactions
+=============================
+
+These filters help you with common network tasks.
+
+.. note::
+
+ These filters have migrated to the `ansible.netcommon <https://galaxy.ansible.com/ansible/netcommon>`_ collection. Follow the installation instructions to install that collection.
+
+.. _ipaddr_filter:
+
+IP address filters
+------------------
+
+.. versionadded:: 1.9
+
+To test if a string is a valid IP address:
+
+.. code-block:: yaml+jinja
+
+ {{ myvar | ansible.netcommon.ipaddr }}
+
+You can also require a specific IP protocol version:
+
+.. code-block:: yaml+jinja
+
+ {{ myvar | ansible.netcommon.ipv4 }}
+ {{ myvar | ansible.netcommon.ipv6 }}
+
+IP address filter can also be used to extract specific information from an IP
+address. For example, to get the IP address itself from a CIDR, you can use:
+
+.. code-block:: yaml+jinja
+
+ {{ '192.0.2.1/24' | ansible.netcommon.ipaddr('address') }}
+ # => 192.0.2.1
+
+More information about ``ipaddr`` filter and complete usage guide can be found
+in :ref:`playbooks_filters_ipaddr`.
+
+.. _network_filters:
+
+Network CLI filters
+-------------------
+
+.. versionadded:: 2.4
+
+To convert the output of a network device CLI command into structured JSON
+output, use the ``parse_cli`` filter:
+
+.. code-block:: yaml+jinja
+
+ {{ output | ansible.netcommon.parse_cli('path/to/spec') }}
+
+The ``parse_cli`` filter will load the spec file and pass the command output
+through it, returning JSON output. The YAML spec file defines how to parse the CLI output.
+
+The spec file should be valid formatted YAML. It defines how to parse the CLI
+output and return JSON data. Below is an example of a valid spec file that
+will parse the output from the ``show vlan`` command.
+
+.. code-block:: yaml
+
+ ---
+ vars:
+ vlan:
+ vlan_id: "{{ item.vlan_id }}"
+ name: "{{ item.name }}"
+ enabled: "{{ item.state != 'act/lshut' }}"
+ state: "{{ item.state }}"
+
+ keys:
+ vlans:
+ value: "{{ vlan }}"
+ items: "^(?P<vlan_id>\\d+)\\s+(?P<name>\\w+)\\s+(?P<state>active|act/lshut|suspended)"
+ state_static:
+ value: present
+
+
+The spec file above will return a JSON data structure that is a list of hashes
+with the parsed VLAN information.
+
+The same command could be parsed into a hash by using the key and values
+directives. Here is an example of how to parse the output into a hash
+value using the same ``show vlan`` command.
+
+.. code-block:: yaml
+
+ ---
+ vars:
+ vlan:
+ key: "{{ item.vlan_id }}"
+ values:
+ vlan_id: "{{ item.vlan_id }}"
+ name: "{{ item.name }}"
+ enabled: "{{ item.state != 'act/lshut' }}"
+ state: "{{ item.state }}"
+
+ keys:
+ vlans:
+ value: "{{ vlan }}"
+ items: "^(?P<vlan_id>\\d+)\\s+(?P<name>\\w+)\\s+(?P<state>active|act/lshut|suspended)"
+ state_static:
+ value: present
+
+Another common use case for parsing CLI commands is to break a large command
+into blocks that can be parsed. This can be done using the ``start_block`` and
+``end_block`` directives to break the command into blocks that can be parsed.
+
+.. code-block:: yaml
+
+ ---
+ vars:
+ interface:
+ name: "{{ item[0].match[0] }}"
+ state: "{{ item[1].state }}"
+ mode: "{{ item[2].match[0] }}"
+
+ keys:
+ interfaces:
+ value: "{{ interface }}"
+ start_block: "^Ethernet.*$"
+ end_block: "^$"
+ items:
+ - "^(?P<name>Ethernet\\d\\/\\d*)"
+ - "admin state is (?P<state>.+),"
+ - "Port mode is (.+)"
+
+
+The example above will parse the output of ``show interface`` into a list of
+hashes.
+
+The network filters also support parsing the output of a CLI command using the
+TextFSM library. To parse the CLI output with TextFSM use the following
+filter:
+
+.. code-block:: yaml+jinja
+
+ {{ output.stdout[0] | ansible.netcommon.parse_cli_textfsm('path/to/fsm') }}
+
+Use of the TextFSM filter requires the TextFSM library to be installed.
+
+Network XML filters
+-------------------
+
+.. versionadded:: 2.5
+
+To convert the XML output of a network device command into structured JSON
+output, use the ``parse_xml`` filter:
+
+.. code-block:: yaml+jinja
+
+ {{ output | ansible.netcommon.parse_xml('path/to/spec') }}
+
+The ``parse_xml`` filter will load the spec file and pass the command output
+through formatted as JSON.
+
+The spec file should be valid formatted YAML. It defines how to parse the XML
+output and return JSON data.
+
+Below is an example of a valid spec file that
+will parse the output from the ``show vlan | display xml`` command.
+
+.. code-block:: yaml
+
+ ---
+ vars:
+ vlan:
+ vlan_id: "{{ item.vlan_id }}"
+ name: "{{ item.name }}"
+ desc: "{{ item.desc }}"
+ enabled: "{{ item.state.get('inactive') != 'inactive' }}"
+ state: "{% if item.state.get('inactive') == 'inactive'%} inactive {% else %} active {% endif %}"
+
+ keys:
+ vlans:
+ value: "{{ vlan }}"
+ top: configuration/vlans/vlan
+ items:
+ vlan_id: vlan-id
+ name: name
+ desc: description
+ state: ".[@inactive='inactive']"
+
+
+The spec file above will return a JSON data structure that is a list of hashes
+with the parsed VLAN information.
+
+The same command could be parsed into a hash by using the key and values
+directives. Here is an example of how to parse the output into a hash
+value using the same ``show vlan | display xml`` command.
+
+.. code-block:: yaml
+
+ ---
+ vars:
+ vlan:
+ key: "{{ item.vlan_id }}"
+ values:
+ vlan_id: "{{ item.vlan_id }}"
+ name: "{{ item.name }}"
+ desc: "{{ item.desc }}"
+ enabled: "{{ item.state.get('inactive') != 'inactive' }}"
+ state: "{% if item.state.get('inactive') == 'inactive'%} inactive {% else %} active {% endif %}"
+
+ keys:
+ vlans:
+ value: "{{ vlan }}"
+ top: configuration/vlans/vlan
+ items:
+ vlan_id: vlan-id
+ name: name
+ desc: description
+ state: ".[@inactive='inactive']"
+
+
+The value of ``top`` is the XPath relative to the XML root node.
+In the example XML output given below, the value of ``top`` is ``configuration/vlans/vlan``,
+which is an XPath expression relative to the root node (<rpc-reply>).
+``configuration`` in the value of ``top`` is the outer most container node, and ``vlan``
+is the inner-most container node.
+
+``items`` is a dictionary of key-value pairs that map user-defined names to XPath expressions
+that select elements. The Xpath expression is relative to the value of the XPath value contained in ``top``.
+For example, the ``vlan_id`` in the spec file is a user defined name and its value ``vlan-id`` is the
+relative to the value of XPath in ``top``
+
+Attributes of XML tags can be extracted using XPath expressions. The value of ``state`` in the spec
+is an XPath expression used to get the attributes of the ``vlan`` tag in output XML.:
+
+.. code-block:: none
+
+ <rpc-reply>
+ <configuration>
+ <vlans>
+ <vlan inactive="inactive">
+ <name>vlan-1</name>
+ <vlan-id>200</vlan-id>
+ <description>This is vlan-1</description>
+ </vlan>
+ </vlans>
+ </configuration>
+ </rpc-reply>
+
+.. note::
+ For more information on supported XPath expressions, see `XPath Support <https://docs.python.org/3/library/xml.etree.elementtree.html#xpath-support>`_.
+
+Network VLAN filters
+--------------------
+
+.. versionadded:: 2.8
+
+Use the ``vlan_parser`` filter to transform an unsorted list of VLAN integers into a
+sorted string list of integers according to IOS-like VLAN list rules. This list has the following properties:
+
+* Vlans are listed in ascending order.
+* Three or more consecutive VLANs are listed with a dash.
+* The first line of the list can be first_line_len characters long.
+* Subsequent list lines can be other_line_len characters.
+
+To sort a VLAN list:
+
+.. code-block:: yaml+jinja
+
+ {{ [3003, 3004, 3005, 100, 1688, 3002, 3999] | ansible.netcommon.vlan_parser }}
+
+This example renders the following sorted list:
+
+.. code-block:: text
+
+ ['100,1688,3002-3005,3999']
+
+
+Another example Jinja template:
+
+.. code-block:: yaml+jinja
+
+ {% set parsed_vlans = vlans | ansible.netcommon.vlan_parser %}
+ switchport trunk allowed vlan {{ parsed_vlans[0] }}
+ {% for i in range (1, parsed_vlans | count) %}
+ switchport trunk allowed vlan add {{ parsed_vlans[i] }}
+ {% endfor %}
+
+This allows for dynamic generation of VLAN lists on a Cisco IOS tagged interface. You can store an exhaustive raw list of the exact VLANs required for an interface and then compare that to the parsed IOS output that would actually be generated for the configuration.
+
+
+.. _hash_filters:
+
+Hashing and encrypting strings and passwords
+==============================================
+
+.. versionadded:: 1.9
+
+To get the sha1 hash of a string:
+
+.. code-block:: yaml+jinja
+
+ {{ 'test1' | hash('sha1') }}
+ # => "b444ac06613fc8d63795be9ad0beaf55011936ac"
+
+To get the md5 hash of a string:
+
+.. code-block:: yaml+jinja
+
+ {{ 'test1' | hash('md5') }}
+ # => "5a105e8b9d40e1329780d62ea2265d8a"
+
+Get a string checksum:
+
+.. code-block:: yaml+jinja
+
+ {{ 'test2' | checksum }}
+ # => "109f4b3c50d7b0df729d299bc6f8e9ef9066971f"
+
+Other hashes (platform dependent):
+
+.. code-block:: yaml+jinja
+
+ {{ 'test2' | hash('blowfish') }}
+
+To get a sha512 password hash (random salt):
+
+.. code-block:: yaml+jinja
+
+ {{ 'passwordsaresecret' | password_hash('sha512') }}
+ # => "$6$UIv3676O/ilZzWEE$ktEfFF19NQPF2zyxqxGkAceTnbEgpEKuGBtk6MlU4v2ZorWaVQUMyurgmHCh2Fr4wpmQ/Y.AlXMJkRnIS4RfH/"
+
+To get a sha256 password hash with a specific salt:
+
+.. code-block:: yaml+jinja
+
+ {{ 'secretpassword' | password_hash('sha256', 'mysecretsalt') }}
+ # => "$5$mysecretsalt$ReKNyDYjkKNqRVwouShhsEqZ3VOE8eoVO4exihOfvG4"
+
+An idempotent method to generate unique hashes per system is to use a salt that is consistent between runs:
+
+.. code-block:: yaml+jinja
+
+ {{ 'secretpassword' | password_hash('sha512', 65534 | random(seed=inventory_hostname) | string) }}
+ # => "$6$43927$lQxPKz2M2X.NWO.gK.t7phLwOKQMcSq72XxDZQ0XzYV6DlL1OD72h417aj16OnHTGxNzhftXJQBcjbunLEepM0"
+
+Hash types available depend on the control system running Ansible, 'hash' depends on `hashlib <https://docs.python.org/3.8/library/hashlib.html>`_, password_hash depends on `passlib <https://passlib.readthedocs.io/en/stable/lib/passlib.hash.html>`_. The `crypt <https://docs.python.org/3.8/library/crypt.html>`_ is used as a fallback if ``passlib`` is not installed.
+
+.. versionadded:: 2.7
+
+Some hash types allow providing a rounds parameter:
+
+.. code-block:: yaml+jinja
+
+ {{ 'secretpassword' | password_hash('sha256', 'mysecretsalt', rounds=10000) }}
+ # => "$5$rounds=10000$mysecretsalt$Tkm80llAxD4YHll6AgNIztKn0vzAACsuuEfYeGP7tm7"
+
+The filter `password_hash` produces different results depending on whether you installed `passlib` or not.
+
+To ensure idempotency, specify `rounds` to be neither `crypt`'s nor `passlib`'s default, which is `5000` for `crypt` and a variable value (`535000` for sha256, `656000` for sha512) for `passlib`:
+
+.. code-block:: yaml+jinja
+
+ {{ 'secretpassword' | password_hash('sha256', 'mysecretsalt', rounds=5001) }}
+ # => "$5$rounds=5001$mysecretsalt$wXcTWWXbfcR8er5IVf7NuquLvnUA6s8/qdtOhAZ.xN."
+
+Hash type 'blowfish' (BCrypt) provides the facility to specify the version of the BCrypt algorithm.
+
+.. code-block:: yaml+jinja
+
+ {{ 'secretpassword' | password_hash('blowfish', '1234567890123456789012', ident='2b') }}
+ # => "$2b$12$123456789012345678901uuJ4qFdej6xnWjOQT.FStqfdoY8dYUPC"
+
+.. note::
+ The parameter is only available for `blowfish (BCrypt) <https://passlib.readthedocs.io/en/stable/lib/passlib.hash.bcrypt.html#passlib.hash.bcrypt>`_.
+ Other hash types will simply ignore this parameter.
+ Valid values for this parameter are: ['2', '2a', '2y', '2b']
+
+.. versionadded:: 2.12
+
+You can also use the Ansible :ref:`vault <vault>` filter to encrypt data:
+
+.. code-block:: yaml+jinja
+
+ # simply encrypt my key in a vault
+ vars:
+ myvaultedkey: "{{ keyrawdata|vault(passphrase) }}"
+
+ - name: save templated vaulted data
+ template: src=dump_template_data.j2 dest=/some/key/vault.txt
+ vars:
+ mysalt: '{{ 2**256|random(seed=inventory_hostname) }}'
+ template_data: '{{ secretdata|vault(vaultsecret, salt=mysalt) }}'
+
+
+And then decrypt it using the unvault filter:
+
+.. code-block:: yaml+jinja
+
+ # simply decrypt my key from a vault
+ vars:
+ mykey: "{{ myvaultedkey|unvault(passphrase) }}"
+
+ - name: save templated unvaulted data
+ template: src=dump_template_data.j2 dest=/some/key/clear.txt
+ vars:
+ template_data: '{{ secretdata|unvault(vaultsecret) }}'
+
+
+.. _other_useful_filters:
+
+Manipulating text
+=================
+
+Several filters work with text, including URLs, file names, and path names.
+
+.. _comment_filter:
+
+Adding comments to files
+------------------------
+
+The ``comment`` filter lets you create comments in a file from text in a template, with a variety of comment styles. By default Ansible uses ``#`` to start a comment line and adds a blank comment line above and below your comment text. For example the following:
+
+.. code-block:: yaml+jinja
+
+ {{ "Plain style (default)" | comment }}
+
+produces this output:
+
+.. code-block:: text
+
+ #
+ # Plain style (default)
+ #
+
+Ansible offers styles for comments in C (``//...``), C block
+(``/*...*/``), Erlang (``%...``) and XML (``<!--...-->``):
+
+.. code-block:: yaml+jinja
+
+ {{ "C style" | comment('c') }}
+ {{ "C block style" | comment('cblock') }}
+ {{ "Erlang style" | comment('erlang') }}
+ {{ "XML style" | comment('xml') }}
+
+You can define a custom comment character. This filter:
+
+.. code-block:: yaml+jinja
+
+ {{ "My Special Case" | comment(decoration="! ") }}
+
+produces:
+
+.. code-block:: text
+
+ !
+ ! My Special Case
+ !
+
+You can fully customize the comment style:
+
+.. code-block:: yaml+jinja
+
+ {{ "Custom style" | comment('plain', prefix='#######\n#', postfix='#\n#######\n ###\n #') }}
+
+That creates the following output:
+
+.. code-block:: text
+
+ #######
+ #
+ # Custom style
+ #
+ #######
+ ###
+ #
+
+The filter can also be applied to any Ansible variable. For example to
+make the output of the ``ansible_managed`` variable more readable, we can
+change the definition in the ``ansible.cfg`` file to this:
+
+.. code-block:: ini
+
+ [defaults]
+
+ ansible_managed = This file is managed by Ansible.%n
+ template: {file}
+ date: %Y-%m-%d %H:%M:%S
+ user: {uid}
+ host: {host}
+
+and then use the variable with the `comment` filter:
+
+.. code-block:: yaml+jinja
+
+ {{ ansible_managed | comment }}
+
+which produces this output:
+
+.. code-block:: sh
+
+ #
+ # This file is managed by Ansible.
+ #
+ # template: /home/ansible/env/dev/ansible_managed/roles/role1/templates/test.j2
+ # date: 2015-09-10 11:02:58
+ # user: ansible
+ # host: myhost
+ #
+
+URLEncode Variables
+-------------------
+
+The ``urlencode`` filter quotes data for use in a URL path or query using UTF-8:
+
+.. code-block:: yaml+jinja
+
+ {{ 'Trollhättan' | urlencode }}
+ # => 'Trollh%C3%A4ttan'
+
+Splitting URLs
+--------------
+
+.. versionadded:: 2.4
+
+The ``urlsplit`` filter extracts the fragment, hostname, netloc, password, path, port, query, scheme, and username from an URL. With no arguments, returns a dictionary of all the fields:
+
+.. code-block:: yaml+jinja
+
+ {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('hostname') }}
+ # => 'www.acme.com'
+
+ {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('netloc') }}
+ # => 'user:password@www.acme.com:9000'
+
+ {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('username') }}
+ # => 'user'
+
+ {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('password') }}
+ # => 'password'
+
+ {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('path') }}
+ # => '/dir/index.html'
+
+ {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('port') }}
+ # => '9000'
+
+ {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('scheme') }}
+ # => 'http'
+
+ {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('query') }}
+ # => 'query=term'
+
+ {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit('fragment') }}
+ # => 'fragment'
+
+ {{ "http://user:password@www.acme.com:9000/dir/index.html?query=term#fragment" | urlsplit }}
+ # =>
+ # {
+ # "fragment": "fragment",
+ # "hostname": "www.acme.com",
+ # "netloc": "user:password@www.acme.com:9000",
+ # "password": "password",
+ # "path": "/dir/index.html",
+ # "port": 9000,
+ # "query": "query=term",
+ # "scheme": "http",
+ # "username": "user"
+ # }
+
+Searching strings with regular expressions
+------------------------------------------
+
+To search in a string or extract parts of a string with a regular expression, use the ``regex_search`` filter:
+
+.. code-block:: yaml+jinja
+
+ # Extracts the database name from a string
+ {{ 'server1/database42' | regex_search('database[0-9]+') }}
+ # => 'database42'
+
+ # Example for a case insensitive search in multiline mode
+ {{ 'foo\nBAR' | regex_search('^bar', multiline=True, ignorecase=True) }}
+ # => 'BAR'
+
+ # Extracts server and database id from a string
+ {{ 'server1/database42' | regex_search('server([0-9]+)/database([0-9]+)', '\\1', '\\2') }}
+ # => ['1', '42']
+
+ # Extracts dividend and divisor from a division
+ {{ '21/42' | regex_search('(?P<dividend>[0-9]+)/(?P<divisor>[0-9]+)', '\\g<dividend>', '\\g<divisor>') }}
+ # => ['21', '42']
+
+The ``regex_search`` filter returns an empty string if it cannot find a match:
+
+.. code-block:: yaml+jinja
+
+ {{ 'ansible' | regex_search('foobar') }}
+ # => ''
+
+
+.. note::
+
+
+ The ``regex_search`` filter returns ``None`` when used in a Jinja expression (for example in conjunction with operators, other filters, and so on). See the two examples below.
+
+ .. code-block:: Jinja
+
+ {{ 'ansible' | regex_search('foobar') == '' }}
+ # => False
+ {{ 'ansible' | regex_search('foobar') is none }}
+ # => True
+
+ This is due to historic behavior and the custom re-implementation of some of the Jinja internals in Ansible. Enable the ``jinja2_native`` setting if you want the ``regex_search`` filter to always return ``None`` if it cannot find a match. See :ref:`jinja2_faqs` for details.
+
+To extract all occurrences of regex matches in a string, use the ``regex_findall`` filter:
+
+.. code-block:: yaml+jinja
+
+ # Returns a list of all IPv4 addresses in the string
+ {{ 'Some DNS servers are 8.8.8.8 and 8.8.4.4' | regex_findall('\\b(?:[0-9]{1,3}\\.){3}[0-9]{1,3}\\b') }}
+ # => ['8.8.8.8', '8.8.4.4']
+
+ # Returns all lines that end with "ar"
+ {{ 'CAR\ntar\nfoo\nbar\n' | regex_findall('^.ar$', multiline=True, ignorecase=True) }}
+ # => ['CAR', 'tar', 'bar']
+
+
+To replace text in a string with regex, use the ``regex_replace`` filter:
+
+.. code-block:: yaml+jinja
+
+ # Convert "ansible" to "able"
+ {{ 'ansible' | regex_replace('^a.*i(.*)$', 'a\\1') }}
+ # => 'able'
+
+ # Convert "foobar" to "bar"
+ {{ 'foobar' | regex_replace('^f.*o(.*)$', '\\1') }}
+ # => 'bar'
+
+ # Convert "localhost:80" to "localhost, 80" using named groups
+ {{ 'localhost:80' | regex_replace('^(?P<host>.+):(?P<port>\\d+)$', '\\g<host>, \\g<port>') }}
+ # => 'localhost, 80'
+
+ # Convert "localhost:80" to "localhost"
+ {{ 'localhost:80' | regex_replace(':80') }}
+ # => 'localhost'
+
+ # Comment all lines that end with "ar"
+ {{ 'CAR\ntar\nfoo\nbar\n' | regex_replace('^(.ar)$', '#\\1', multiline=True, ignorecase=True) }}
+ # => '#CAR\n#tar\nfoo\n#bar\n'
+
+.. note::
+ If you want to match the whole string and you are using ``*`` make sure to always wraparound your regular expression with the start/end anchors. For example ``^(.*)$`` will always match only one result, while ``(.*)`` on some Python versions will match the whole string and an empty string at the end, which means it will make two replacements:
+
+.. code-block:: yaml+jinja
+
+ # add "https://" prefix to each item in a list
+ GOOD:
+ {{ hosts | map('regex_replace', '^(.*)$', 'https://\\1') | list }}
+ {{ hosts | map('regex_replace', '(.+)', 'https://\\1') | list }}
+ {{ hosts | map('regex_replace', '^', 'https://') | list }}
+
+ BAD:
+ {{ hosts | map('regex_replace', '(.*)', 'https://\\1') | list }}
+
+ # append ':80' to each item in a list
+ GOOD:
+ {{ hosts | map('regex_replace', '^(.*)$', '\\1:80') | list }}
+ {{ hosts | map('regex_replace', '(.+)', '\\1:80') | list }}
+ {{ hosts | map('regex_replace', '$', ':80') | list }}
+
+ BAD:
+ {{ hosts | map('regex_replace', '(.*)', '\\1:80') | list }}
+
+.. note::
+ Prior to ansible 2.0, if ``regex_replace`` filter was used with variables inside YAML arguments (as opposed to simpler 'key=value' arguments), then you needed to escape backreferences (for example, ``\\1``) with 4 backslashes (``\\\\``) instead of 2 (``\\``).
+
+.. versionadded:: 2.0
+
+To escape special characters within a standard Python regex, use the ``regex_escape`` filter (using the default ``re_type='python'`` option):
+
+.. code-block:: yaml+jinja
+
+ # convert '^f.*o(.*)$' to '\^f\.\*o\(\.\*\)\$'
+ {{ '^f.*o(.*)$' | regex_escape() }}
+
+.. versionadded:: 2.8
+
+To escape special characters within a POSIX basic regex, use the ``regex_escape`` filter with the ``re_type='posix_basic'`` option:
+
+.. code-block:: yaml+jinja
+
+ # convert '^f.*o(.*)$' to '\^f\.\*o(\.\*)\$'
+ {{ '^f.*o(.*)$' | regex_escape('posix_basic') }}
+
+
+Managing file names and path names
+----------------------------------
+
+To get the last name of a file path, like 'foo.txt' out of '/etc/asdf/foo.txt':
+
+.. code-block:: yaml+jinja
+
+ {{ path | basename }}
+
+To get the last name of a windows style file path (new in version 2.0):
+
+.. code-block:: yaml+jinja
+
+ {{ path | win_basename }}
+
+To separate the windows drive letter from the rest of a file path (new in version 2.0):
+
+.. code-block:: yaml+jinja
+
+ {{ path | win_splitdrive }}
+
+To get only the windows drive letter:
+
+.. code-block:: yaml+jinja
+
+ {{ path | win_splitdrive | first }}
+
+To get the rest of the path without the drive letter:
+
+.. code-block:: yaml+jinja
+
+ {{ path | win_splitdrive | last }}
+
+To get the directory from a path:
+
+.. code-block:: yaml+jinja
+
+ {{ path | dirname }}
+
+To get the directory from a windows path (new version 2.0):
+
+.. code-block:: yaml+jinja
+
+ {{ path | win_dirname }}
+
+To expand a path containing a tilde (`~`) character (new in version 1.5):
+
+.. code-block:: yaml+jinja
+
+ {{ path | expanduser }}
+
+To expand a path containing environment variables:
+
+.. code-block:: yaml+jinja
+
+ {{ path | expandvars }}
+
+.. note:: `expandvars` expands local variables; using it on remote paths can lead to errors.
+
+.. versionadded:: 2.6
+
+To get the real path of a link (new in version 1.8):
+
+.. code-block:: yaml+jinja
+
+ {{ path | realpath }}
+
+To get the relative path of a link, from a start point (new in version 1.7):
+
+.. code-block:: yaml+jinja
+
+ {{ path | relpath('/etc') }}
+
+To get the root and extension of a path or file name (new in version 2.0):
+
+.. code-block:: yaml+jinja
+
+ # with path == 'nginx.conf' the return would be ('nginx', '.conf')
+ {{ path | splitext }}
+
+The ``splitext`` filter always returns a pair of strings. The individual components can be accessed by using the ``first`` and ``last`` filters:
+
+.. code-block:: yaml+jinja
+
+ # with path == 'nginx.conf' the return would be 'nginx'
+ {{ path | splitext | first }}
+
+ # with path == 'nginx.conf' the return would be '.conf'
+ {{ path | splitext | last }}
+
+To join one or more path components:
+
+.. code-block:: yaml+jinja
+
+ {{ ('/etc', path, 'subdir', file) | path_join }}
+
+.. versionadded:: 2.10
+
+Manipulating strings
+====================
+
+To add quotes for shell usage:
+
+.. code-block:: yaml+jinja
+
+ - name: Run a shell command
+ ansible.builtin.shell: echo {{ string_value | quote }}
+
+To concatenate a list into a string:
+
+.. code-block:: yaml+jinja
+
+ {{ list | join(" ") }}
+
+To split a string into a list:
+
+.. code-block:: yaml+jinja
+
+ {{ csv_string | split(",") }}
+
+.. versionadded:: 2.11
+
+To work with Base64 encoded strings:
+
+.. code-block:: yaml+jinja
+
+ {{ encoded | b64decode }}
+ {{ decoded | string | b64encode }}
+
+As of version 2.6, you can define the type of encoding to use, the default is ``utf-8``:
+
+.. code-block:: yaml+jinja
+
+ {{ encoded | b64decode(encoding='utf-16-le') }}
+ {{ decoded | string | b64encode(encoding='utf-16-le') }}
+
+.. note:: The ``string`` filter is only required for Python 2 and ensures that text to encode is a unicode string. Without that filter before b64encode the wrong value will be encoded.
+
+.. note:: The return value of b64decode is a string. If you decrypt a binary blob using b64decode and then try to use it (for example by using :ref:`copy <copy_module>` to write it to a file) you will mostly likely find that your binary has been corrupted. If you need to take a base64 encoded binary and write it to disk, it is best to use the system ``base64`` command with the :ref:`shell module <shell_module>`, piping in the encoded data using the ``stdin`` parameter. For example: ``shell: cmd="base64 --decode > myfile.bin" stdin="{{ encoded }}"``
+
+.. versionadded:: 2.6
+
+Managing UUIDs
+==============
+
+To create a namespaced UUIDv5:
+
+.. code-block:: yaml+jinja
+
+ {{ string | to_uuid(namespace='11111111-2222-3333-4444-555555555555') }}
+
+.. versionadded:: 2.10
+
+To create a namespaced UUIDv5 using the default Ansible namespace '361E6D51-FAEC-444A-9079-341386DA8E2E':
+
+.. code-block:: yaml+jinja
+
+ {{ string | to_uuid }}
+
+.. versionadded:: 1.9
+
+To make use of one attribute from each item in a list of complex variables, use the :func:`Jinja2 map filter <jinja2:jinja-filters.map>`:
+
+.. code-block:: yaml+jinja
+
+ # get a comma-separated list of the mount points (for example, "/,/mnt/stuff") on a host
+ {{ ansible_mounts | map(attribute='mount') | join(',') }}
+
+Handling dates and times
+========================
+
+To get a date object from a string use the `to_datetime` filter:
+
+.. code-block:: yaml+jinja
+
+ # Get total amount of seconds between two dates. Default date format is %Y-%m-%d %H:%M:%S but you can pass your own format
+ {{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime('%Y-%m-%d'))).total_seconds() }}
+
+ # Get remaining seconds after delta has been calculated. NOTE: This does NOT convert years, days, hours, and so on to seconds. For that, use total_seconds()
+ {{ (("2016-08-14 20:00:12" | to_datetime) - ("2016-08-14 18:00:00" | to_datetime)).seconds }}
+ # This expression evaluates to "12" and not "132". Delta is 2 hours, 12 seconds
+
+ # get amount of days between two dates. This returns only number of days and discards remaining hours, minutes, and seconds
+ {{ (("2016-08-14 20:00:12" | to_datetime) - ("2015-12-25" | to_datetime('%Y-%m-%d'))).days }}
+
+.. note:: For a full list of format codes for working with python date format strings, see the `python datetime documentation <https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior>`_.
+
+.. versionadded:: 2.4
+
+To format a date using a string (like with the shell date command), use the "strftime" filter:
+
+.. code-block:: yaml+jinja
+
+ # Display year-month-day
+ {{ '%Y-%m-%d' | strftime }}
+ # => "2021-03-19"
+
+ # Display hour:min:sec
+ {{ '%H:%M:%S' | strftime }}
+ # => "21:51:04"
+
+ # Use ansible_date_time.epoch fact
+ {{ '%Y-%m-%d %H:%M:%S' | strftime(ansible_date_time.epoch) }}
+ # => "2021-03-19 21:54:09"
+
+ # Use arbitrary epoch value
+ {{ '%Y-%m-%d' | strftime(0) }} # => 1970-01-01
+ {{ '%Y-%m-%d' | strftime(1441357287) }} # => 2015-09-04
+
+.. versionadded:: 2.13
+
+strftime takes an optional utc argument, defaulting to False, meaning times are in the local timezone:
+
+.. code-block:: yaml+jinja
+
+ {{ '%H:%M:%S' | strftime }} # time now in local timezone
+ {{ '%H:%M:%S' | strftime(utc=True) }} # time now in UTC
+
+.. note:: To get all string possibilities, check https://docs.python.org/3/library/time.html#time.strftime
+
+Getting Kubernetes resource names
+=================================
+
+.. note::
+
+ These filters have migrated to the `kubernetes.core <https://galaxy.ansible.com/kubernetes/core>`_ collection. Follow the installation instructions to install that collection.
+
+Use the "k8s_config_resource_name" filter to obtain the name of a Kubernetes ConfigMap or Secret,
+including its hash:
+
+.. code-block:: yaml+jinja
+
+ {{ configmap_resource_definition | kubernetes.core.k8s_config_resource_name }}
+
+This can then be used to reference hashes in Pod specifications:
+
+.. code-block:: yaml+jinja
+
+ my_secret:
+ kind: Secret
+ metadata:
+ name: my_secret_name
+
+ deployment_resource:
+ kind: Deployment
+ spec:
+ template:
+ spec:
+ containers:
+ - envFrom:
+ - secretRef:
+ name: {{ my_secret | kubernetes.core.k8s_config_resource_name }}
+
+.. versionadded:: 2.8
+
+.. _PyYAML library: https://pyyaml.org/
+
+.. _PyYAML documentation: https://pyyaml.org/wiki/PyYAMLDocumentation
+
+
+.. seealso::
+
+ :ref:`about_playbooks`
+ 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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_handlers.rst b/docs/docsite/rst/playbook_guide/playbooks_handlers.rst
new file mode 100644
index 0000000..69a8658
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_handlers.rst
@@ -0,0 +1,196 @@
+.. _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 within that include, on the other hand, can be notified individually.
+
+
+Meta tasks as handlers
+----------------------
+
+Since Ansible 2.14 :ref:`meta tasks <ansible_collections.ansible.builtin.meta_module>` are allowed to be used and notified as handlers. Note that however ``flush_handlers`` cannot be used as a handler to prevent unexpected behavior.
+
+
+Limitations
+-----------
+
+A handler cannot run ``import_role`` or ``include_role``.
diff --git a/docs/docsite/rst/playbook_guide/playbooks_intro.rst b/docs/docsite/rst/playbook_guide/playbooks_intro.rst
new file mode 100644
index 0000000..8592b04
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_intro.rst
@@ -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`` through 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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_lookups.rst b/docs/docsite/rst/playbook_guide/playbooks_lookups.rst
new file mode 100644
index 0000000..785f150
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_lookups.rst
@@ -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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_loops.rst b/docs/docsite/rst/playbook_guide/playbooks_loops.rst
new file mode 100644
index 0000000..5e8afde
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_loops.rst
@@ -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: true
+ 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: true
+
+.. 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: true
+ extended_allitems: false
+
+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 through 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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_module_defaults.rst b/docs/docsite/rst/playbook_guide/playbooks_module_defaults.rst
new file mode 100644
index 0000000..3a4bdfc
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_module_defaults.rst
@@ -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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_privilege_escalation.rst b/docs/docsite/rst/playbook_guide/playbooks_privilege_escalation.rst
new file mode 100644
index 0000000..7222ab3
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_privilege_escalation.rst
@@ -0,0 +1,776 @@
+.. _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: true
+
+To run a command as the ``apache`` user:
+
+.. code-block:: yaml
+
+ - name: Run a command as the apache user
+ command: somecommand
+ become: true
+ 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: true
+ 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: true
+ 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: true
+ 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: true
+ 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: true
+ 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` 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 with the exception
+of ``become_user``. As there is no sensible default for ``become_user`` on Windows
+it is required when using ``become``. See :ref:`ansible.builtin.runas become plugin <ansible_collections.ansible.builtin.runas_become>` for details.
+
+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: true
+
+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: true
+ vars:
+ ansible_become: true
+ 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: true
+ become_flags: logon_type=batch
+
+ - name: run a command and not load the user profile
+ ansible.windows.win_whomai:
+ become: true
+ 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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_prompts.rst b/docs/docsite/rst/playbook_guide/playbooks_prompts.rst
new file mode 100644
index 0000000..1afb3fa
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_prompts.rst
@@ -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: false
+
+ - 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: true
+ encrypt: sha512_crypt
+ confirm: true
+ 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: true
+ private: true
+
+.. 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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_python_version.rst b/docs/docsite/rst/playbook_guide/playbooks_python_version.rst
new file mode 100644
index 0000000..ee18b60
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_python_version.rst
@@ -0,0 +1,68 @@
+.. _pb-py-compat:
+
+********************
+Python3 in templates
+********************
+
+Ansible uses Jinja2 to take advantage of Python data types and standard functions in templates and variables.
+You can use these data types and standard functions to perform a rich set of operations on your data. However,
+if you use templates, you must be aware of differences between Python versions.
+
+These topics help you design templates that work on both Python2 and Python3. They might also help if you are upgrading from Python2 to Python3. Upgrading within Python2 or Python3 does not usually introduce changes that affect Jinja2 templates.
+
+.. _pb-py-compat-dict-views:
+
+Dictionary views
+================
+
+In Python2, the :meth:`dict.keys`, :meth:`dict.values`, and :meth:`dict.items`
+methods return a list. Jinja2 returns that to Ansible using a string
+representation that Ansible can turn back into a list.
+
+In Python3, those methods return a :ref:`dictionary view <python3:dict-views>` object. The
+string representation that Jinja2 returns for dictionary views cannot be parsed back
+into a list by Ansible. It is, however, easy to make this portable by
+using the :func:`list <jinja2:jinja-filters.list>` filter whenever using :meth:`dict.keys`,
+:meth:`dict.values`, or :meth:`dict.items`.
+
+.. code-block:: yaml+jinja
+
+ vars:
+ hosts:
+ testhost1: 127.0.0.2
+ testhost2: 127.0.0.3
+ tasks:
+ - debug:
+ msg: '{{ item }}'
+ # Only works with Python 2
+ #loop: "{{ hosts.keys() }}"
+ # Works with both Python 2 and Python 3
+ loop: "{{ hosts.keys() | list }}"
+
+.. _pb-py-compat-iteritems:
+
+dict.iteritems()
+================
+
+Python2 dictionaries have :meth:`~dict.iterkeys`, :meth:`~dict.itervalues`, and :meth:`~dict.iteritems` methods.
+
+Python3 dictionaries do not have these methods. Use :meth:`dict.keys`, :meth:`dict.values`, and :meth:`dict.items` to make your playbooks and templates compatible with both Python2 and Python3.
+
+.. code-block:: yaml+jinja
+
+ vars:
+ hosts:
+ testhost1: 127.0.0.2
+ testhost2: 127.0.0.3
+ tasks:
+ - debug:
+ msg: '{{ item }}'
+ # Only works with Python 2
+ #loop: "{{ hosts.iteritems() }}"
+ # Works with both Python 2 and Python 3
+ loop: "{{ hosts.items() | list }}"
+
+.. seealso::
+ * The :ref:`pb-py-compat-dict-views` entry for information on
+ why the :func:`list filter <jinja2:jinja-filters.list>` is necessary
+ here.
diff --git a/docs/docsite/rst/playbook_guide/playbooks_reuse.rst b/docs/docsite/rst/playbook_guide/playbooks_reuse.rst
new file mode 100644
index 0000000..89cc5a4
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_reuse.rst
@@ -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 through 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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_reuse_roles.rst b/docs/docsite/rst/playbook_guide/playbooks_reuse_roles.rst
new file mode 100644
index 0000000..99ca2bf
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_reuse_roles.rst
@@ -0,0 +1,615 @@
+.. _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 and optional Galaxy metadata such as platforms supported.
+
+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_spec:
+
+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 should use `true/false` if you want to be compatible with `ansible-lint`.
+
+ :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/myapp/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
+======================================
+
+.. note::
+ This applies only to standalone roles. Roles in collections do not support plugin embedding; they must use the collection's ``plugins`` structure to distribute plugins.
+
+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 provides an excellent default framework for creating your own roles.
+
+Read the `Ansible Galaxy documentation <https://galaxy.ansible.com/docs/>`_ page for more information. A page that refers back to this one frequently is the Galaxy Roles document which explains the required metadata your role needs for use in Galaxy <https://galaxy.ansible.com/docs/contributing/creating_role.html>.
+
+.. 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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_roles.rst b/docs/docsite/rst/playbook_guide/playbooks_roles.rst
new file mode 100644
index 0000000..f79e230
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_roles.rst
@@ -0,0 +1,19 @@
+:orphan:
+
+Playbook Roles and Include Statements
+=====================================
+
+.. contents:: Topics
+
+
+The documentation regarding roles and includes for playbooks have moved. Their new location is here: :ref:`playbooks_reuse`. Please update any links you may have made directly to this page.
+
+.. seealso::
+
+ :ref:`ansible_galaxy`
+ How to share roles on galaxy, role management
+ :ref:`working_with_playbooks`
+ Review the basic Playbook language features
+ :ref:`playbooks_reuse`
+ Creating reusable Playbooks.
+
diff --git a/docs/docsite/rst/playbook_guide/playbooks_special_topics.rst b/docs/docsite/rst/playbook_guide/playbooks_special_topics.rst
new file mode 100644
index 0000000..5df72c1
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_special_topics.rst
@@ -0,0 +1,8 @@
+:orphan:
+
+.. _playbooks_special_topics:
+
+Advanced playbooks features
+===========================
+
+This page is obsolete. Refer to the :ref:`main User Guide index page <user_guide_index>` for links to all playbook-related topics. Please update any links you may have made directly to this page.
diff --git a/docs/docsite/rst/playbook_guide/playbooks_startnstep.rst b/docs/docsite/rst/playbook_guide/playbooks_startnstep.rst
new file mode 100644
index 0000000..e6c78d2
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_startnstep.rst
@@ -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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_strategies.rst b/docs/docsite/rst/playbook_guide/playbooks_strategies.rst
new file mode 100644
index 0000000..43d1605
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_strategies.rst
@@ -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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_tags.rst b/docs/docsite/rst/playbook_guide/playbooks_tags.rst
new file mode 100644
index 0000000..4da0af0
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_tags.rst
@@ -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: true
+ 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: true
+
+ - 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: true
+
+ - 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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_templating.rst b/docs/docsite/rst/playbook_guide/playbooks_templating.rst
new file mode 100644
index 0000000..4382f15
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_templating.rst
@@ -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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_templating_now.rst b/docs/docsite/rst/playbook_guide/playbooks_templating_now.rst
new file mode 100644
index 0000000..1ca6080
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_templating_now.rst
@@ -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. \ No newline at end of file
diff --git a/docs/docsite/rst/playbook_guide/playbooks_tests.rst b/docs/docsite/rst/playbook_guide/playbooks_tests.rst
new file mode 100644
index 0000000..71eaee8
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_tests.rst
@@ -0,0 +1,542 @@
+.. _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, pep440
+
+``loose``
+ This type corresponds to the Python ``distutils.version.LooseVersion`` class. All version formats are valid for this type. The rules for comparison are simple and predictable, but may not always give expected results.
+
+``strict``
+ This type corresponds to the Python ``distutils.version.StrictVersion`` class. A version number consists of two or three dot-separated numeric components, with an optional "pre-release" tag on the end. The pre-release tag consists of a single letter 'a' or 'b' followed by a number. If the numeric components of two version numbers are equal, then one with a pre-release tag will always be deemed earlier (lesser) than one without.
+
+``semver``/``semantic``
+ This type implements the `Semantic Version <https://semver.org>`_ scheme for version comparison.
+
+``pep440``
+ This type implements the Python `PEP-440 <https://peps.python.org/pep-0440/>`_ versioning rules for version comparison. Added in version 2.14.
+
+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') }}
+
+In Ansible 2.14, the ``pep440`` option for ``version_type`` was added, and the rules of this type are defined in `PEP-440 <https://peps.python.org/pep-0440/>`_. The following example showcases how this type can differentiate pre-releases as being less than a general release.
+
+.. code-block:: yaml+jinja
+
+ {{ '2.14.0rc1' is version('2.14.0', 'lt', version_type='pep440') }}
+
+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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_variables.rst b/docs/docsite/rst/playbook_guide/playbooks_variables.rst
new file mode 100644
index 0000000..9482845
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_variables.rst
@@ -0,0 +1,534 @@
+.. _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"
+
+.. _boolean_variables:
+
+Boolean variables
+=================
+
+Ansible accepts a broad range of values for boolean variables: ``true/false``, ``1/0``, ``yes/no``, ``True/False`` and so on. The matching of valid strings is case insensitive.
+While documentation examples focus on ``true/false`` to be compatible with ``ansible-lint`` default settings, you can use any of the following:
+
+.. table::
+ :class: documentation-table
+
+ =============================================================================================== ====================================================================
+ Valid values Description
+ =============================================================================================== ====================================================================
+ ``True`` , ``'true'`` , ``'t'`` , ``'yes'`` , ``'y'`` , ``'on'`` , ``'1'`` , ``1`` , ``1.0`` Truthy values
+
+ ``False`` , ``'false'`` , ``'f'`` , ``'no'`` , ``'n'`` , ``'off'`` , ``'0'`` , ``0`` , ``0.0`` Falsy values
+
+ =============================================================================================== ====================================================================
+
+.. _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 playbook runs. Registered variables are only valid on the host for the rest of the current playbook run, including subsequent plays within the same 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"\!"\\\"\"}"
+
+
+vars from a JSON or YAML file
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you have a lot of special characters, use a JSON or YAML file containing the variable definitions. Prepend both JSON and YAML filenames with `@`.
+
+.. code-block:: text
+
+ ansible-playbook release.yml --extra-vars "@some_file.json"
+ ansible-playbook release.yml --extra-vars "@some_file.yaml"
+
+
+.. _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
diff --git a/docs/docsite/rst/playbook_guide/playbooks_vars_facts.rst b/docs/docsite/rst/playbook_guide/playbooks_vars_facts.rst
new file mode 100644
index 0000000..e90947e
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_vars_facts.rst
@@ -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: false
+
+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: true
+ 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"
+ }
+ }
diff --git a/docs/docsite/rst/playbook_guide/playbooks_vault.rst b/docs/docsite/rst/playbook_guide/playbooks_vault.rst
new file mode 100644
index 0000000..03bd2c0
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/playbooks_vault.rst
@@ -0,0 +1,6 @@
+:orphan:
+
+Using vault in playbooks
+========================
+
+The documentation regarding Ansible Vault has moved. The new location is here: :ref:`vault`. Please update any links you may have made directly to this page.
diff --git a/docs/docsite/rst/playbook_guide/shared_snippets/role_directory.txt b/docs/docsite/rst/playbook_guide/shared_snippets/role_directory.txt
new file mode 100644
index 0000000..5722f0e
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/shared_snippets/role_directory.txt
@@ -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/ # ""
diff --git a/docs/docsite/rst/playbook_guide/shared_snippets/with2loop.txt b/docs/docsite/rst/playbook_guide/shared_snippets/with2loop.txt
new file mode 100644
index 0000000..c563dce
--- /dev/null
+++ b/docs/docsite/rst/playbook_guide/shared_snippets/with2loop.txt
@@ -0,0 +1,206 @@
+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) }}"
+ loop: "{{ range(0, 4 + 1, 2)|list }}"
+
+The range of the loop is exclusive of the end point.
+
+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