Deploy VMs Using Azure Resource Manager and PowerShell

Tutorial Hero

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.

Logging in and setting a subscription to use with Azure Resource Manager (ARM). (Image Credit: Russell Smith)
Logging in and setting a subscription to use with Azure Resource Manager (ARM). (Image Credit: Russell Smith)

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.

Deploy VM using PowerShell ARM

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 
}
Create a new Resource Group using PowerShell ARM (Image Credit: Russell Smith)
Create a new Resource Group using PowerShell ARM (Image Credit: Russell Smith)

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.

VM properties in Azure RM (Image Credit: Russell Smith)
VM properties in Azure RM (Image Credit: Russell Smith)
$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
RDP connection string to the new VM (Image Credit: Russell Smith)
RDP connection string to the new VM (Image Credit: Russell Smith)

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.