Create new Azure VM using PowerShell (Az)

Deploying the Virtual Machines in the Azure cloud using the templates is the best way to create the VM to satisfy the attributes like quick, consistent, reusable and handles the dependency among the resources. However PowerShell has the flexibility to deploy the VMs with ease and allows the user to choose the required parameters necessary for the particular deployment alone without even touching the code. Ofcourse this approach is also quick and reusable, but the user has to ensure the consistency and dependency among the resources if required while creating the resources in the Azure cloud.

Since the new Azure PowerShell Module Az 1.0.1 is released, I have written the scripts using the Az module CmdLets. So please install Az module on Windows PowerShell or PowerShell Core, import the module and connect to Azure account using ‘Connect-AzAccount’.

Add required parameters to the script…

#requires -Modules Az
param
(
[Parameter(Mandatory=$true)]
[string] $ResourceGroupName, # Resource Group
[Parameter(Mandatory=$true)]
[string] $VMName, # VM Name
[Parameter(Mandatory=$true)]
[string] $Location, # Location
[Parameter(Mandatory=$true)]
[ValidateSet('Windows','Linux')]
[string] $OSType, # OS Type (Windows/Linux)
[Parameter(Mandatory=$true)]
[string] $VirtualNetworkName, # VNet
[Parameter(Mandatory=$true)]
[string] $SubnetName, # Subnet
[Parameter(Mandatory=$true)]
[string] $SecurityGroupName, # NSG
[Parameter(Mandatory=$false)]
[string] $VMSize, # VM Size
[Parameter(Mandatory=$false)]
[switch] $AssignPublicIP, # Assign PIP
[Parameter(Mandatory=$false)]
[pscredential]$VMCredential, # VM login credential
[Parameter(Mandatory=$false)]
[Int[]] $AllowedPorts # NSG rules
)

Ensure you are connected to Azure subscription, if the script exits then connect to Azure subscription using Connect-AzAccount CmdLet, and this is a browser-based authentication.

# Verify Login
if( -not $(Get-AzContext) ) { return }

Ensure that there is no existing vm with the same name in the resurce group. If there is a VM already exists then exit the script.

