How to do fast, repeatable Linux installations #3 — Ansible

0

In previous articles about my use of automation in performing post-install tasks for new Linux installations, I discussed my progression from no automation at all but at least using written notes for consistency, to simple scripts, to using RPM packages to install tools from the Fedora repositories and my own scripts and files. Those approaches worked well for the times I used them, but as the requirements of my network and the hosts connected to it grew and became more complex, the capabilities of those tools couldn’t keep up.

I needed a new method for doing post-install tasks. I also needed new ways to perform other tasks, too. So as a series of experiments, I started working with Ansible to first familiarize myself with it and then to perform more complex tasks such as Fedora updates and my post-install tasks.

The three articles in the list below describe my progression with using Ansible for tasks ranging from simple to a bit more complex. Please read those whether you’re familiar with Ansible or not so you can understand how I employ it in my home lab setting. They’re also a good learning tool if you’re not already familiar with Ansible.

  1. Ansible #1: My first day using Ansible
  2. Ansible #2 How to create an Ansible Playbook
  3. Ansible #3: Finishing our Ansible playbook to manage workstation and server updates

How Ansible works — the short version

The key to using Ansible is in the way it works. The function of Ansible is to ensure that a Linux1 host is maintained in a certain state. That means that if the Ansible playbook defines certain RPMs and local files as being present or not present, particular text lines to be changed from the default in a configuration file, specific systemd services to be on or off, and much more, running the playbook will always set the system to the desired state.

This statefull approach to system configuration makes it possible to run the playbook at a later time, after undesired or temporary changes have been made to the system, to achieve the defined state. I’ve had many situations in which I’ve made changes to a system as part of my process or problem determination, or as the result of some experiments I performed to determine whether I wanted to make some configuration changes on a permanent basis. While making those changes, I don’t need to worry about making notes so I can get the host back to the state I want for those things that matter to me.

Preparation

For my experiments, I use my personal workstation as the Ansible hub and the VM as the target host. I also take a snapshot of the VM immediately after the new installation and setting up the PPKP. That makes it easy to restore the VM to a pristine condition before performing additional experiments.

First login to the target VM and enable SSH. Ensure that root can perform a remote login via SSH. If the host being used as the Ansible hub doesn’t already have a PPKP created, do that now and then copy the public key to the target. The article, How I use Public/Private/KeyPairs with SSH, will show you how to do both of those tasks.

The Playbook

Warning!!! — Don’t use this Ansible playbook on a production system until you know exactly what it does and have modified it to meet your own needs. This article and the playbook that goes with it are intended as a tool to demonstrate what can be done with Ansible to perform post-installation tasks. I strongly recommend that you experiment with this tool on a non-production virtual machine.

Now let’s look at how I used Ansible to perform post-install tasks. This is best done with the playbook itself, PostInstall.yml, starting in Figure 1. This is a very long listing of my Postinstall.yml playbook. A simple explanation of what it does begins at the end of Figure 1.

################################################################################
#                                PostInstall.yml                               #
#                                                                              #
# This Ansible playbook performs post-installation updates and configuration   #
# for newly installed Fedora hosts.                                            #
#                                                                              #
# Note: This playbook is intended for use only on one or more newly installed  #
#       hosts. It is not intended for use with hosts that have been in use     #
#       for a considerable period of time after installation. But it might     #
#       work fine for that with most of my personal hosts.                     #
#                                                                              #
#                                                                              #
#------------------------------------------------------------------------------#
#                                                                              #
# Change History                                                               #
# Date        Name         Version   Description                               #
# 2020/10/01  David Both   00.00     Started new code                          #
<SNIP>
# 2023/08/13  David Both   01.50     Add code to convert to NetworkManager for #
#                                    resolv.conf to use nss-DNS and disable    #
#                                    mDNS.                                     #
# 2023/09/22  David Both   01.51     Add sipcalc to cli tools.                 #
# 2023/09/22  David Both   01.52     Remove install of deltarpm.               #
# 2024/06/23  David Both   01.53     Install fastfetch.                        #
#                                                                              #
################################################################################

