Microsoft Azure

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.

Sponsored Content

What is “Inside Microsoft Teams”?

“Inside Microsoft Teams” is a webcast series, now in Season 4 for IT pros hosted by Microsoft Product Manager, Stephen Rose. Stephen & his guests comprised of customers, partners, and real-world experts share best practices of planning, deploying, adopting, managing, and securing Teams. You can watch any episode at your convenience, find resources, blogs, reviews of accessories certified for Teams, bonus clips, and information regarding upcoming live broadcasts. Our next episode, “Polaris Inc., and Microsoft Teams- Reinventing how we work and play” will be airing on Oct. 28th from 10-11am PST.

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.


$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 = '' $Range = '' $subNetRange = '' $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 + '' 
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.

Related Topics:


Don't have a login but want to join the conversation? Sign up for a Petri Account

Comments (0)

Leave a Reply

IT consultant, Contributing Editor @PetriFeed, and trainer @Pluralsight. All about Microsoft, Office 365, Azure, and Windows Server.
External Sharing and Guest User Access in Microsoft 365 and Teams

This eBook will dive into policy considerations you need to make when creating and managing guest user access to your Teams network, as well as the different layers of guest access and the common challenges that accompany a more complicated Microsoft 365 infrastructure.

You will learn:

  • Who should be allowed to be invited as a guest?
  • What type of guests should be able to access files in SharePoint and OneDrive?
  • How should guests be offboarded?
  • How should you determine who has access to sensitive information in your environment?

Sponsored by: