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…

 

Just a tip #3 – $args in PowerShell

In scripting, there are many things to experience in many ways, the traditional way is always the best practice though, the formal way is always an option…

Traditional way


Function Add
{
param
(
[Parameter(Mandatory=$true)]
<span></span>[int] $Number1,
[Parameter(Mandatory=$true)]
[int] $Number2
)
[int] $Sum = 0
$Sum = $Number1 + $Number2
return, $Sum
}

Add -Number1 4 -Number2 5

Add tip 1

Formal way


<span></span>$Add = {$args[0] + $args[1]}
. $Add 4 5

Add tip 2

SQL Server PSObject – Working with SQL Server using PowerShell

SQL Server loves PowerShell, it makes SQL Server DBA life easy and simple. I have seen SQL Server automated with PowerShell to an extent where I stopped using SQL Server Management Studio (SSMS) ever since I started using PowerShell. Database Administrator doesn’t require SSMS all the time to connect to SQL Server if you are accompanying with PowerShell. There are quite a few tools are already available in the internet from dbatools.io, idera PowerShell scripts and etc., but every approach is unique.

SQL Server PSObject, SQL Server functionalities within a single PowerShell object. PSObject, I love the most in PowerShell, you can customise the object of your own choice of properties & methods, and the usage is also as simple as just initiate the object and call the methods of your choice.

I have created a new PSObject with ConnectSQL and ExecuteSQL methods, they are the very basic and predominant functionalities to work with SQL Server.


# Create an object
$SQLServerObject = New-Object -TypeName psobject

And added few essential properties, mainly used to establish the connection to Sql Server…


# Basic properties
$SQLServerObject | Add-Member -MemberType NoteProperty -Name ServerName -Value 'SQLServer' # Server Name
$SQLServerObject | Add-Member -MemberType NoteProperty -Name DefaultPort -Value 1433 # Port
$SQLServerObject | Add-Member -MemberType NoteProperty -Name Database -Value 'master' # Database
$SQLServerObject | Add-Member -MemberType NoteProperty -Name ConnectionTimeOut -Value 15 # Connection Timeout
$SQLServerObject | Add-Member -MemberType NoteProperty -Name QueryTimeOut -Value 15 # Query Timeout
$SQLServerObject | Add-Member -MemberType NoteProperty -Name SQLQuery -Value '' # SQL Query
$SQLServerObject | Add-Member -MemberType NoteProperty -Name SQLConnection -Value '' # SQL Connection

The properties like ServerName, Port, Database and ConnectionTimeout are must to define before you call either connect method or execute method, SQLConnection property holds the sql server connection object to execute the sql queries with execute method. SQLQuery property holds the query text to execute the query against the sql server mentioned in the ServerName property, you can also enter the query while calling the execute method. Ensure the Server is ping-able usign the TestConnection method…


# Method to ensure the server is pingable
$SQLServerObject | Add-Member -MemberType ScriptMethod -Name TestConnection -Value {
Test-Connection -ComputerName $this.ServerName -ErrorAction SilentlyContinue
}

Establish the connection and store the connection object in the SQLConnection property of the object.


# Method to establish the connection to SQL Server and holds the connection object for further use
$SQLServerObject | Add-Member -MemberType ScriptMethod -Name ConnectSQL -Value {

[string] $ServerName= $this.ServerName
[int] $Port = $this.DefaultPort
[string] $Database = $this.Database
[int] $TimeOut = $this.ConnectionTimeOut

$SQLConnection = New-Object -TypeName System.Data.SqlClient.SqlConnection
$SQLConnection.ConnectionString = "Server = $ServerName,$Port; Database = $Database; Integrated Security = True;Connection Timeout=$TimeOut;"
$SQLConnection.Open()

$this.SQLConnection = $SQLConnection
}

ExecuteSQL method to execute the queries using the connection established using the ConnectSQL method…


