Dealing with deployment blockers with Bicep

This post has been republished via RSS; it originally appeared at: New blog articles in Microsoft Tech Community.

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.

 

Scenario

Here is an example scenario.

 

Picture1.png

 

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.

 

Picture2.png

But when you deploy any resource in Azure using a Bicep file the identity of the user\service 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.

 

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/sshKeyGen.sh') //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, UAIKVAdmin.id, roleDefinitionId2) resource UAIRoleContributors 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { name: roleAssignmentName_var2 properties: { roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId2) principalId: UAIKVAdmin.properties.principalId principalType: 'ServicePrincipal' } dependsOn: [ UAIKVAdmin ] } //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: { '${UAIKVAdmin.id}': {} } } 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: [ UAIKVAdmin UAIRoleContributors keyvault ] } // set deployment entity as Key Vault Secrets User var KVroleDefinitionId = '4633458b-17de-408a-b874-0445c86b69e6' var KVroleDefinitiontName = guid(subscription().id, deploymentUser.id, KVroleDefinitionId) resource KVRoleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { name: KVroleDefinitiontName properties: { roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', KVroleDefinitionId) principalId: deploymentUser.properties.outputs.userAccount principalType: 'User' } dependsOn: [ deploymentUser ] } output userAccount string = deploymentUser.properties.outputs.userAccount

 

 

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.

 

Cheers!

 

Pierre

 

 

Leave a Reply

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

*

This site uses Akismet to reduce spam. Learn how your comment data is processed.