Running Tasks on Different Remote Systems Using delegate_to in Ansible
By default, Ansible runs tasks on the remote systems that you specify in your inventory. The delegate_to overrides this behavior, instructing Ansible to execute a particular task on a specific host or group. This can be useful for tasks that need to run on a specific machine due to dependencies or resource requirements.
Ansible's delegate_to directive works by temporarily shifting control of task execution to a different host. Here's a breakdown of the process:
- Playbook Execution: You run your Ansible playbook targeting specific hosts (or groups) defined in your inventory.
- Task Encountered: When the playbook encounters a task with the delegate_to directive, Ansible identifies the target host specified in the directive.
- Connection and Delegation: Ansible establishes a connection to the target host using the appropriate connection plugin (e.g., SSH for Linux). It then sends the specific task instructions and any required files to the target host.
- Local Execution on Target Host: The target host receives the task instructions and files. It then executes the task locally using its own resources and environment.
- Result Gathering: Once the task finishes execution on the target host, the results (success/failure, output) are sent back to the Ansible controller machine.
- Playbook Resumes: After receiving the results from the target host, Ansible continues execution of the playbook on the originally targeted hosts (or groups).
In this post, we will explain how to use Ansible delegate_to using practical examples.
Prerequisites
To proceed with this tutorial, you will need the following:
- An Ansible management node with Ansible installed and configured
- An Ansible target node.
- SSH key-based authentication between the management node and the target node
Basic Syntax of delegate_to
The delegate_to parameter in Ansible allows you to specify a different host where a particular task should be executed. Its basic syntax is:
delegate_to: <host_pattern>
Here's a breakdown:
- delegate_to: This is the keyword used to specify that the task should be delegated to a different host.
- <host_pattern>: This can be the name of a specific host, a group of hosts, or a pattern that matches multiple hosts. It follows the same syntax used for specifying hosts in Ansible inventory files.
For example, you could use:
- A specific hostname, like
delegate_to: node1
- A group name, like
delegate_to: webservers
- A pattern to match multiple hosts, like
delegate_to: "*.example.com"
When you use delegate_to
, the task will be executed on the specified host instead of the default target host for the play.
Example 1
In this simple example playbook, task 1 is executed on node1 and displays its hostname. Task 2 is delegated to node2, which means it will execute on node2 instead of the default target host.
- name: Display the hostname of the remote server
hosts: node1
tasks:
- name: "Task 1: Display hostname of node1"
debug:
msg: "Hostname of {{ inventory_hostname }} is {{ ansible_hostname }}"
- name: "Task 2: Display hostname of node2"
debug:
msg: "Hostname of {{ inventory_hostname }} is {{ ansible_hostname }}"
delegate_to: node2
Let's run this playbook:
ansible-playbook playbook.yml
You will see the following screenshot:
Example 2
Suppose you have a playbook for deploying a LAMP stack across multiple servers. Instead of burdening the target hosts with resource-intensive tasks such as package installation or database setup, you can delegate these tasks to a designated node2 with higher computational capacity.
- name: Install LAMP Server
hosts: webservers
tasks:
- name: Install LAMP packages
apt:
name: "{{ item }}"
state: present
delegate_to: node2
with_items:
- apache2
- mariadb-server
- php
- php-common
- php-fpm
- php-curl
- php-json
- php-cli
Save the above playbook file and run it using the following command.
ansible-playbook playbook.yml
You will see the following screenshot:
Example 3
Imagine you need to copy a file from node1 to node2 using the Ansible copy module. In this playbook, the delegate_to
parameter is used to delegate the task of copying a file from node1 to node2.
- name: Copy file from node1 to node2
hosts: node1
tasks:
- name: Copy file from node1 to control node
fetch:
src: /etc/hosts
dest: /tmp/
flat: yes
- name: Copy fetched file from control node to node2
copy:
src: /tmp/hosts
dest: /mnt/
delegate_to: node2
Let's break down the role of delegate_to
in this playbook:
- Task Description: The "Copy file" task uses the fetch module to copy a file from the source path
/etc/hosts
on node1 to the destination path/tmp/
on the control node. The "Copy fetched file" task uses the copy module to copy a file from the source path/tmp/hosts
on control host to the destination path/mnt/
on the node2. - delegate_to: node2: This specifies that the task should be delegated to a specific host (node2). This means that although the playbook is targeting the control node, this specific task will execute on node2.
Now, run the above playbook.
ansible-playbook playbook.yml
You will see the following screenshot.
Example 4
In this example, the playbook starts a quick web server on the webservers then retrieves information from that server and processes it locally using delegate_to
.
- name: "Ansible Delegate_to examples"
hosts: webservers
tasks:
- name: "Task1: start a quick webserver with NC on remote server"
shell: 'while true; do echo -e "HTTP/1.0 200 OK\nContent-Length: 13\n\nHello from WebServer!" | nc -l -p 8091 -q 1; done &'
async: 1
poll: 0
- name: "Task2: Check the status of the remote server from localhost"
command: curl --http0.9 -i http://{{ansible_default_ipv4.address}}:8091
delegate_to: localhost
register: curl_output
- name: "Task3: Printing the website output from the Task2"
debug:
msg: "{{ curl_output.stdout }}"
This Ansible playbook performs the following tasks:
- The playbook starts a temporary web server on webservers using Task 1 (asynchronous).
- Task 2, delegated to the Ansible controller (localhost), attempts to access the web server on
webservers:8091
. - If the web server is running and accessible, Task 2 will register a successful response with the content ("Hello from WebServer!").
- Task 3 retrieves the content from the curl_output variable and displays it using the debug module.
This playbook demonstrates how delegate_to
allows tasks to be executed on the Ansible controller for specific purposes. The asynchronous nature of Task 1 enables the playbook to continue without waiting for the web server to start.
Now, run this playbook using the following command:
ansible-playbook playbook.yml
You will see the following screenshot:
Conclusion
In this article, we've explored the powerful functionality of Ansible's delegate_to directive and its usage in various scenarios.
By leveraging delegate_to, Ansible users can extend their playbook capabilities beyond the confines of the target hosts defined in the playbook. Whether it's executing tasks on a different host, orchestrating actions on specific hosts, or managing tasks that require interaction with external systems, delegate_to provides a flexible and versatile solution.
If you are new to Ansible and want to learn it from the scratch, our Ansible tutorial series will be of great help. It's written for RHCE exam but it helps you the same whether you are preparing for the exam or not.