Dealing with deployment blockers with Bicep

Hello Folks!

Have you ever had a deployment blocked because the behavior you get from the portal is different than the deployment you get from an Azure Resource Manager (ARM) template or a Bicep deployment?  Well, I had that issue for a little internal project I was working on.  Let me tell you how I resolved it using built-in Bicep functionality.

As you may know Bicep is a domain-specific language (DSL) that uses declarative syntax to deploy Azure resources.  It provides concise syntax, reliable type safety, and support for code reuse.  And as far as I'm concerned, the authoring experience is far superior to writing ARM templates.


Here is an example scenario.


When using the portal to deploy a Key Vault. You can assign an access policy and give yourself access to get/list/set/delete/recover/backup/restore secrets.


But when you deploy any resource in Azure using a Bicep file the identity of the userservice principal executing the deployment is NOT exposed to the deployment environment.

So, to resolve this,  I used a Deployment Script, a User Assigned Identities and a role assignment.  Basically, in my Bicep deployment file I create a User Assigned Identities, assign the adequate role to that identity so it can execute the Deployment Script and get the result I am looking for.  That result is the ObjectID of the user or Service Principal that initiated the deployment.  One I have the ObjectID i can assign the Access Policy I need to the Key Vault.

Here is a sample Bicep file illustrating the process I used.

param logStartMinsAgo int = 50
param now string = utcNow('F')

var keyvaultName = toLower('KeyVault${uniqueString(resourceGroup().id)}')
var keyvaultTenantId = subscription().tenantId
var getDeployObjectIDScript = loadTextContent('./scripts/getDeployObjectID.ps1')
var sshKeyGenScript = loadTextContent('./scripts/')

//Create User Defined Identity

resource UAIKVAdmin 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
    name: '${resourceGroup().name}-UAIKVAdmin'
    location: resourceGroup().location

// Assign previously assigned identity the contributor role in the current context

var roleDefinitionId2 = 'b24988ac-6180-42a0-ab88-20f7382dd24c'
var roleAssignmentName_var2 = guid(subscription().id,, roleDefinitionId2)

resource UAIRoleContributors 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
  name: roleAssignmentName_var2
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId2)
    principalType: 'ServicePrincipal'
  dependsOn: [

//create a key vault with RBAC access policy enabled

resource keyvault 'Microsoft.KeyVault/vaults@2021-04-01-preview' = {
  name: keyvaultName
  location: resourceGroup().location
  properties: {
    enabledForDeployment: true
    enabledForTemplateDeployment: true
    enableRbacAuthorization: true
    enableSoftDelete: false
    networkAcls: {
      defaultAction: 'Allow'
      bypass: 'AzureServices'
    sku: {
      name: 'standard'
      family: 'A'
    tenantId: keyvaultTenantId

// execute a PowerShell script that will search the Azure Activity log for the userid of the entity that create the Keyvault
// the script uses the user defined identitiy to access the log info

resource deploymentUser 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
  name: 'getDeploymentUser'
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${}': {}
  location: resourceGroup().location
  kind: 'AzurePowerShell'
  properties: {
    azPowerShellVersion: '6.2.1'
    arguments: ' -ResourceGroupName ${resourceGroup().name} -DeploymentName ${deployment().name} -StartTime ${logStartMinsAgo}'
    scriptContent: getDeployObjectIDScript
    forceUpdateTag: now
    cleanupPreference: 'OnSuccess'
    retentionInterval: 'P1D'
    timeout: 'PT${logStartMinsAgo}M'
  dependsOn: [

// set deployment entity as Key Vault Secrets User

var KVroleDefinitionId = '4633458b-17de-408a-b874-0445c86b69e6'
var KVroleDefinitiontName = guid(subscription().id,, KVroleDefinitionId)

resource KVRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
  name: KVroleDefinitiontName
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', KVroleDefinitionId)
    principalType: 'User'
  dependsOn: [

output userAccount string =

some of you will have realized that I'm now using the loadTextContent function to read the script file instead of the more regularly used “inline” format.

So,  this article is just to illustrate that IT and Operations can get around deployment blockers by applying some creativity to their deployment and using the proper tools.

If you have a scenario where you are experiencing a blocker.  Please,  tell me in the comments below.  I'd love to try to figure it out.  No promisses, but i'll give it a try.




This article was originally published by Microsoft's Secure Blog. You can find the original article here.