Building a Debian 11 Vagrant Box using Packer and Ansible

Sometimes it might be necessary to create one’s own Vagrant base box for reasons too numerous to mention here. Let’s assume you want to build a new base box for Debian 11 (bullseye) to run on Virtualbox. Previously I would have run through the installation process followed by customising the VM’s installed packages and installing Guest Additions before creating the base box. As it turns out, this repetitive (and boring) process isn’t required as pretty much the whole thing can be automated using Packer.

Debian 11 is still quite new and a few things related to the Guest Additions don’t work yet but it can’t hurt to be prepared.

As I’m notoriously poor at keeping my code in sync between my various computers I created a new repository on Github for sharing my Packer builds. If you are interested head over to https://github.com/martincarstenbach/packer-blogposts. As with every piece of code you find online, it’s always a good idea to vet it first before even considering using it. Kindly take the time to read the license as well as the README associated with the repository in addition to this post.

Please note this is code I wrote for myself, a little more generic than it might have to be but ultimately you’ll have to read the code and adjust it for your own purposes. The preseed and kickstart files are specifically single-purpose only and shouldn’t be used for anything other than what is covered in this post. My Debian 11 base box is true to the word: it’s really basic, apart from SSH and the standard utilities (+ Virtualbox Guest Additions) I decided not to include anything else.

Software Releases

I should have added that I used Packer’s Virtualbox ISO builder. It is documented in great detail at the Packer website. Further software used:

  • Ubuntu 20.04 LTS
  • Ansible 2.9
  • Packer 1.7.4
  • Virtualbox 6.1.26

All of these were current at the time of writing.

Preparing the Packer build JSON and Debian Preseed file

I have missed the opportunity of creating all my computer systems with the same directory structure, hence there are small, subtle differences. To accommodate all of these I created a small shell script, prepare-debian11.sh. This script prompts me for the most important pieces of information and creates both the preseed file as well as the JSON build-file required by Packer.

martin@ubuntu:~/packer-blogposts$ bash prepare-debian11.sh 

INFO: preparing your packer environment for the creation of a Debian 11 Vagrant base box

Enter your local Debian mirror (http://ftp2.de.debian.org): 
Enter the mirror directory (/debian): 

/home/martin/.ssh/id_rsa.pub

Enter the full path to your public SSH key (/home/martin/.ssh/id_rsa.pub): 
Identity added: /home/martin/.ssh/id_rsa (/home/martin/.ssh/id_rsa)
Enter the location of the Debian 11 network installation media (/m/stage/debian-11.0.0-amd64-netinst.iso):
Enter the full path to store the new vagrant box (/home/martin/vagrant/boxes/debian-11-01.box):/home/martin/vagrant/boxes/blogpost.box    

INFO: preparation complete, next run packer validate vagrant-debian-11.json && packer build vagrant-debian-11.json

One of the particularities of my Packer builds is the use of agent authentication. My number 1 rule when coding is to never store authentication details in files if it can be avoided at all. Relying on the SSH agent to connect to the Virtualbox VM while it’s created allows me to do that, at least for Packer. Since I tend to forget adding my Vagrant SSH key to the agent, the prepare-script does that for me.

Sadly I have to store the vagrant user’s password in the preseed file. I can live with that this time as the password should be “vagrant” by convention and I didn’t break with it. Out of habit I encrypted the password anyway, it’s one of these industry best-known-methods worth applying every time.

Building the Vagrant Base Box

Once the build file and its corresponding preseed file are created by the prepare-script, I suggest you review them first before taking any further action. Make any changes you like, then proceed by running a packer validate followed by the packer build command once you understood/agree with what’s happening next. The latter of the 2 commands kicks the build off, and you’ll see the magic of automation for yourself ;)

Here is a sample of one of my sessions:

martin@ubuntu:~/packer-blogposts$ packer build vagrant-debian-11.json
virtualbox-iso: output will be in this color.

==> virtualbox-iso: Retrieving Guest additions
==> virtualbox-iso: Trying /usr/share/virtualbox/VBoxGuestAdditions.iso
==> virtualbox-iso: Trying /usr/share/virtualbox/VBoxGuestAdditions.iso
==> virtualbox-iso: /usr/share/virtualbox/VBoxGuestAdditions.iso => /usr/share/virtualbox/VBoxGuestAdditions.iso
==> virtualbox-iso: Retrieving ISO
==> virtualbox-iso: Trying file:///m/stage/debian-11.0.0-amd64-netinst.iso
==> virtualbox-iso: Trying file:///m/stage/debian-11.0.0-amd64-netinst.iso?checksum=sha256%3Aae6d563d2444665316901fe7091059ac34b8f67ba30f9159f7cef7d2fdc5bf8a
==> virtualbox-iso: file:///m/stage/debian-11.0.0-amd64-netinst.iso?checksum=sha256%3Aae6d563d2444665316901fe7091059ac34b8f67ba30f9159f7cef7d2fdc5bf8a => /m/stage/debian-11.0.0-amd64-netinst.iso
==> virtualbox-iso: Starting HTTP server on port 8765
==> virtualbox-iso: Using local SSH Agent to authenticate connections for the communicator...
==> virtualbox-iso: Creating virtual machine...
==> virtualbox-iso: Creating hard drive output-virtualbox-iso-debian11base/debian11base.vdi with size 20480 MiB...
==> virtualbox-iso: Mounting ISOs...
    virtualbox-iso: Mounting boot ISO...
