Introduction

Ansible is a powerful automation tool that makes managing IT infrastructure easier. One of its lesser-known but highly useful directives is “run_once.” While using it at the play level might seem unremarkable, its true potentials reveals itself when employed within a task. Let’s explore the wonders of run_once and how it can simplify complex automation scenarios.

Play Level

At the play level, using run_once is equivalent to changing the host selection. Instead of specifying hosts as "foo” you’d use “foo[0]”. This makes it convenient for directing tasks to a specific host but doesn’t necessarily elicit much excitement.

``yaml

  • name: Play level

hosts: host1,host2,host3,host4,host5,host6,host7

run_once: true

tasks:

- name: Print message

ansible.builtin.debug:

msg: Hello World

`

However, the real enchantment occurs when you use run_once within a task. When you do this, tasks with run_once are executed on a single host, typically the first host in the runlist. This simple feature allows for intricate choreography. For instance, you can send a command to a cluster only once, even if there are multiple equally available hosts in the cluster group. Furthermore, with the ability to continue executing playbooks even when some hosts are unavailable, you can ensure your command reaches the first available node in the cluster. It’s very useful in its utility.

Task Level

What happens when you combine run_once with the “register” directive? You might wonder if the variable for hosts where the command wasn’t run remains undefined or contains some “skipped message, as can occur with other combinations of directives like “when” and “register.”

In this case, Ansible behaves in the best way imaginable. The registered variable is created for all hosts in the runlist, even if the task was executed on a single host. When you combine it with delegation, it becomes even more elegant.

One of the best applications of this feature is in gathering “pseudo-facts.” For example:

``yaml

  • name: Get data

ansible.builtin.command: data get

run_once: true

register: data

`

Previously, you might have added this to the play without concerns. While it’s slightly inefficient, it’s concise and straightforward. Now, it becomes a real trick that dramatically reduces the number of calls to the delegated host.

Pseudo-facts

Imagine a playbook with multiple hosts, and you want to set a fact using run_once:

`yaml

  • name: Pseudo-facts playbook

hosts: host1,host2,host3,host4,host5,host6,host7

tasks:

- name: Set fact

run_once: true

ansible.builtin.set_fact:

foo: 42

- name: Print the fast

ansible.builtin.debug:

var: foo

`

What do you think will happen to “foo” for all hosts in the play? It’s not just magic; it’s sheer brilliance. All hosts will have “foo: 42”. The primary difference from a version lacking run_once` is the reduction in output. This feature