Bulk tagging of Azure resources with PowerShell

Recently I came across a situation wherein we need to apply tags (multiple tags) to many resoruces. When I say many resoruces it means multiple subscription (25 Azure subscritpions) and in each of these subscription we have at least 500+ resources. The requirement becomes complex when we add multiple tags simultaneously. I want to achieve it in such a way that this becomes configurable and extensible solution which avoids any code change. It should work by updating the configuration.

I wrote two scripts for it. First one is Discovery script which discovers all the resoruces from all the subscriptions. Now once you have discovered all the resources from Azure you can tweaked the spreadsheet by adding or deleting the resources and this spreadhsheet will become the input for the second script which actually tags the resources. Now let’s understand these scripts:

Azure Resource Discovery script

Discovery script discovers all the resoruces from all the subscriptions and dump the info into a spreadhsheet. The name of the spreadsheet contains the current date. Here is the script.

connect-azAccount
$date = Get-Date -UFormat("%m-%d-%y")
$currentDir = $(Get-Location).Path
$oFile = "$($currentDir)\List_Of_All_Azure_Resources_$($date).csv"

if(Test-Path $oFile){
    Remove-Item $oFile -Force
}

"SUBSCRIPTION_NAME,SUBSCRIPTION_ID, RESOURCE_GROUP_NAME,RESOURCE_NAME,RESOURCE_TYPE,TAGS" | Out-File $oFile -Append -Encoding ascii

Get-AzSubscription | ForEach-Object{
    $subscriptionId = $_.Id
    $subscriptionName = $_.Name
    
    Set-AzContext -SubscriptionId $subscriptionId
    Get-AzResourceGroup | ForEach-Object{
        $resourceGroupName = $_.ResourceGroupName
        Get-AzResource -ResourceGroupName $resourceGroupName | ForEach-Object{
            $resourceName = $_.Name
             
            $resourceType = $_.ResourceType
            
            if(!([string]::IsNullOrEmpty($_.Tags))){
                $tags = @()
                $_.Tags.GetEnumerator() |ForEach-Object {
                    [string]$tags += $_.key+ "=" + $_.value+ ";"
                }
            }
            else{
                $tags = ""
            }
            
            "$subscriptionName,$subscriptionId,$resourceGroupName,$resourceName,$resourceType,$tags" | Out-File $oFile -Append -Encoding ascii
        }
    }
}

This script will create the csv file containing the list of all the resources. Here is the sample:

Azure Resource Tagging script

This script takes the inputs from csv file generated by the discovery script and you can add /delete or update the values in the spreadsheet based on your requirement. Once the spreadsheet it ready, you can create csv file which contains comma separated list of tags. This script will take this csv file as another input. Here is the tag csv file:

Here is the sample tag csv file

Here is the script we have used to apply the tags provided in the csv to the discovered resoruces from earlier script.

Connect-AzAccount 
# This can be modified based on your requirement.
$TagFilePath="C:\tags\inventory\tags.csv"

#You can modify the path here based on your requirements.
$ResourceToTagFilePath="C:\tags\inventory\List_Of_All_Azure_Resources_05-25-21.csv"
 
function convertCsvToHashTable($csvFile){
    $csv=import-csv $csvFile
    $headers=$csv[0].psobject.properties.name
    $key=$headers[0]
    $value=$headers[1]
    $hashTable = @{}
    $csv | % {$hashTable[$_."$key"] = $_."$value"}
    return $hashTable
}
 $TagsHashTable=@{}
$TagsHashTable=convertCsvToHashTable $TagFilePath
  $csv = import-csv $ResourceToTagFilePath

$csv | ForEach-Object {
    Write-Host " $($_.RESOURCE_NAME), $($_. RESOURCE_GROUP_NAME), $($_.SUBSCRIPTION_ID ) "
   Set-AzContext $_.SUBSCRIPTION_ID

   $resource = Get-AzResource -Name $_.RESOURCE_NAME -ResourceGroup $_.RESOURCE_GROUP_NAME
   Update-AzTag -ResourceId $resource.id -Tag $TagsHashTable -Operation Merge
 }

Here is how it looks when you applied the tags into the resource.

Hope this helps.

13 Comments

  1. Hi Rajaniesh. Thanks for this. This has saved me heaps of time.
    It seems to work about 95% of the time. Certain resources like Application Insights seem to throw this error:
    “Update-AzTag : Cannot convert ‘System.Object[]’ to the type ‘System.String’ required by parameter ‘ResourceId’. Specified method is not supported.
    At line:26 char:29
    + Update-AzTag -ResourceId $resource.id -Tag $TagsHashTable -Operati …
    + ~~~~~~~~~~~~
    + CategoryInfo : InvalidArgument: (:) [Update-AzTag], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgument,Microsoft.Azure.Commands.Tags.Tag.UpdateAzureTagCommand”
    Did you run into this issue?
    Thanks

    Like

  2. Hi rajaniesh, Thank you so much for your article and script. I have been tasked to tag many resources! Trying to get this working but bit confused. I have success exporting the existing tag csv file but not sure how to create tags.csv file and what should I modify the exported csv. please help. if possible please share a sample file in the email. Thank you in advance _/\_

    Like

  3. Hi, Thank you so much for the script and the article. I have managed to get this working to export csv file but not sure how to create tags.csv and how to get the tag updated. if you have any sample csv file please share. Thank you so much in advance. Tx

    Like

  4. Excellent. Thank you so much for your help. you are a savior! It worked!! I owe you a drink. please send me your paypal id (you can email it to me). I will sponsor you a drink! Thank you bro again.

    Like

  5. Hi Rajaniesh.

    I think I found the issue. It looks to be when you have resources that were badly named, 2 resources being of different types but having the same name. It seems to occur when it attempts the calling of “Update-AzTag…” on line 26. The root cause seems to be that “$resource = Get-AzResource…” on line 26 seems to pick up multiple resources as an array if there is more than 1 resource of different types but with the same name. When it then tries to pass the array object into “Update-AzTag…” on line 26 it blows up.

    I assume a way to handle this in the script would probably be to output the Resource id’s and then when calling “$resource = Get-AzResource…” on line 26 looking up via resource Id instead of Name.

    Thanks for this post. It was extremely helpful.

    Like

Leave a Reply to Manikandan Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.