Warning: call_user_func_array() expects parameter 1 to be a valid callback, no array or string given in /home/kalofero/public_html/blog/wp-includes/class-wp-hook.php on line 286
ABX Action to Set a Tag to an Azure Resource Group (SKKB1049) | Spas Kaloferov's Blog

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

  1. # Resource Group Tags
  2. resourceGroupTagsProp: '${to_string(input.resourceGroupTags)}'
  3. # Cloud Zone
  4. cloudZoneProp: '${input.cloudZone}'

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

  1. @{
  2.   'Az.Accounts' = '1.7.4'
  3.   'Az.Resources' = '1.13.0'
  4.   'Az.Storage' = '1.13.0'
  5.  
  6. }

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

  1. # ----- Execution ----- #
  2. Write-Host "[ABX] $fn Connecting to Azure..."
  3. $resp_connectAzAccount = Connect-AzAccount -ServicePrincipal -Credential $cred -Tenant $tenantId    # For username/password auth remove the ServicePrincipal switch
  4.  
  5. $tagsHash = (Get-AzResourceGroup -Name $resourceGroupName ).Tags   # Create Tags Hashtable and add any existing tags
  6.  
  7. $while_count = 0    # count var used within loop
  8. while($while_count -lt $resourceGroupTags.Count)    # Loop to add user tags to the Tags Hashtable
  9.     {
  10.         $tagsHash.add($resourceGroupTags[$while_count],$resourceGroupTags[$while_count+1])    # Add user defined tags to the existing tags
  11.         $while_count = $while_count + 2    # Skip 2 values to get to the next key/value pair
  12.     }   # End Loop
  13.  
  14. Write-Host "[ABX] $fn Setting Resource Group..."
  15. $resp_setAzResourceGroup = Set-AzResourceGroup -Name $resourceGroupName -Tag $tagsHash    # Set tags

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:

  1. [ABX] handler -  Action started.
  2. [ABX] handler -  Function started.
  3. [ABX] handler -  runOnPropertyName:  cloudZoneProp
  4. [ABX] handler -  runOnPorperty:  cas.cloud.zone.type:azure
  5. [ABX] handler -  runOnPorpertyMatch:  cas.cloud.zone.type:azure
  6. [ABX] handler -  runOnProperty_eval:  RUN
  7. [ABX] handler -  runOnProperty matched or RunOnProperty action option disabled.
  8. [ABX] handler -  Running myActionFunction...
  9. [ABX] myActionFunction - myActionFunction function started.
  10. [ABX] myActionFunction - Using PAYLOAD inputs based on AcceptPayloadInput action option
  11. [ABX] myActionFunction - resourceGroupName:  group5decd024e8a9991eadac9e0c07b48346
  12. [ABX] myActionFunction - resourceGroupTags:  key1 value1
  13. [ABX] myActionFunction - Connecting to Azure...
  14. [ABX] myActionFunction - Function return:
  15. {
  16. resp_setAzResourceGroup: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceGroup
  17. }
  18. [ABX] myActionFunction - Function completed.
  19. [ABX] handler -  Function return:
  20. {
  21. runOnProperty_eval: RUN
  22. }
  23. [ABX] handler -  Function completed.
  24. [ABX] handler -  Action return:
  25. {
  26. resp_setAzResourceGroup: Microsoft.Azure.Commands.ResourceManager.Cmdlets.SdkModels.PSResourceGroup,
  27. runOnProperty_eval: RUN
  28. }
  29. [ABX] handler -  Action completed.
  30. [ABX] handler -  P.S. Spas Is Awesome !!!

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.

DISCLAIMER; This is a personal blog. Any views or opinions represented in this blog are personal and belong solely to the blog owner and do not represent those of people, institutions or organizations that the owner may or may not be associated with in professional or personal capacity, unless explicitly stated. Any views or opinions are not intended to malign any religion, ethnic group, club, organization, company, or individual.
All content provided on this blog is for informational purposes only. The owner of this blog makes no representations as to the accuracy or completeness of any information on this site or found by following any link on this site. The owner will not be liable for any errors or omissions in this information nor for the availability of this information. The owner will not be liable for any losses, injuries, or damages from the display or use of this information.
Photos
Unless stated, all photos are the work of the blog owner and are licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License. If used with watermark, no need to credit to the blog owner. For any edit to photos, including cropping, please contact me first.
Recipes
Unless stated, all recipes are the work of the blog owner and are licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License. Please credit all recipes to the blog owner and link back to the original blog post.
Downloadable Files
Any downloadable file, including but not limited to pdfs, docs, jpegs, pngs, is provided at the user’s own risk. The owner will not be liable for any losses, injuries, or damages resulting from a corrupted or damaged file.
Comments
Comments are welcome. However, the blog owner reserves the right to edit or delete any comments submitted to this blog without notice due to
– Comments deemed to be spam or questionable spam
– Comments including profanity
– Comments containing language or concepts that could be deemed offensive
– Comments containing hate speech, credible threats, or direct attacks on an individual or group
The blog owner is not responsible for the content in comments.
This policy is subject to change at anytime.

Leave a Reply

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