# Verify VM doesn't exist
[Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine] `
$VM = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $VMName -ErrorAction SilentlyContinue
if($null -ne $VM) { return }

Create VM login credentials, if not provided along with the script…

# Create user object
if (-not $PSBoundParameters.ContainsKey('VMCredential'))
{
[pscredential] $VMCredential = Get-Credential -Message 'Please enter the vm credentials'
}

# Verify credential
if ($VMCredential.GetType().Name -ne "PSCredential") { return }

The script identifies the existing resources with the names provided, if exist then they will be used and if they don’t exist then will be created with different names.

Two things that you need to choose based on your requirements, one is the VM Size and the other is OS Image (Sku)…

# Lists all the VM Sizes available in South India region
PS C:\> Get-AzVMSize -Location southindia

To retrieve the OS Skus, I have written an another post ‘List of available Azure VM Image skus using new Azure PowerShell module Az.‘, please refer…

Now the main block starts from here…

Github Link: https://github.com/kpatnayakuni/PowerShell/blob/master/Create-AzVM.ps1


# Verify/Create a resource group
[Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceGroup] $ResourceGroup = Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue
if ($null -eq $ResourceGroup)
{
$ResourceGroup = New-AzResourceGroup -Name $ResourceGroupName -Location $Location
}

# Verify the virtual network
[Microsoft.Azure.Commands.Network.Models.PSVirtualNetwork] $VNet = Get-AzVirtualNetwork -Name $VirtualNetworkName -ResourceGroupName $ResourceGroup.ResourceGroupName -ErrorAction SilentlyContinue
if ($null -eq $VNet)
{
[Microsoft.Azure.Commands.Network.Models.PSSubnet] $SubnetConfig = New-AzVirtualNetworkSubnetConfig -Name $SubnetName -AddressPrefix 192.168.1.0/24
$VNet = New-AzVirtualNetwork -ResourceGroupName $ResourceGroup.ResourceGroupName -Location $Location -Name $VirtualNetworkName -AddressPrefix 192.168.0.0/16 -Subnet $SubnetConfig
}
else 
{
[Microsoft.Azure.Commands.Network.Models.PSSubnet[]] $Subnets = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $VNet
$SubnetConfig = $Subnets | Where-Object -FilterScript {$_.Name -eq $SubnetName}
if ($null -eq $SubnetConfig)
{
$VNetAddressPrefixes = $VNet.AddressSpace.AddressPrefixes
$AddressPrefix = @($VNetAddressPrefixes.Split('.'))
$AddressPrefix[2] = [int]($Subnets.AddressPrefix|Measure-Object -Maximum).Maximum.ToString().Split('.')[2] + 1
$AddressPrefix = $AddressPrefix -join '.'
$VNet | Add-AzVirtualNetworkSubnetConfig -Name $SubnetName -AddressPrefix $AddressPrefix | Set-AzVirtualNetwork
}
}

[Microsoft.Azure.Commands.Network.Models.PSSubnet] $Subnet = Get-AzVirtualNetworkSubnetConfig -Name $SubnetName -VirtualNetwork $VNet

# Create a public IP address and specify a DNS name
if ($PSBoundParameters.ContainsKey('AssignPublicIP'))
{
[string] $PipName = $VMName + '-pip'
[Microsoft.Azure.Commands.Network.Models.PSPublicIpAddress] $VerifyPip = Get-AzPublicIpAddress -Name $PipName -ResourceGroupName $ResourceGroup.ResourceGroupName -ErrorAction SilentlyContinue
if ($null -ne $VerifyPip) 
{ 
$PipName = $VMName + '-pip-' + $(Get-Random).ToString()
}
[Microsoft.Azure.Commands.Network.Models.PSPublicIpAddress] $PublicIP = New-AzPublicIpAddress -ResourceGroupName $ResourceGroup.ResourceGroupName -Location $Location -Name $PipName -AllocationMethod Static -IdleTimeoutInMinutes 4
}

# Create/Select a network security group
[Microsoft.Azure.Commands.Network.Models.PSNetworkSecurityGroup] $NSG = Get-AzNetworkSecurityGroup -Name $SecurityGroupName -ResourceGroupName $ResourceGroup.ResourceGroupName -ErrorAction SilentlyContinue
if ($null -eq $NSG)
{
# Create an inbound network security group rules
if ($PSBoundParameters.ContainsKey('AllowedPorts'))
{
[System.Array] $NsgRules = @()
[int] $Priority = 1000
foreach ($Port in $AllowedPorts)
{
[Microsoft.Azure.Commands.Network.Models.PSSecurityRule] $Rule = New-AzNetworkSecurityRuleConfig -Name "Allow_$Port" -Protocol Tcp -Direction Inbound -Priority $Priority -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange $Port -Access Allow
$Priority++
$NsgRules += $Rule
}
[Microsoft.Azure.Commands.Network.Models.PSNetworkSecurityGroup] $NSG = New-AzNetworkSecurityGroup -ResourceGroupName $ResourceGroup.ResourceGroupName -Location $Location -Name $SecurityGroupName -SecurityRules $NsgRules
}
else
{
$NSG = New-AzNetworkSecurityGroup -ResourceGroupName $ResourceGroup.ResourceGroupName -Location $Location -Name $SecurityGroupName
}
}
else 
{
# Add an inbound network security group rules, if missing any
if ($PSBoundParameters.ContainsKey('AllowedPorts'))
{
[int[]] $NSGAllowedPorts = $NSG.SecurityRules | Where-Object -FilterScript {$_.Access -eq "Allow"} | Select-Object -ExpandProperty DestinationPortRange
[int[]] $PortsToAllow = $AllowedPorts | Where-Object -FilterScript {$_ -notin $NSGAllowedPorts}
[int] $Priority = ($NSG.SecurityRules.Priority|Measure-Object -Maximum).Maximum + 100
if ($PortsToAllow.Count -gt 0)
{
foreach($Port in $PortsToAllow)
{
$NSG | Add-AzNetworkSecurityRuleConfig -Name "Allow_$Port" -Protocol Tcp -Direction Inbound -Priority $Priority -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange $Port -Access Allow | Set-AzNetworkSecurityGroup
}
}
}
}

# Create a virtual network card and associate with public IP address and NSG
[string] $NICName = "$VMName-nic"
[Microsoft.Azure.Commands.Network.Models.PSNetworkInterface] $NIC = Get-AzNetworkInterface -Name $NICName -ResourceGroupName $ResourceGroup.ResourceGroupName -ErrorAction SilentlyContinue
if ($null -ne $NIC)
{
$NICName = $VMName + "-nic-" + $(Get-Random).ToString()
}
[Microsoft.Azure.Commands.Network.Models.PSNetworkInterface] $NIC = New-AzNetworkInterface -Name $NICName -ResourceGroupName $ResourceGroup.ResourceGroupName -Location $Location -SubnetId $Subnet.Id -NetworkSecurityGroupId $NSG.Id 
if ($PSBoundParameters.ContainsKey('AssignPublicIP'))
{
$NIC | Set-AzNetworkInterfaceIpConfig -Name $NIC.IpConfigurations[0].Name -PublicIpAddressId $PublicIP.Id -SubnetId $Subnet.Id | Set-AzNetworkInterface | Out-Null
}

# VM Size
if($PSBoundParameters.ContainsKey('VMSize') -eq $false )
{
$VMSize = 'Standard_A1'
}

# OS Type
[hashtable] $VMSourceImage = @{PublisherName='';Offer='';Sku=''}
switch ($OSType) {
'Windows' { $VMSourceImage.PublisherName = 'MicrosoftWindowsServer'
$VMSourceImage.Offer = 'WindowsServer'
$VMSourceImage.Sku = '2016-Datacenter'
}
'Linux'{
$VMSourceImage.PublisherName = 'Canonical'
$VMSourceImage.Offer = 'UbuntuServer'
$VMSourceImage.Sku = '18.10-DAILY'
}  
}

# Create a virtual machine configuration
[Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine] $VMConfig = New-AzVMConfig -VMName $VMName -VMSize $VMSize 
if ($OSType -eq 'Windows')
{
$VMConfig | Set-AzVMOperatingSystem -Windows -ComputerName $VMName -Credential $VMCredential | Out-Null
}
else 
{
$VMConfig | Set-AzVMOperatingSystem -Linux -ComputerName $VMName -Credential $VMCredential | Out-Null
}
$VMConfig | Set-AzVMSourceImage -PublisherName $VMSourceImage.PublisherName -Offer $VMSourceImage.Offer -Skus $VMSourceImage.Sku -Version latest | Out-Null
$VMConfig | Add-AzVMNetworkInterface -Id $NIC.Id | Out-Null
$VMConfig | Set-AzVMBootDiagnostics -Disable | Out-Null

# Create a virtual machine
New-AzVM -ResourceGroupName $ResourceGroup.ResourceGroupName -Location $Location -VM $VMConfig

To create a Windows VM…


.\Create-AzVM.ps1 -ResourceGroupName test-rg `
-VMName testvm -Location southindia `
-OSType Windows `
-VirtualNetworkName test-vnet `
-SubnetName testnet `
-SecurityGroupName test-nsg `
-AssignPublicIP `
-AllowedPorts 3389 `
-VMCredential $cred `
-Verbose

To create a Linux VM…


.\Create-AzVM.ps1 -ResourceGroupName test-rg `
-VMName testvm -Location southindia `
-OSType Linux `
-VirtualNetworkName test-vnet `
-SubnetName testnet `
-SecurityGroupName test-nsg `
-AssignPublicIP `
-AllowedPorts 22 `
-VMCredential $cred `
-Verbose