---
- name: Post-installation updates, package installation, and configuration
  hosts: testvm1
  vars:
    # Set this to false to prevent rebooting after installation of updates
    # and after the entire procedure has completed. 
    # However setting this to false is not recommended.
    Reboot: "true"

    # When set to True the Gui variable allows install of some GUI tools,
    # wallpapers, and themes. 
    Gui: "true"
  
    # When set to True, the Apps variable tells Ansible to install applications like 
    # LibreOffice and more.
    Apps: "true"

    # When set to True, install a large number of background pics. 
    # Takes a long time and space so default is false.
    Wallpapers: "true"
    
    # Performs tasks for my own hosts when set to true. Skips those when set to false
    # for when I work on computers that are not my own. Default is true for 
    # my own stuff.
    David: "true"

    # Install development tools for a primary workstation
    development: "false"

    # When false, this variable removes dnfdragora from the host because it used
    # to suck and did not work. I don't like it on my stuff so delete it which
    # allows me to remove it from my systems. The default is false.
    dnfdragora: "false"

    # When true install glances
    glances: "false"

    # When true, install the designated desktops
    xfce: "true"
    cinnamon: "false"
    # Problems installing LXDE due to conficts
    lxde: "false"
    # Problems installing Mate due to conficts
    mate: "false"

  tasks:

