Updating Packer Builds to Use Managed Disks

A few months back we looked at how we can use a tool called Packer to automate the creation of virtual machine images, which could then be used with VM Scale Sets. If this is something you are interested in doing, I would recommend taking a look at the original article, which walks you through the whole process.

At the end of that article, I said we were next going to look at how we can use continuous integration to build this image on a regular schedule, or when applications are updated. I’ve been a bit lax on creating this article, but you will see that soon. Before we do that we need to update our Packer process a bit. In the original article, we used Packer to create images in the VHD format, stored in a storage account, as this was the only way to do it at the time. Things have moved on now, and Packer now supports managed disks and managed disk images, which is what you want to be using for all your Azure disks.

In this article, we are going to update the process to switch to using managed disks.

Current Process

Let’s remind ourselves of the Packer script we used in the previous example (if you’re not familiar with Packer terms, please do read the previous post). In this script we have:

  • An Azure Builder to Create the VM
  • A PowerShell Provisioner to setup WinRM
  • A DSC Provisioner to install the required software
  • A final PowerShell Provisioner to run Sysprep

Here’s the full script:

{
    "builders": [
        {
            "type": "azure-arm",
            "client_id": "<this is the ApplicationID from your service principle>",
            "client_secret": "<this is the password from your service principle>",
            "object_id": "<object ID for the service principle>",
            "tenant_id": "<ID of the Azure AD tenant your SP is in>",
            "subscription_id": "<subscription ID you want the VM and images created in>",
            "resource_group_name": "<name of the resource group you created to store images>",
            "storage_account": "<name of the storage account you created to store images>",
            "capture_container_name": "<name of the storage container, in your storage account, you created to store images>",
            "capture_name_prefix": "<prefix to use for your images, up to you>",
            "os_type": "Windows",
            "image_publisher": "MicrosoftWindowsServer",
            "image_offer": "WindowsServer",
            "image_sku": "2016-Datacenter",
            "communicator": "winrm",
            "winrm_use_ssl": "true",
            "winrm_insecure": "true",
            "winrm_timeout": "3m",
            "winrm_username": "packer",
            "location": "West Europe",
            "vm_size": "Standard_DS2"
        }
    ],
    "provisioners": [
        {
            "type": "powershell",
            "inline": [
                "WINRM QuickConfig -q"
            ]
        },
        {
            "type": "dsc",
            "manifest_file": "DSCConfiguration.ps1",
            "configuration_name": "WebServer",
            "install_package_management": true,
            "install_modules": {
                "xPSDesiredStateConfiguration": "6.4.0.0"
            },
            "configuration_params": {
                "-PackageLocation": "https://urlformyfiles.blob.core.windows.net/files",
                "-AppliationName": "App1"
            }
        },
        {
            "type": "powershell",
            "inline": [
                "if( Test-Path $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml ){ rm $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml -Force}",
                "& $Env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /shutdown /quiet"
            ]
        }
    ]
}

We don’t need to make changes to any of the provisioners, as the change does not impact these (they all occur inside the VM). So we are just going to be looking to change the builder.

Updated Process

To switch to managed disks, we need to make the following changes to the Azure Builder:

  • Add a managed_image_name parameter that contains the name of the managed image we want to create
  • Add a managed_image_resource_group_name parameter that contains the name of the resource group we want to store the image in
  • Remove the following parameters, as these all relate to the VHD and storage account, which we will no longer be using.
    • resource_group_name
    • storage_account
    • capture_container_name
    • capture_name_prefix
  • We’re also going to remove the object_id field, this is not related to managed images, but is no longer required as Packer works it out from the App ID.

Image Naming

As mentioned, we need to pass in the managed_image_name parameter, to name the image we are creating. This process differs from the one used to create VHDs, which required a name prefix, rather than a full name, Packer would then add on suffix to keep the name unique. The process to create a Managed Image does not add this suffix, so we need to do that in our name parameter. The easiest them to keep images unique and identifiable is to add a date to the end. We can use the Packer template language to do this. The “isotime” function allows us to return the current date and format it as required, note the formatting uses a magic date of “Mon Jan 2 15:04:05 -0700 MST 2006”, hence that being used in the format string.

"managed_image_name": "app1OSImage-{{isotime \"200601020304\"}}"

This command creates a managed image with the name of “app1OSImage-201901021259” (given today’s date of the 2nd Jan 2019).

New Script

When we put all those changes together, our new script looks like this:

{
    "builders": [
        {
            "type": "azure-arm",
            "client_id": "<this is the ApplicationID from your service principle>",
            "client_secret": "<this is the password from your service principle>",
            "tenant_id": "<ID of the Azure AD tenant your SP is in>",
            "subscription_id": "<subscription ID you want the VM and images created in>",
            "managed_image_name": "app1OSImage-{{isotime \"200601020304\"}}",
            "managed_image_resource_group_name": "app1ResourceGroup",
            "os_type": "Windows",
            "image_publisher": "MicrosoftWindowsServer",
            "image_offer": "WindowsServer",
            "image_sku": "2016-Datacenter",
            "communicator": "winrm",
            "winrm_use_ssl": "true",
            "winrm_insecure": "true",
            "winrm_timeout": "3m",
            "winrm_username": "packer",
            "location": "West Europe",
            "vm_size": "Standard_DS2"
        }
    ],
    "provisioners": [
        {
            "type": "powershell",
            "inline": [
                "WINRM QuickConfig -q"
            ]
        },
        {
            "type": "dsc",
            "manifest_file": "DSCConfiguration.ps1",
            "configuration_name": "WebServer",
            "install_package_management": true,
            "install_modules": {
                "xPSDesiredStateConfiguration": "6.4.0.0"
            },
            "configuration_params": {
                "-PackageLocation": "https://urlformyfiles.blob.core.windows.net/files",
                "-AppliationName": "App1"
            }
        },
        {
            "type": "powershell",
            "inline": [
                "if( Test-Path $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml ){ rm $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml -Force}",
                "& $Env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /shutdown /quiet"
            ]
        }
    ]
}

Running a Build

We can run this script in the same way as we did previously:

packer build script.json

Once the script runs through you will see a slightly different output, as you are now provided with the resource ID for the Managed image, rather than the URL of the VHD.

2019-01-02_14-57-24

If you look in the resource group, you provided as the destination for the image you will now see a managed image object has been created.

2019-01-02_14-51-25

We can now use this image to create a new VM or create a scale set. Once you can successfully create a managed image using this script your ready to look at using CI to build the image, which will look at in the next article.