Running one-off tasks with Ansible

In the previous chapter, we used the ansible webservers -i cobbler.py -m ping command to test connectivity to all of the servers in the webservers group of our dynamic inventory. This type of Ansible command is known as an ad hoc command, and it is typically used to run a single Ansible module against an inventory, with a set of parameters.

Throughout this book, we have encouraged the use of full playbooks and roles for all Ansible tasksand for good reason! If you frequently run commands without storing the code in some shape or form, it will soon become very difficult, if not impossible, to know who ran what and when they ran it. Indeed, if you have looked into AWX/Ansible Tower, you will see that it does not even support ad hoc Ansible commandsrunning them is not aligned with the principles of auditability and role-based access control that underpin this product.

The example ping command we have looked at is the same as writing a playbook that looks like this:

---
- hosts: webservers
gather_facts: no

tasks:
- ping:

The question is, then, why would you want to learn about ad hoc commands in Ansible? The answer often comes about for one-off maintenance tasks. The beauty of Ansible is that once you have implemented it throughout your infrastructure (and set up authentication, inventories, and so on), it has access to all of your servers.

For example, suppose you need to distribute an emergency patch to a set of systems by copying a file across. There are several ways you could solve this problem, including these:

  • Write an Ansible playbook (and/or reusable role) to copy the file
  • Copy across the file manually using scp or a similar tool
  • Execute an ad hoc Ansible command

Of these three options, the first is almost certainly going to be inefficient in an emergency scenario. The manual copying using scp is perfectly valid but is inefficient, especially when you have gone to the trouble of setting up Ansible.

In an ad hoc command, you can use any module that you can use in a playbook or role. You can specify the same arguments too, only they are formatted a little differently as we specify them on the command line rather than in a YAML file. 

Let's suppose an error has been found on the front page of our web server, and we urgently need to copy across a new version that has the fix in it. The ad hoc command to run this might look like this:

$ ansible webservers -i inventory -m copy -a "src=frontpage.html dest=/var/www/html/frontpage.html" --become

Let's break that command down—the group and inventory script are specified just as before, but this time, we have the following:

-m copy

Tells Ansible to use the copy module for the ad hoc command

-a "..."

Provides the parameters or arguments for the module

src=frontpage.html

The src parameter, which tells the copy module where to obtain the file from on the Ansible server

dest=/var/www/html/frontpage.html

The dest parameter, which tells the copy module where to write the file on the destination server

--become

Tells Ansible to become root (that is, sudo)

 

When you run this command, you will note that the output is quite different from the ansible-playbook command. Nonetheless, the files are faithfully copied to all specified hosts in the inventory without you needing to write an entire playbook. The following screenshot shows an example of the output from this command:

What is doubly useful about these ad hoc commands is that not only is the file copied to all hosts specified without writing an entire playbook, but that the output from the command shows all of the return values from the module that you launched—copy, in this case. This is incredibly useful in playbook and role development as you might want to register the output of a particular task into a variable, and using an ad hoc command such as this shows you what this variable would contain.

For example, say that you wanted to actually perform the preceding task in a role instead of an ad hoc command, and register the results of this task in a variable called filecopy. The main.yml file in the role tasks/ directory might look like this:

---
- name: Copy across new web server front page
copy:
src: "frontpage.html"
dest: "/var/www/html/frontpage.html"
register: filecopy

We know from our ad hoc command that filecopy will be a dictionary containing several useful items, including changed and size. Hence, we could easily perform some conditional processing on these in a later task—for example, perhaps running another related task with the following clause:

  when: filecopy.changed == true

Of course, if you needed to just run a raw shell command, you could do that too using the shell command—a simple example is shown as follows:

$ ansible webservers -i inventory -m shell -a "echo test > /tmp/test"

This, of course, is a contrived example, but it demonstrates to you how you could run an identical shell command across all of the servers in an Ansible inventory with relative ease. You can even inject variables into the module arguments using the format now familiar to you from your role and playbook development, as in this example:

$ ansible webservers -i inventory -m shell -a "echo Hello from {{ inventory_hostname }} > /tmp/test && cat /tmp/test"

The output of this specific command should look something like the following screenshot—see how the shell module returns the output from the command within the Ansible output—this is incredibly powerful and would, for example, enable you to gather information from all of the machines in an inventory with ease:

Hence, you could use Ansible ad hoc commands to perform a quick audit of your systems or to check the value of a specific setting across a set of servers.

Another place where ad hoc commands are valuable is in testing Jinja2 expressions. We have come across these a few times in the book, and when developing a playbook or role, the last thing you want to do is run through an entire play, only to discover that one of your Jinja2 expressions was wrong. Ad hoc commands enable you to easily and rapidly test these on the command line.

Say, for example, you want to develop a Jinja2 expression to put into a playbook that returns the uppercase value of a variable called vmname if it is defined, and otherwise, return the keyword all in lowercase. This would be useful in defining a host pattern for use in a playbook workflow, for example. This is not a trivial Jinja2 expression, and so rather than testing it within a playbook, let's figure it out on the command line. What we would do is print the Jinja2 expression using a debug msg, and then set the vmname variable using the -e flag. Hence, we might run this:

$ ansible localhost -m debug -a "msg={% if vmname is defined %}{{ vmname | upper }}{% else %}all{% endif %}" -e vmname=test

$ ansible localhost -m debug -a "msg={% if vmname is defined %}{{ vmname | upper }}{% else %}all{% endif %}"

The following screenshot shows this in action:

As you can see from the preceding screenshot, the commands produce the desired output when vmname is set and undefined, and so we can copy this into our playbook or role and proceed with confidence!

That concludes our chapter on tips and tricks—it is hoped that these final words will help you with implementing a highly reliable and scalable Linux automation infrastructure based upon Ansible in your enterprise.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset