I was asked to make up a short demo video about creating a custom image for deployment in Microsoft Azure the other week to support some new content going into Microsoft Learn. Since this involves making an on-prem virtual machine first and then preparing it to upload into Azure – I figured I would make a new Windows Server 2019 CORE image (default deployment option) instead of a full desktop.
I always found it strange you didn't have an option to deploy a Core server deployment of Windows Server from Azure Marketplace.
** UPDATE **
@Rick Claus wrote:
... I figured I would make a new Windows Server 2019 CORE image (default deployment option) instead of a full desktop. I always found it strange you didn't have an option to deploy a Core server deployment of Windows Server from Azure Marketplace.
Time to fix that.
Shout out to ALL the MVPs (and other friends) who pointed out my statement about "Core images not being available in Azure Marketplace" being incorrect and keeping me honest. I was working on old info from my days in the Azure Compute group where I worked next to the team who built and published images for Windows Server. I thought it would be a good subtext storyline starting the blog post instead of just a click-walk-through on the actual meat of the article with is about making a Custom Image and bringing it to Azure. Brings up a good discussion point:
Why SHOULD you use an image in Marketplace over a Custom image.
and when would it make MORE sense to use Custom image
** back to the original blog post **
Why Windows Server Core? Windows Server Core implementations are:
I thought this was going to be a simple process that was documented in a single doc. Little did I know that the info I needed was spread across three different official docs as well as some good old trial and error. To save you time - I’ve pulled everything together and have the main steps here, but include links back to the source documents in case you want more detailed information.
The TL;DR of this process is the following:
From there – you can make a new VM from that custom uploaded image. Management wise – the deployed image is compatible with Azure Bastion, Just-In-Time remote access, Remote PowerShell or PowerShell commands via the Azure Portal.
Lets get started!
Build a Hyper-V VM image of Windows Server 2019 with a Core interface.
This should be self-explanatory. You have a Server that is running Hyper-V and you can make a new Windows Server 2019 VM using the install media (ISO file) for Windows Server 2019. The default install experience is to have a CORE install (i.e. no desktop experience) and you create a new password for the local administrator account to further configure the system. To keep things simple – I created a Generation 1 VM initially to do the install and for the most part kept the defaults for the base creation process.
I don’t know what it is, but I really like the simple logon for a Windows Server core box – if I have to logon to the console at all. I need to do some tasks from the Hyper-V host before customizing the local VM – so I’ll shut it down for now.
Configure specific settings unique to uploading a Custom VM Image for Azure
For this example, I am taking this base image as is, and doing the recommended configuration changes as per the “Prepare a Windows VHD or VHDX to upload to Azure”. These include:
At this point the document goes through an extensive list of checks and settings you should review and implement in your base image in order to ensure a smooth deployment. I am not going to list them all off here – but refer you to the document to follow:
Note: You will get some errors based on if your image is domain joined or if there are group policies in place. I got a number of red error dumps from PowerShell commands, but they were expected since my VM is not domain joined.
OK – we’re ready to go, no turning back now.
Generalize your local VM image and shut it down
You have prepared your machine, set it up for optimal Azure compatibility and you have it tested for remote connectivity. Time to Generalize it with good old sysprep.exe. Logon to the box and change to the c:\windows folder. You can save a bit of space (or a lot of space if this image was an upgrade) by deleting the c:\windows\panther directory. Once that’s done, change into c:\windows\system32\sysprep folder and then run sysprep.exe.
Make sure you check the Generalize checkbox and choose to Shutdown instead of Reboot.
OK – you are all set for an UPLOAD to Azure now.
Upload VHD into a new Azure Managed Disk in your Azure Subscription
NOTE: I only ever use Managed Disks for my virtual machines now, since it saves me from having to architect a strategy around how many VM disks can be in each storage account before maxing out my throughput OR having issues with storage cluster failures… Just keep it simple and promise me you will always use Azure Managed Disks for your VMs.
You will already need to have a ResourceGroup in azure that you can store these VM images in and you will want to define the location for the storage group to be in the same area where you will be using this image. I assume you are using the same system where the VHD is located OR you have copied it to your admin workstation locally before uploading it.
On this system – you will need to ensure you have the latest version of AzCopy v10 installed and the Azure PowerShell modules installed. We’re following the procedures outlined in the “Upload a VHD to Azure” document.
To upload the image – you first have to create an empty standard HDD managed disk in your pre-created ResourceGroup that is the same size as your soon to be uploaded VHD. These example commands will get your VHD disk size and set the configuration parameters required for making a disk. In order for this to work, you will need to replace <fullVHDFilePath>, <yourdiskname>, <yourresourcegroupname>, and <yourregion> from the example below with your information.
$vhdSizeBytes = (Get-Item "<fullVHDFilePath>").length $diskconfig = New-AzDiskConfig -SkuName 'Standard_LRS' -OsType 'Windows' -UploadSizeInBytes $vhdSizeBytes -Location '<yourregion>' -CreateOption 'Upload' New-AzDisk -ResourceGroupName '<yourresourcegroupname>' -DiskName '<yourdiskname>' -Disk $diskconfig
In my example, the complete commands were:
$vhdSizeBytes = (Get-Item "C:\vms\ContosoVM2.vhd").length $diskconfig = New-AzDiskConfig -SkuName 'Standard_LRS' -OsType 'Windows' -UploadSizeInBytes $vhdSizeBytes -Location 'eastus' -CreateOption 'Upload' New-AzDisk -ResourceGroupName ContosoResourceGroup -DiskName ContosoVM2 -Disk $diskconfig
Next You need to grant SAS access to the empty disk
$diskSas = Grant-AzDiskAccess -ResourceGroupName ContosoResourceGroup -DiskName ContosoVM2 -DurationInSecond 86400 -Access 'Write' $disk = Get-AzDisk -ResourceGroupName ContosoResourceGroup -DiskName ContosoVM2
Now Upload the local VHD file to the Azure Managed Disk. Don’t forget to replace the <fullVHDFilePath> with your local VHD filePath
AzCopy.exe copy "<fullVHDFilePath>" $diskSas.AccessSAS --blob-type PageBlob
Once the AzCopy command completes, you need to revoke the SAS access in order to change the state of the manage disk and enable the disk to function as an image for deployment.
Revoke-AzDiskAccess -ResourceGroupName ContosoResourceGroup -DiskName ContosoVM2
Create a VM Image for deployment using the Azure Managed Disk
OK – final stretch. You’ve made a Windows Server 2019 Core image locally, prepared it for use in Azure, generalized it and uploaded it into you Azure subscription as a Managed Disk. Now you have to identify that managed disk as a VM Image that can be deployed. We’re following our third document on this called “Upload a generalized VHD and use it to create new VMs in Azure”.
$disk = Get-AzDisk -ResourceGroupName ContosoResourceGroup -DiskName ContosoVM2
$location = 'East US' $imageName = 'ContosoVM2Image' $rgName = 'ContosoResourceGroup'
$imageConfig = New-AzImageConfig -Location $location $imageConfig = Set-AzImageOsDisk -Image $imageConfig -OsState Generalized -OsType Windows -ManagedDiskId $disk.Id
$image = New-AzImage -ImageName $imageName -ResourceGroupName $rgName -Image $imageConfig
And with that – we are finally DONE.
If you open up the Azure portal and explore what is in that resource group where you uploaded the VHD – you should see something similar to what I see in this portal screenshot: a simple VHD uploaded and an Image definition that you can use to deploy new VMs.
In this blog post, the custom local VM that was created was a Windows Server 2019 core install server that was customized, generalized, uploaded and converted into a specialized Image for use in Azure. Because I took the time to build my own custom image and upload it into my Azure subscription - I can deploy as many Windows Server 2019 core boxes as I need for my projects now.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.