Skip to main content

PowerShell + Azure : Validate ResourceGroup Tags


Recently been working on some DevOps stuff in Azure using Python & PowerShell, so would be doing few posts revolving around that.

Why I have added the below pic ?
Python is what I have been picking from the Dev world (currently) and PowerShell is what I have picked from the Ops world.


In Azure Resource Manager, one can add tags to the Resource Groups (check out the new Azure Portal to explore ResourceGroups ). Last week had to script a way to check that there is a Tag on the resource group with a valid set of values. Python Azure SDK doesn't yet support Azure Resource Manager operations so had to turn to the Ops side (PowerShell way).

Don't worry if you have no idea what a tag is, the validation code is pretty neat.


For Example - the Resource Group should have a tag named "Environment" on it with the valid values of "Dev","QA" & "Prod" .


There can be other tags on it but we are looking only for the existence of this tag & values.

Let's get started.
  1. Since Azure Resource Manager cmdlets doesn't support Certificate based authentication. We have to use Azure AD here. First step is to use below cmdlet to add your account using which you login to the Azure Portal.
  2. Add-AzureAccount

  3. Once authenticated , Switch to using Azure resource manager cmdlets using the below cmdlet. So that we get the AzureResourceManager Module loaded.
  4. Switch-AzureMode -Name AzureResourceManager

  5. Now you can see in the PowerShell host that the AzureResourceManager Module is loaded:
  6. PS>Get-Module
    
    ModuleType Version    Name                                ExportedCommands
    ---------- -------    ----                                ----------------
    Manifest   0.9.1      AzureResourceManager                {Add-AlertRule, Add-AutoscaleSetting, Add-AzureAccount, Ad...
    
  7.  Use the cmdlet Get-AzureResourceGroup to get Resource Groups and store them in a variable for later processing.
  8. PS>$ResourceGroups = Get-AzureResourceGroup
  9. Now we can filter Resource Groups which have tags property like below :
    PS>$ResourceGroups | where tags
    
    
    ResourceGroupName : DexterPOSHCloudService
    Location          : southeastasia
    ProvisioningState : Succeeded
    Tags              :
                        Name         Value
                        ===========  =========
                        Environment  QA
                        TestKey      TestValue
    
    ResourceId        : /subscriptions/4359ee69-61ce-430c-b885-4083b2656de7/resourceGroups/DexterPOSHCloudService
    
    ResourceGroupName : dexterseg
    Location          : southeastasia
    ProvisioningState : Succeeded
    Tags              :
                        Name         Value
                        ===========  =======
                        Environment  Testing
    
    ResourceId        : /subscriptions/4359ee69-61ce-430c-b885-4083b2656de7/resourceGroups/dexterseg
    

    Maybe those who don't have the Tags property we can throw a warning but I leave the Scripting logic for you to build upon.
  10. Now out of the 2 Resource groups above one has a valid Environment Tag of value "QA" (in green) but the other one has an invalid tag value of "Testing"  (in yellow).
    Before we start validating the Values we need to check if the Tag contains the desired Environment tag, for this we can use the contains() method.

    But if you look closely you will find something strange with the tags property :
    PS>$ResourceGroups[0].tags
    
    Name                           Value
    ----                           -----
    Value                          LAB
    Name                           Environment
    Value                          TestValue
    Name                           TestKey
    

    Tags property is a hashtable but the keys are "Name" & "Value" literal. Not very intuitive as I thought I would be getting the Environment as one of the key for the hashtable returned, submitted a issue at the GitHub repo for this.
    Until it is fixed we can check if at all our Environment tag is present by using the ContainsValue method on the Hashtable, like below :
    PS>$ResourceGroups[0].tags[0].ContainsValue('Environment')
    True

    Now if the tag has the Environment as the value for 'Name' key then evidently the value for 'Value' key will be the one we are seeking.
    PS>$ResourceGroups[0].tags[0]['Name']
    Environment
    PS>$ResourceGroups[0].tags[0]['Value']
    QA

    Wow ! so much work am already dozing off :P
  11. It appears validating the value is one of our permitted values ('Dev','QA','Prod') is relatively easy using the validateset() parameter attribute, see the below:
    PS>[Validateset('DEV','QA','PROD')]$testme = $ResourceGroups[0].tags[0]['Value']
    PS>[Validateset('DEV','QA','PROD')]$testme = $ResourceGroups[1].tags[0]['Value']
    The attribute cannot be added because variable testme with value LAB would no longer be valid.
    At line:1 char:1
    + [Validateset('DEV','QA','PROD')]$testme = $ResourceGroups[0].tags[0]['Value']
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : MetadataError: (:) [], ValidationMetadataException
        + FullyQualifiedErrorId : ValidateSetFailure
    

    When we decorate the $testme variable with the ValidateSet() attribute and perform the assignment it will throw an exception if the value is not in the set (note that the second resource group in step 5 doesn't have a valid Environment tag), which we can catch later and display a message saying that the Environment tag doesn't have a valid value.

[Update] - Forgot to mention a detail, which I got reminded when I saw a tweet by Stefan Stranger mentioning that the ContainsValue() method on a hashtable is case-sensitive. Workaround to that by MVP Dave Wyatt is in below tweet :



Below is the sample code for one to build upon :
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
Add-AzureAccount
Switch-AzureMode -Name AzureResourceManager

$ResourceGroups = Get-AzureResourceGroup | where Tags

foreach ($ResourceGroup in $ResourceGroups) {  
  if ($ResourceGroup.tags.values.Contains('Environment')) {
    Write-Verbose -Message "$($ResourceGroup.ResourceGroupName) Environment tag not found" -Verbose
        foreach ($Tag in $ResourceGroup.Tags) {
        
            if ($Tag.ContainsValue('Environment')) {
                TRY {
                    [validateset('DEV','QA','PROD')]$testme = $Tag['Value']
                }
                CATCH [System.Management.Automation.ValidationMetadataException] {
                    Write-Error -Message "Environment tag doesn't contain a valid value -->('DEV','QA','PROD')"
                }
            }
        }
    }
    else {
        Write-Warning -Message "$($ResourceGroup.ResourceGroupName) Environment tag not found"
    }    
}



Thanks for reading and that is it for today's post.
~Dex~



Popular posts from this blog

Test connectivity via a specific network interface

Recently while working on a Private cloud implementation, I came across a scenario where I needed to test connectivity of a node to the AD/DNS via multiple network adapters.  Many of us would know that having multiple network routes is usually done to take care of redundancy. So that if a network adapter goes down, one can use the other network interface to reach out to the node. In order to make it easy for everyone to follow along, below is an analogy for the above scenario: My laptop has multiple network adapters (say Wi-Fi and Ethernet) connected to the same network. Now how do I test connectivity to a Server on the network only over say Wi-Fi network adapter?

PowerShell + SCCM : Run CM cmdlets remotely

Today I saw a tweet about using implicit remoting to load the Configuration Manager on my machine by Justin Mathews . It caught my eye as I have never really tried it, but theoretically it can be done. Note - The second tweet says "Cannot find a provider with the name CMSite", resolution to which is in the Troubleshooting section at the end.

PowerShell : Trust network share to load modules & ps1

Problem Do you have a central network share, where you store all the scripts or PowerShell modules ? What happens if you try to run the script from a network share ? or if you have scripts (local) which invoke scripts or import PowerShell modules stored on this network share ? Well you would see a security warning like below (Note - I have set execution policy as 'Unrestricted' not 'bypass' here): Run a .ps1 from the network share Well this is a similar warning, which you get when you download scripts from Internet. As the message says run Unblock-File cmdlet to unblock the script and then run it, let's try it.