==> virtualbox-iso: Creating forwarded port mapping for communicator (SSH, WinRM, etc) (host port 2302)
==> virtualbox-iso: Executing custom VBoxManage commands...
    virtualbox-iso: Executing: modifyvm debian11base --memory 2048
    virtualbox-iso: Executing: modifyvm debian11base --cpus 2
==> virtualbox-iso: Starting the virtual machine...
==> virtualbox-iso: Waiting 10s for boot...
==> virtualbox-iso: Typing the boot command...
==> virtualbox-iso: Using SSH communicator to connect: 127.0.0.1
==> virtualbox-iso: Waiting for SSH to become available...
==> virtualbox-iso: Connected to SSH!
==> virtualbox-iso: Uploading VirtualBox version info (6.1.26)
==> virtualbox-iso: Uploading VirtualBox guest additions ISO...
==> virtualbox-iso: Provisioning with Ansible...
    virtualbox-iso: Setting up proxy adapter for Ansible....
==> virtualbox-iso: Executing Ansible: ansible-playbook -e packer_build_name="virtualbox-iso" -e packer_builder_type=virtualbox-iso -e packer_http_addr=10.0.2.2:8765 --ssh-extra-args '-o IdentitiesOnly=yes' -e ansible_ssh_private_key_file=/tmp/ansible-key610730318 -i /tmp/packer-provisioner-ansible461216853 /home/martin/devel/packer-blogposts/ansible/vagrant-debian-11-guest-additions.yml
    virtualbox-iso:
    virtualbox-iso: PLAY [all] *********************************************************************
    virtualbox-iso:
    virtualbox-iso: TASK [Gathering Facts] *********************************************************
    virtualbox-iso: ok: [default]
    virtualbox-iso: [WARNING]: Platform linux on host default is using the discovered Python
    virtualbox-iso: interpreter at /usr/bin/python3, but future installation of another Python
    virtualbox-iso: interpreter could change this. See https://docs.ansible.com/ansible/2.9/referen
    virtualbox-iso: ce_appendices/interpreter_discovery.html for more information.
    virtualbox-iso:
    virtualbox-iso: TASK [install additional useful packages] **************************************
    virtualbox-iso: changed: [default]
    virtualbox-iso:
    virtualbox-iso: TASK [create a temporary mount point for vbox guest additions] *****************
    virtualbox-iso: changed: [default]
    virtualbox-iso:
    virtualbox-iso: TASK [mount guest additions ISO read-only] *************************************
    virtualbox-iso: changed: [default]
    virtualbox-iso:
    virtualbox-iso: TASK [execute guest additions script] ******************************************
    virtualbox-iso: changed: [default]
    virtualbox-iso:
    virtualbox-iso: TASK [unmount guest additions ISO] *********************************************
    virtualbox-iso: changed: [default]
    virtualbox-iso:
    virtualbox-iso: TASK [remove the temporary mount point] ****************************************
    virtualbox-iso: ok: [default]
    virtualbox-iso:
    virtualbox-iso: TASK [upgrade all packages] ****************************************************
    virtualbox-iso: ok: [default]
    virtualbox-iso:
    virtualbox-iso: PLAY RECAP *********************************************************************
    virtualbox-iso: default                    : ok=8    changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
    virtualbox-iso:
==> virtualbox-iso: Gracefully halting virtual machine...
==> virtualbox-iso: Preparing to export machine...
    virtualbox-iso: Deleting forwarded port mapping for the communicator (SSH, WinRM, etc) (host port 2302)
==> virtualbox-iso: Exporting virtual machine...
    virtualbox-iso: Executing: export debian11base --output output-virtualbox-iso-debian11base/debian11base.ovf
==> virtualbox-iso: Cleaning up floppy disk...
==> virtualbox-iso: Deregistering and deleting VM...
==> virtualbox-iso: Running post-processor: vagrant
==> virtualbox-iso (vagrant): Creating a dummy Vagrant box to ensure the host system can create one correctly
==> virtualbox-iso (vagrant): Creating Vagrant box for 'virtualbox' provider
    virtualbox-iso (vagrant): Copying from artifact: output-virtualbox-iso-debian11base/debian11base-disk001.vmdk
    virtualbox-iso (vagrant): Copying from artifact: output-virtualbox-iso-debian11base/debian11base.ovf
    virtualbox-iso (vagrant): Renaming the OVF to box.ovf...
    virtualbox-iso (vagrant): Compressing: Vagrantfile
    virtualbox-iso (vagrant): Compressing: box.ovf
    virtualbox-iso (vagrant): Compressing: debian11base-disk001.vmdk
    virtualbox-iso (vagrant): Compressing: metadata.json
Build 'virtualbox-iso' finished after 13 minutes 43 seconds.

==> Wait completed after 13 minutes 43 seconds

==> Builds finished. The artifacts of successful builds are:
--> virtualbox-iso: 'virtualbox' provider box: /home/martin/vagrant/boxes/blogpost.box

The operations should complete with the message shown in the output – build complete, box created and added in the directory specified. From that point onward you can add it to your inventory.

Happy Automation!