Skip to content

miztiik/azure-identity-rbac-with-bicep

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

User Identity with narrow scopes

It is commong for Azure Storage accounts to host multiple blob containers. In that case it becomes necessary to scope the access to individual blobs as narrowly as possible. For ex, Application-A should only have access to Blob-A and nothing else. This can be achieved by using Azure RBAC1. Attribute based access control(ABAC) is getting really common now-a-days, scope the access even narrower, i,e The identity will have access to only certain files with certain attributes - for example tagged owner:Application-A.

In this blog, we will have a look at RBAC - Create a user managed identity role scoped with conditional access specific to a container. This is stratight-forward with portal/ARM, But with bicep the publicly available documentation is unclear. So lets try to build in bicep

🎯 Solutions

To store all the order events, we will create a blob container. To write the events to the container, lets use a user managed identity with it permission scoped narrowly to a specific blob conainer using Azure RBAC.1, 2, 3.

Miztiik Automaton: Azure Virtual Machine Access To Blob Storage With User Identity

Lets create a storage account and a blob container,

@description('Azure region of the deployment')
param location string = 'westeurope'

@description('Name of the storage account')
param storageAccountPrefix string = 'enterpriseA'

@description('Name of the Blob Container')
param blobContainerName string = 'app-A-Blob'

@description('Name of the User Managed Identity')
param userManagedIdentityName string = 'app-A-Owner'

var saName = '${storageAccountPrefix}${substring(uniqueString(resourceGroup().id), 0, 6)}'

resource r_sa 'Microsoft.Storage/storageAccounts@2022-05-01' = {
  name: saName
  location: location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
  properties: {
    minimumTlsVersion: 'TLS1_2'
    allowBlobPublicAccess: false
    supportsHttpsTrafficOnly: true
    networkAcls: {
      bypass: 'AzureServices'
      defaultAction: 'Allow'
    }
  }
}

// Create a blob storage container in the storage account
resource r_blobSvc 'Microsoft.Storage/storageAccounts/blobServices@2021-06-01' = {
  parent: r_sa
  name: 'default'
}

resource r_blobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-04-01' = {
  parent: r_blobSvc
  name: blobContainerName
  properties: {
    publicAccess: 'None'
  }
}

Lets create the user managed identity,

// Create User-Assigned Identity
resource r_userManagedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
  name: '${userManagedIdentityName}_Identity'
  location: location
}

So far, nothing out of the ordinary. When it comes to scoping this identity's permission to the specific storage account, we need to conditions to our role assignment resource. The official docs4 gives this recommendations,

resource symbolicname 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: 'string'
  scope: resourceSymbolicName or tenant()
  properties: {
    condition: 'string'
    conditionVersion: 'string'
    delegatedManagedIdentityResourceId: 'string'
    description: 'string'
    principalId: 'string'
    principalType: 'string'
    roleDefinitionId: 'string'
  }
}
  1. Issue - The guidance for the condition property is expected to be a string and gives this example value @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase 'foo_storage_container'

    If you try to set this in your bicep, you will get an "Condition Invalid" error & If you have tried setting up conditions in the portal, you will realize this string is missing the Action permitted on the resource. If we take a look at the ARM Documentation5, the condition will look like this (formatted for readability),

    (
      (
        !(ActionMatches{'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read'})
      )
      OR 
      (
        @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringEquals 'blobs-example-container'
      )
    )
    
  2. Issue - The doc doesn't clearly say if conditionVersion is a required field. But missing that out will result in an error. Currently the value is hard bound to 2.0.

    With this knowledge we can create our bicep with a Blob owner Role with read and write privileges like this. To build the string properly and to improve readability of the code, I have put the condition in another string.

    // Azure Built-In Roles Ref: https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles
    param blobOwnerRoleId string = 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'
    param blobContributorRoleId string = 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'
    
    var conditionStr= '((!(ActionMatches{\'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read\'}) AND !(ActionMatches{\'Microsoft.Storage/storageAccounts/blobServices/containers/blobs/write\'}) ) OR (@Resource[Microsoft.Storage/storageAccounts/blobServices/containers:name] StringEquals \'${blobContainerName}\'))'
    
    
    // Refined Scope with conditions
    resource r_attachBlobOwnerPermsToRole 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
      name: guid('r_attachBlobOwnerPermsToRole', r_userManagedIdentity.id, blobOwnerRoleId)
      scope: r_blobContainer
      properties: {
        description: 'Blob Owner Permission to ResourceGroup scope'
        roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', blobOwnerRoleId)
        principalId: r_userManagedIdentity.properties.principalId
        conditionVersion: '2.0'
        condition: conditionStr
        principalType: 'ServicePrincipal'
      }
    }

Putting it all toegether, the main.bicep in this blog has all the code. Let us deploy this bicep,(update the RG Name)

MAIN_BICEP_TEMPL_NAME="main.bicep"
SUB_DEPLOYMENT_PREFIX="userManagedIdentityDemo"
LOCATION="westeurope"

RG_NAME=""


az deployment group create \
    --name ${SUB_DEPLOYMENT_PREFIX}"-Deployment" \
    --resource-group ${RG_NAME} \
    --location ${LOCATION} \
    --template-file ${MAIN_BICEP_TEMPL_NAME}

Upon successful deployment you can check the Role Assignment conditions from the storage account portal.

Miztiik Automaton: Azure Virtual Machine Access To Blob Storage With User Identity

💡 Help/Suggestions or 🐛 Bugs

Thank you for your interest in contributing to our project. Whether it is a bug report, new feature, correction, or additional documentation or solutions, we greatly value feedback and contributions from our community. Start here

👋 Buy me a coffee

ko-fi Buy me a coffee ☕.

📚 References

  1. Azure Docs: Azure RBAC
  2. Azure Docs: Azure ABAC
  3. Azure Docs: Azure RBAC Example Conditions
  4. Azure Docs: Bicep Role Assignments
  5. Azure Docs: ARM Role assignment conditions

🏷️ Metadata

miztiik-success-green

Level: 100

Releases

No releases published

Packages

No packages published