Skip to content

Commit f37c24f

Browse files
authored
Merge branch 'main' into dkershaw10-users-sample
2 parents 0295beb + 16f39aa commit f37c24f

File tree

5 files changed

+219
-0
lines changed

5 files changed

+219
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Configure an app with OAuth2.0 scopes to call Microsoft Graph
2+
3+
This template sample creates a client application and depending on the mode parameter, either:
4+
5+
1. Sets required resource access on the client application definition, or
6+
2. Grants OAuth2.0 scopes to the client application.
7+
8+
In either case, the target resource used is Microsoft Graph, and the deployer can select which Microsoft Graph OAuth2.0 scopes are used.
9+
10+
## Details
11+
12+
This sample operates in two modes, depending on the `mode` parameter.
13+
14+
1. `set-required-scopes`:
15+
16+
- The sample creates a basic client application configuring the `requiredResourceAccess` property with Microsoft Graph Oauth2.0 scopes
17+
- This option uses the appRequiredResourceAccess.bicep module.
18+
- **NOTE:** The `requiredResourceAccess` configures which permissions the client application requires and this drives the **user consent experience** where those permissions can be granted. `requiredResourceAccess` itself does **not** grant any permissions to the client application.
19+
20+
2. `grant-scopes`:
21+
22+
- The sample creates a basic client application (and does not configure `requiredResourceAccess`), creates a service principal from the application, and finally grants the desired Microsoft Graph OAuth2.0 scopes to the service principal (using the `Microsoft.Graph/oauth2PermissionGrants` bicep type).
23+
- This option uses the appGrantScopes.bicep module.
24+
- **NOTE:** Setting `requiredResourceAccess` on a client application is **not** required to grant OAuth2.0 scopes to the client application.
25+
26+
The `appScopes` array parameter allows the deployer to select the Microsoft Graph Oauth2.0 scopes to set for or grant to the client application. The sample validates the set of provided scopes in the array parameter against [Microsoft Graph delegated permission scopes][graph-permissions]. Any invalid scopes provided are ignored. `appScores` should contain a list of scope names (for example *User.Read.All* and *Group.ReadWrite.All*).
27+
28+
### Prerequisites
29+
30+
- A valid **Azure subscription**: If you don't own an Azure subscription, [create a free account](https://azure.microsoft.com/free/) before you begin.
31+
- An **Azure resource group** that you own under a valid Azure subscription, or [deploy without an Azure subscription][no-azure-sub].
32+
- [Bicep tools for authoring and deployment](https://learn.microsoft.com/graph/templates/quickstart-install-bicep-tools). The minimum required Bicep version is v0.30.3.
33+
- Have the requisite **Microsoft Entra roles** to deploy this template:
34+
35+
- Permissions to create applications. [Users have this permission by default](https://learn.microsoft.com/entra/fundamentals/users-default-permissions#compare-member-and-guest-default-permissions). However, [admins can turn off this default](https://learn.microsoft.com/entra/fundamentals/users-default-permissions#restrict-member-users-default-permissions) in which case you need to be assigned at least the [Application Developer](https://learn.microsoft.com/entra/identity/role-based-access-control/permissions-reference#application-developer) role.
36+
- **Additionally**, if using the `grant-scopes` mode, you'll also need the privileges to grant Microsoft Graph app roles to the application. This requires the [Privileged Role Administrator][priv-role-admin]
37+
38+
### Deploy the Bicep template
39+
40+
By default, the Bicep template (main.bicep) will operate in the `set-required-scopes` mode.
41+
42+
#### Az CLI
43+
44+
```sh
45+
az deployment group create --resource-group <resource-group> --template-file main.bicep --parameter date='2025-01-24' appScopes="['User.Read','Application.Read.All']"
46+
```
47+
48+
#### Az Powershell
49+
50+
```powershell
51+
New-AzResourceGroupDeployment -ResourceGroupName <resource-group> -TemplateFile .\main.bicep -date "2025-01-24" -appScopes @('User.Read','Application.Read.All')
52+
```
53+
54+
[priv-role-admin]:https://learn.microsoft.com/entra/identity/role-based-access-control/permissions-reference#privileged-role-administrator
55+
[graph-permissions]:https://learn.microsoft.com/graph/permissions-reference
56+
[no-azure-sub]:https://learn.microsoft.com/graph/templates/how-to-deploy-without-azure-sub?view=graph-bicep-1.0&tabs=CLI
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
extension microsoftGraphV1
2+
3+
// TEMPLATE DESCRIPTION
4+
/* Grant OAuth2.0 scopes to a client application definition,
5+
where the target resource used is Microsoft Graph, and the deployer can select which
6+
Microsoft Graph OAuth2.0 scopes are granted on the client app.
7+
8+
NOTE: Setting requiredResourceAccess on a client application is NOT required
9+
to grant OAuth2.0 permissions to the client application.
10+
*/
11+
12+
param date string
13+
param displayName string?
14+
param filteredScopes array
15+
param graphSpId string
16+
17+
var app = 'myApp'
18+
19+
// convert scopes array into space separate scopes string
20+
var scopeArray = [for (scopeItem,i) in filteredScopes: filteredScopes[i].value]
21+
var scopeString = join(scopeArray, ' ')
22+
23+
// create basic app
24+
resource myApp 'Microsoft.Graph/[email protected]' = {
25+
displayName: displayName == null ? '${app}-${date}' :'${displayName}-${app}-${date}'
26+
uniqueName: uniqueString(app, date)
27+
}
28+
29+
// Create service principal for the basic app
30+
resource mySP 'Microsoft.Graph/[email protected]' = {
31+
appId: myApp.appId
32+
}
33+
34+
// Grant the OAuth2.0 scopes (requested in parameters) to the basic app,
35+
// for all users in the tenant
36+
resource graphScopesAssignment 'Microsoft.Graph/[email protected]' = {
37+
clientId: mySP.id
38+
resourceId: graphSpId
39+
consentType: 'AllPrincipals'
40+
scope: scopeString
41+
}
42+
43+
// output information
44+
output appName string = myApp.displayName
45+
output appObjectID string = myApp.id
46+
output appID string = myApp.appId
47+
output scopes array = scopeArray
48+
output grantedScopes string = graphScopesAssignment.scope
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
extension microsoftGraphV1
2+
3+
// TEMPLATE DESCRIPTION
4+
/* Set the required resource access on a client application definition.
5+
The target resource used is Microsoft Graph, and the deployer can select which
6+
Microsoft Graph OAuth2.0 scopes are configured on the client app.
7+
8+
NOTE: requiredResourceAccess configures which permissions the client application
9+
requires and this drives the user consent experience where permissions are granted.
10+
requiredResourceAccess itself does NOT grant any permissions to the client application.
11+
*/
12+
13+
param date string
14+
param displayName string?
15+
param filteredScopes array
16+
17+
var app = 'myApp'
18+
var graphAppId = '00000003-0000-0000-c000-000000000000'
19+
20+
// create an application with the requiredResourceAccess property
21+
// creates a resourceAccess scope for each Microsoft Graph scope in filteredScopes
22+
resource myApp 'Microsoft.Graph/[email protected]' = {
23+
displayName: displayName == null ? '${app}-${date}' :'${displayName}-${app}-${date}'
24+
uniqueName: uniqueString(app, date)
25+
requiredResourceAccess: [
26+
{
27+
resourceAppId: graphAppId
28+
resourceAccess: [ for (scope, i) in filteredScopes: {
29+
id: filteredScopes[i].id
30+
type: 'Scope'
31+
}
32+
]
33+
}
34+
]
35+
}
36+
37+
// output information
38+
output appName string = myApp.displayName
39+
output appObjectID string = myApp.id
40+
output appID string = myApp.appId
41+
output scopes array = [for (scopeItem,i) in filteredScopes: filteredScopes[i].value]
42+
output clientAppResourceAccessList array = myApp.requiredResourceAccess[0].resourceAccess
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"experimentalFeaturesEnabled": {
3+
"extensibility": true
4+
},
5+
// specify an alias for the version of the v1.0 dynamic types package you want to use
6+
"extensions": {
7+
"microsoftGraphV1": "br:mcr.microsoft.com/bicep/extensions/microsoftgraph/v1.0:0.1.9-preview"
8+
}
9+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
extension microsoftGraphV1
2+
3+
// TEMPLATE DESCRIPTION
4+
/* Create a client application and depending on the mode parameter, either:
5+
1. Sets required resource access on the client application definition OR
6+
2. Grants OAuth2.0 scopes to the client application
7+
8+
In either case, the target resource used is Microsoft Graph, and the deployer
9+
can select which Microsoft Graph OAuth2.0 scopes are used.
10+
11+
This bicep file utilizes two modules (one for each mode).
12+
*/
13+
14+
@description('Supply today\'s date to deploy the template')
15+
param date string
16+
17+
@description('Provide a friendly display name for the app')
18+
param displayName string?
19+
20+
@description('Provide an array of Microsoft Graph scopes like "User.Read"')
21+
param appScopes array = ['profile','User.Read']
22+
23+
@description('Configure is setting required resource access or granting scopes')
24+
@allowed(['set-required-scopes','grant-scopes'])
25+
param mode string = 'set-required-scopes'
26+
27+
var graphAppId = '00000003-0000-0000-c000-000000000000'
28+
29+
// Get the Microsoft Graph service principal so that the scope names
30+
// can be looked up and mapped to a permission ID
31+
resource msGraphSP 'Microsoft.Graph/[email protected]' existing = {
32+
appId: graphAppId
33+
}
34+
35+
var graphScopes = msGraphSP.oauth2PermissionScopes
36+
var filteredScopes = filter(graphScopes, scope => contains(appScopes, scope.value))
37+
38+
39+
module appCreateRraModule './appRequiredResourceAccess.bicep' = if(mode == 'set-required-scopes'){
40+
name: 'appRraDeploy'
41+
params: {
42+
filteredScopes: filteredScopes
43+
date: date
44+
displayName: displayName
45+
}
46+
}
47+
48+
module appCreateGrantScopesModule './appGrantScopes.bicep' = if (mode == 'grant-scopes') {
49+
name: 'appScopeGrantDeploy'
50+
params: {
51+
filteredScopes: filteredScopes
52+
date: date
53+
displayName: displayName
54+
graphSpId: msGraphSP.id
55+
}
56+
}
57+
58+
// outputs
59+
output appName string = ((mode == 'set-required-scopes') ? appCreateRraModule.outputs.appName : appCreateGrantScopesModule.outputs.appName)
60+
output appObjectID string = ((mode == 'set-required-scopes') ? appCreateRraModule.outputs.appObjectID : appCreateGrantScopesModule.outputs.appObjectID)
61+
output appID string = ((mode == 'set-required-scopes') ? appCreateRraModule.outputs.appID : appCreateGrantScopesModule.outputs.appID)
62+
output foundInputScopes array = ((mode == 'set-required-scopes') ? appCreateRraModule.outputs.scopes: appCreateGrantScopesModule.outputs.scopes)
63+
output clientAppResourceAccessList array = ((mode == 'set-required-scopes') ? appCreateRraModule.outputs.clientAppResourceAccessList : ['Not set'])
64+
output grantedScopes string = ((mode == 'grant-scopes') ? appCreateGrantScopesModule.outputs.grantedScopes : 'Not set')

0 commit comments

Comments
 (0)