Recently I came across a situation wherein we need to apply tags (multiple tags) to many resources. When I say many resources it means multiple subscriptions (for example 25 Azure subscriptions) and in each of these subscriptions, 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 a configurable and extensible solution that avoids any code change. It should work by updating the configuration.
I wrote two scripts for it. The first one is the Discovery script which discovers all the resources from all the subscriptions. Now once you have discovered all the resources from Azure you can tweak it by adding or deleting the resources and this will become the input for the second script which actually tags the resources. Now let’s understand these scripts:
Azure Resource Discovery script
The discovery script discovers all the resources from all the subscriptions and dumps the info into a CSV File. 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 the 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 is ready, you can create a CSV file that contains the list of tags. This script will take this CSV file as another input. Here is the sample tag CSV file:
Another sample tag file:
Here is the script we have used to apply the tags provided in the CSV to the discovered resources from the earlier script.
Connect-AzAccount # This can be modified based on your requirement. $TagFilePath="$($currentDir)\tags.csv" #You can modify the path here based on your requirements. $ResourceToTagFilePath="$($currentDir)\List_Of_All_Azure_Resources_$($date).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 to the resource.
What if I want to tag all the Resource groups?
Sometimes you have to tag only Resource Groups instead of tagging individual resources inside Resource Groups. There may be certain scenarios where you would like to tag empty Resource Groups. In order to achieve it, I have created a separate Discovery script and tagging script. Here is the Discovery script. First, run this discovery script. It will create the CSV file List_Of_All_Azure_Resources_Today_Date.csv. This CSV file will contain all the subscriptions along with Resource Groups and you can update it.
Here is the discovery 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" | Out-File $oFile -Append -Encoding ascii Get-AzSubscription | ForEach-Object{ $subscriptionId = $_.Id $subscriptionName = $_.Name Set-AzContext -SubscriptionId $subscriptionId Get-AzResourceGroup | ForEach-Object{ $resourceGroupName = $_.ResourceGroupName "$subscriptionName,$subscriptionId,$resourceGroupName" | Out-File $oFile -Append -Encoding ascii } }
Now once you have the CSV file ready you can run the following tag script to tag the Resource Groups.
Connect-AzAccount # This can be modified based on your requirement. $TagFilePath="$($currentDir)\tags.csv" #You can modify the path here based on your requirements. $ResourceToTagFilePath="$($currentDir)\List_Of_All_Azure_Resources_$($date).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_GROUP_NAME), $($_.SUBSCRIPTION_ID ) " Set-AzContext $_.SUBSCRIPTION_ID $resource = Get-AzResourceGroup -Name $_.RESOURCE_GROUP_NAME write-Host "$resource.ResourceId" Update-AzTag -ResourceId $resource.ResourceId -Tag $TagsHashTable -Operation Merge }
Hope this helps.