May 23, 2024 Marco

Deploy Entra ID resources declaratively using Azure Bicep

Microsoft Graph Bicep is currently in PREVIEW!

When using Azure Bicep for declarative deployments, an important piece of the puzzle was missing for a long time – until now! The development of a Microsoft Graph provider/extension for Bicep was announced back in July 2022 and has now reached public preview status.

The Graph extension allows you to create and include Entra ID resources like groups or service principals declaratively in your Bicep deployments. Therefore, this is especially useful for end-to-end deployments including Entra ID permission groups and role assignments. Until now, some goals were only achievable through Bicep deployment scripts and scripting efforts, which are prone to errors and difficult to troubleshoot.

Let’s take a closer look at the new Graph extension and how it works.

Entra ID / Graph object support

The following Microsoft Graph resource types are currently available to use with the new extension:

  • Applications (App Registrations)
  • Federated identity credentials (Federated credentials on a Managed Identity)
  • App role assigned to (Azure RBAC role assignments)
  • Groups (Entra ID groups)
  • Oauth2 permission grants (Graph permission assignments)
  • Service principals (Enterprise Applications)

Concept of Bicep extensions

Azure Bicep is built to talk to the Azure Resource Manager in the background and has (almost) no capabilities outside of that. As the name states, the new Microsoft Graph capabilities use the concept of Bicep extensions, which allows targeting other endpoints like the Microsoft Graph API (kubernetes is another provider).

Extensions, also known as providers, need to be declared at the top of your Bicep files like so (make sure you enabled the experimental features in your bicepconfig.json file first, check the prerequisites below for more information):

provider microsoftGraph

Benefits of using the Microsoft Graph extension

The biggest advantage for me is the ability to achieve end-to-end deployments including service principals, permission groups, and corresponding permission assignments. Not to mention all the declarative and repeatable benefits Bicep itself brings with it.

Prerequisites

To use the new Graph extension, ensure you have

  • Azure CLI installed on your machine (to run deployments)
  • the Bicep extension installed in VS Code (v0.27.1 or later)
  • the correct permissions to run appropriate tasks, i.e. create groups or App Registrations


Enable Bicep preview features
Before we can use the new extension in our files, we need to enable preview features in our bicepconfig.json file. Check this link if you want to know more about the bicepconfig.json configuration file: 5 nifty Azure Bicep tips and tricks – marcogerber.ch

Create a new file called bicepconfig.json in your current working directory and add the following lines:

{
    "experimentalFeaturesEnabled": {
        "extensibility": true
    }
}

Save the file and preview features will now be available for Bicep files in the current working directory.

Include the Graph extension in your Bicep files
Since the new Graph extension is – as the name states – an extension on not a native part of the Bicep DSL, we have to include it at the beginning of each Bicep file like so:

provider microsoftGraph

After the provider is declared, linting is automatically be available in VSCode:

Lab: Graph extension in action

For this quick lab, we’ll create the following resources:

  1. User-assigned Managed Identity
  2. Entra ID security group including the Managed Identity as a member
  3. Storage Account
  4. Role assignment, which assigns the role Storage Blob Data Reader to the Storage Account with the security group as principal

Let’s start by creating a new main.bicep file in your current working directory. Declare the Graph extension at the top and add some parameters and variables to simplify the deployment:

provider microsoftGraph

// Parameters
param managedIdentityName string = 'd-id-graph-demo'
param storageAccountName string = 'dstographdemo6294'
param appRoleDefinitionId string = '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1' // Storage Blob Data Reader
param groupDisplayName string = '${storageAccountName}-StorageBlobDataReader'

// Variables
var groupUniqueName = toLower(replace(groupDisplayName, ' ', ''))
var roleAssignmentName = guid(groupUniqueName, appRoleDefinitionId, resourceGroup().id)

We are now ready to create our first resource, the user-assigned Managed Identity:

// Resources
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-07-31-preview' = {
  name: managedIdentityName
  location: resourceGroup().location
}

Next, let’s create our first Graph resource, the Entra ID security group. Note lines 25 – 27 where we add the Managed Identity as a group member:

resource securityGroup 'Microsoft.Graph/groups@v1.0' = {
  displayName: groupDisplayName
  uniqueName: groupUniqueName
  mailEnabled: false
  mailNickname: groupUniqueName
  securityEnabled: true
  members: [
    managedIdentity.properties.principalId
  ]
}

Second to last, the Storage Account:

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' = {
  name: storageAccountName
  location: resourceGroup().location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}

And finally, the role assignment. Note lines 42 and 43 where link the security group and the RBAC role together in an assignment:

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: roleAssignmentName
  properties: {
    principalId: securityGroup.id
    roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', appRoleDefinitionId)
  }
}

Here you have the final main.bicep file:

provider microsoftGraph

// Parameters
param managedIdentityName string = 'd-id-graph-demo'
param storageAccountName string = 'dstographdemo6294'
param appRoleDefinitionId string = '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1' // Storage Blob Data Reader
param groupDisplayName string = '${storageAccountName}-StorageBlobDataReader'

// Variables
var groupUniqueName = toLower(replace(groupDisplayName, ' ', ''))
var roleAssignmentName = guid(groupUniqueName, appRoleDefinitionId, resourceGroup().id)

// Resources
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-07-31-preview' = {
  name: managedIdentityName
  location: resourceGroup().location
}

resource securityGroup 'Microsoft.Graph/groups@v1.0' = {
  displayName: groupDisplayName
  uniqueName: groupUniqueName
  mailEnabled: false
  mailNickname: groupUniqueName
  securityEnabled: true
  members: [
    managedIdentity.properties.principalId
  ]
}

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' = {
  name: storageAccountName
  location: resourceGroup().location
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: roleAssignmentName
  properties: {
    principalId: securityGroup.id
    roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', appRoleDefinitionId)
  }
}

Deployment steps

To deploy the main.bicep file to Azure, we use simple Azure CLI commands.

1. Login to Azure
First, log in to your Azure account via the Azure CLI. This step authenticates your session and allows you to manage your Azure resources from the command line.

az login

2. Set Azure Subscription
Once logged in, specify which Azure subscription you want to use for the deployment. Replace <subscriptionId> with your actual subscription ID. Skip this step if you only have one subscription.

az account set --subscription <subscriptionId>

3. Deploy Resources
Finally, we use the az deployment group create command to deploy the resources. This command deploys all resources defined in your Bicep file to the specified resource group. In this example, the resource group is named d-rgr-graph-demo. Create a resource group first with your desired name.

Replace the path to the Bicep file if it’s located in a different directory or has a different name.

az deployment group create -g d-rgr-graph-demo -f .\main.bicep

And that’s it, you’ll find all the resources in the specified resource group after a few seconds.

You can find the code from this post in my GitHub repository.

Known Issues

Since the feature is still in preview there are some rather major issues, for instance:

  • Applications (App Registrations) do not yet support the creation of secrets as you can via the Azure portal, CLI, or PowerShell. Only keys from an Azure Key Vault are supported as of now.
  • Only Azure PowerShell and Azure CLI apps are supported for interactive deployments of Microsoft Graph resources.
  • At the moment, you may get an Another object with the same value for property uniqueName already exists error when recreating Graph resources (delete and redeploy a resource).

Current known issues and corresponding resolution steps are documented here: Known issues: Microsoft Graph Bicep templates – Microsoft Graph Bicep | Microsoft Learn

Resources

, , ,
Marco Gerber

Marco

Senior Cloud Engineer, keen on Azure and cloud technologies.

Leave a Reply

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