Project

General

Profile

Actions

Packer Templates Examples

Ubuntu 16.04.2 LTS

Simple example for Ubuntu using the qemu builder and outputs a qcow2 image.
While booting the ISO, packer will enter the template's boot command which contains the path to a unattended configuration file (like kickstart/preseed).
Once the installation finishes, the VM is rebooted and is ready to be provisioned by, e.g., an ansible playbook (more on that later).
To support ansible "out of the box", both openssh-server and ansible are installed during the unattended install phase.

Here a sample packer template file for Ubuntu 16.04:

{
  "builders": [{
    "type": "qemu",
    "iso_url": "http://releases.ubuntu.com/16.04/ubuntu-16.04.2-server-amd64.iso",
    "iso_checksum": "737ae7041212c628de5751d15c3016058b0e833fdc32e7420209b76ca3d0a535",
    "iso_checksum_type": "sha256",
    "output_directory": "output-ubuntu-16.04-amd64-{{build_type}}",
    "vm_name": "packer-ubuntu-16.04-amd64",
    "disk_size": "40000",
    "format": "qcow2",
    "headless": "true",
    "http_directory": "http",
    "boot_wait": "5s",
    "boot_command": [
      "<enter><wait>",
      "<f6><esc>",
      "<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
      "<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
      "<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
      "<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
      "<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
      "<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
      "<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
      "<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
      "<bs><bs><bs>",
      "/install/vmlinuz ",
      "initrd=/install/initrd.gz ",
      "auto-install/enable=true ",
      "debconf/priority=critical ",
      "preseed/url=http://{{.HTTPIP}}:{{.HTTPPort}}/ubuntu-preseed.cfg ",
      "<enter>" 
    ],
    "ssh_timeout": "10m",
    "ssh_username": "root",
    "ssh_password": "s0m3p4ss",
    "shutdown_command": "systemctl poweroff" 
  }]
}

... and the preseed file for the debian-installer:


d-i debian-installer/locale string en_US
d-i time/zone string UTC
d-i keyboard-configuration/xkb-keymap select de
d-i partman-basicfilesystems/choose_label string gpt
d-i partman-basicfilesystems/default_label string gpt
d-i partman-basicfilesystems/no_swap boolean false
d-i partman-partitioning/choose_label string gpt
d-i partman-partitioning/default_label string gpt
d-i partman/choose_label string gpt
d-i partman/default_label string gpt
d-i partman-auto/method string regular
d-i partman-auto/expert_recipe string \
        scheme ::                     \
        32 32 32 free                \
        $gptonly{ }         \
                $primary{ }           \
                $bios_boot{ }          \
        method{ biosgrub } .   \
        1 0 -1 ext4                   \
        $gptonly{ }         \
                $primary{ }           \
                method{ format }      \
                format{ }             \
                use_filesystem{ }     \
                filesystem{ ext4 }    \
        label{ SLX_SYS }    \
                mountpoint{ / } .
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true

d-i base-installer/excludes string laptop-detect
d-i passwd/make-user boolean false
d-i passwd/root-login boolean true
d-i passwd/root-password-crypted password $6$6yzbOJJy5auuBh9$XUAtAt/ErLkz6.1t8J4UpyZPPUbKjGO1uATaZaxmG02IhJbOwnJMqI6MTJw.SzbBvy8THCmmoia1tVMfXhGVJ1

d-i pkgsel/include string openssh-server ansible
d-i finish-install/reboot_in_progress note

CentOS 7.3

Slighty more complex example showing how to support multiple builders, here virtualbox, vmware and qemu.
This also shows the use of variables shared accross mutliple builders.

