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.

RHCE Ansible EX294 Exam Preparation Course
Learn all the essentials of Ansible with this hands-on tutorial series. It is ideal for RHCE Ansible EX294 exam preparation.
✍️
Author: Hitesh Jethwa has more than 15+ years of experience with Linux system administration and DevOps. He likes to explain complicated topics in easy to understand way.