Search code examples
powershellazure-bicep

When referencing a Managed Identity from a bicep module the identity cannot be found (resource group is passed as <null>)


I am trying to deploy a resource group using the New-AzDeployment PowerShell command. In this resource group I create a managed identity and assign this identity the Key Vault Secret Users role (ResourceId: '4633458b-17de-408a-b874-0445c86b69e6'). In reality I try to do a whole lot more, but I brought back the issue I am facing to this bare minimum.

In this example I have set everything as hard-coded variables/parameters for ease of testing if people are willing to try this themselves.

My main.bicep:

targetScope = 'subscription'


var location = 'westeurope'
var resourceGroupName = 'test_resourcegroup'


module resourcegroup 'resourcegroup/resourcegroup.bicep' = {
  name: 'resource_group_deployment'
  params: {
    resourceGroupName: resourceGroupName
    location: location
  }
}


module managedidentity 'serviceprincipal/managedidentity.bicep' = {
  scope: resourceGroup(resourceGroupName)
  name: 'managed_identity_deployment'
  params: {
    location: location
  }
  dependsOn: [
    resourcegroup
  ]
}


module serviceprincipal 'serviceprincipal/roleassignment.bicep' = {
  name: 'role_assignment'
  params: {
    principalId: (reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', 'test_idenity'), '2023-01-31', 'Full')).properties.principalId
    roleDefinitionResourceId: '4633458b-17de-408a-b874-0445c86b69e6'
  }
  dependsOn: [
    resourcegroup
    managedidentity
  ]
}

This main.bicep uses the following modules:

resourcegroup/resourcegroup.bicep

targetScope = 'subscription'

param resourceGroupName string
param location string = deployment().location

resource resourcegroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
  name: resourceGroupName
  location: location
}

serviceprincipal/managedidentity.bicep

param location string

resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
  name: 'test_idenity'
  location: location
}

serviceprincipal/roleassignment.bicep

targetScope = 'subscription'
param roleDefinitionResourceId string
param principalId string

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(subscription().id, principalId, roleDefinitionResourceId)  
  properties: {
    principalId: principalId
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionResourceId)
    principalType: 'ServicePrincipal'
  }
}

The deployment of the resourcegroup and the managed identity works as it should, yet when assigning the role the following error occurs:

The Resource 'Microsoft.ManagedIdentity/userAssignedIdentities/test_idenity' under resource group '<null>' was not found.

I can retrieve the Managed Identity using the New-AzResourceGroupDeployment (including the ResourceGroupName parameter) with the following bicep template:

output outputvalue object = (reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', 'test_idenity'), '2023-01-31', 'full'))

This returns the following output:

{
    "apiVersion": "2023-01-31",
    "location": "westeurope",
    "tags": {},
    "properties": {
        "tenantId": "removed_from_output",
        "principalId": "1b01c00a-8ca1-4bc1-81cb-b290156c0153",
        "clientId": "removed_from_output"
    },
    "condition": true,
    "isConditionTrue": true,
    "subscriptionId": "removed_from_output",
    "resourceGroupName": "test_resourcegroup",
    "scope": "",
    "resourceId": "Microsoft.ManagedIdentity/userAssignedIdentities/test_idenity",
    "referenceApiVersion": "2023-01-31",
    "existing": false,
    "isTemplateResource": false,
    "isAction": false,
    "provisioningOperation": "Read"
}

So I know the Managed Identity can be retrieved correctly when not called from a module from within a bicep file.

I believe I have the exact same issue as discussed by Gordon Byers, where the Resource Group isn't passed as the context of the module. He describes the solution when using ARM Templates though and not Bicep.

Is there anyone who knows how I can solve this issue? If not I am afraid I have to set the roles by using a PowerShell script during the deployment. This isn't my preference since this defeats the purpose of using a declarative syntax like Bicep.

I have added adding the scope to the call of the roleassignment module.

module serviceprincipal 'serviceprincipal/roleassignment.bicep' = {
  scope: resourcegroup(resourceGroupName)
  name: 'role_assignment'
  params: {
    principalId: (reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', 'test_idenity'), '2023-01-31', 'Full')).properties.principalId
    roleDefinitionResourceId: '4633458b-17de-408a-b874-0445c86b69e6'
  }
  dependsOn: [
    resourcegroup
    managedidentity
  ]
}

The only allowed scope for this module however is Subscription. When I set the scope to Subscription this still returns the exact same error where the resource group is referenced as <null>.


Solution

  • I've tested this and it works 😊

    Start by adding an output for the principalId to your managedId module... Then simply refer to that instead of the reference() code. Reducing the complexity here by using an output digs you out of a scoped reference lookup and cleans up those unnecessary dependsOn statements.

    module serviceprincipal 'serviceprincipal/roleassignment.bicep' = {
      name: 'role_assignment'
      params: {
        principalId: managedidentity.outputs.principalId
        roleDefinitionResourceId: '4633458b-17de-408a-b874-0445c86b69e6'
      }
    }