Microsoft Graph Bicep – Part 1

Microsoft announced last spring that Bicep templates for Microsoft Graph are coming in public preview.

What does this mean now?
Bicep support can now be used to create a declarative Infrastructure as Code (IaC) capability for Microsoft Graph resources. If you are already using Bicep for Azure resources, for example, then you will find your way around relatively quickly. In addition, PowerShell or Graph REST requests will no longer be necessary for certain activities in your automation scripts.

Important to mention
This is still a public preview which is very limited.

The aim of this blog series is to show you what is already possible with Bicep for Microsoft Graph and what is not. I will be posting step-by-step examples over the next few weeks. In Part 1 we focus on the setup, the service user and groups.

Overview

The following resources are supported in the current v1.0 version of Bicep for Microsoft Graph:

Service Microsoft Graph Bicep resource type
Applications Microsoft.Graph/applications
App role assignments Microsoft.Graph/appRoleAssignedTo
Federated identity credentials Microsoft.Graph/applications/federatedIdentityCredentials
Groups Microsoft.Graph/groups
OAuth2 permission grants (delegated permission grants) Microsoft.Graph/oauth2PermissionGrants
Service principals Microsoft.Graph/servicePrincipals
Users Microsoft.Graph/users

prerequisites

The following prerequisites must be met in order to use Bicep for Microsoft Graph successfully.

Bicep preview feature

To be able to use Bicep for Microsoft Graph at all, it is absolutely necessary to activate the preview features in the bicepconfig.json file.

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

Microsoft Graph Bicep extension

In order for Bicep to understand the Microsoft Graph resources, the extension must also be specified in bicepconfig.json.

{
  "extensions": {
    "microsoftGraphV1": "br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:0.1.9-preview"
  }
}

It was possible to define the provider until January 2025. This is no longer possible, see the following Microsoft article.

Final Bicep config file

The first bicepconfig.json file should now look like this:

{
  "experimentalFeaturesEnabled": {
    "extensibility": true
  },
  "extensions": {
    "microsoftGraphV1": "br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:0.1.9-preview"
  }
}

Services

Users

As of today, the service users can only «read» and cannot create or mutate new Entra identities.
Your app registration requires the following least privileged Microsoft Graph permission:

Permission type Least privileged permissions
Delegated (work or school account) User.Read
Delegated (personal Microsoft account) User.Read
Application User.Read.All

 

The following resource format is used to read a user object using Bicep.

resource symbolicname 'Microsoft.Graph/[email protected]' existing = {
  userPrincipalName: 'string'
}

All available properties can be found here.

Groups

The situation is slightly different for the groups. You can read and also create new groups.
The resource format looks like this and all properties can be found here.

resource symbolicname 'Microsoft.Graph/[email protected]' = {
  classification: 'string'
  description: 'string'
  displayName: 'string'
  groupTypes: [
    'string'
  ]
  isAssignableToRole: bool
  isManagementRestricted: bool
  mailEnabled: bool
  mailNickname: 'string'
  members: [
    'string'
  ]
  membershipRule: 'string'
  membershipRuleProcessingState: 'string'
  owners: [
    'string'
  ]
  preferredDataLocation: 'string'
  preferredLanguage: 'string'
  securityEnabled: bool
  theme: 'string'
  uniqueName: 'string'
  visibility: 'string'
}

You need the following Microsoft Graph permission to be able to execute the whole process:

Permission type Least privileged permissions
Delegated (work or school account) User.Read
Delegated (personal Microsoft account) User.Read
Application User.Read.All

Example

So that the theory can now be applied and you can see a result, I have packed the two Microsoft Graph Bicep resources Users and Groups together.In the following example, a new Entra group is created and filled with existing members in the tenant. I will intentionally not show the complete code here, as it would go beyond the scope. I am happy to share with you the Github repository where you can find the code.

The two resources were created in separate bicep modules so that the whole thing is clearer in the main.bicep file.

users.bicep

extension microsoftGraphV1

@description('List of User Principal Names (UPNs)')
param upnList array

var upnListLength = length(upnList)

resource userList 'Microsoft.Graph/[email protected]' existing = [for i in range(0, upnListLength): {
  userPrincipalName: upnList[i]
}]

output userIds array = [for i in range(0, upnListLength): userList[i].id]
output userPrincipalNames array = upnList

groups.bicep

extension microsoftGraphV1

@description('Name of the group to be created')
param groupName string

@description('List of User IDs to be added as members')
param userIds array

resource group 'Microsoft.Graph/[email protected]' = {
  displayName: groupName
  mailEnabled: false
  mailNickname: uniqueString(groupName)
  securityEnabled: true
  uniqueName: groupName
  members: userIds
}

output groupId string = group.id
output groupDisplayName string = group.displayName

main.bicep

extension microsoftGraphV1

// Parameters
@description('Name of the group to be created')
param groupName string

@description('List of User Principal Names (UPNs)')
param upnList array

// Module invocations
module users 'modules/users.bicep' = {
  name: 'users'
  params: {
  upnList: upnList
  }
}

module group 'modules/groups.bicep' = {
  name: 'group'
  params: {
  groupName: groupName
  userIds: users.outputs.userIds
  }
}

// Outputs
output addedUserList array = users.outputs.userPrincipalNames
output groupName string = group.outputs.groupDisplayName
output groupId string = group.outputs.groupId

main.bicepparam

using './main.bicep'

param groupName = 'contoso'
param upnList = [
  '[email protected]'
  '[email protected]'
  '[email protected]'
]

Conclusion

To summarise, we can say that Microsoft is taking another step in the right direction with the support of Graph in Bicep. The current integration of Microsoft Graph into existing automation pipelines in particular is not ideal, as at least a second language has to be used and does not really follow the declarative approach.

To be fair, however, it has to be said that almost a year after the public preview was communicated, no real progress has been made here. The samples provided by Microsoft employees are still relatively few and my example in particular has shown that they don’t always work.

Nevertheless, I look forward to blogging more about this in the coming weeks and finding out what works and what doesn’t. I’m also already looking forward to the first GA release of Microsoft Graph for Bicep.

References

You might also like