About Archive Other Contact

How to create an image for Azure with Packer

Cloud and automation are hot in the infrastructure space these days. As such I am constantly looking at how to automate things for FreeBSD. HashiCorp provides some very cool and good Cloud Infrastructure Automation tools. The nice thing is that these are all available for FreeBSD! One such tool is Packer. It is an open source tool for creating identical machine images for multiple platforms from a single source configuration.

This post shows how to create a customized FreeBSD image with Packer for Azure. And all is done from a FreeBSD box!


The following requirements have to be in place to be able to implement what is described in this post:

Install Packer

Our first step is to install Packer:

$ sudo pkg install packer

Configure Azure credentials for Packer

Packer authenticates with Azure using a service principal. An Azure service principal is a security identity that you can use with apps, services, and automation tools like Packer. I've issued the commands below to create an Azure service principle for Packer.

First sign in with the Azure CLI using an account with administrator privileges on the subscription:

$ read -sp "Azure password: " varAzurePass && echo && az login -u  -p $varAzurePass

To create our service principle we do:

$ az ad sp create-for-rbac --name "packer" \
  --query "{ client_id: appId, client_secret: password, tenant_id: tenant }"

The output looks something like:

Retrying role assignment creation: 1/36
Retrying role assignment creation: 2/36
  "client_id": "...",
  "client_secret": "...",
  "tenant_id": "..."

These 3 items are needed in our Packer configuration in the next chapter. But before we go, we need 1 more item for our Packer configuration. And that is the id of our Azure subscription:

$ az account show --query "{ subscription_id: id }"

with output like:

  "subscription_id": "..."

Now we have all the input needed for Packer to authenticate with Azure.

Create the Packer configuration file

An example and working Packer configuration file for a FreeBSD image is shown below. The file is called freebsd.json and is put in the directory packer in the home directory of a normal user we are using.

Packer has lots of possibilities and far from all of them have been used here. This configuration:

The freebsd.json Packer configuration file contents are:

   "variables": {
      "var_ssh_user": "giveitaname",
      "var_ssh_pass": "A!diFfiCult?pa$sw0rD",
      "var_location": "North Europe",
      "var_vm_size": "Standard_B1S"
   "builders": [{
      "type": "azure-arm",

      "client_id": "...",
      "client_secret": "...",
      "tenant_id": "...",
      "subscription_id": "...",

      "managed_image_resource_group_name": "packer-image-rg",
      "managed_image_name": "packer-freebsd",

      "ssh_username": "{{user `var_ssh_user`}}",
      "ssh_password": "{{user `var_ssh_pass`}}",

      "os_type": "Linux",
      "image_publisher": "MicrosoftOSTC",
      "image_offer": "FreeBSD",
      "image_sku": "11.2",
      "image_version": "11.2.20180829",

      "location": "{{user `var_location`}}",
      "vm_size": "{{user `var_vm_size`}}"
   "provisioners": [
         "type": "shell",
         "scripts": [
        "execute_command": "chmod +x {{ .Path }}; /bin/sh -c '{{ .Vars }} {{ .Path }}'"
         "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'",
         "inline": [
            "/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"
         "inline_shebang": "/bin/sh -x",
         "type": "shell",
         "skip_clean": "true",
         "expect_disconnect": "true"

The content of the base.sh shell script:

set -ex

echo A!diFfiCult?pa$sw0rD/n | sudo -S sh -c 'freebsd-update \
     --not-running-from-cron fetch install || :'
echo A!diFfiCult?pa$sw0rD/n | sudo -S sh -c 'pkg update'
echo A!diFfiCult?pa$sw0rD/n | sudo -S sh -c 'pkg install -y py27-salt'
echo A!diFfiCult?pa$sw0rD/n | sudo -S sh -c 'pkg upgrade -y'

Please note:

Run Packer to create the FreeBSD image

First we cd into the packer directory

$ cd packer

and then we validate our Packer configuration file:

$ packer validate freebsd.json

and if all goed well it produces the following output:

Template validated successfully.

And then (at last) we can build our image using the command:

$ packer build freebsd.json

This will produce some output and if all goes well the end will look something like:

Build 'azure-arm' finished.

==> Builds finished. The artifacts of successful builds are:
--> azure-arm: Azure.ResourceManagement.VMImage:

OSType: Linux
ManagedImageResourceGroupName: packer-image-rg
ManagedImageName: packer-freebsd
ManagedImageId: /subscriptions/.../resourceGroups/packer-image-rg/providers/\
ManagedImageLocation: northeurope

Possible next steps

You can do lots of stuff with this newly created image. HashiCorp has some more amazing tools like i.e. Terraform (also available on FreeBSD!). Terraform enables you to safely and predictably create, change, and improve infrastructure. It also integrates with Azure and it is able to use the image we just created to deploy FreeBSD virtual machines on Azure!

That is maybe something for another time and blog post!


Some (other) resources about this subject:

Updated: November 1, 2018