# Execute SQL method to execute queries using the connection established with ConnectSQL
$SQLServerObject | Add-Member -MemberType ScriptMethod -Name ExecuteSQL -Value {

param
(
[Parameter(Mandatory=$false)]
[string] $QueryText
)

# Select runtime query / predefined query
[string] $SQLQuery = $this.SQLQuery
if ([string]::IsNullOrEmpty($QueryText) -eq $false)
{
$SQLQuery = $QueryText
}

# Verify the query is not null and empty, then execute
if ([string]::IsNullOrEmpty($SQLQuery))
{
Write-Host "Please add query to this object or enter the query." -ForegroundColor Red
}
else
{
if ($this.SQLConnection.State -eq 'Open')
{
# SQL Command
$SQLCommand = New-Object System.Data.SqlClient.SqlCommand
$SQLCommand.CommandText = $SQLQuery
$SQLCommand.CommandTimeout = $this.QueryTimeOut
$SQLCommand.Connection = $this.SQLConnection
# SQL Adapter
$SQLAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SQLAdapter.SelectCommand = $SQLCommand
# Dataset
$DataSet = New-Object System.Data.Dataset
$SQLAdapter.Fill($DataSet) | Out-Null
return $DataSet.Tables[0]
}
else
{
Write-Host "No open connection found." -ForegroundColor Red
}
}
}

And finally return the object…


# Return the object
return, $SQLServerObject

Now, lets see how we can connect to sql server and execute the sql queries…

First, create an object…


PS C:\GitRepo> $SQL = .\Create-SQLServerObject.ps1
PS C:\GitRepo> $SQL

Create-SQLServerObject1

By default it takes the localhost name as servername, default sql server port and master database a default database to establish the connection. Assign a server name and test the connectivity…


PS C:\GitRepo> $SQL.ServerName = 'SQLServer'
PS C:\GitRepo> $SQL.TestConnection()

Create-SQLServerObject2

If the server is accessible, then establish the connection to the SQL Server, if the sql server port is other than default port, then assign the port to the object…


PS C:\GitRepo> $SQL.DefaultPort = 2866 # Just an example

Establish the connection…


PS C:\GitRepo> $SQL.ConnectSQL()
PS C:\GitRepo> $SQL.SQLConnection

Create-SQLServerObject3

Add query text to the object and call ExecuteSQL method…


PS C:\GitRepo> $SQL.SQLQuery = "Select database_id,name from sys.databases"
PS C:\GitRepo> $SQL.ExecuteSQL()

Create-SQLServerObject4

You can also enter the query while calling the method itself…


PS C:\GitRepo> $SQL.ExecuteSQL("Select @@Version as Version")

Create-SQLServerObject5

You can add any number of methods of your choice and customise as per your requirements, you can also execute the *.sql files as well…

Create-SQLServerObject6

The complete code is available in my git repository https://github.com/kpatnayakuni/PowerShell/blob/master/Create-SQLServerObject.ps1

Thank you.

PowerShell Integrated Console on VSCode – Show On Startup

I preferably use Visual Studio Code for my PowerShell scripting and it has the PowerShell extension along with intellisense and syntax highlighting, PowerShell integrated console as well, however I will use Windows PowerShell to execute the commands. VSCode has an option to show the PowerShell console on startup by default, and it annoys me a bit, so I decided to disable that option…

Open VSCode, go to File -> Preferences -> Settings or click on Manage VSCode-Manage button on Side Bar then select Settings or simply use the shortcut ‘Ctrl+,’ then search for ‘PowerShell Integrated Console Show On Startup’ option in the settings and you will see the option below, just uncheck the option highlighted below to diable the console from your next start.

VSCode-ShowOnStartup

Retieve Azure VM public ip and establish the RDP session through PowerShell

I don’t know for some reason Microsoft doesn’t provide some solutions directly, directly as in cannot achieve the outcome with a single command in PowerShell, for example to get the public ip address of an Azure VM, in fact I expected it to be as simple as this…


Get-AzureRmPublicIpAddress -ResourceGroupName lab-rg -Name Workstation

