Auto-Tagging Azure resources with tags from parent Resource Group

I’ve always enjoyed analyzing Azure Cost usage and patterns and have successfully helped 2 clients so far get a deeper understanding of their azure cost by exposing the data in a nice PowerBI dashboard.

As everyone who’s played with this before know, the key to good cost control and insights is good tagging.

So once I had the tagging strategy established we went ahead and tagged all the Resource Groups in the subscription(s). Once that was done it was time to push those tags down to the resources.

To do that, I decided the quickest (?) way to get there was to write a nice little power shell script to apply tags set at a Resource Group level, to its child resources.

Without further ado, here’s the full script:


$tagNames = "Environment","Department","BusinessUnit"

$rgNames = az group list --query [].name --output tsv
$rgNames | foreach {
$rgName = $_
$rgResources = az resource list -g $rgName --query [].id --output tsv
if($rgResources.GetType().Name -eq 'String') {
$rgResources = $rgResources.split([Environment]::NewLine)

$allTags = (az group show --name $rgName --query tags -o json) | ConvertFrom-Json
if($allTags.SkipAutoTagging -eq $null) {
$tagValues = New-Object HashTable
$tagNames | where { $allTags.$_ -ne $null } | foreach { $tagValues.$_ = $allTags.$_ }
$rgResources | foreach { Update-AzTag -ResourceId $_ -Tag $tagValues -Operation Merge }

#$tagValues = $tagNames | where { $allTags.$_ -ne $null } | foreach { "tags.$_=$($allTags.$_)" }
#$rgResources | foreach { az resource update --set $tagValues --ids $_ --output none}


Now, let’s unpack that script a bit and see what it’s doing.

The first thing you might notice is that it’s using both the az CLI as well as the Az PowerShell module - in fact, you can exclusively use the az CLI, see the last 2 commented lines at the end. The reason I chose the AZ module is because it runs lot faster than deploying with the az command.

The parameters you need to send to each command is slightly different - hence the 2 different approaches for the $tagValues variable. The AZ module needs a dictionary of tagName-tagValue pairs, whereas the az CLI takes them as inline name=value strings (i.e. tags.Environment=test tags.Product=myProduct). Pro tip: pay close attention to the spacing.

The other notable thing is that for the Update-AzTag cmdlet to work, you need to be on the latest version (1.13 at the time of writing) so make sure you update the modules using Update-Module Az.Resources command. You can check the version you’re on using Get-Module -ListAvailable Az.Resources

It’s always fun to play with PowerShell scripts, you absolutely learn something new every time you try it. This time, I learned that az resource list returns an array of strings if the group has more than one resource, or it returns a string - not an array with one string - if the group has only one resource inside. To get around that, check my little clever trick of checking the result type and using a split on newline to create an array out of my string.

Lastly, I threw in a small little bonus feature, since this script is meant to be run at a subscription level and update all your Resrouce Gruoups, there might sometimes be the case that you want to skip one or two, or more resrouce groups from being handled. For that purpose, I “invented” a special tag called “SkipAutoTagging”, if your Resource Group is “decorated” with a tag named like that, that group will not be processed by the script.

I hope you find this useful, or at least can reuse parts of it for other purposes in the future.

Share Comments