Building Custom Debian Images with Packer and Ansible

- Hariharan

I like to use virtual machines to test new software or to host services in my homelab. I even use a Windows virtual machine to play games. It can get quite tedious to create a VM and install the OS every time. So I have been looking at building pre-packaged images with all the necessary tools and configuration baked in.

I have been wanting to experiment with Packer, but I didn’t have a use for it. This time I had a perfect use case. Packer is an open-source tool for building images for multiple platforms. All the images are defined as code.

Debian

I like to use Debian as the base OS for most of my projects. It is very stable and well-supported. There is a net install image which is a minimal image that can be modified as per your needs.

Debian uses a preseed file for unattended installs. This file has the answers to the interactive questions asked by the installer. I used this file to set a default root password and to allow root SSH login along with some other configuration items like partitioning, and installing some standard packages like SSH to allow Packer to connect.

On the Packer QEMU plugin, we can then use boot_command to pass this file. Many builders, like the QEMU builder, have an option of specifying HTTP content which Packer can expose by spinning up a server. This can later be referenced in the boot_command. The preseed file is placed in a directory called http as specified in http_directory.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
source "qemu" "debian-qemu" {
    http_directory = "http"
    boot_command = [
      "<esc><wait>",
      "auto<wait>",
      " auto-install/enable=true",
      " debconf/priority=critical",
      " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg<wait>",
      " -- <wait>",
      "<enter><wait>"
    ]
    iso_url = "${var.iso_url}"
    iso_checksum = "${var.iso_checksum}"
    accelerator = "kvm"
    ssh_username = "root"
    ssh_password = "debianrootpassword"
    ssh_timeout = "60m"
    boot_wait = "10s"
    vm_name = "debian-builder"
    disk_size = "${var.disk_size}"
    format = "qcow2"
    memory = var.vm_memory
    cpus = var.vm_cpu
    net_device = "virtio-net"
    efi_boot = false
}

HTTPIP and HTTPPort are set by Packer.

Ansible Provisioning

Packer supports various provisioners. I used the Ansible provisioner.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
build {
    name = "debian-image"
    sources = ["source.qemu.debian-qemu"]

    provisioner "ansible" {
      playbook_file = "./playbook.yml"
      extra_arguments = [
        "--extra-vars", "ansible_become_method=su"
      ]
    }
}

I created a few different roles:

The build takes about 25 mins and generates a qcow2 image which can directly be imported into virt-manager. This is a full disk image with all the necessary partitions to run the OS. The project files can be found here.