Role Assignment with Azure Resource SDK


I love bicep. I really love bicep. I love it to deploy resources in the Azure Cloud. It’s simple to read and also simple to manage. Bicep takes the JavaScript-JSON-styled files and transforms it into ARM templates, which is needed to get things up and running in the Cloud. It works fine for almost all cases. But sometimes it’s necessary to switch to another way for deployment.

Actually as a developer, if you want to deploy infrastructure into the Azure Cloud, you can go different ways. There is Pulumi. Terraform, ARM templates or bicep and more IaC solutions. In case of Azure, you can go the SDK way too. This SDK sits on top of the public REST API of Azure Resource Management. The last version I used is a result of a long hard way, when I look into the old documentation. In the beginning of the SDK, Microsoft tried to design all the resources in a Domain specific language, which is cool to read and use, but the target system is too dynamic, so the SDK became senseless after a while.

Now it is completely redesigned, and actually the behavior is close to the bicep style, which I love. (Did I mentioned it already? 😄)

Okay, the intention of this post is not to describe how the SDK works.

Many times, when you want to deploy an infrastructure, you want to set up rights for the resource who works together. For example, a Web App wants to get data from a database, so the Web App needs the right for it. You might ask, isn't it not solved by using a connection string. Well, in the good old time it was the way, but for a way better security it’s not anymore. Now we work with managed identities. Which means, described in a very simple way: any resource is a kind of user and gets rights to do things, or not.

Microsoft did not the best job in this topic. I mean, when I set up a role for a resource with bicep, it looks like:

param storageAccountName string
param principalId string
param roleId string

var roleAssignmentName = guid(subscription().subscriptionId, storageAccountName, roleId, principalId)

resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' existing = {
name: storageAccountName
}

resource blobRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: roleAssignmentName
scope: storageAccount
properties: {
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleId)
principalId: principalId
principalType: 'ServicePrincipal'
}
}

Here I set up a role for a resource, referenced by its principalId, to allow access to a storage account. The resource blobRoleAssignment needs a role definition ID, which is somehow organized by the subscription. That’s why I have to transform the roleId into a roleDefinitionId. Nobody knows why it is so, because the roleId is a unique ID all over the Azure Cloud. Why I have to transform it then? 🤷‍♂️

Now I want to do the same but different thing by using the Azure SDK. Here I assign the role “KeyVault Secret Reader” to a KeyVault

Step 1: Get role definition

using Azure;
using Azure.ResourceManager;
using Azure.ResourceManager.Authorization;
using Azure.ResourceManager.Authorization.Models;
using Azure.ResourceManager.KeyVault;

...

ResourceIdentifier kvSecretReader =
new ("4633458b-17de-408a-b874-0445c86b69e6");

Response<AuthorizationRoleDefinitionResource> kvSecretReaderDefinition =
await subscription.GetAuthorizationRoleDefinitionAsync(kvSecretReader);

...

A magic GUID transformed into a magic role definition.

Step 2: Get the KeyVault where I want to set up the role assignment

...

Response<KeyVaultResource> getKeyVaultResponse =
await resourceGroup.GetKeyVaultAsync(keyVaultName);

KeyVaultResource keyVault = getKeyVaultResponse.Value;

...

All resources can be grabbed from the resourceGroup component.

Step 3: Create a role assignment content. It’s the definition what we want to do soon.

...

RoleAssignmentCreateOrUpdateContent roleAssignment =
new(kvSecretReaderDefinition.Value.Id, principalId)
{
PrincipalType = RoleManagementPrincipalType.ServicePrincipal
};

string roleAssignmentName = Guid.NewGuid().ToString();

...

Here we have the definition that we want to assign a role (kvSecretReaderDefinition) for a resource (principalId) with a special name (roleAssignmentName) — yes a GUID is not a nice name, but it’s better to do so. 😉

Step 4: The final step. Bring it all together.

...

ArmOperation<RoleAssignmentResource>? createRoleAssignmentOperation =
await keyVault
.GetRoleAssignments()
.CreateOrUpdateAsync(WaitUntil.Completed,roleAssignmentName,roleAssignment);

...

Disclaimer: All my lines of code have more infrastructure than just this few lines. In the original I have try-catches, checks and exception handling, of course. 😄

Firstly, bicep is less code than using the SDK. But the SDK have its right to exist. I used it to create resources on demand in an Application which had no access to a build pipeline system. Actually I’ve planned to prepare a build pipeline to trigger a custom build for my use case, but then I found out, I can’t do it. So the SDK was my solution.

Secondly, the SDK way is quite similar to bicep. Get a resource, grab its sub resources, add or update the resource… done. I like the strong typed way very much but also the dynamic parts of it.