Creating The Perfect Software Development Environment: Day 08

Installing Development Tools

Last time we customized our Vagrant box using Bash scripts. While that is simple and straightforward, a more powerful and elegant solution is to use Ansible. Packer's Ansible Local Provisioner allows us to do just that.

Ansible

Working in IT, you're likely doing the same tasks over and over. What if you could solve problems once and then automate your solutions going forward? Ansible is here to help.

I'm not going to try and teach you Ansible because there are plenty of resources out there already. What I will talk about are the key features of Ansible that might cause you to reach for it as your provisioning tool.

  1. convenience - Ansible has the notion of a module which is a baked-in recipe for how to accomplish a task. The module index has the building blocks needed to accomplish almost any task.
  2. shared knowledge - Ansible Galaxy is a repository of custom modules provided by the community, often combining many of the lower level modules into a sigle, more convenient module.
  3. ease of use - all Ansible needs to work is an SSH connection and legacy Python.
  4. templating - Ansible has many nice features but the ability to levarage Jinja2 templates is one of my favorites. I've used this feature to construct configuration files using values specified when Ansible is run. Very useful.

Tool Installation via Ansible

Let's install the same set of packages we installed using scripts/install-packages.sh using Ansible instead.


"provisioners": [
    {
        "type"            : "shell",
        "environment_vars": ["DEBIAN_FRONTEND=noninteractive"],
        "script"          : "scripts/install-ansible.sh"
    },
    {
        "type"            : "ansible-local",
        "playbook_file"   : "ansible/playbook.yml",
        "extra_arguments" : ["--verbose"]
    },
    {
        "type"            : "shell",
        "environment_vars": ["DEBIAN_FRONTEND=noninteractive"],
        "scripts"         : ["scripts/prepare-for-compression.sh"]
    }
],
            

#!/usr/bin/env bash

# there are numerous ways to install Ansible but let's use the package manager
echo "Installing Ansible via package manager..."

until sudo apt-get --yes update; do echo "Waiting for apt lock"; sleep 5; done

sudo apt-get update
sudo apt-get --yes install software-properties-common
sudo apt-add-repository ppa:ansible/ansible
sudo apt-get update
sudo apt-get --yes install ansible

ansible --version
            

---
- name: Install Tools
  hosts: localhost
  become: true
  tasks:
      - name: Favorite Tools
        apt:
            name        : "{{ item }}"
            state       : present
            update_cache: yes
        with_items:
            - "tree"
            - "ntpdate"
            - "grsync"
            
Grsync installed.
Grsync is installed

Conclusion

Today we learned how Ansible can simplify the customization of our Vagrant box. The Ansible playbook we created today is very simple but effective. Next time, we'll learn how to leverage other people's work, using modules pulled from Ansible Galaxy.

Full Packer File


{
    "description": "Builds a Xubuntu 18.04 desktop box with various software development tools installed",
    "min_packer_version": "1.2.3",

    "variables": {
        "ssh_name"            : "vagrant",
        "ssh_pass"            : "vagrant",
        "virtualbox_appliance": "bionic-beaver.ova",
        "comment"             : "Test of Vagrant Cloud upload.",
        "cloud_user"          : "{{env `VAGRANT_CLOUD_ACCOUNT`}}",
        "cloud_token"         : "{{env `VAGRANT_CLOUD_TOKEN`}}"
    },

    "builders": [{
        "type"        : "virtualbox-ovf",
        "source_path" : "{{user `virtualbox_appliance`}}",
        "ssh_username": "{{user `ssh_name`}}",
        "ssh_password": "{{user `ssh_pass`}}",

        "boot_wait"           : "30s",
        "format"              : "ova",
        "guest_additions_mode": "disable",
        "headless"            : false,
        "keep_registered"     : false,
        "shutdown_command"    : "sudo shutdown --poweroff now",
        "shutdown_timeout"    : "2m",
        "skip_export"         : false,
        "output_directory"    : "output-virtualbox-ovf",
        "vboxmanage": [
		        ["modifyvm", "{{.Name}}", "--vram", "32"],
		        ["modifyvm", "{{.Name}}", "--memory", "2048"],
		        ["modifyvm", "{{.Name}}", "--cpus", "1"]
	      ],
        "vm_name"             : "packer-bionic-xubuntu"
    }],
    "provisioners": [
        {
            "type"            : "shell",
            "environment_vars": ["DEBIAN_FRONTEND=noninteractive"],
            "script"          : "scripts/install-ansible.sh"
        },
        {
            "type"            : "ansible-local",
            "playbook_file"   : "ansible/playbook.yml",
            "extra_arguments" : ["--verbose"]
        },
        {
            "type"            : "shell",
            "environment_vars": ["DEBIAN_FRONTEND=noninteractive"],
            "scripts"         : ["scripts/prepare-for-compression.sh"]
        }
    ],
    "post-processors": [
        [
            {
                "compression_level"  : 9,
                "keep_input_artifact": false,
                "output"             : "vagrant/bionic-xubuntu.box",
                "type"               : "vagrant"
            },
            {
                 "access_token"       : "{{user `cloud_token`}}",
                 "box_tag"            : "{{user `cloud_user`}}/bionic-xubuntu",
                 "type"               : "vagrant-cloud",
                 "version"            : "{{isotime \"2006.01.0203\"}}",
                 "version_description": "{{user `comment`}}"
            }
        ]
    ]
}