################################################################################
################################################################################
# Start with some kernel and system level configuration.                       #
################################################################################
################################################################################
# Configure maximum number of kernels to keep, install some local scripts that #
# we will use to do updates, and then install updates.                         #
# The default number of kernels is three and we change it to 5.                #
################################################################################
    - name: Set installonly_limit to 5. Allows saving up to 5 old kernels.
      replace:
        path: /etc/dnf/dnf.conf
        regexp: 'installonly_limit=.*'
        replace: 'installonly_limit=5'

    - name: Remove possible old deprecated scripts
      file:
        path: "{{ item }}"
        state: absent
      with_items:
        - /home/dboth/development/doUpdates
        - /usr/local/bin/doUpdates
        - /home/dboth/development/updatePrep.sh
        - /usr/local/bin/updatePrep.sh
          
    - name: Install local scripts
      copy:
        src: "{{ item }}"
        dest: /usr/local/bin
        mode: 0774
        owner: root
        group: root
      with_fileglob:
          - /home/dboth/development/ansible/PostInstall/files/usrlocal/bin/*

################################################################################
# Disable mDNS and configure nss-DNS instead.                                  #
################################################################################
################################################################################
# Stop and disable AVAHI services that are not needed. Masked means that it    #
# cannot be restarted until it is unmasked. This is mDNS                       #
################################################################################
    - name: Stop and disable the Avahi service
      systemd:
        name: avahi-daemon.service
        state: stopped
        masked: yes
        enabled: no

    - name: Stop and disable the Avahi socket
      systemd:
        name: avahi-daemon.socket
        state: stopped
        masked: yes
        enabled: no

# Disable the systemd resolver.
    - name: Stop and disable the systemd resolver
      systemd:
        name: systemd-resolved.service
        state: stopped
        masked: no
        enabled: no
          
# Delete /etc/resolv.conf link
    - name: Remove old resolv.conf link
      ansible.builtin.file:
        path: /etc/resolv.conf
        state: absent

# Restart NetworkManager which creates a new resolv.conf link every time
# it is started.
    - name: Restart NetworkManager to create the new resolv.conf link.
      systemd:
        name: NetworkManager
        state: restarted
        enabled: yes

################################################################################
# Disable pcscd smart card daemons                                             #
################################################################################

# Disable the smart card socket
    - name: Stop and disable the pcscd smart card socket
      systemd:
        name: pcscd.socket
        state: stopped
        masked: no
        enabled: no
# Disable the smart card service
    - name: Stop and disable the pcscd smart card service
      systemd:
        name: pcscd.service
        state: stopped
        masked: no
        enabled: no

################################################################################
################################################################################
# Install some files to standardize all kernel options (kernelopts) into       #
# one place -- the /etc/sysctl.d directory. And a single file in the           #
# directory local-sysctl.conf.                                                 #
################################################################################
################################################################################
    - name: Install local-sysctl.conf
      copy:
        src: /home/dboth/development/ansible/BasicTools/files/local-sysctl.conf
        dest: /etc/sysctl.d/local-sysctl.conf
        mode:  644
        owner: root
        group: root

    - name: Install latest MyStartup.sh
      copy:
        src: /home/dboth/development/ansible/BasicTools/files/MyStartup.sh
        dest: /usr/local/bin/MyStartup.sh
        mode:  754
        owner: root
        group: root

    - name: Create directory /usr/local/lib/systemd/system/ which does not exist by default
      ansible.builtin.file:
        path: /usr/local/lib/systemd/system/
        state: directory
        mode: '0755'
        owner: root
        group: root

    - name: Install latest MyStartup.service
      copy:
        src: /home/dboth/development/ansible/BasicTools/files/MyStartup.service
        dest: /usr/local/lib/systemd/system/MyStartup.service
        mode:  754
        owner: root
        group: root

    - name: Enable and start MyStartup.service
      service: 
        name: MyStartup.service
        state: started 
        enabled: yes

################################################################################
# This lets us know if we need to reboot after installing updates. We probaly  #
# will need to in most cases.                                                  #
################################################################################
    - name: Check for currently available updates
      command: doUpdates.sh -c
      register: check
    - debug: var=check.stdout_lines

################################################################################
# Now install all available updates.                                           #
################################################################################
    - name: Install all current updates
      dnf:
        name: "*"
        state: latest

################################################################################
# This will reboot and then wait for the remote host to complete its           #
# restart before proceeding with the rest of the tasks                         #
################################################################################
    - name: Reboot if necessary and reboot extra variable is true
      reboot:
      when: (check.stdout | regex_search('reboot will be required')) and Reboot == "true"

################################################################################
# Install some additional repositories                                         #
################################################################################
    - name: Install and enable the RPM Fusion Free and nonfree repositories
      dnf:
        name: "{{ item }}"
        state: present
        # This is necessary to prevent a GPG error (version 32-1 - where did -1 come from?)
        disable_gpg_check: yes
      with_items:
        - http://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-{{ ansible_distribution_version }}.noarch.rpm
        - http://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-{{ ansible_distribution_version }}.noarch.rpm

################################################################################
# Install a bunch of tools and other packages. Install them all here with a    #
# bit of grouping to make things a bit easier to find.                         #
################################################################################
# Install command line tools 
    - name: Install command line tools
      dnf:
        name: 
          - apcupsd
          - atop
          - bc
          - clamav
          - python3-cpuinfo
          - ddrescue
          - detox
          - dictd
          - dmidecode
          - fail2ban
          - hddtemp
          - htop 
          - hwinfo
          - iftop
          - inxi
          - iotop
          - konsole
          - iptables-services
          - iptraf-ng
          - lm_sensors
          - logwatch
          - lshw
          - mc
          - fastfetch
          - newfetch
          - nmon
          - nvme-cli
          - plocate
          - powertop
          - psutils
          - rpmorphan 
          - screen
          - screenfetch
          - sendmail
          - sendmail-cf
          - ShellCheck
          - sipcalc
          - sysstat
          - task
          - trash-cli
          - units
          - util-linux-user
          - vim-common 
          - vim-enhanced
          - vim-filesystem
          - vim-minimal
          - wget
          - whois
        state: latest

    - name: Install some fun command line programs
      dnf:
        name: 
          - asciiquarium
          - banner
          - boxes
          - bsd-games
          - cmatrix
          - cowsay
          - figlet
          - fortune-mod
          - sl
        state: latest

################################################################################
# Install development tools required for Python pip and VirtualBox as well as  #
# for development and creating RPM packages.                                   #
################################################################################
    - name: Install some development tools
      dnf:
        name: 
          - kernel-devel
          - gcc
          - dkms
          - rpm-build
          - python-devel
        state: latest
      when: development == "true"

################################################################################
# Add some configuration customization to /etc/screenrc for the                #
# hard status line.                                                            #
################################################################################
    - name: Customize the /etc/screenrc hard status line 1.
      lineinfile:
        path: /etc/screenrc
        insertafter: EOF
        line: '# Customize the hard status line by David Both'

    - name: Customize the /etc/screenrc hard status line 2.
      lineinfile:
        path: /etc/screenrc
        insertafter: EOF
        line: 'hardstatus alwayslastline'

    - name: Customize the /etc/screenrc hard status line 3.
      lineinfile:
        path: /etc/screenrc
        insertafter: EOF
        line: "hardstatus string '%{= kG}[ %{G}%H %{g}][%= %{= kw}%?%-Lw%?%{r}(%{W}%n*%f%t%?(%u)%?%{r})%{w}%?%+Lw%?%?%= %{g}][%{B} %m-%d %{W}%c %{g}]'"
          
    - name: Customize the /etc/screenrc hard status line 4.
      lineinfile:
        path: /etc/screenrc
        insertafter: EOF
        line: ''

################################################################################
# Install Midnight Commander configuration files                               #
################################################################################
    - name: create ~/.config directory in /etc/skel
      file:
        path: /etc/skel/.config
        state: directory
        mode: 0775
        owner: root
        group: root

    - name: copy latest personal Midnight Commander skin to /usr/share
      copy:
        src: /home/dboth/development/ansible/PostInstall/files/MidnightCommander/DavidsGoTar.ini
        dest: /usr/share/mc/skins/DavidsGoTar.ini
        mode: 0644
        owner: root
        group: root

    - name: create ~/.config/mc directory for root
      file:
        path: /home/dboth/development/.config/mc
        state: directory
        mode: 0755
        owner: root
        group: root

    - name: Copy the most current Midnight Commander configuration files to /home/dboth/development/.config/mc
      copy:
        src: "{{ item }}"
        dest: /home/dboth/development/.config/mc
        mode: 0755
        owner: root
        group: root
      with_fileglob:
          - /home/dboth/development/ansible/PostInstall/files/MidnightCommander/*ini

# Placing the config files in /etc/skel makes them available for 
# all users created from now on.
    - name: create ~/.config/mc directory in /etc/skel
      file:
        path: /etc/skel/.config/mc
        state: directory
        mode: 0775
        owner: root
        group: root

    - name: Copy the most current Midnight Commander configuration files to /etc/skel
      copy:
        src: "{{ item }}"
        dest: /etc/skel/.config/mc
        mode: 0644
        owner: root
        group: root
      with_fileglob:
          - /home/dboth/development/ansible/PostInstall/files/MidnightCommander/*ini

################################################################################
# Install other configuration files                                            #
################################################################################
# Copy .bash_logout to /etc/skel becuase it is not created by default
    - name: copy .bash_logout file to /etc/skel
      copy:
        src: /home/dboth/development/ansible/PostInstall/files/BashConfigFiles/.bash_logout
        dest: /etc/skel/
        mode: 0644
        owner: root
        group: root

# Copy .bash_logout to /root because it is not created by default
    - name: copy .bash_logout file to /root
      copy:
        src: /home/dboth/development/ansible/PostInstall/files/BashConfigFiles/.bash_logout
        dest: /home/dboth/development/
        mode: 0644
        owner: root
        group: root

    - name: copy myBashConfig.sh file to /etc/profile.d
      copy:
        src: /home/dboth/development/ansible/PostInstall/files/BashConfigFiles/myBashConfig.sh
        dest: /etc/profile.d
        mode: 0644
        owner: root
        group: root

    - name: copy top configuration file to root
      copy:
        src: /home/dboth/development/ansible/PostInstall/files/top/toprc
        dest: /root
        mode: 0644
        owner: root
        group: root

    - name: copy top configuration file to /etc/skel
      copy:
        src: /home/dboth/development/ansible/PostInstall/files/top/toprc
        dest: /etc/skel
        mode: 0644
        owner: root
        group: root

    - name: copy root crontab to /var/spool/cron
      copy:
        src: /home/dboth/development/ansible/PostInstall/files/crontab/root
        dest: /var/spool/cron
        mode: 0644
        owner: root
        group: root

    - name: Restart the crond daemon
      systemd:
        name: crond
        state: restarted
        enabled: yes

################################################################################
# Install some additional files and Bash scripts to /root                      #
################################################################################
    - name: Copy some additional Bash scripts to /root
      copy:
        src: "{{ item }}"
        dest: /root
        mode: 0774
        owner: root
        group: root
      with_fileglob:
          - /home/dboth/development/ansible/PostInstall/files/root/scripts/*

    - name: Copy some additional non-executable files to /root
      copy:
        src: "{{ item }}"
        dest: /root
        mode: 0664
        owner: root
        group: root
      with_fileglob:
          - /home/dboth/development/ansible/PostInstall/files/root/files/*

################################################################################
# Stop the ABRT services to prevent crashes from sucking up huge amounts of    #
# resources when a dump occurs. ABRT is the Automated Bug Reporting Tool that  #
# is used for reporting crash information.                                     #
################################################################################
    - name: Stop and disable the abrt-xorg daemon
      systemd:
        name: abrt-xorg
        state: stopped
        masked: yes
        enabled: no

    - name: Stop and disable the abrt-journal-core daemon
      systemd:
        name: abrt-journal-core
        state: stopped
        masked: yes
        enabled: no

    - name: Stop and disable the abrt-oops daemon
      systemd:
        name: abrt-oops
        state: stopped
        masked: yes
        enabled: no

    - name: Stop and disable the abrtd daemon
      systemd:
        name: abrtd
        state: stopped
        masked: yes
        enabled: no

################################################################################
# Start and enable some services that are needed.                              #
################################################################################
    - name: Start and enable the sysstat daemon
      systemd:
        name: sysstat
        state: started
        enabled: yes

################################################################################
# Set up daily creation of the MOTD                                            #
################################################################################

    - name: Install create_motd script in /etc/cron.daily
      copy:
        src: /home/dboth/development/ansible/PostInstall/files/usrlocal/bin/create_motd
        dest: /etc/cron.daily
        mode: 0774
        owner: root
        group: root

    - name: Create the /etc/motd file for the first time
      command: /usr/local/bin/create_motd


################################################################################
# Setting SSHD configuration                                                   #
################################################################################

    - name: Add banner location to end of sshd_config
      lineinfile:
        path: /etc/ssh/sshd_config
        insertafter: EOF
        line: 'Banner /etc/LogBanner'

    - name: Copy the LogBanner file
      copy:
        src: /home/dboth/development/ansible/PostInstall/files/etc/LogBanner
        dest: /etc
        mode: 0664
        owner: root
        group: root

###############################################################################
# Set grub.conf to remove "rhgb" from kernel config line, set menu timeout to #
# 10 seconds and comment out "hiddenmenu". This gives more time to interrupt  #
# the boot process and make on-the-fly changes to it.                         #
#                                                                             #
# Also set selinux=0 to disable selinux on the kernel command line.           #
###############################################################################
# modify /etc/default/grub
    - name: Change /etc/default/grub to allow recovery boot option.
      replace:
        path: /etc/default/grub
        regexp: 'GRUB_DISABLE_RECOVERY="true"'
        replace: 'GRUB_DISABLE_RECOVERY="false"'

    - name: Change /etc/default/grub to 10 second timeout
      replace:
        path: /etc/default/grub
        regexp: 'GRUB_TIMEOUT=[0-9]*'
        replace: 'GRUB_TIMEOUT=10'

    - name: Remove rhgb option from /etc/default/grub kernel command line
      replace:
        path: /etc/default/grub
        regexp: ' rhgb'
        replace: ''

    - name: Remove quiet option from /etc/default/grub kernel command line
      replace:
        path: /etc/default/grub
        regexp: ' quiet'
        replace: ''

    - name: Set selinux=0 on /etc/default/grub kernel command line
      replace:
        path: /etc/default/grub
        regexp: 'usr"'
        replace: 'usr selinux=0"'

# Rebuild grub.cfg files
    - name: Rebuild /boot/grub2/grub.cfg
      command:
        cmd: grub2-mkconfig -o /boot/grub2/grub.cfg

################################################################################
# Also set the selinux configuration file to permissive.                       #
################################################################################
    - name: Set selinux to disabled in /etc/selinux/config
      replace:
        path: /etc/selinux/config
        regexp: 'SELINUX=enforcing'
        replace: 'SELINUX=permissive'


################################################################################
# Set the level of detail for logwatch.conf and start timer.                   #
################################################################################
    - name: Add the highest level of detail to logwatch.conf
      lineinfile:
        path: /etc/logwatch/conf/logwatch.conf
        insertafter: EOF
        line: 'Detail = 6'

################################################################################
# Add my email address to /etc/aliases if it exists, so that all emails to     #   
# root go to my email address. This must be done after installing SendMail.    #   
################################################################################
    - name: Add my email address to /etc/aliases
      lineinfile:
        path: /etc/aliases
        insertafter: EOF
        line: 'root:           david@both.org'

    - name: Activate revised aliases
      command:
        cmd: newaliases

################################################################################
# Install BOINC                                                                #
################################################################################
    - name: Install the Boinc client and tui
      dnf:
        name: 
          - boinc-client
          - boinc-tui
      when: David == "true"
################################################################################
# Copy default boinc configuration files.                                      #
################################################################################
    - name: Install default boinc configuration files.
      copy:
        src: "{{ item }}"
        dest: /var/lib/boinc
        mode: 0644
        owner: boinc
        group: boinc
      with_fileglob:
          - /home/dboth/development/ansible/PostInstall/files/boinc/*
      when: David == "true"

################################################################################
# Add a line to end of /etc/vimrc to set listchars to allow viewing of         #
# whitespace characters.                                                       #
################################################################################
    - name: Add a line to end of /etc/vimrc to allow viewing of whitespace characters.
      lineinfile:
        path: /etc/vimrc
        insertafter: EOF
        line: 'set listchars=eol:$,nbsp:_,tab:<->,trail:~,extends:>,space:+'

################################################################################
# Create non-root users                                                        #
# Use mkpasswd --method=sha-512 <Password> to generate the password hash.      #
################################################################################
# Add non-root user dboth (me) This needs to be done before some other
# tasks and after installing Midnight Commander configuration files in /etc/skel
    - name: Add user dboth
      user:
        name: dboth
        comment: David Both
        password: $6$iVSAb5VYBem/ZC.W$KcHhEiylLXsPFy2tu.Z2Mr2TnE8Ed69ojOmf57HfceJvf8y6J9QSAXmKuq9WDxdWJH9y09CZTuBPj3eTjZpak.
        groups: wheel
        state: present
        create_home: yes
      when: David == "true"

    - name: Add student user for testing
      user:
        name: student
        comment:  Student User
        password: $6$.N.Kuu3j8pu.WDZH$HyQeho4itMqwsy61PxiFwrOsDtCO8Km7jLDD0zQcvhL/LzB0zErHnr2zyrX/qIpMwhloegA4DcugUsNZnWYma0
        state: present
        create_home: yes
      when: David == "true"

    - name: Install some GUI admninistration tools
      dnf:
        name: gnote,vim-X11,xfe
        state: latest
      when: Gui == "true"

################################################################################
# Lightdm is the default display manager but install others just in case.      #
################################################################################
# Install the display managers
    - name: Install Alternative display managers, lxdm,xorg-x11-xdm,sddm,and lightdm
      dnf:
        name: lxdm,xorg-x11-xdm,sddm,lightdm
        state: latest

################################################################################
# Install additional desktops if this is to be a GUI workstation.              #
# - Mate Desktop                                                               #
# - Cinnamon Desktop                                                           #
# - LXDE Desktop                                                               #
# - Xfce Desktop                                                               #
#                                                                              #
################################################################################
    - name: Install Mate desktop
      dnf:
        name: '@Mate Desktop'
        state: latest
      when: mate == "true"
    - name: Install Cinnamon desktop
      dnf:
        name: '@Cinnamon Desktop'
        state: latest
      when: cinnamon == "true"
    - name: Install LXDE desktop
      dnf:
        name: '@LXDE Desktop'
        state: latest
      when: lxde == "true"
    - name: Install XFCE desktop
      dnf:
        name: '@Xfce Desktop'
        state: latest
      when: xfce == "true"

################################################################################
# Installing these new desktops changes the enabled display manager. So we     #
# reenable lightdm and restart the display manager service.                    #
################################################################################
    - name: Disable the current display manager
      ansible.builtin.systemd:
        name: display-manager.service
        enabled: no
 
    - name: Enable lightdm
      ansible.builtin.systemd:
        name: lightdm.service
        enabled: yes


    - name: Copy a large number of big background files to /usr/share/wallpapers. This will take a while.
      copy:
        src: "{{ item }}"
        dest: /usr/share/wallpapers/
        mode: 0664
        owner: root
        group: root
      with_fileglob:
          - /home/dboth/development/ansible/PostInstall/files/wallpapers/*
      when: Wallpapers == "true"

################################################################################
# Install some GUI applications here if variables Gui and Apps are both true.  #
################################################################################
# Start with some groups
    - name: Install basic LibreOffice applications
      dnf:
        name: '@LibreOffice'
        state: latest
      when: Gui == "true" and Apps == "true"

    - name: Install fonts group from the Fedora repository
      dnf:
        name: '@fonts'
        state: latest
      when: Gui == "true" and Apps == "true"

    - name: Install additional individual fonts from the Fedora repository
      dnf:
        name: 'cmatrix-x11-fonts'
        state: latest
      when: Gui == "true" and Apps == "true"

    - name: Install some additional GUI applications and tools 
      dnf:
        name: 
          - libreoffice-base
          - libreoffice-draw
          - libreoffice-math
          - libreoffice-icon-theme-papirus
          - libreoffice-TexMaths
          - krusader
          - kmymoney
          - skrooge
          - tellico
          - gramps
          - gnucash
          - stellarium 
          - celestia
          - thunderbird
          - firefox
          - okular  
          - amarok
          - asunder
          - audacious
          - audacity
        state: latest
      when: Gui == "true" and Apps == "true"

################################################################################
# Remove some things that are not needed, not wanted, or just don't work.      #
################################################################################
    - name: Remove dnfdragora because it does not work.
      dnf:
        name: dnfdragora
        state: absent
      when: dnfdragora == "false"



################################################################################
# Perform some final tasks that need to be done after everything else          #
################################################################################
    - name: Install Rootkit Hunter
      dnf:
        name: 
          - rkhunter
        state: latest

    - name: Create a current file properties database for rkhunter
      command:
        cmd: rkhunter --propupd

# Rebuild the man pages
    - name: Rebuild man pages
      command: mandb

################################################################################
# This will reboot the target host and then wait for it to complete its final  #
# reboot when the reboot variable is true. Which it is by default. The         #
# sequence of operations in this playbook does not result in showing the added #
# user accounts on the graphical login screen. The reboot does that. It also   #
# ensures that all newly created users and groups are acive for new logins and #
# the latest libraries are in effect.                                          #
################################################################################
    - name: Reboot at the end of the procedure 
      reboot:
      when: Reboot == "true"

################################################################################
################################################################################

Figure 1: Ansible playbook for performing all my post-install tasks.

Warning!!! — Don’t use this Ansible playbook on a production system until you know exactly what it does and have modified it to meet your own needs. This article and the playbook that goes with it are intended as a tool to demonstrate what can be done with Ansible to perform post-installation tasks. I strongly recommend that you experiment with this tool on a non-production virtual machine.

What it does — a bit

Don’t worry — despite including the entire playbook so you can see some of the many things that can be done with Ansible, I’m not going to describe every line of this playbook. The comments and the Ansible tasks themselves are fairly explanatory. However, you do need to know that some of the files that are installed via this playbook are local ones and work only on my hosts.

The first part of the playbook sets a number of variables that are used throughout. The only one I usually change is the hostname. Any variables can be set from the command line using the ansible-playbook option, -e, but I do like to change them in the playbook; that’s just my preference.

Then there’s a short section that sets some configuration. The section that disables AVAHI services and enables the NSS DNS service is important to me. I find that, for my network, that’s a better option for name services. I have a DNS server in my network and, when configured with NSS, is faster than the very chatty, peer-dependent, AVAHI mDNS.

I also disable the smart-card deamon because I don’t use smart cards in my environment. I’ve never encountered a situation where this service was used, so I’m not sure why it’s even included by default, let alone enabled.

Then Ansible installs some files that need to be there after the installation of updates and the reboot.

It also installs all current updates and reboots the target host. This is where some of the advantages of using Ansible become apparent. Unless it’s updating itself, Ansible doesn’t run on the target host; it runs on the Ansible hub and sends commands to the target host via SSH. When Ansible sends the reboot command to the target host, it waits until that system has rebooted and is responding to SSH commands. Ansible then continues sending instructions from the rest of the playbook.

That bit of coolness prevents the SysAdmin, me, from needing to intervene manually to either perform the reboot or to restart the playbook after the reboot.

A large part of the rest of the playbook installs a large number of problem solving tools I like to use and that aren’t installed by default. Any of those tools that are already installed because I used a different Fedora spin, or whatever, are recognized by Ansible and noted with the green OK message. It also performs a number of configuration tasks that saves me a lot of time, both in performing the configuration, as well as in performance of my SysAdmin tasks.

You’ll also notice that the playbook does install some fun command line games. SysAdmins like to have fun, too.

Desktops

Whether your system already has a GUI desktop installed or not, this playbook can install any desktops that you specify, like Xfce, LCDE, MATE, or Cinnamon. Those are the ones I usually install. If you want to add others, you can add tesks to do so. Just list the desktop groups to install for Fedora with the command in Figure 2…

$ dnf grouplist | grep -i desk
   Phosh Desktop
   LXQt Desktop
   MATE Desktop
   Sugar Desktop Environment
   Deepin Desktop
   Budgie Desktop
   Basic Desktop
   i3 desktop
   Sway Desktop
   Xfce Desktop
   LXDE Desktop
   Cinnamon Desktop
   Desktop accessibility
   Budgie Desktop Applications
   GNOME Desktop Environment
   KDE (K Desktop Environment)

Figure 2: This command displays all Fedora package groups.

… Then get more information about the LXDE desktop, for example, as in Figure 3. You can use this command to learn more about any of the desktop groups.

$ dnf groupinfo "LXDE Desktop"
Last metadata expiration check: 0:05:06 ago on Mon 08 Jul 2024 01:01:35 PM EDT.
Environment Group: LXDE Desktop
 Description: LXDE is a lightweight X11 desktop environment designed for computers with low hardware specifications like netbooks, mobile devices or older computers.
 Mandatory Groups:
   Administration Tools
   Common NetworkManager Submodules
   Core
   Desktop accessibility
   Dial-up Networking Support
   Fonts
   Guest Desktop Agents
   Hardware Support
   Input Methods
   LXDE
   Multimedia
   Printing Support
   Standard
   base-x
 Optional Groups:
   3D Printing
   Applications for the LXDE Desktop
   Cloud Management Tools
   LXDE Office
   Multimedia support for LXDE

Figure 3: More detailed information about the LXDE desktop.

Once you’ve decided which desktops to install, add tasks to do that. It’s not necessary to delete any of the ones that are already there.

Parting thoughts

Performing all of my post-installation tasks using Ansible saves me having to monitor the process and to intervene. I have configured Ansible to send commands to the remote host using the root account, but it also provides options for using non-root accounts.

Modifying the Ansible playbook is easy so don’t hesitate to experiment with it on your VM.

If you have multiple target hosts you can have Ansible work on them both at the same time. Just list the hostnames in the hosts variable in the playbook or the extended-variables at the command line. Ansible keeps track of each host separately so even if they’re significantly different in speed, the fastest host isn’t forced to wait untill the slower hosts catch up. All hosts finish at their own speed.

I currently use Ansible to perform all of my post-installation tasks. It’s the best tool I’ve used in that it does everything I need it to and I don’t need to intervene at any point.


  1. Ansible can also be used with Windows hosts. ↩︎