It is essential to keep your system up to date and to apply all security patches. If you are tasked with managing multiple servers, the process of logging in to each and every server to perform the task can be cumbersome. You can use Ansible to achieve the functionality. The Ansible apt module can be used to manage apt updates and apt upgrades. The module uses either aptitude or the apt-get command on the remote server for package management.
Similarly, if there are kernel updates that require OS reboot, it would be best to use the reboot ansible module to reboot the machine, wait for it to go down, come back up and to respond to commands
In this guide, we will create a script to update Debian based systems cache and upgrade packages installed. We will also include a task to restart the server if there were kernel updates that requires reboot.
Also check:
Table of Content
- Using Ansible apt module to update all packages
- Rebooting the system if there are Kernel Upgrades
- Creating hosts file
- The whole playbook
- Running the playbook
1. Using Ansible apt module to update all packages
Before doing a package upgrade in Debian based systems, it is always recommended to perform an apt cache refresh. This can be achieved using this command:
sudo apt-get update
We can achieve the same using Ansible with this task:
- name: Update apt repo and cache on all Debian/Ubuntu boxes
apt:
update_cache: yes
force_apt_get: yes
cache_valid_time: 3600
Where,
- update_cache: yes – Run the equivalent of
apt-get update
command on all servers - force_apt_get: yes – Do not use the aptitude command, instead use the apt-get command on Debian/Ubuntu boxes
- cache_valid_time: 3600 – Update the apt cache if it’s older than the
cache_valid_time
. This option is set in seconds. In this examples, it is set to 3600 seconds.
Next we do the upgrade. We normally run this apt-get command to achieve the function:
sudo apt-get upgrade -y
This is the Ansible task to achieve the upgrade:
- name: Upgrade all packages on servers
apt:
upgrade: dist
force_apt_get: yes
Where,
- upgrade: dist – Run the equivalent of
apt-get upgrade
command on all Ubuntu or Debian Linux servers. In other words, upgrade all packages to latest version. - force_apt_get: yes – Use apt-get instead of aptitude.
2. Rebooting the system if there are Kernel Upgrades
If there are Kernel upgrades, we would need to reboot the system to apply those changes. If a reboot is required a file with this path /var/run/reboot-required
will be created.
What we would want to do is check if that file exist, then reboot the system. We can register a new variable if file /var/run/reboot-required exists on the system using the ansible stat:
- name: Check if a reboot is needed on all servers register: reboot_required_file stat: path: /var/run/reboot-required get_md5: no
Where:
- register: reboot_required_file – The
register
keyword decides what variable to save a result in and we are going to use it as follows to reboot the box. - stat: path: /var/run/reboot-required – Determine if a path (/var/run/reboot-required) exists
- get_md5: no – Algorithm to determine checksum of file. In this example, I am using md5, but you can use sha1, sha224, sha256, sha384, and sha512.
Now that we know if the server is to be rebooted or not, let us add a task to reboot the server if the file exist:
- name: Reboot the server if kernel updated
reboot:
msg: "Reboot initiated by Ansible for kernel updates"
connect_timeout: 5
reboot_timeout: 300
pre_reboot_delay: 0
post_reboot_delay: 30
test_command: uptime
when: reboot_required_file.stat.exists
Where,
- test_command: uptime – Execute uptime command on the rebooted server and expect success from to determine the machine is ready for further tasks.
- when: reboot_required_file.stat.exists – First, check that the file named /var/run/reboot-required exists using a variable named reboot_required_file. The reboot module will only work if that file exists and it is enforced using ‘when: reboot_required_file.stat.exists’ Ansible condition.
3. Creating the hosts file
Now that we have the logic in place, let us create a hosts.yaml file. Save this content to a file called hosts.yaml
.
all:
hosts:
ubuntusrv:
ansible_ssh_host: 10.2.11.10
ansible_ssh_user: ubuntu
debiansrv:
ansible_ssh_host: 10.2.11.11
ansible_ssh_user: admin
children:
allservers:
hosts:
ubuntusrv:
debiansrv:
4. The whole playbook
We can put the whole logic in a playbook. Save this content to the file upgrade.yaml
---
- name: Playbook to Update cache, upgrade packages and reboot os if necessary
hosts: allservers
become: yes
gather_facts: False
tasks:
- name: Update apt repo and cache on all Debian/Ubuntu boxes
apt:
update_cache: yes
force_apt_get: yes
cache_valid_time: 3600
- name: Upgrade all packages on servers
apt:
upgrade: dist
force_apt_get: yes
- name: Check if a reboot is needed on all servers
register: reboot_required_file
stat:
path: /var/run/reboot-required
get_md5: no
- name: Reboot the server if kernel updated
reboot:
msg: "Reboot initiated by Ansible for kernel updates"
connect_timeout: 5
reboot_timeout: 300
pre_reboot_delay: 0
post_reboot_delay: 30
test_command: uptime
when: reboot_required_file.stat.exists
5. Running the playbook
Make sure you set up ssh keys and run it as follows:
ansible-playbook -i hosts.yaml upgrade.yaml -vv
This is the output on my server
➜ ansible-playbook -i hosts.yaml upgrade.yaml -vv
ansible-playbook [core 2.12.1]
config file = /Users/etowett/.ansible.cfg
configured module search path = ['/Users/etowett/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /Library/Python/3.8/site-packages/ansible
ansible collection location = /Users/etowett/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/local/bin/ansible-playbook
python version = 3.8.2 (default, Apr 8 2021, 23:19:18) [Clang 12.0.5 (clang-1205.0.22.9)]
jinja version = 3.0.3
libyaml = True
Using /Users/etowett/.ansible.cfg as config file
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
Skipping callback 'default', as we already have a stdout callback.
Skipping callback 'minimal', as we already have a stdout callback.
Skipping callback 'oneline', as we already have a stdout callback.
PLAYBOOK: upgrade.yaml ************************************************************************************************************************************************************
1 plays in upgrade.yaml
PLAY [Playbook to Update cache, upgrade packages and reboot os if necessary] ******************************************************************************************************
META: ran handlers
TASK [Update apt repo and cache on all Debian/Ubuntu boxes] ***********************************************************************************************************************
task path: /Users/etowett/Proj/me/infme/ansible/upgrade.yaml:7
changed: [ubuntusrv] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python3"}, "cache_update_time": 1639737368, "cache_updated": true, "changed": true}
TASK [Upgrade all packages on servers] ********************************************************************************************************************************************
task path: /Users/etowett/Proj/me/infme/ansible/upgrade.yaml:13
ok: [ubuntusrv] => {"changed": false, "msg": "Reading package lists...\nBuilding dependency tree...\nReading state information...\nCalculating upgrade...\n0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.\n", "stderr": "", "stderr_lines": [], "stdout": "Reading package lists...\nBuilding dependency tree...\nReading state information...\nCalculating upgrade...\n0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.\n", "stdout_lines": ["Reading package lists...", "Building dependency tree...", "Reading state information...", "Calculating upgrade...", "0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded."]}
TASK [Check if a reboot is needed on all servers] *********************************************************************************************************************************
task path: /Users/etowett/Proj/me/infme/ansible/upgrade.yaml:18
ok: [ubuntusrv] => {"changed": false, "stat": {"atime": 1639737351.6237016, "attr_flags": "", "attributes": [], "block_size": 4096, "blocks": 8, "charset": "us-ascii", "checksum": "20f7959b87e8cd55b7c985e46d6fa38a4063037d", "ctime": 1639679787.0725238, "dev": 26, "device_type": 0, "executable": false, "exists": true, "gid": 0, "gr_name": "root", "inode": 1143, "isblk": false, "ischr": false, "isdir": false, "isfifo": false, "isgid": false, "islnk": false, "isreg": true, "issock": false, "isuid": false, "mimetype": "text/x-diff", "mode": "0644", "mtime": 1639679787.0725238, "nlink": 1, "path": "/var/run/reboot-required", "pw_name": "root", "readable": true, "rgrp": true, "roth": true, "rusr": true, "size": 32, "uid": 0, "version": null, "wgrp": false, "woth": false, "writeable": true, "wusr": true, "xgrp": false, "xoth": false, "xusr": false}}
TASK [Reboot the server if kernel updated] ****************************************************************************************************************************************
task path: /Users/etowett/Proj/me/infme/ansible/upgrade.yaml:24
changed: [ubuntusrv] => {"changed": true, "elapsed": 73, "rebooted": true}
META: ran handlers
META: ran handlers
PLAY RECAP ************************************************************************************************************************************************************************
ubuntusrv : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Wrapping up
You learned how to update all packages on your Debian and Ubuntu Linux boxes and reboot the server if required using Ansible playbooks.