As you may have seen from some of my other posts, I’ve recently been doing more work with Azure Local. It’s an area that’s developed significantly in recent times, and one I am seeing lots of interest in. Previously I wrote about the use of GPU-P, and in this post, I wanted to cover off the use of the Azure Image Builder (AIB) and Packer with Azure Local too. I know many people will be using AIB or Packer for images in Azure, so wanted to explore this in a little more detail.
Please note, this post assumes you already have images deployed with the Azure Image Builder or Packer, into a Compute Gallery. A brief overview with Packer is provided below to get you up and running if not, but please do consult documentation, as the focus here is on Azure Local. Also, please note: this is just my way of doing this, other methods, options, and ways of achieving this are available!
Why are Custom Images important for Azure Local?
There are a few key reasons why custom images are important for Azure Local (and Azure in general!) – I’ve summarised a few below:
- You may have customised images, with security tooling, specific configurations, or applications, and having these available on Azure Local will be key to deploying new services quickly and in line with existing deployments elsewhere.
- Whilst you can deploy images from the Public Marketplace easily for AVD to Azure Local, having custom images configured with your own applications and settings is key to deploying AVD – and critically, allows deployment of custom images to both Azure Local deployments and Azure Public Regions.
- Being able to manage images across Azure and Azure Local in a unified way is also important – not just for ongoing management, but also for continuity and DR – for example if there is an outage locally, being able to scale up in the Public Azure Regions, with images containing your applications or configurations would be very useful.
- Automation and Pipelines also play a key part here – managing images in this way provides standardisation and repeatability, which are key for scale.
Preparing an Image and Gallery for Testing
Note – if you already have an image ready, you can skip this step!
To get up and running quickly, use the Azure CLI Commands below to create a Packer Image and Build Resource Group, and a Compute Gallery to store the image in. This is a process I’ve used before, when needing to create an image to use within Azure – you could also be using AIB or working with images created manually too.
az group create -n packer-images -l uksouth az group create -n packer-build -l uksouth az sig create --resource-group packer-images --gallery-name jwblog01 az sig image-definition create --resource-group packer-images --gallery-name jwblog01 --gallery-image-definition win11-24h2-avd --publisher yourname --offer youroffer --sku yoursku --os-type windows --os-state Generalized
If you need a test template to use with Packer – you can download one from my GitHub here.
We then need to create a Service Principal for Packer to use – note there are a few ways to authenticate using Packer, I’m using a Service Principal here to get up and running quickly, but in production you’ll want to consider the options available based on your environment and needs.
az ad sp create-for-rbac --name packer --role contributor --scopes /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx --query "{ client_id: appId, client_secret: password, tenant_id: tenant }"
This then provides an output, from which we can add the client_id, client_secret, and subscription_id to our Packer configuration file:
"client_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "client_secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "subscription_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
Once we have run packer build filename.json we will have an image ready to go – here’s mine:
We now have an image in a Compute Gallery – as this is a Windows 11 Multi Users image, this would be ideal for Azure Virtual Desktop within the Azure Cloud Regions as an example let’s move onto getting this to Azure Local too.
Extending Images to Azure Local
At the current time – you can only add images from the following locations to Azure Local, and the Compute Gallery we would normally use in the Azure Public Regions, is not one of those options:
However, the good news is we can tweak the image build to resolve this… we could tweak our process and run the builder on Azure Local, but my preference would be to create 1 image, in our Compute Gallery, and use it across multiple locations, so let’s explore that! You can also read more about the process of using images within Storage Accounts here.
My view on this, is that if we can do this programmatically in a way that can be automated via CLI and Pipelines, this is the best option 😊
So, what would this process look like?
We can start by confirming our Image Version is in the Compute Gallery:
az sig image-version list --resource-group packer-images --gallery-name jwblog01 --gallery-image-definition win11-24h2-avd -o table
The output confirms our Image is stored as expected:
Next, we need to create a Disk from this Image – we can do this with the following Azure CLI Commands:
$source = "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/packer-images/providers/Microsoft.Compute/galleries/jwblog01/images/win11-24h2-avd/versions/1.0.0" az disk create --resource-group packer-images --location uksouth --name win11-24h2-avd --gallery-image-reference $source
We then have an Unattached Disk as a standalone resource, which has been created from our Image in the Compute Gallery:
Microsoft has a process for copying Disks to Storage Accounts – see here: https://learn.microsoft.com/en-us/azure/virtual-machines/scripts/virtual-machines-powershell-sample-copy-managed-disks-vhd.
You’ll need to add your own environment values here in the below script:
#Provide the subscription Id of the subscription where managed disk is created $subscriptionId = "yourSubscriptionId" #Provide the name of your resource group where managed is created $resourceGroupName ="yourResourceGroupName" #Provide the managed disk name $diskName = "yourDiskName" #Provide Shared Access Signature (SAS) expiry duration in seconds e.g. 3600. #Know more about SAS here: https://docs.microsoft.com/en-us/Az.Storage/storage-dotnet-shared-access-signature-part-1 $sasExpiryDuration = "3600" #Provide storage account name where you want to copy the underlying VHD of the managed disk. $storageAccountName = "yourstorageaccountName" #Name of the storage container where the downloaded VHD will be stored $storageContainerName = "yourstoragecontainername" #Provide the key of the storage account where you want to copy the VHD of the managed disk. $storageAccountKey = 'yourStorageAccountKey' #Provide the name of the destination VHD file to which the VHD of the managed disk will be copied. $destinationVHDFileName = "yourvhdfilename" #Set the value to 1 to use AzCopy tool to download the data. This is the recommended option for faster copy. #Download AzCopy v10 from the link here: https://docs.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10 #Ensure that AzCopy is downloaded in the same folder as this file #If you set the value to 0 then Start-AzStorageBlobCopy will be used. Azure storage will asynchronously copy the data. $useAzCopy = 0 # Set the context to the subscription Id where managed disk is created Select-AzSubscription -SubscriptionId $SubscriptionId #Generate the SAS for the managed disk $sas = Grant-AzDiskAccess -ResourceGroupName $ResourceGroupName -DiskName $diskName -DurationInSecond $sasExpiryDuration -Access Read #Create the context of the storage account where the underlying VHD of the managed disk will be copied $destinationContext = New-AzStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $storageAccountKey #Copy the VHD of the managed disk to the storage account if($useAzCopy -eq 1) { $containerSASURI = New-AzStorageContainerSASToken -Context $destinationContext -ExpiryTime(get-date).AddSeconds($sasExpiryDuration) -FullUri -Name $storageContainerName -Permission rw azcopy copy $sas.AccessSAS $containerSASURI }else{ Start-AzStorageBlobCopy -AbsoluteUri $sas.AccessSAS -DestContainer $storageContainerName -DestContext $destinationContext -DestBlob $destinationVHDFileName }
Once this process is done – we have an image in our Storage Account! Here’s mine:
Note – the copy operation runs in the background, so you may need to leave this running for a while before attempting to deploy it to your Azure Local environment.
Finally, we can deploy this to our Azure Local environment – guidance on this process is available here if you wish to do this programmatically.
Via the Azure Portal, this is the same process as normal, we browse to VM Images, then select “from Storage Account” – and we can then select our Customised Image in a Storage Account, and this will then be copied to the Azure Local deployment:
Once the copy process is completed – we can use our custom image!
Testing our Image
To test our image, I created a test VM on Azure Local. Based on the Packer configuration used, we know that this image should have 7Zip and FSLogix installed… so let’s log on and confirm that:
Conclusion
As you can see, with this process we can utilise built images across the Azure Public Cloud Regions and our own Azure Local deployments – I hope this has been useful!