Chapter 10: Automating Linux Administration Tasks

Managing software

Almost every playbook installs something. Ansible's package modules install, update and remove software, configure the repositories it comes from, and report what is already there, so a play can make decisions based on it.

The module for every administration job

This chapter covers five areas. Use this as a map as you go; each section explains one area in depth.

Installing and removing packages

ansible.builtin.dnf takes a package name, or a list, and the state you want:

- name: Web packages are installed
  ansible.builtin.dnf:
    name:
      - httpd
      - mod_ssl
    state: present
stateResult
present (default)Installed; left alone if it already is
latestInstalled and updated to the newest available version
absentRemoved

Give name a list rather than looping: dnf then resolves everything in one transaction, which is much faster.

A few more forms of name:

name:Means
httpdThe package
"@Development Tools"A package group (quote it: @ is special in YAML)
"*" with state: latestUpdate every package on the host
"@perl:5.26/minimal"A module stream and profile; leave out /profile for the stream's default
https://…/tool-1.0.rpm or a local pathAn RPM file

Registering a system

A RHEL host needs a subscription before it can install from Red Hat's repositories. Two modules from community.general handle it: redhat_subscription registers the host (and attaches a subscription pool), and rhsm_repository enables repositories.

- name: Register and subscribe the system
  community.general.redhat_subscription:
    username: yourusername
    password: yourpassword
    pool_ids: poolID
    state: present

- name: Enable Red Hat repositories
  community.general.rhsm_repository:
    name:
      - rhel-9-for-x86_64-baseos-rpms
      - rhel-9-for-x86_64-appstream-rpms
    state: present

state: absent unregisters the host. These are community modules, not supported by Red Hat; running subscription-manager through ansible.builtin.command is the alternative.

Rocky Linux needs no subscription: its BaseOS and AppStream repositories are enabled from the start. Skip registration in the home lab.

Repositories and signing keys

Packages come from repositories defined in /etc/yum.repos.d/. ansible.builtin.yum_repository writes those files:

- name: The CRB repository is configured
  ansible.builtin.yum_repository:
    name: lab-crb
    description: Rocky Linux 9 CRB (lab)
    mirrorlist: https://mirrors.rockylinux.org/mirrorlist?arch=$basearch&repo=CRB-$releasever
    gpgcheck: true
    gpgkey: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Rocky-9
    enabled: true

The result on the host:

[lab-crb]
enabled = 1
gpgcheck = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-Rocky-9
mirrorlist = https://mirrors.rockylinux.org/mirrorlist?arch=$basearch&repo=CRB-$releasever
name = Rocky Linux 9 CRB (lab)

Use baseurl for a single server, or mirrorlist or metalink for a list of mirrors. Always keep gpgcheck: true: it makes dnf refuse packages that were not signed by the key you trust. Import that key with ansible.builtin.rpm_key, from a local file or a URL:

- name: The Rocky Linux signing key is trusted
  ansible.builtin.rpm_key:
    key: /etc/pki/rpm-gpg/RPM-GPG-KEY-Rocky-9
    state: present

EPEL is the usual example of an extra repository, but its metadata is large. On the 1 GiB lab VMs, dnf runs out of memory reading it and the kernel stops it. In Ansible that shows up as MODULE FAILURE … Shared connection to servera.lab.example.com closed., and the kernel log on the host says Out of memory: Killed process … (python3). The exercises use Rocky's own CRB repository instead. The technique is identical.

On RHEL, repositories usually come from a Red Hat subscription. The community.general.redhat_subscription module registers a host, and community.general.rhsm_repository enables or disables subscription repositories by name.

Rocky Linux has no subscriptions: its repositories (BaseOS, AppStream, CRB, extras) are defined by the rocky-repos package and are free to use. The subscription modules do not apply at home.

What is installed?

ansible.builtin.package_facts adds every installed package to the facts, as a dictionary keyed by name. Each value is a list, because several versions of one package (such as kernels) can be installed at once.

- name: Collect the list of installed packages
  ansible.builtin.package_facts:
    manager: auto

- name: Show the version
  ansible.builtin.debug:
    msg: "lynx {{ ansible_facts['packages']['lynx'][0]['version'] }}"
  when: "'lynx' in ansible_facts['packages']"

A condition such as when: "'httpd' in ansible_facts['packages']" lets a play act only on hosts that have a package, without trying to install it.

Takeaway

Install, update and remove packages and package groups with ansible.builtin.dnf. Use the chapter lab to check this on a real host.