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

 

Tagging Microsoft Azure Resources using PowerShell (Az)

In Azure Cloud, Tags play a major role to manage resources in an easy way, in an other words Tags are an additional meta data associated with the Azure resources. We can assign the tags to the individual resources like VM, Storage Account, VNet and etc., and we can also assign the tags to the Resource Groups as well. Resource groups allow us to organize the related resources together and facilitate the management, but tags are used to group the resources beyond the resource groups including the resource groups, and at the same time resources inside the resource group do not inherit the tags associated with the resource group. Tags are Key and Value combination that can be assigned to the resources in the Azure cloud, for example…

Tag Key Tag Value
ResourceType VM
Project MyProject
Department Marketing
Environment Production
CostCenterCode 123456

Do bear in mind that each individual resource can have up to 15 tags max (Microsoft keeps updating the numbers time to time, so please refer the Microsoft Docs for the exact number), and ensure the tags are unique and consistent naming convention among Azure resources.

Tags are used to organize the deployed resources in the Azure cloud, we could search the resources by tag key/value, for example search the resources with the tags associated {Key:Value} Type:VM and Environment:Production, then the search results all the production VMs across the resource groups within a subscription. Tags are also used to view the related resources, like all the resources tagged to a specific project or a specific cost center and to facilitate the billing and cost management.

Tags can be created at the time of creating resources or at the later time by using the Azure portal or any command line tools like PowerShell or Azure CLI.

Let’s see how we can create and manage the tags using PowerShell…


#requires -Module Az

# Connect-AzAccount

### Add new tags to an existing resource

# Get the resource
[Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResource] $Resource = Get-AzResource -Name testvm -ResourceGroupName test-rg

# Resource tags
[hashtable] $Tags = $Resource.Tags

# Ensure not to overwrite the tags
if ($null -eq $Tags) {
[hashtable] $Tags = @{Type="VM"; Environment="Test"} }
else {
$Tags += @{Type="VM"; Environment="Test"} }

# Add new tags to the resource (-Force to override the confirmation if there are any existing tags)
Set-AzResource -ResourceId $Resource.Id -Tag $Tags -Force

### Remove an existing tag / remove all tags from a resource

# Get the resource
[Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResource] $Resource = Get-AzResource -Name testvm -ResourceGroupName test-rg

# Resource tags
[hashtable] $Tags = $Resource.Tags

# Remove the specific tag
$Tags.Remove("Type")

# Overwrite the remaing tags to the resource (-Force to override the confirmation if there are any existing tags)
Set-AzResource -ResourceId $Resource.Id -Tag $Tags -Force

## Remove all tags
Set-AzResource -ResourceId $Resource.Id -Tag @{} -Force

### List all the resources with a specific tag key
Get-AzResource -TagName "Environment"

### List all the resources with a specific tag value
Get-AzResource -TagValue "Test"

### List all the resources with a specific tag key and value
Get-AzResource -Tag @{Environment="Test"}

### List all the tags and number of resources associated in a subscription.
Get-AzTag

Github link: https://github.com/kpatnayakuni/PowerShell/blob/master/Demo-Tags.ps1

 

 

Create and assign a public ip to an Azure Virtual Machine using Azure PowerShell (Az).

Sometimes deliberately we don’t create and assign a public ip to an Azure Virtual Machine to not to expose to the internet as a safety measure, but later at some point of time we may require the VM to be accessed via internet and we definitely need a public ip to access the VM, the script below will help to create and assign a public ip address to an Azure VM…

Note: If no Network Security Group is associated with Virtual Machine, by default all ports are open to the internet, and please be careful.


#requires -Module Az

# Function to create and assign a public ip address 
# to an Azure Virtual Machine using Az PowerShell module.
Function Assign-AzVMPublicIP2
{
Param
(
# Resource Group Name
[Parameter(Mandatory=$true)]
[string] $ResourceGroupName,
# Virtual Machine Name
[Parameter(Mandatory=$true)]
[string] $VMName
)
# Retrieve the Virtual Machine details
$VM = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $VMName -ErrorAction SilentlyContinue

# Checking the VM existance
if($null -eq $VM)
{
Write-Error "Please enter a valid and existing Resource Group Name and Virtual Machine Name"
return
}

$Location = $VM.Location # Location to create a public ip
$NICId = $VM.NetworkProfile.NetworkInterfaces.Id # Network Interface resource id
$NICResource = Get-AzResource -ResourceId $NICId # Retrieve the NIC resource details

# Retrive the NIC Object
$NIC = Get-AzNetworkInterface -Name $NICResource.Name -ResourceGroupName $NICResource.ResourceGroupName
$NICIPConfigName = $NIC.ipConfigurations[0].Name # IP Config Name to be used with Set-AzNetworkInterfaceIpConfig CmdLet
$NICSubnetId = $NIC.ipConfigurations[0].subnet.id # Subnet id to be used with Set-AzNetworkInterfaceIpConfig CmdLet

# Create a public ip
$PublicIP = New-AzPublicIpAddress -ResourceGroupName $ResourceGroupName -Location $Location -Name "$VMName-pip" -AllocationMethod Static -IdleTimeoutInMinutes 4

# Warn the user if no NSG is associated with this VM
if ($null -eq $NIC.NetworkSecurityGroup)
{
Write-Warning "Since no Network Security Group is associated with this Virtual Machine, by default all ports are open to the internet."
}

# Assign the public ip to the VM NIC
$NIC | Set-AzNetworkInterfaceIpConfig -Name $NICIPConfigName -SubnetId $NICSubnetId -PublicIpAddressId $PublicIP.Id | Set-AzNetworkInterface
}

Assign-AzVMPublicIP2 -ResourceGroupName test-rg -VMName test-vm

List of available Azure VM Image skus using new Azure PowerShell module Az.

As you might already know, Microsoft has released a new Azure PowerShell module Az to replace with AzureRM module in future. As of now both the versions are available for Windows PowerShell and PowerShellCore. But no further developments for AzureRM module except for bg fixes and all the updates and feature enchantments come along with the new modules Az itself.

Just to start with the Az module, lets retrieve the list of Azure VM Images (skus) available in a given location from the mentioned publisher with the offerings…