Sometimes, when Ansible is running a play to ensure the correct configuration of a system, it might need to perform one or more tasks on another host on behalf of the managed host.
For example, you might need to log in to a network device to change a DHCP configuration, make sure that certain groups exist in an Active Directory domain, or communicate with the API of a service by using tools that are not available on the managed host.
In a play, you can delegate a task to run on another host instead of the current managed host.
A task delegates the action to a host by using the delegate_to directive.
This directive points Ansible to the host that executes the task in place of the corresponding target.
The host that you delegate the task to does not need to be listed in the Ansible inventory.
The following example runs the uname -a command on each host in the play, and then runs the uname -a command on host.lab.example.com on behalf of each host in the play.
---
- name: Delegation Example
hosts: demo.lab.example.com
become: false
tasks:
- name: Get system information
ansible.builtin.command: uname -a
register: managed_host
- name: Display demo system information
ansible.builtin.debug:
var: managed_host
- name: Get system information
ansible.builtin.command: uname -a
delegate_to: host.lab.example.com
register: delegated
- name: Display localhost system information
ansible.builtin.debug:
var: delegatedIn the following example, the first task is delegated to each of the HAProxy load balancers in the lbservers Ansible group in turn, removing the managed host from all the load balancers.
The second task, which is not delegated, then stops the web server on the managed host.
Both tasks run for each host in the play.
- name: Remove the server from HAProxy
community.general.haproxy:
state: disabled
host: "{{ ansible_facts['fqdn'] }}"
socket: /var/lib/haproxy/stats
delegate_to: "{{ item }}"
loop: "{{ groups['lbservers'] }}"
- name: Make sure Apache HTTPD is stopped
ansible.builtin.service:
name: httpd
state: stoppedYou might also delegate tasks to another host when you want to verify access to a service on the managed host currently being processed by Ansible.
For example, you might want to ensure that communication from a test client can pass through a host-based firewall running on that host.
- name: Access the web service
uri:
url: http://{{ ansible_facts['fqdn'] }}
timeout: 5
delegate_to: test-client.example.comDelegating to the Execution Environment
One of the most common places you might delegate a task is to the execution environment, or localhost environment.
For example, you need to communicate with the API for a controller that you cannot reach from the managed host, but that you can reach from the execution environment, so you delegate the task.
- name: Get information about controller instance
ansible.builtin.uri:
url: https://{{ ansible_facts['fqdn'] }}/api/v2/ping/
method: GET
validate_certs: no
return_content: yes
delegate_to: localhost
register: controller_pingImportant
The introduction of automation execution environments in Red Hat Ansible Automation Platform 2 changes how delegation to localhost works.
With the ansible-navigator command, using delegate_to: localhost causes the module to run within the automation execution environment container.
In earlier versions that used the ansible-playbook command, the module ran directly on the control node, with various consequences.
Privilege escalation might fail if the sudo command is not present.
The supported automation execution environments provided by Red Hat require the sudo command.
If you are reusing a playbook that assumes it can access resources or commands on the control node, those resources might not be available inside the automation execution environment container.
In an earlier example, the ansible_facts['fqdn'] fact was used in a task that was delegated to each of the load balancers.
In that task, the fully qualified domain name (FQDN) for the managed host is used, not that of the current load balancer.
When you delegate a task, Ansible uses the host variables and facts for the managed host (the current inventory_hostname) for which the task is running.
For example, if a task is running for demo, but has been delegated to localhost, then the variables and facts for demo are used.
In most cases, it is correct to use the variables and facts for the managed host, even when the task is delegated to another host.
However, sometimes you might want to assign the facts collected by a delegated task to the host to which the task was delegated.
To change this setting, set the delegate_facts directive to true.
- name: Delegate Fact Example
hosts: localhost
gather_facts: false
tasks:
- name: Set a fact in delegated task on demo
ansible.builtin.set_fact:
myfact: Where am I set?
delegate_to: demo.lab.example.com
delegate_facts: true
- name: Display the facts from demo.lab.example.com
ansible.builtin.debug:
msg: "{{ hostvars['demo.lab.example.com']['myfact'] }}"The preceding play runs for localhost, but the first task is delegated to demo.lab.example.com.
Using the delegate_facts directive on that task instructs Ansible to gather facts into the hostvars['demo.lab.example.com'] namespace for the delegated host, instead of the default hostvars['localhost'] namespace for the current managed host.