You love PowerShell, have a custom VM image (specialized or generalized) and you want to create a new virtual machine from it under Azure Resource Manager API? You're at the right place...
Prerequisites
- Microsoft Azure PowerShell v0.9.4 or later, I used 0.9.7
- A custom VM image captured using the Azure Resource Manager API (more detail just below)
Things to know before you get started
Here is few items you need to be aware before you continue...
The custom VM image need to have been captured using a v2 Compute resource provider
VM images captured from a v1 Compute resource provider (Microsoft.ClassicCompute) cannot be used to create virtual machines using a v2 Compute resource provider (Microsoft.Compute) or the other way around.
If you need a new image after reading this, see Step by Step: How to capture your own custom virtual machine image under Azure Resource Manager API in the References section.
The Custom VM image need to reside in the same storage account as your virtual machine at creation time
Another important limitation is that the image need to be present at creation time in the very same storage account that will host your virtual machine's vhd. The custom VM image can be deleted afterward without problems.
It's not that big thing if you are using a standard storage account. On the other hand, if you want your virtual machine to run on premium storage, leaving that P10 (or bigger) custom VM image around, doing nothing, will cost you the same money per month as any other premium storage disk. For more details, see Azure Storage Pricing link in the References section.
Creating a virtual machine from the custom image
Now the fun part... In a basic example, you'll see how to reference your custom image in the VM creation process using PowerShell cmdlets. Ready?
We assume the storage account already exists since you are supposed to have your custom VM image already in it at this point.
First we need to authenticate against Azure, use the desired subscription and storage account.
Add-AzureAccount
Switch-AzureMode AzureResourceManager
Select-AzureSubscription -SubscriptionId $subscriptionId
$storageAccount = Get-AzureStorageAccount -Name $storageAccountName
We create the networking resources for the VM
# Create the VNET
$vnetDef = New-AzureVirtualNetwork -ResourceGroupName $resourceGroupName -Location $location -Name $vnetName -AddressPrefix '10.0.0.0/16'
$vnet = $vnetDef | Add-AzureVirtualNetworkSubnetConfig -Name 'Subnet-1' -AddressPrefix '10.0.0.0/24' | Set-AzureVirtualNetwork
# Create the NIC
$pip = New-AzurePublicIpAddress -ResourceGroupName $resourceGroupName -Location $location -Name $ipName -DomainNameLabel $domName -AllocationMethod Dynamic
$nic = New-AzureNetworkInterface -ResourceGroupName $resourceGroupName -Location $location -Name $nicName -PublicIpAddressId $pip.Id -SubnetId $vnet.Subnets[0].Id
Now we can create the VM configuration and glue it all together. Pay special attention to the -SourceImageUri
parameter of the Set-AzureVMOSDisk
cmdlet, this is where we specify the location of the custom VM image to use.
# Specify the VM name and size
$vm = New-AzureVMConfig -VMName $vmName -VMSize $vmSize
# Specify local administrator account, and then add the NIC
$cred = New-Object PSCredential $adminUsername, ($adminPassword | ConvertTo-SecureString -AsPlainText -Force)
$vm = Set-AzureVMOperatingSystem -VM $vm -Windows -ComputerName $vmName -Credential $cred -ProvisionVMAgent -EnableAutoUpdate
$vm = Add-AzureVMNetworkInterface -VM $vm -Id $nic.Id
# Specify the OS disk
$diskName = 'osdisk'
$osDiskUri = '{0}vhds/{1}{2}.vhd' -f $storageAccount.PrimaryEndpoints.Blob.ToString(), $vmName.ToLower(), $diskName
$vm = Set-AzureVMOSDisk -VM $vm -Name $diskName -VhdUri $osDiskUri -CreateOption fromImage -SourceImageUri $sourceImageUri -Windows
Sample script
Here is a basic but complete script, without much error handling to reduce complexity... Please note that this script deploy a Windows machine, replace the -Windows
switch for the -Linux
switch in the Set-AzureVMOperatingSystem
& Set-AzureVMOSDisk
cmdlets if you want to deploy a Linux machine.
# We'll use a couple of variable here, fill these with your own values
$subscriptionId = '' # Your SubscriptionId
$storageAccountName = '' # Storage account name where your custom image is and where your VM vhd will go
$sourceImageUri = '' # custom VM image blob uri, ex: 'https://vmcapturetest.blob.core.windows.net/system/Microsoft.Compute/Images/mytemplates/template-osDisk.187d9455-535b-48b4-b10d-8370ec9bad42.vhd'
# end of custom variables
# Authenticate against Azure and cache subscriptions data
Add-AzureAccount
# Switch to the Resource Manager mode, this will be deprecated really soon...
Switch-AzureMode AzureResourceManager
# Switch subscription
Select-AzureSubscription -SubscriptionId $subscriptionId
# Get the storage account
$storageAccount = Get-AzureStorageAccount -Name $storageAccountName
# Enable verbose output and stop on error
$VerbosePreference = 'Continue'
$ErrorActionPreference = 'Stop'
# some reserved script variables
$resourceGroupName = $storageAccount.ResourceGroupName
$location = $storageAccount.Location
$adminUsername = 'VmAdministrator'
$adminPassword = '123!SomeUnSecurePassword!098'
$vmSuffix = Get-Random -Minimum 10000 -Maximum 99999
$vmName = 'VM{0}' -f $vmSuffix
$vmSize = 'Standard_D3'
$nicName = 'VM{0}-NIC' -f $vmSuffix
$ipName = 'VM{0}-IP' -f $vmSuffix
$domName = 'vm-from-customimage-powershell-{0}' -f $vmSuffix
$vnetName = $vmName
# Create the VNET
Write-Verbose 'Creating Virtual Network'
$vnetDef = New-AzureVirtualNetwork -ResourceGroupName $resourceGroupName -Location $location -Name $vnetName -AddressPrefix '10.0.0.0/16'
Write-Verbose 'Adding subnet to Virtual Network'
$vnet = $vnetDef | Add-AzureVirtualNetworkSubnetConfig -Name 'Subnet-1' -AddressPrefix '10.0.0.0/24' | Set-AzureVirtualNetwork
# Create the NIC
Write-Verbose 'Creating Public IP'
$pip = New-AzurePublicIpAddress -ResourceGroupName $resourceGroupName -Location $location -Name $ipName -DomainNameLabel $domName -AllocationMethod Dynamic
Write-Verbose 'Creating NIC'
$nic = New-AzureNetworkInterface -ResourceGroupName $resourceGroupName -Location $location -Name $nicName -PublicIpAddressId $pip.Id -SubnetId $vnet.Subnets[0].Id
# Specify the VM name and size
Write-Verbose 'Creating VM Config'
$vm = New-AzureVMConfig -VMName $vmName -VMSize $vmSize
# Specify local administrator account, and then add the NIC
$cred = New-Object PSCredential $adminUsername, ($adminPassword | ConvertTo-SecureString -AsPlainText -Force) # you could use Get-Credential instead to get prompted
# NOTE: if you are deploying a Linux machine, replace the -Windows switch with a -Linux switch.
$vm = Set-AzureVMOperatingSystem -VM $vm -Windows -ComputerName $vmName -Credential $cred -ProvisionVMAgent -EnableAutoUpdate
$vm = Add-AzureVMNetworkInterface -VM $vm -Id $nic.Id
# Specify the OS disk
$diskName = 'osdisk'
$osDiskUri = '{0}vhds/{1}{2}.vhd' -f $storageAccount.PrimaryEndpoints.Blob.ToString(), $vmName.ToLower(), $diskName
# NOTE: if you are deploying a Linux machine, replace the -Windows switch with a -Linux switch.
$vm = Set-AzureVMOSDisk -VM $vm -Name $diskName -VhdUri $osDiskUri -CreateOption fromImage -SourceImageUri $sourceImageUri -Windows
Write-Verbose 'Creating VM...'
$result = New-AzureVM -ResourceGroupName $resourceGroupName -Location $location -VM $vm
if($result.Status -eq 'Succeeded') {
$result
Write-Verbose ('VM named ''{0}'' is now ready, you can connect using username: {1} and password: {2}' -f $vmName, $adminUsername, $adminPassword)
} else {
Write-Error 'Virtual machine was not created sucessfully.'
}
At the end you should have an output that look like this in your PowerShell window
Hope it helped you a bit in your Azure adventure.