ABX Action to Set a Tag to an Azure Resource Group (SKKB1049)

In this blog post we are going to look at how to assign a tag to an Azure Resource Group using an ABX Action during a VMware Cloud Assembly blueprint deployment.

 

Update Log:

Introduction

When Assembly deploys resources in Azure it places them in either predefined Azure Resource Groups (RG) or on an on-demand create RG’s. Unfortunately for the time being the Blueprint editor does not let us assign tags to those RG’s . In this blog we are going to show how we can get the RG from the deployment and assign a tag using an ABX Action. We are not only going to show how to assign a tag but in general show how we can integrate with Microsoft Azure using the Azure PowerShell modules

Preparing the Blueprint

Before we move to building the ABX action , we need couple of pieces of information. In this use case we want the user to provide the Tags via blueprint input (named resourceGroupTags) and we also want to capture another use input (named cloudZone) which holds the selected zone for the deployment.
On our machine resource(s) we have specified the following custom properties

[sourcecode language=”yaml”]

# Resource Group Tags
resourceGroupTagsProp: ‘${to_string(input.resourceGroupTags)}’
# Cloud Zone
cloudZoneProp: ‘${input.cloudZone}’

[/sourcecode]

cloudZone will resolve to an constraint tag for a particular cloud zone e.g. cas.cloud.zone.type:aws or cas.cloud.zone.type:azure
resourceGroupTags will resolve to an string holding key value pairs of tags provided by a user during provisioning.

Creating the ABX Action

To reach our goal we are going to use an ABX PowerShell action. The whole azureSetAzureResourceGroupTags-ps action can be found in my Gitlab Repo bit.ly/The-Gitlab
Before we continue with the action script itself, a couple of things in general for all actions that I build. Most of the action script in my actions acts as a wrapper and allows us to do a couple of things:

  • Run the action script only if a particular deployment payload property is found. As this is an Azure action we want to run it only when our cloud agnostic blueprint is provisioned on Azure and not on any other public clouds like AWS or GCP. Although we can filter our Cloud Accounts in the Action Subscription this can be used as an alternative or just supplement the subscription condition filtering and add more flexibility . In this particular case I will configure this action to run only if the deployment payload contains cloudZoneProp custom property tag that equals cas.cloud.zone.type:azure, which tells me this deployment went to an Azure Cloud Account .
  • Allow to run the action with action inputs instead of payload inputs – When an action script requires payload data it cannot be tested unless there is an active deployment to provide it. This allows us to supply some of the necessary payload data as action inputs and test the action in ABX while developing. Without the need to do constant deployments.

To accomplish this I’m using, what I call Action Option inputs .

  • actionOptionAcceptPayloadInputIn (Boolean):
    • True: Accept deployment payload inputs if present. Fallback is action input.
    • False: Accept only action inputs
  • actionOptionRunOnPropertyIn (Boolean):
    • True: Run action if property value is matched.
    • False: Run action without matching property values.
  • runOnPorpertyIn (String): This is the property we are looking for in the payload data when actionOptionRunOnPropertyIn=True . In this example this would be the value of the cloudZoneProp custom property e.g. cas.cloud.zone.type:azure
  • runOnPropertyNameIn (String): The name of the property in the payload data which holds the value we are matching against when actionOptionRunOnPropertyIn=True e.g. cloudZoneProp
  • runOnPorpertyMatchIn (String): Used just for testing without payload. This will simulate the value returned by the payload data. e.g. cas.cloud.zone.type:azure

To use the Azure modules, we need to import them into the action. We can do this by specifying the Az module in the action dependences

[sourcecode language=”json”]

@{
‘Az.Accounts’ = ‘1.7.4’
‘Az.Resources’ = ‘1.13.0’
‘Az.Storage’ = ‘1.13.0’

}

[/sourcecode]

The action itself has two functions:

  • handler – main entry function for the action. This is the function that will evaluate if the action should continue to run based on the Action Option inputs. If runOn action option is enabled and all runOnPriperties are met, or runOn action option is disabled, the function will call the myActionFunction.
  • myActionFunction – the function that will do something. This is the function where we specify what need to be done. In this example where is where we will be writing the PowerShell script to set the tags to the RG.