However there is no such command with those parameters, but still it’s not very complicated. I have written a small PowerShell wrapper to retrieve the public ip address of Azure VM and added few more functionalities as well apart from getting only the public ip address it will start the VM if it is not running by enabling the ‘-StartIfVMIsNotRunning’ flag and connect to RDP session with ‘-ConnectRDP’ flag.

Note: Most of the organizations use either private ip or dns name to connect to the VM from their network, and this is only useful for small businesses or where there is no need of domain authentication and access from outside the local network.

The script has two parameter sets ‘Name’ and ‘Object’, which accepts ResourceGroupName and VMName or VMObject along with the other common parameters, and the parameters are as below…


[Parameter(Mandatory=$true,ParameterSetName='Name')]
[string] $ResourceGroupName, # ResourceGroup Name when the ParameterSetName is 'Name'
[Parameter(Mandatory=$true,ParameterSetName='Name')]
[string] $VMName, # Virtual Machine Name when the ParameterSetName is 'Name'
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ParameterSetName='Object')]
[Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine]
$VMObject, # VM Object when the ParameterSetName is 'Object'
[Parameter(Mandatory=$false,ParameterSetName='Name')]
[Parameter(Mandatory=$false,ParameterSetName='Object')]
[switch] $StartIfVMIsNotRunning, # Start the VM, if it is not running
[Parameter(Mandatory=$false,ParameterSetName='Name')]
[Parameter(Mandatory=$false,ParameterSetName='Object')]
[switch] $ConnetRDP, # Connect Remote Desktop Session
[Parameter(Mandatory=$true,ParameterSetName='Help')]
[switch] $H # Get Help

Since the latest AzureRM (6.13.1) PowerShell module has some significant changes in the outcome of some CmdLets, ensuring the latest module is loaded…

# Ensure the 6.13.1 version AzureRM module is loaded,
# because some commands output have been changed in this version
[System.Version] $RequiredModuleVersion = '6.13.1'
[System.Version] $ModuleVersion = (Get-Module -Name AzureRM).Version
if ($ModuleVersion -lt $RequiredModuleVersion)
{
Write-Verbose -Message "Import latest AzureRM module"
break
}

Login into Azure account, if not logged in already…


# Login in into the Azure account, if it is not already logged in
if([string]::IsNullOrEmpty($(Get-AzureRmContext)))
{
$null = Add-AzureRmAccount
}

Retrieve the VM running state and ensure it is running, if ‘-StartIfVMIsNotRunning’ flag is enabled then the VM will be started if it is not running. If VM is not running and ‘PublicIPAllocationMethod’ is set to static then still public ip can be retrieved, but if it is dynamic then the VM should be in running state itself…


# Retrieve the virtual machine running status
try
{
if ($PSCmdlet.ParameterSetName -eq 'Name')
{
[Microsoft.Azure.Commands.Compute.Models.PSVirtualMachineInstanceView] $VM = Get-AzureRmVM -ResourceGroupName $ResourceGroupName -Name $VMName -Status
}
elseif ($PSCmdlet.ParameterSetName -eq 'Object')
{
[Microsoft.Azure.Commands.Compute.Models.PSVirtualMachineInstanceView] $VM = Get-AzureRmVM -ResourceGroupName $VMObject.ResourceGroupName -Name $VMObject.Name -Status
}
}
catch
{
Write-Verbose -Message $_.Exception.Message
break
}

# Check whether the vm PowerState is running
[Microsoft.Azure.Management.Compute.Models.InstanceViewStatus] $VMStatus = $VM.Statuses | Where-Object { $_.Code -match 'running' }
if ([string]::IsNullOrEmpty($VMStatus))
{
[bool] $ISVMRunning = $false } else { [bool] $ISVMRunning = $true
}

