Creating a generalized image in Azure isn’t what it used to be. That’s a good thing.
When Azure initially released functionality a few years ago to allow users to customize and generalize their own OS images the process that had to be followed was a bit convoluted and tedious to complete. Things improved when ‘Resource Management’ was introduced, but the VM creation process still involved leveraging Azure Storage and had some pitfalls that still seemed to overly complicate what should be a simple process.
With the introduction of managed disks also came a new set of ‘imaging’ related cmdlets which provide additional simplification to the generalization process for both Windows and Linux. The New-AzureRmImage and New-AzureRmImageConfig cmdlets allow an engineer to easily create a new image that can then be redeployed as needed.
Below is a sample script which provides functionality for creating, deploying and managing OS images in Azure. Please note that the script does not ‘Sysprep’ the VM (or in the case of Linux see the “sudo waagent -deprovision+user” command) in order to prepare it for the imaging process.
Simple, easy to script against, easy to reuse. Exciting to see these improvements, looking forward to seeing what comes next.
<#
.SYNOPSIS
Written By John Lewis
Ver 1.0
Generalizes image captures of Windows and Linux that have been 'sysprepd' to a new Azure Managed Image in an Azure Resource Group. Provides ability to then redeploy the image to a new VM, as well as the capability to list and remove images from the Resource Group.
.PARAMETER action
.PARAMETER generalizevmname
.PARAMETER rg
.PARAMETER newvmname
.PARAMETER newimagename
.PARAMETER Location
.PARAMETER removeimagename
.PARAMETER deployimagename
.PARAMETER newvmpip
.PARAMETER vnet
.PARAMETER subnet
.EXAMPLE
.LINK
#>
[CmdletBinding(DefaultParameterSetName = 'default')]
Param(
[Parameter(Mandatory=$False,ValueFromPipelinebyPropertyName=$true)]
[ValidateSet("generalizeonly","createimage","removeimage","deployvmfromimage")]
[string]
$action = 'deployvmfromimage',
[Parameter(Mandatory=$False,ValueFromPipelinebyPropertyName=$true)]
[string]
$rg = "",
[Parameter(Mandatory=$False,ValueFromPipelinebyPropertyName=$true)]
[string]
$Location = 'westus',
[Parameter(Mandatory=$False,ValueFromPipelinebyPropertyName=$true)]
[string]
$generalizevmname = 'vm1',
[Parameter(Mandatory=$False,ValueFromPipelinebyPropertyName=$true)]
[string]
$newvmname = 'vm2',
[Parameter(Mandatory=$False,ValueFromPipelinebyPropertyName=$true)]
[string]
$newimagename = 'vmimg',
[Parameter(Mandatory=$False,ValueFromPipelinebyPropertyName=$true)]
[string]
$removeimagename = '',
[Parameter(Mandatory=$False,ValueFromPipelinebyPropertyName=$true)]
[string]
$deployimagename = 'vmimg',
[Parameter(Mandatory=$False,ValueFromPipelinebyPropertyName=$true)]
[string]
$newvmpip = 'vmimg',
[Parameter(Mandatory=$False,ValueFromPipelinebyPropertyName=$true)]
[string]
$vnet = 'vnet',
[Parameter(Mandatory=$False,ValueFromPipelinebyPropertyName=$true)]
[string]
$subnet = "default"
)
Function generalize-VM {
Write-Host "Stopping VM $generalizevmname"
Stop-AzureRmVM -ResourceGroupName $rg -Name $generalizevmname -Force
Write-Host "Setting generalized flag on $generalizevmname"
Set-AzureRmVM -ResourceGroupName $rg -Name $generalizevmname -Generalized
}
Function create-image {
Write-Host "Preparing to create new image $newimagename"
$vm = Get-AzureRmVM -Name $generalizevmname -ResourceGroupName $rg
$image = New-AzureRmImageConfig -Location $Location -SourceVirtualMachineId $vm.ID
Write-Host "Creating image $newimagename"
New-AzureRmImage -Image $image -ImageName $newimagename -ResourceGroupName $rg
}
Function Deploy-Img {
Write-Host "Deploying $newvmname from image: $deployimagename"
New-AzureRmVm -ResourceGroupName $rg -Name $newvmname -ImageName $deployimagename -Location $Location -VirtualNetworkName $vnet -SubnetName $subnet -PublicIpAddressName $newvmpip -OpenPorts 22
}
Function List-Img {
$images = Find-AzureRMResource -ResourceType Microsoft.Compute/images
$images.name
}
Function Remove-Img {
Remove-AzureRmImage -ImageName $removeimagename -ResourceGroupName $rg
}
Function flow-actions {
switch ($action)
{
"generalizeonly" {
generalize-VM
}
"createimage" {
generalize-VM
create-image
}
"deployvmfromimage" {
Deploy-Img
}
"removeimage" {
Remove-Img
}
default{"An unsupported command was used"}
}
}
List-Img
flow-actions