Learn about the new HCL2-only templatefile function and how to use it in an http_content to preseed a virtual machine.
In HashiCorp Packer 1.7, we tagged HCL2 as stable and implemented HCL2-only functions. You can use one such function, the templatefile
function to build multiple operating systems with less duplication of configuration.
Currently, you need to use the boot_command
argument to configure an OS before you connect to the machine. You can use it with many builders, including the vmware-iso or virtualbox-iso builders. A boot_command
mimicks manual keystrokes and sends them at a regular cadence. You aggregate these keystrokes for installing and configuring packages in a preseed file. Packer enables sharing preseed files by making them available statically through an HTTP server. You can also access static files using CD files or a floppy.
In this post, we’ll use the http_content
and the templatefile
functions together to build preseed file templates for two Ubuntu images, one with HashiCorp Nomad and one with HashiCorp Consul.
Say the file preseed.pkrtpl
is your preseed template file, and you would like to be able to set a user’s name, ID, and password and also the packages installed with it :
d-i apt-setup/universe boolean trued-i pkgsel/include %{ for install in installs ~}${install} %{ endfor }string openssh-server cryptsetup build-essential libssl-dev libreadline-dev zlib1g-dev linux-source dkms nfs-common linux-headers-$(uname -r) perl cifs-utils software-properties-common rsync ifupdown d-i passwd/user-fullname string ${user.name}d-i passwd/user-uid string ${user.id}d-i passwd/user-password password ${user.password}d-i passwd/user-password-again password ${user.password}d-i passwd/username string ${user.name} choose-mirror-bin mirror/http/proxy stringd-i base-installer/kernel/override-image string linux-serverd-i clock-setup/utc boolean trued-i clock-setup/utc-auto boolean trued-i finish-install/reboot_in_progress noted-i grub-installer/only_debian boolean trued-i grub-installer/with_other_os boolean trued-i mirror/country string manuald-i mirror/http/directory string /ubuntu/d-i mirror/http/hostname string archive.ubuntu.comd-i mirror/http/proxy stringd-i partman-auto-lvm/guided_size string maxd-i partman-auto/choose_recipe select atomicd-i partman-auto/method string lvmd-i partman-lvm/confirm boolean trued-i partman-lvm/confirm boolean trued-i partman-lvm/confirm_nooverwrite boolean trued-i partman-lvm/device_remove_lvm boolean trued-i partman/choose_partition select finishd-i partman/confirm boolean trued-i partman/confirm_nooverwrite boolean trued-i partman/confirm_write_new_label boolean trued-i pkgsel/install-language-support boolean falsed-i pkgsel/update-policy select noned-i pkgsel/upgrade select full-upgraded-i time/zone string UTCd-i user-setup/allow-password-weak boolean trued-i user-setup/encrypt-home boolean falsetasksel tasksel/first multiselect standard, server
This template file can be used to install binaries from a variable. In this example, we made it possible to configure the user settings and pass an arbitrary list of packages to install. This simplifies the build while making it more powerful.
Note: The
.pkrtpl
extension is a recommendation and not a requirement. It allows editors to recognize a Packer template file written in HCL2.
The following configuration file can then be defined:
variables {
headless = true
}
source "virtualbox-iso" "base-ubuntu-amd64" {
headless = var.headless
iso_url = local.ubuntu_2010_iso_url
iso_checksum = "file:${local.ubuntu_2010_iso_checksum_url}"
guest_os_type = "Ubuntu_64"
hard_drive_interface = "sata"
ssh_wait_timeout = "15m"
boot_wait = "5s"
}
locals {
ubuntu_2010_dl_folder = "http://cdimage.ubuntu.com/ubuntu/releases/18.04/release/"
ubuntu_2010_iso_url = "${local.ubuntu_2010_dl_folder}ubuntu-18.04.5-server-amd64.iso"
ubuntu_2010_iso_checksum_url = "${local.ubuntu_2010_dl_folder}SHA256SUMS"
builds = {
consul = {
user = {
id = 1000
name = "bob"
password = "s3cr2t"
}
installs = ["consul"]
}
nomad = {
user = {
id = 1000
name = "bob"
password = "s3cr2t"
}
installs = ["nomad"]
}
}
}
build {
name = "ubuntu"
description = <<EOF
This build creates images for :
* Ubuntu 18.04
For the following builders :
* virtualbox-iso
It will create base images with:
* Nomad
* Consul
EOF
dynamic "source" {
for_each = local.builds
labels = ["source.virtualbox-iso.base-ubuntu-amd64"]
content {
name = source.key
ssh_username = source.value.user.name
ssh_password = source.value.user.password
shutdown_command = "echo '${source.value.user.password}' | sudo -S shutdown -P now"
http_content = {
"/preseed.cfg" = templatefile("${path.root}/preseed.pkrtpl", source.value)
}
boot_command = [
"<esc><wait>",
"<esc><wait>",
"<enter><wait><wait>",
"/install/vmlinuz<wait>",
" auto<wait>",
" console-setup/ask_detect=false<wait>",
" console-setup/layoutcode=us<wait>",
" console-setup/modelcode=pc105<wait>",
" debconf/frontend=noninteractive<wait>",
" debian-installer=en_US.UTF-8<wait>",
" fb=false<wait>",
" initrd=/install/initrd.gz<wait>",
" kbd-chooser/method=us<wait>",
" keyboard-configuration/layout=USA<wait>",
" keyboard-configuration/variant=USA<wait>",
" locale=en_US.UTF-8<wait>",
" netcfg/get_domain=vm<wait>",
" netcfg/get_hostname=vagrant<wait>",
" grub-installer/bootdev=/dev/sda<wait>",
" noapic<wait>",
" preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg<wait>",
" -- <wait>",
"<enter><wait>"
]
output_directory = "virtualbox_iso_ubuntu_2010_amd64_${source.key}"
}
}
provisioner "shell" {
environment_vars = [ "HOME_DIR=/home/vagrant" ]
execute_command = "echo '${local.builds[source.name].user.password}' | {{.Vars}} sudo -S -E sh -eux '{{.Path}}'"
expect_disconnect = true
inline = [
"echo hello from the ${source.name} image",
"${source.name} version"
]
}
}
Now we can see that this has made it easier to maintain a preseed project. Pretty cool, right? Effectively, the templatefile
function can be used in other scenarios, same for the http_content option. We could extend this by skipping the template file and setting everything from the map:
[...]
http_content = {
"/preseed.cfg" = <<<EOF
d-i apt-setup/universe boolean true
d-i pkgsel/include string openssh-server cryptsetup build-essential libssl-dev libreadline-dev zlib1g-dev linux-source dkms nfs-common linux-headers-$(uname -r) perl cifs-utils software-properties-common rsync ifupdown consul
...
EOF
}
For more information on Packer’s recent additions, review Packer's changelog. Lastly, if you have any issues, do not hesitate to open an issue in the Packer repository on GitHub.
Webhooks for HCP Packer automatically notify external systems about image-related events.
To boost stability, Packer 1.11 introduces a predictable plugin loading approach, loading only binaries from its plugin directory with accompanying SHA256SUM files.
Learn the installation and verification workflow for any Linux distribution that does not include HashiCorp software in its package repository.