Last Update: Sep 04, 2024 | Published: Mar 28, 2016
In today’s Ask the Admin, I’ll show you how to deploy a VM in Azure using the new Resource Manager deployment model and PowerShell.
If you think back to my article Deploy VMs Using Azure Resource Manager on the Petri IT Knowledgebase, you’ll recall that I showed you how to deploy a VM in Azure using the new management portal, which supports a deployment method called Resource Manager.
While Azure Resource Manager (ARM) brings a new level of flexibility to Azure over the classic deployment method, if you followed through the instructions in the link above, you’ll have realized that the process is more complex than before. In fact, ARM reminds me somewhat of deploying VMs in the Amazon cloud, which is more complicated than deploying Azure VMs in classic mode. To get a better understanding of Resource Manager and how it differs from the classic deployment method, see Aidan Finn’s piece A Tale of Two Azures on Petri.
So it should come as no surprise that deploying ARM-based VMs using PowerShell is also considerably more complicated than was previously the case. Each resource must be provisioned manually, unlike using the classic-mode cmdlets, where much of the heavy lifting was automated behind the scenes.
The following script deploys a basic A0 VM in Azure using standard storage, a dynamic public IP address and friendly DNS name. A storage account is also created. The script creates a Resource Group (RG), Virtual Network (VNET), and subnet if resources with the names specified in the script variables are not found in the Azure subscription.
Note that before you can work with ARM in PowerShell, you’ll need to install Microsoft Azure PowerShell 1.0 or later. See Microsoft Releases Azure PowerShell 1.0 on Petri for more details.
First the script needs to log in to ARM, and while most readers will only have one Azure subscription associated with their Microsoft Account, the script selects the first [0] sub listed by the Select-AzureRmSubscription cmdlet.
Login-AzureRmAccount $subs = Get-AzureRmSubscription Select-AzureRmSubscription -TenantId $subs[0].TenantId -SubscriptionId $subs[0].SubscriptionId
Next I set variables required throughout the rest of the script, such as the RG name ($rgName), location ($location), VM name, virtual network name ($vnetName), amongst others. The username and password for the VM are converted to a secure string and system object. The $vmName variable should be given in lowercase with no special characters because I reuse it for the storage account name, which has these restrictions placed upon it.
$rgName ='ContosoSRVs' $location = 'North Europe' $vnetName = 'CONTOSO' $ipAddress = '10.0.0.7' $Range = '10.0.0.0/16' $subNetRange = '10.0.0.0/24' $subnetname = 'Subnet-1' $vmSize = 'Basic_A0' $osSKU = '2012-R2-Datacenter' $vmName = 'contososrv1' $storaccName = $vmName + 'stor' # Usernames and passwords $username = 'srvadmin' $password = 'PassW0rd!' $passwordsec = convertto-securestring $password -asplaintext -force $creds = New-Object System.Management.Automation.PSCredential($username, $passwordsec)
Now let’s create a new RG, if one with the name given in $rgName doesn’t already exist in the Azure subscription:
try { Get-AzureRmResourceGroup -Name $rgName -Location $location -ErrorAction Stop Write-Host 'RG already exists... skipping' -foregroundcolor yellow -backgroundcolor red } catch { New-AzureRmResourceGroup -Name $rgName -Location $location }
In a similar manner, a new storage account will be created. The script is aborted if the storage account name isn’t globally unique, i.e. it must not exist in any Azure subscription.
if (Test-AzureName -Storage $storaccName) { Throw 'Storage account already name exists... aborting' } else { New-AzureRmStorageAccount -Name $storaccName -ResourceGroupName $rgName –Type 'Standard_LRS' -Location $location }
Now we need to set up a VHD file in Azure storage:
$storacct = Get-AzureRmStorageAccount -ResourceGroupName $rgName –StorageAccountName $storaccName $disknameOS = $vmname + 'diskOS' $vhduri = $storacct.PrimaryEndpoints.Blob.OriginalString + 'vhds/${disknameOS}.vhd'
The script then determines the latest available image for Windows Server 2012 R2 Datacenter:
$images = Get-AzureRmVMImage -Location $location -PublisherName 'MicrosoftWindowsServer' -Offer 'WindowsServer' -Skus $osSKU | Sort-Object -Descending -Property PublishedDate
Next up is networking, and here I create a public IP address resource with friendly DNS name, and a subnet object so that a virtual network can be created if one doesn’t already exist with the given name:
$pip = New-AzureRmPublicIpAddress -Name "${vmname}_nic1" -ResourceGroupName $rgName -DomainNameLabel $vmName -Location $location -AllocationMethod Dynamic $subnet1 = New-AzureRmVirtualNetworkSubnetConfig -Name $subnetname -AddressPrefix $subNetRange try { $vnet = Get-AzureRmVirtualNetwork -Name $vnetName -ResourceGroupName $rgName -ErrorAction Stop Write-Host 'VNET already exists... skipping' -foregroundcolor yellow -backgroundcolor red } catch { $vnet = New-AzureRmVirtualNetwork -Name $vnetName -ResourceGroupName $rgName -Location $location -AddressPrefix $Range -Subnet $subnet1 } $subnet = Get-AzureRmVirtualNetworkSubnetConfig -VirtualNetwork $vnet
Here the VM is assigned a Network Interface Card (NIC) resource:
$nic = New-AzureRmNetworkInterface -Name "${vmname}_nic1" -Location $location -ResourceGroupName $rgName -SubnetId $vnet.Subnets[0].Id -PublicIpAddressId $pip.Id
Now we’re ready to provision the VM, using the New-AzureRmVMConfig, Add-AzureRmVMNetworkInterface, Set-AzureRmVMOperatingSystem, Set-AzureRmVMSourceImage, Set-AzureRmVMOSDisk to define the VM’s properties, and New-AzureRmVM to perform the actual deployment job. The New-AzureRmVM cmdlet can take a long time to complete.
$newVM = New-AzureRmVMConfig -Name $vmName -VMSize $vmSize $newVM = Add-AzureRmVMNetworkInterface -VM $newVM -Id $nic.Id Set-AzureRmVMOperatingSystem -Windows -VM $newVM -ProvisionVMAgent -EnableAutoUpdate -Credential $creds -ComputerName $vmname Set-AzureRmVMSourceImage -VM $newVM -PublisherName $images[0].PublisherName -Offer $images[0].Offer -Skus $images[0].Skus -Version $images[0].Version Set-AzureRmVMOSDisk -VM $newVM -Name $disknameOS -VhdUri $vhduri -Caching ReadWrite -CreateOption fromImage New-AzureRmVM -ResourceGroupName $rgName -Location $location -VM $newVM -Verbose
Finally, the script outputs to the console the URL for connecting to the new VM using RDP:
$rdpVM = get-azurermvm -ResourceGroupName $rgName -Name $vmName $rdpString = $vmName + '.' + $rdpVM.Location + '.cloudapp.azure.com:3389' Write-Host 'Connect to the VM using the URL below:' -foregroundcolor yellow -backgroundcolor red Write-Host $rdpString
You can now use this script to quickly deploy VMs, and associated resources, in Azure without having to wade through the wizards in the web management portal. Keep a look out on Petri for some forthcoming articles on how to deploy domain controllers and member servers using PowerShell ARM.