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 :
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.
HCP Packer’s latest additions to channels give platform teams more visibility into their artifact estate, shorten times to remediation, and unify workflows with Terraform Cloud.
HCP Packer’s new channel assignment history and rollback provide a complete record of artifacts in a channel and enable a simple, one-click rollback to previous iterations.
Ancestry tracking for HCP Packer provides visibility into image dependencies across your cloud environment for image lifecycle management.