# If VM is not running and -StartIfVMIsNotRunning flag is enabled, then start the VM
if ($ISVMRunning -eq $false -and $StartIfVMIsNotRunning -eq $true)
{
$null = Start-AzureRMVM -ResourceGroupName $VM.ResourceGroupName -Name $VM.Name
$ISVmRunning = $true
}

Now retrieve the public ip address of an Azure VM…


# Get Public IP address
[Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine] $VirtualMachine = Get-AzureRMVM -ResourceGroupName $VM.ResourceGroupName -Name $VM.Name
[string] $NICId = $VirtualMachine.NetworkProfile.NetworkInterfaces.id
[Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResource] $NICResource = Get-AzureRmResource -ResourceId $NICId
[string] $PIPId = $NICResource.Properties.ipConfigurations.properties.publicIPAddress.id
[Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResource] $PIPResource = Get-AzureRmResource -ResourceId $PIPId
[ipaddress] $PIP = $PIPResource.Properties.ipAddress

Exit the script if the VM is not running and PublicIPAllocationMethod is Dynamic or public ip is not assigned…


# Exit the script if the VM is not running and PublicIPAllocationMethod is Dynamic or public ip is not assigned
[string] $PublicIPAllocationMethod = $PIPResource.Properties.publicIPAllocationMethod
if ([string]::IsNullOrEmpty($PIP.IPAddressToString) -and $ISVMRunning -eq $false -and $PublicIPAllocationMethod -eq 'Dynamic')
{
Write-Verbose -Message $("Since {0} VM is not running and 'Public IP Allocation Method is Dynamic', unable to determine the Public IP.`nRun the command with -StartIfVMIsNotRunning flag" -f $VMName)
break
}
elseif ([string]::IsNullOrEmpty($PIP.IPAddressToString) -and $ISVMRunning -eq $true)
{
Write-Verbose -Message $("No public ip id assigned to this {0} VM." -f $VMName)
break
}

If the ‘-ConnectRDP’ flag is enabled then the remote desktop connection will be established (only when the default port for RDP is allowed in the inbound security rules) otherwise it simply returns the public ip address…


# Connect the VM when -ConnectRDP flag is enabled and VM is running
if ($ConnetRDP -and $ISVMRunning)
{
Invoke-Expression "mstsc.exe /v $($PIP.IPAddressToString)"
break
}

# Just return the IP address when no flags are enabled
return, $PIP.IPAddressToString

And lets see some examples…


.EXAMPLE

C:\GitRepo> .\Get-ARMVMPIP.ps1 -ResourceGroupName lab-rg -Name Workstation
xxx.xxx.xxx.xxx

Returns the public ip address when the VM is running or the VM is deallocated but the publicIPAllocationMethod is set to 'Static'.

.EXAMPLE

C:\GitRepo> $VM = Get-AzureRmVM -ResourceGroupName lab-rg -Name Workstation
C:\GitRepo> $VM | .\Get-ARMVMPIP.ps1
xxx.xxx.xxx.xxx

Returns the public ip address when the VM is running or the VM is deallocated but the publicIPAllocationMethod is set to 'Static'.

.EXAMPLE

C:\GitRepo> .\Get-ARMVMPIP.ps1 -ResourceGroupName lab-rg -Name Workstation -StartIfVMIsNotRunning
xxx.xxx.xxx.xxx

Returns the public ip address when the VM is running or starts the VM if it is not running and returns the public ip.

.EXAMPLE

C:\GitRepo> .\Get-ARMVMPIP.ps1 -ResourceGroupName lab-rg -Name Workstation -ConnectRDP

# Doesn't return any output simply connects to RDP session

Connect to RDP session when the VM is running

.EXAMPLE

C:\GitRepo> .\Get-ARMVMPIP.ps1 -ResourceGroupName lab-rg -Name Workstation -ConnectRDP

# Doesn't return any output simply connects to RDP session

Connect to RDP session when the VM is running and if the VM is not running it will start and establish the RDP session.

The complete code is available in my git repository https://github.com/kpatnayakuni/PowerShell/blob/master/Get-ARMVMPIP.ps1