Boost your Azure Pipeline with Bicep's deployer() function

Azure Bicep deployer function

Overview

Context

Last month Microsoft released its newest Bicep version v0.32.4 which includes an interesting function called deployer(). This function can retrieve the ObjectId and TenantId of the deploying Service principal. This can be very helpful in certain cases such as assigning tags to Azure resources or to assign a role to the deploying Service principal.

In my experience you have strong Infrastructure as Code (IaC) setup when you combine both Bicep with Azure Pipelines for deploying resources to Azure. Let's have a look what we can do with the new deployer() function.

What’s the deal with the deployer() function?

The deployer() function, as per Microsoft's documentation, retrieves the object ID of the service principal, managed identity, or user initiating the deployment. It’s essentially like asking, “Who did this?” and Azure says, “It was them! 😄”

The output is the ObjectID of the service principal, which can be useful when tagging resources or for auditing purposes.

Why should you care?

Imagine the situation where multiple teams deploy resources every other minute, understanding who or what initiated a deployment is important. The deployer() function helps by giving you that extra layer of visibility. You can then combine this with build tags in Azure DevOps, and you can trace resource deployments back to specific pipeline runs.

1. Setting Up Your Azure DevOps Pipeline

Link to GitHub project

First, let’s configure the Azure DevOps pipeline to pass the predefined Build.BuildNumber to the Bicep deployment. I will also tag the pipeline run with the deployed resource information.

Here’s an example pipeline YAML:

 1trigger: none
 2
 3pool:
 4  vmImage: ubuntu-latest
 5
 6variables:
 7  resourceGroupName: 'arash-shared-rg' # Resource group name. Change it to your own resource group name
 8
 9steps:
10- task: AzureCLI@2
11  displayName: 'Bicep Deployer function'
12  inputs:
13    azureSubscription: 'test-automatic' # Azure Resource Manager service connection. Change it to your own service connection
14    scriptType: 'pscore'
15    scriptLocation: 'inlineScript'
16    inlineScript: |
17      $deploymentName="pipeline-run-$(Build.BuildNumber)"
18      az deployment group create `
19        --resource-group $(resourceGroupName) `
20        --template-file ./infra/storageAccount.bicep `
21        --name $deploymentName `
22        --parameters tags="{'PipelineRunID':'$(Build.BuildNumber)'}"
23      
24      # Add build tag to Azure Pipeline run
25      echo "##vso[build.addbuildtag]$deploymentName"      

What’s happening here?

  • $(Build.BuildNumber) provides the unique build number for the current pipeline run.
  • az deployment group create deploys the Bicep template with an additional PipelineRunID tag.
  • ##vso[build.addbuildtag] adds a tag to the Azure DevOps build, linking the deployment name with the pipeline run.

2. Updating the Bicep template

In this example a Bicep module including an Azure storage account is used. The Bicep template will dynamically tag resources with both the pipeline run ID and the deploying identity.

Here’s an example main.bicep:

 1param tags object = {}
 2
 3param location string = resourceGroup().location
 4
 5module storageAccounts 'br/public:avm/res/storage/storage-account:0.15.0' = {
 6  name: 'storageAccountDeployment'
 7  params: {
 8    // Required parameters
 9    name: 'sa${uniqueString(deployment().name)}'
10    // Non-required parameters
11    allowBlobPublicAccess: false
12    location: location
13    skuName: 'Standard_LRS'
14    networkAcls: {
15      bypass: 'AzureServices'
16      defaultAction: 'Deny'
17    }
18    tags: union(tags, {
19      DeployerObjectID: deployer().objectId
20      Environment: 'Development'
21    })
22  }
23}
  • The tags parameter accepts tags passed from the pipeline.
  • The union() function merges the provided tags with additional tags like DeployerObjectID.

When deployed, the resources will include:

  • The PipelineRunID passed from Azure DevOps.
  • The deploying principal’s object ID retrieved using deployer() function.

3. Testing the deployment

After executing the Azure pipeline the Azure storage account will be deployed to Azure. When navigating to the new Azure storage account the tags should be visible:

Storage account tags

The build in Azure DevOps is also tagged with the build number:

Azure pipelines tag

This provides a clear link between the Azure resources and the specific pipeline run that deployed them.

Notes

  • Role assignments: Ensure the deploying service principal has the Contributor role or equivalent permissions on the resource group / azure subscription.
  • Tag overhead: Avoid overloading your resources with too many tags. Stick to meaningful ones.
  • Multiple deployers: In multi-stage pipelines with different identities, deployer() reflects the identity of the current stage’s deployer.

Conclusion

Bicep’s deployer() function, paired with Azure DevOps build tags, is for traceability and governance in Azure deployments. Whether you’re an DevOps Engineer or just starting your IaC journey, this combination is worth adding to your setup.

And remember, IaC isn’t just about infrastructure; it’s about making life easier for you and your team. 😊

Happy coding! 🚀