Skip to main content

Using an Ansible playbook to manage workstation and server updates

In part two of this series on writing playbooks, we examine updates for servers and workstations. This playbook manages updates differently depending on the role the systems play on the network.
Image
Using Ansible to update servers and workstations
Image by wiredsmartio from Pixabay

I have decided to streamline my update process for customer systems I support as well as for my own Linux devices. While a series of complex scripts have served me well for a long time, the advantages of using Ansible for this task are just too many to pass up.

In part one of this article series, I discussed the actual update process, established the structure of my playbook, and presented keywords and comments. The playbook contains three plays. Each play manages a category of systems. Play 1 handles my Ansible controller device (my primary workstation), while Plays 2 and 3 manage servers and any remaining workstations.

Let's begin this second article by examining the second play.

The second play

Here is the entire second play. The purpose of this play is to perform updates on the firewall and server.

The firewall needs to be up and running while the servers—and all other hosts—are being updated so they can have Internet access to download the update packages. The server needs to run while the firewall and other hosts are being updated to provide DHCP and name services. To accomplish that, this play updates those two hosts one at a time.

The names for these two hosts are contained in the [all_servers] group in the /etc/ansible/hosts inventory file.

#######################################################################
#######################################################################
# Play 2 - Do servers 
#######################################################################
#######################################################################
- name: Play 2 - Install updates for servers yorktown and wally
  hosts: all_servers
  serial: 1
  remote_user: root
  vars:
    run: false
    reboot: false

  tasks:
#######################################################################
# Do some preliminary checking
#######################################################################
    - name: Install the latest version of the doUpdates script
      copy:
        src: /root/ansible/Updates/files/doUpdates
        dest: /usr/local/bin
        mode: 0774
        owner: root
        group: root

    - name: Check for currently available updates
      command: doUpdates -c
      register: check
    - debug: var=check.stdout_lines

#######################################################################
# Do the updates.
#######################################################################
# Install all available updates
    - name: Install all current updates
      dnf:
        name: "*"
        state: latest
      when: (check.stdout | regex_search('updates ARE available')) and run == "true"

    - name: Update the man database
      command: mandb
      when: run

    - name: Reboot if necessary and reboot extra variable is true
      reboot:
      when: (check.stdout | regex_search('reboot will be required')) and reboot == "true" and run == "true"

[ You might also like: An introduction to Ansible Tower ]

This entire second play is almost the same as the first, except for two lines.

serial: This additional statement tells Ansible to run this play on one host at a time, that is, serially rather than in parallel. If the all_servers group in the inventory contained ten servers, I could use a higher limit, such as two, so that this play would be run against two servers at a time. In this case, I need wally, the firewall, to be up and running so that the yorktown server has access to the Internet to download the updated packages. It does not matter which sequence these two hosts are updated so long as they are not both done simultaneously.

reboot: Ansible has a built-in reboot capability, so that we can use that in this play instead of the Linux poweroff command. The critical feature of the Ansible reboot function is that it performs a verification that the reboot has been successful, the remote host is up and running, and SSH communication is once again working. The default timeout for this is 10 minutes, after which Ansible throws an error.

The third play

And here is the third play. This play updates all of the remaining hosts on my network. The names for these hosts are contained in the [workstations] group of the /etc/ansible/hosts inventory file.

#######################################################################
#######################################################################
# Play 3 - Do all workstations except david
#######################################################################
#######################################################################
- name: Play 3 - Install updates for all workstations except david
  hosts: workstations
  strategy: free
  remote_user: root
  vars:
    run: false
    reboot: false

  tasks:
#######################################################################
# Do some preliminary checking
#######################################################################
    - name: Install the latest version of the doUpdates script
      copy:
        src: /root/ansible/Updates/files/doUpdates
        dest: /usr/local/bin
        mode: 0774
        owner: root
        group: root

    - name: Check for currently available updates
      command: doUpdates -c
      register: check
    - debug: var=check.stdout_lines

#######################################################################
# Do the updates.
#######################################################################
# Install all available updates
    - name: Install all current updates
      dnf:
        name: "*"
        state: latest
      when: (check.stdout | regex_search('updates ARE available')) and run == "true"

    - name: Reboot if necessary and reboot extra variable is true
      reboot:
      when: (check.stdout | regex_search('reboot will be required')) and reboot == "true" and run == "true"

There is only one change in this playbook other than the list of hosts.

strategy: The free strategy tells Ansible to perform the tasks in this play freely. The tasks in this play are run on each host as quickly as the host can do them. This means that some hosts may finish the last task well before other, slower hosts have completed even the first task in the playbook. It can appear to be sort of a free-for-all as you read the STDOUT data stream.

Each strategy is a separate plugin, and there are a couple of other plugins that can be used with this keyword. The default is linear, which performs each task on all hosts before moving on to the next task. The host_pinned plugin performs all tasks in the play on each host before moving on to the next host. The debug plugin runs tasks interactively so that the play can be debugged.

Running the playbook

I run this playbook using the command:

# ansible-playbook -e "run=true reboot=true" doUpdates.yml

The -e option stands for "extra variables." Here it specifies values for the two variables defined in each play. In this case, setting them both to true allows the updates to be performed and the reboot (poweroff) to take place if it is required.

I won't reproduce the STDOUT data stream here because it is quite long.

Final thoughts

With a few changes to reflect your network's details, this playbook can be used to automate your update tasks. Performing updates using a playbook similar to mine is a good way to get started using Ansible. Although it uses several keywords that can perform complex tasks, this is a relatively simple playbook. I started with just the first play to update my personal workstation, and the rest was mostly copy/paste, with a few minor changes to accommodate the needs of the different groups of hosts.

Yes, there are other ways of doing this. I probably could have used different tasks using conditionals within one or two plays instead of three plays. Or I could have used conditionals and blocks to deal with handling specific hosts differently. Personally, I think that individual plays help to separate the logic enough that changing how tasks are handled in one play does not affect the others. In my opinion, this separation is also more elegant because the overall logic is simpler to write, understand, and easier to manage.

[ A free guide from Red Hat: 5 steps to automate your business. ] 

Resources

The most complete and useful document I have found is the Ansible User Guide on the Ansible web site. This document is intended as a reference and not as a how-to or getting-started document.

Opensource.com has published many articles about Ansible over the years, and I have found most of them very helpful for my needs. The Red Hat Enable Sysadmin web site also has a lot of Ansible articles that I have found to be beneficial.

There are also some good but terse man pages available.

Image
Linking automation to the bottom line
You'd think it would be easy to convince management that automation saves money. But you might have to prove that the time you spend doing repetitive tasks can be better spent on more strategic work and that the results of automation are good for the bottom line.
Topics:   Linux   Linux administration   Ansible   Automation  
Author’s photo

David Both

David Both is an open source software and GNU/Linux advocate, trainer, writer, and speaker who lives in Raleigh, NC. He is a strong proponent of and evangelist for the "Linux Philosophy." David has been in the IT industry for over 50 years. More about me

Try Red Hat Enterprise Linux

Download it at no charge from the Red Hat Developer program.