{
  "builders": [{
    "type": "qemu",
    "iso_url": "{{user `mirror`}}/7/isos/x86_64/CentOS-7-x86_64-NetInstall-1611.iso",
    "iso_checksum": "{{user `iso_checksum`}}",
    "iso_checksum_type": "{{user `iso_checksum_type`}}",
    "output_directory": "output-centos-7.3-x86_64-{{build_type}}",
    "vm_name": "packer-centos-7.3-x86_64",
    "format": "qcow2",
    "disk_size": "{{user `disk_size`}}",
    "disk_interface": "virtio-scsi",
    "headless": "{{user `headless`}}",
    "http_directory": "http",
    "boot_wait": "5s",
    "boot_command": [
      "<esc>",
      "<wait>",
      "linux inst.gpt inst.ks=http://{{.HTTPIP}}:{{.HTTPPort}}/anaconda-ks.cfg biosdevname=0 net.ifnames=0",
      "<enter>" 
    ],
    "ssh_timeout": "{{user `ssh_timeout`}}",
    "ssh_username": "{{user `ssh_username`}}",
    "ssh_password": "{{user `ssh_password`}}",
    "shutdown_command": "sudo systemctl poweroff",
    "qemuargs": [
      ["-m", "{{user `memory`}}"],
      ["-smp", "{{user `cpus`}}"]
    ]
  }, {
    "type": "virtualbox-iso",
    "guest_os_type": "RedHat_64",
    "iso_url": "{{user `mirror`}}/7/isos/x86_64/CentOS-7-x86_64-NetInstall-1611.iso",
    "iso_checksum": "{{user `iso_checksum`}}",
    "iso_checksum_type": "{{user `iso_checksum_type`}}",
    "output_directory": "output-centos-7.3-x86_64-{{build_type}}",
    "vm_name": "packer-centos-7.3-x86_64",
    "disk_size": "40000",
    "headless": "true",
    "http_directory": "http",
    "boot_wait": "5s",
    "boot_command": [
      "<esc>",
      "<wait>",
      "linux inst.gpt inst.ks=http://{{.HTTPIP}}:{{.HTTPPort}}/centos-7.3/anaconda-ks.cfg biosdevname=0 net.ifnames=0",
      "<enter>" 
    ],
    "ssh_timeout": "{{user `ssh_timeout`}}",
    "ssh_username": "{{user `ssh_username`}}",
    "ssh_password": "{{user `ssh_password`}}",
    "shutdown_command": "systemctl poweroff",
    "vboxmanage": [
      ["modifyvm", "{{.Name}}", "--memory", "{{user `memory`}}"],
      ["modifyvm", "{{.Name}}", "--cpus", "{{user `cpus`}}"]
    ]
  }, {
    "type": "vmware-iso",
    "guest_os_type": "centos-64",
    "iso_url": "{{user `mirror`}}/7/isos/x86_64/CentOS-7-x86_64-NetInstall-1611.iso",
    "iso_checksum": "{{user `iso_checksum`}}",
    "iso_checksum_type": "{{user `iso_checksum_type`}}",
    "output_directory": "output-centos-7.3-x86_64-{{build_type}}",
    "vm_name": "packer-centos-7.3-x86_64",
    "disk_size": "{{user `disk_size`}}",
    "headless": "{{user `headless`}}",
    "http_directory": "http",
    "boot_wait": "5s",
    "boot_command": [
      "<esc>",
      "<wait>",
      "linux inst.gpt inst.ks=http://{{.HTTPIP}}:{{.HTTPPort}}/centos-7.3/anaconda-ks.cfg biosdevname=0 net.ifnames=0",
      "<enter>" 
    ],
    "ssh_timeout": "{{user `ssh_timeout`}}",
    "ssh_username": "{{user `ssh_username`}}",
    "ssh_password": "{{user `ssh_password`}}",
    "tools_upload_flavor": "linux",
    "shutdown_command": "systemctl poweroff",
    "vmx_data": {
      "memsize": "{{user `memory`}}",
      "numvcpus": "{{user `cpus`}}" 
    }
  }],
  "variables": {
    "cpus": "1",
    "disk_size": "40000",
    "headless": "false",
    "iso_checksum": "f2f7367deb90a25822947660c71638333ca0eceeabecc2d631be6cd508c24494",
    "iso_checksum_type": "sha256",
    "memory": "1024",
    "mirror": "http://mirrors.kernel.org/centos",
    "ssh_timeout": "60m",
    "ssh_username": "root",
    "ssh_password": "s0m3p4ss" 
  }
}