Let’s take a look at the myActionFunction function. The script will perform the following steps in a nutshell:

  • Based on the AcceptPayloadInput Action Option it will take inputs either from the payload data or from the action inputs
  • If inputs are provided from Payload data it will do some calculations to cleanup the values and set them in a proper format for PowerShell.
  • Connect to Azure using the Connect-AzAccount cmdlet
  • Get existing tags from the RG via Get-AzResourceGroup
  • Combine existing tags with user tags and store them in a PS Hashmap.
  • Apply the tags to the RG using Set-AzResourceGroup.

here a portion of that script

[sourcecode language=”powershell”]

# —– Execution —– #
Write-Host “[ABX] $fn Connecting to Azure…”
$resp_connectAzAccount = Connect-AzAccount -ServicePrincipal -Credential $cred -Tenant $tenantId # For username/password auth remove the ServicePrincipal switch

$tagsHash = (Get-AzResourceGroup -Name $resourceGroupName ).Tags # Create Tags Hashtable and add any existing tags

$while_count = 0 # count var used within loop
while($while_count -lt $resourceGroupTags.Count) # Loop to add user tags to the Tags Hashtable
{
$tagsHash.add($resourceGroupTags[$while_count],$resourceGroupTags[$while_count+1]) # Add user defined tags to the existing tags
$while_count = $while_count + 2 # Skip 2 values to get to the next key/value pair
} # End Loop

Write-Host “[ABX] $fn Setting Resource Group…”
$resp_setAzResourceGroup = Set-AzResourceGroup -Name $resourceGroupName -Tag $tagsHash # Set tags

[/sourcecode]

Now as we said before although we can filter when the action should run , based on the runOnProperty action, and run it only if a deployment is going to Azure, we can also filter this on an Subscription level. In Cloud Assembly we have an Azure Cloud Account with Account ID “c1e74a1db0a34475598f3005c9683". If we want to filter deployments going only to that Cloud Account ID we can enter the following subscription condition filter:

  • event.data.endpointId == "c1e74a1db0a34475598f3005c9683"

Running the ABX Action

Let’s test the action.
During blueprint provisioning we have specified a key1/value1 key pair for a tag

Once the deployment finishes, we can check the Action Runs and examine the action run output. We will see output similar to the following:

[sourcecode language=”powershell”]

[ABX] handler – Action started.
[ABX] handler – Function started.
[ABX] handler – runOnPropertyName: cloudZoneProp
[ABX] handler – runOnPorperty: cas.cloud.zone.type:azure
[ABX] handler – runOnPorpertyMatch: cas.cloud.zone.type:azure
[ABX] handler – runOnProperty_eval: RUN
[ABX] handler – runOnProperty matched or RunOnProperty action option disabled.
[ABX] handler – Running myActionFunction…
[ABX] myActionFunction – myActionFunction function started.
[ABX] myActionFunction – Using PAYLOAD inputs based on AcceptPayloadInput action option
[ABX] myActionFunction – resourceGroupName: group5decd024e8a9991eadac9e0c07b48346
[ABX] myActionFunction – resourceGroupTags: key1 value1
[ABX] myActionFunction – Connecting to Azure…
[ABX] myActionFunction – Function return:
{
resp_setAzResourceGroup: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceGroup
}
[ABX] myActionFunction – Function completed.
[ABX] handler – Function return:
{
runOnProperty_eval: RUN
}
[ABX] handler – Function completed.
[ABX] handler – Action return:
{
resp_setAzResourceGroup: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceGroup,
runOnProperty_eval: RUN
}
[ABX] handler – Action completed.
[ABX] handler – P.S. Spas Is Awesome !!!

[/sourcecode]

What we see is that :

  • We have a runOn condition where we are looking for a payload property called cloudZoneProp. And we were expecting a value of cas.cloud.zone.type:azure.
  • runOn value was met so action run further.
  • RG and Tags were taken from the payload.
  • Tags we applied to the RG.

If we go in Azure and check the Azure RG which was created for the deployment, we will see the tags applied:

 

 

 

Final Step

If all went well, go grab a beer.

Leave a Reply

Your email address will not be published. Required fields are marked *