General purpose Ansible playbook for installing various software on Linux

When provisioning Linux machine we often start from minimal vanilla base images or even ISO files. We need to install different software packages from different sources and versions over and over again.

  • Sometime from built-in package repositories using the native package manager like apt on Debian/Ubuntu
  • Sometimes we need to add additional package repositories
  • Sometime we need to download DEB files from URLs
  • Sometimes we need to download archive files
  • Sometimes we need to install packages using pip
  • ….

In Ansible you usually use the following collection/modules for those tasks:

  • ansible.builtin.apt
  • ansible.builtin.get_url
  • ansible.builtin.package
  • ansible.builtin.pip
  • ansible.builtin.unarchive

Here is one of our general purpose Ansible playbook for installing various packages. Which packages are actually installed is defined in a variables file, which also can be a host_vars or a group_vars file. If a variable is not defined the specific task is skipped. That means, we only change a few variables for different machines or machine groups.

---
- hosts: linux
  become: true

  tasks:  
    - name: Install apt keys
      apt_key:
        url: '{{ item.name }}'
        state: present
      loop: '{{ install_apt_keys }}'
      when:
        - install_apt_keys is defined
        - install_apt_keys | length > 0
      tags:
        - install_apt_keys

    - name: Install apt repositores
      apt_repository:
        repo: '{{item.name}}'
        filename: '{{item.filename}}'
        state: present
      loop: '{{ install_apt_repositories }}'
      when:
        - install_apt_repositories is defined
        - install_apt_repositories | length > 0
      tags:
        - install_apt_repositories

    - name: Install packages
      package:
        name: '{{item.name}}'
        state: present
      loop: '{{ install_packages }}'
      when:
        - install_packages is defined
        - install_packages | length > 0
      tags:
        - install_packages

    - name: Download and install deb from url
      apt:
        deb: '{{ item.url }}'
      loop: '{{ install_packages_deb_from_url }}'
      when:
        - install_packages_deb_from_url is defined
        - install_packages_deb_from_url | length > 0
      tags: 
        - install_packages_deb_from_url

    - name: Install archives from url
      unarchive:
        src: '{{ item.url }}'
        dest: '{{ item.dest }}'
        remote_src: yes
      loop: '{{ install_archives_from_url }}'
      when:
        - install_archives_from_url is defined
        - install_archives_from_url | length > 0    
      tags: 
        - install_archives_from_url

    - name: Install pips
      pip:
        name: '{{item.name}}'
        state: latest
      loop: '{{ install_pips }}'
      when:
        - install_pips is defined
        - install_pips | length > 0
      tags:
        - install_pips

    - name: Download files from url
      get_url:
        url: '{{ item.url }}'
        dest: '{{ item.dest }}'
        mode: '{{ item.mode }}'
        owner: '{{ item.owner }}'
        group: '{{ item.group }}'
      loop: '{{ download_files_from_url }}'
      when:
        - download_files_from_url is defined
        - download_files_from_url | length > 0
      tags:
        - download_files_from_url

Here is an example variables file, which also can be a host_vars or a group_vars file.

drone_cli_version: 1.4.0
kompose_version: 1.22.0
kubectl_version: 1.19.2
packer_version: 1.7.8
restic_version: 0.12.1
terraform_version: 1.1.2
vagrant_version: 2.2.18

install_apt_keys:
  - { name: 'https://packages.microsoft.com/keys/microsoft.asc' }

install_apt_repositories:
  - { name: 'deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ {{ansible_distribution_release}} main', filename: 'azurecli'}

install_packages:
  - { name: 'apt-transport-https' }
  - { name: 'software-properties-common' }
  - { name: 'aptitude' }        
  - { name: 'ca-certificates' }
  - { name: 'curl' }
  - { name: 'dirmngr' }
  - { name: 'lsb-release' }
  - { name: 'iotop' }
  - { name: 'python3-pip' }
  - { name: 'python3-setuptools'}
  - { name: 'python3-wheel' }
 
install_packages_deb_from_url:
    - { url: 'https://releases.hashicorp.com/vagrant/{{ vagrant_version }}/vagrant_{{ vagrant_version }}_x86_64.deb' }

install_archives_from_url:
    - { dest: /usr/local/bin, url: 'https://releases.hashicorp.com/packer/{{ packer_version }}/packer_{{ packer_version }}_linux_amd64.zip' }
    - { dest: /usr/local/bin, url: 'https://releases.hashicorp.com/terraform/{{ terraform_version }}/terraform_{{ terraform_version }}_linux_amd64.zip' }
    - { dest: /usr/local/bin, url: 'https://github.com/harness/drone-cli/releases/download/v{{ drone_cli_version }}/drone_linux_amd64.tar.gz' }

install_pips:
    - { name: 'passlib' }
    - { name: 'cryptography' }

download_files_from_url:
    - { dest: '/usr/local/bin/kubectl', url: 'https://storage.googleapis.com/kubernetes-release/release/v{{ kubectl_version }}/bin/linux/amd64/kubectl', mode: '755', owner: root, group: root }
    - { dest: '/usr/local/bin/kompose', url: 'https://github.com/kubernetes/kompose/releases/download/v{{ kompose_version }}/kompose-linux-amd64', mode: '755', owner: root, group: root }