... and the kickstart file:

install
text
reboot
url --mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os
lang en_US.UTF-8
keyboard --vckeymap=de-nodeadkeys --xlayouts='de (nodeadkeys)'
timezone Europe/Berlin --isUtc --nontp
auth --enableshadow --passalgo=sha512
rootpw --iscrypted $6$6yzbOJJy5auuBh9$XUAtAt/ErLkz6.1t8J4UpyZPPUbKjGO1uATaZaxmG02IhJbOwnJMqI6MTJw.SzbBvy8THCmmoia1tVMfXhGVJ1
clearpart --none --initlabel
bootloader --location=mbr --boot-drive=sda
part biosboot --fstype="biosboot" --ondisk=sda --size=1
part / --fstype="ext4" --ondisk=sda --grow --label=SLX_SYS

%packages
@^minimal
@core
kexec-tools
%end

%post --erroronfail
yum -y update
yum -y install wget
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
rpm -ivh epel-release-latest-7.noarch.rpm
yum -y install ansible
# allow root login for ansible
sed 's,^[[:blank:]]*#*PermitRootLogin.*,PermitRootLogin yes,g' /etc/ssh/sshd_config

# instruct the VM to do DHCP on reboot
ln -s /dev/null /etc/udev/rules.d/80-net-name-slot.rules
cat > /etc/sysconfig/network-scripts/ifcfg-eth0 <<EOF
DEVICE="eth0" 
BOOTPROTO="dhcp" 
ONBOOT="yes" 
TYPE="Ethernet" 
EOF
%end

Note that this kickstart sets up the EPEL repo needed to install ansible and, more interestingly, triggers an update to (hopefully) get the latest kernel.
Without an update during the initial installation phase, the VM's kernel might be outdated.
Thus, one would need to update packages and reboot the VM to make sure the latest kernel is running.
Since a package update is mandatory anyway, it makes sense to trigger it as early as possible to avoid unnecessary reboots.

Password management

The naive way of specifying the password in cleartext is suboptimal for obvious reasons.
Since both preseed and kickstart support hashed passwords, these should be used whenever possible!

Packer requires the password in cleartext in its templates to be able to connect to the VMs after starting them.
Instead of using static passwords in the templates saved on the repo (requiring the users to change them before building them), the password can be given per environment variable and evaluated within a packer template with:

(...)
"ssh_password":"{{ env `ROOTPW` }}" 
(...)

Preseed

For distros supporting preseed, it can be specified directly in the kernel command line with passwd/root-password:

(...)
"passwd/root-password={{ user `ssh_password` }} ",
"passwd/root-password-again={{ user `ssh_password` }} ",
(...)

Kickstart

Kickstart has no way (that I know of) to configure the root password via kernel command line.
Still, the password can be passed by packer via boot command (here as rootpw) and post-processed by a script:

(...)
%pre --interpreter=/usr/bin/python
import shlex, crypt
arg = 'rootpw='
with open('/proc/cmdline', 'r') as f:
  kcl = f.read().split()
# extract the password
passwords = [x[len(arg):] for x in kcl if x.startswith(arg)]
if len(passwords) == 1:
  kclpass = passwords[0]
# generate SHA512 hash
hash = crypt.crypt(kclpass, crypt.mksalt(crypt.METHOD_SHA512))
with open('/tmp/setup-root-pass', 'w') as f:
  f.write('rootpw --iscrypted ' + hash)
%end
# include the created password file
%include /tmp/setup-root-pass

The trick is to create an include file with a call of the rootpw directive with a generated SHA-512 hash of the given password.
Including the generated file (/tmp/setup-root-pass) would then instruct kickstart to use the given password as root password.

Updated by Jonathan Bauer about 7 years ago · 3 revisions