How to use Azure Bicep to provision permission (Microsoft.Authorization/roleAssignments) for Azure function to access Microsoft graph API? - azure-managed-identity

I have an Azure function with Managed identity that needs to access Microsoft graph api (specifically Application.Read.All, ServicePrincipalEndpoint.Read.All roles). The Azure function infrastructure is created using Bicep file. Is it possible to provision access to Graph API using 'Microsoft.Authorization/roleAssignments'?
Below is what i have attempted but it fails with message The specified role definition with ID '9f9ce928e0384e3b8faf7b59049a8ddc' does not exist.
resource roleAssignmentForMicrosoftGraphApiAppRegistration 'Microsoft.Authorization/roleAssignments#2020-10-01-preview' = {
name: guid('9a5d68dd-52b0-4cc2-bd40-abcf44ac3a30', functionApp.id, functionAppName)
properties: {
principalType: 'ServicePrincipal'
principalId: functionApp.identity.principalId
roleDefinitionId: '/providers/Microsoft.Authorization/roleDefinitions/9a5d68dd-52b0-4cc2-bd40-abcf44ac3a30'
}
}
Note: I know that the roles can be assigned using power shell scripts (called within Bicep), i was looking for much simpler solution. For example, the trust b/w Azure function and its storage account can be done using Managed identity access using below snippet, is smilar solution availabel for accessing Graph api?
resource roleAssignmentForStorageAccount 'Microsoft.Authorization/roleAssignments#2020-10-01-preview' = {
name: guid(storageAccount.id, functionApp.id, functionAppName)
scope: storageAccount
properties: {
principalType: 'ServicePrincipal'
principalId: functionApp.identity.principalId
roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e****************************1')
}
}

Related

Snowflake Connectivity from .NetCore API Service

I have developed an Api service ( c# program) using .NetCore3.1. I have used Snowflake.Client dll library in my Api service to connect to Snowflake Database. We have 3 Snowflake accounts (i.e. Dev account, UAT account and PROD account). Recently, the Snowflake database name is changed in these 3 accounts and NOW the database name is not the same. For example, the database name is changed to DataWareHouse_DEV in Dev account, DataWareHouse_UAT in UAT account and DataWareHouse in PROD account.
Currently the Snowflake.Client library accepts only four parameters. see below code snippet
public SnowflakeClient(string user, string password, string account, string region = null);
My Configuration file:
"SnowflakeSettings": {
"Account": "AB4444",
"User": "SOME_SERVICE_ACCOUNT",
"Password": "Password#123!",
"Region": "east-us.azure"
}
Need help and inputs to handle the need:
I want to pass another parameter Databasename so that I can add it to the configuration. How do i pass another parameter besides the user, password, account and region? what is the appropriate library or methods i have to use for Snowflake connectivity?
Thanks
Vam

Need of scope parameter in Microsoft.Identity.Web downstream API

I am using microsoft.Identity.Web package on my .netcore API project which calls Graph API to get the directory objects of the user.
In the appsettings file the downstream api settings are provided as below,
"DownstreamApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "Directory.Read.All"
},
The relevant permission(Directory.Read.All) is setup in the app registration.
But even if I leave the "Scope" parameter blank the API is giving me the directory objects.
So if the settings is of the format below it still works. Then what is the need of this scope parameter?
"DownstreamApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": ""
},
The scope claim might not had reflected in the token and so you might not seeing any difference with scope assigned.
user_impersonation is the default delegated permission /scope that exists initially for every Web app or API in Azure AD.
Please make sure to add the required delegated permissions or application permission in portal.And grant consent if required.
In your case add directory.read.all Application permission
ex:I added user.read
Appsettings:
"DownstreamApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "user.read"
},
In startUp.cs
Public void ConfigureServices(IServiceCollection services)
{
string[] initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration)
// acquire a token to call a protected web API
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
//
//othercode
...
}
And in controller we need to specify scopes and send to request headers to get access token for required scopes.
References:
call Microsoft Graph | Microsoft Docs
(OR) active-directory-aspnetcore-webapp-openidconnect-v2 (github.com)
How can I create a new Azure App Registration without the user_impersonation OAuth2Permission? - Stack Overflow
If client_credentials is the grant type you may need to use https://graph.microsoft.com/.default for scope in the application settings which will give you the permissions defined for your app.
"DownstreamApi": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": "https://graph.microsoft.com/.default"
}
Try to use /token endpoint in request and not common
Please see:
ASP.NET Core - Call Graph API Using Azure Ad Access Token - Stack Overflow-Reference

code for using O365 groups in a .net core app

I am working on a .net core app and have to integrate O365 security groups for roles assignment, does someone have sample code to share, will be very helpful.
I have already used Azure AD app registration concept for O365 authentication and its working perfectly. .Net core app is hosted on IIS, when accessed by typing in url in browser, it redirects users to login.microsoftonline.com, once authenticated, users then see dashboard part of .net core app.
Not so sure about how O365 groups can be used in .net core app for permissions management, so looking for some sample snippet, thanks in advance.
You can query graph api either as your app or impersonate the user, to read which groups the user is in and then use those Id to filter views or what ever you need to do.
you can use the "List memberOf"
https://learn.microsoft.com/en-us/graph/api/user-list-memberof?view=graph-rest-1.0
Hope it helps.
Office365 security groups can be used for permissions management in your app, by verifying if a user is a member of a security group. You can achieve that by using Microsoft Graph API as MohitVerma suggested.
First, define groups to roles mapping in your app (configuration file seems to be a good place for that). Each group has a unique id, which you can get using e.g. Office365 or Microsoft Graph and map to a custom role in your config.json file:
{
"AppRoles": [
"Admin": "d17a5f86-57f4-48f8-87a0-79761dc8e706",
"Manager": "9a6a616e-5637-4306-b1fe-bceeaa750873"
]
}
Then, after successful login to the app, call the Graph API to get all groups the user belongs to. You will get a list of groups, each containing id property:
GET https://graph.microsoft.com/v1.0/me/memberOf
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#directoryObjects",
"value": [
{
"#odata.type": "#microsoft.graph.directoryRole",
"id": "43a63cc2-582b-4d81-a79d-1591f91d5558",
"displayName": "Company Administrator",
"roleTemplateId": "62e90394-69f5-4237-9190-012177145e10"
},
{
"#odata.type": "#microsoft.graph.group",
"id": "d17a5f86-57f4-48f8-87a0-79761dc8e706",
"createdDateTime": "2017-07-31T17:36:25Z",
"displayName": "Admins group",
"securityEnabled": true
}
]
}
You can use MS Graph SDK for .NET to make a request and to create a group objects form the response:
var userGroups = await graphServiceClient.Me.Groups.Request().GetAsync();
Finally, verify the id of each group with your custom roles, e.g.:
public string GetRole(IEnumerable<Group> userGroups, IConfiguration config)
{
foreach (var group in userGroups)
{
switch (group.id)
{
case config.GetSection("AppRoles:0"):
return "Admin";
case config.GetSection("AppRoles:1"):
return "Manager";
default:
return "Unknown";
}
}
}
Make sure to grant permissions for your app to access Microsoft Graph.

Exception using Azure Managed Service Identity across tenants

I'm building an Azure web app for a client that will be provisioned into many other directories for their customers. This app will call a web API in my client's directory, which will then call back to another web API in the customer's directory. Something like this:
Other Customer AAD1 --------- My client AAD2
App --------------------------------> Web API 2
Web API 1 <-------------------------- Web API 2
We have been able to get the first call to work. This requires a corresponding App Registation for Web API 2 in AAD1. We figure that we could get the callback to work by following the same pattern, with a registration for Web API1 in AAD2. However, that might be a LOT of these 'proxy' registration in my client's AAD, so we're looking at alternatives.
We are exploring using Managed Service Identity, which we think will allow us to get tokens that are valid for resources in other tenants. If there's a better way, I'm certainly interested in knowing about it.
I've followed the code example from here using the Microsoft.Azure.Services.AppAuthentication library: https://learn.microsoft.com/en-us/azure/app-service/app-service-managed-service-identity#obtaining-tokens-for-azure-resources
// In Web API 2
using Microsoft.Azure.Services.AppAuthentication;
// ...
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync(
"https://<App ID URI for Web API1>");
Web API2 is configured to have a Managed Service Identity.
I'm currently running this on my local machine, and I've installed Azure CLI and I'm logged in. I've tried 'az account get-access-token', and I get a valid token.
When Web API2 tries to get the token to be able to call Web API1, I get an exception:
Parameters: Connectionstring: [No connection string specified], Resource: , Authority: . Exception Message: Tried the following 2 methods to get an access token, but none of them worked.
Parameters: Connectionstring: [No connection string specified], Resource: , Authority: . Exception Message: Tried to get token using Managed Service Identity. Unable to connect to the Managed Service Identity (MSI) endpoint. Please check that you are running on an Azure resource that has MSI setup.
Parameters: Connectionstring: [No connection string specified], Resource: , Authority: . Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. ERROR: Get Token request returned http error: 400 and server response: {"error":"invalid_grant","error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID '04b07795-8ddb-461a-bbee-02f9e1bf7b46' named 'Web API 1'. Send an interactive authorization request for this user and resource.\r\nTrace ID: f5bb0d4d-6f92-4fdd-81b7-e82a78720a00\r\nCorrelation ID: 04f92114-8d9d-40c6-b292-965168d6a919\r\nTimestamp: 2017-10-19 16:39:22Z","error_codes":[65001],"timestamp":"2017-10-19 16:39:22Z","trace_id":"f5bb0d4d-6f92-4fdd-81b7-e82a78720a00","correlation_id":"04f92114-8d9d-40c6-b292-965168d6a919"}
What's interesting is that there's no application with ID '04b07795-8ddb-461a-bbee-02f9e1bf7b46' in either AAD1 or AAD2. Is this a known Azure app? I thought that it might be the Service Management API, but I'm not sure.
In any case, I'm not sure of the proper way to grant permission. I've tried building different content URLs like this into my browser, but none of them seem to have done the trick:
https://login.microsoftonline.com/(AAD1 ID)/adminconsent
?client_id=(App ID)
&redirect_uri=https://localhost:44341
&resource=(App ID URI for Web API1)
&prompt=admin_consent
https://login.microsoftonline.com/(AAD1 ID)/adminconsent
?client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46
&redirect_uri=https://localhost:44341
&resource=(App ID URI for Web API1)
&prompt=admin_consent
(This last one tells me that the reply URL is incorrect; since it's not one of my apps, I can't find the reply URL)
Note that the tenant is AAD1.
Am I missing something, or am I not using this feature correctly?
Thanks in advance.
AzureServiceTokenProvider uses Azure CLI (among other options) for local development. For a scenario where a service calls an Azure Service, this works using the developer identity from Azure CLI, since Azure services allow access to both users and applications.
For a scenario where a service calls another custom service (like your scenario), you need to use a service principal for local development. For this, you have two options:
Login to Azure CLI using a service principal.
First, create a service principal for local development
https://learn.microsoft.com/en-us/cli/azure/create-an-azure-service-principal-azure-cli?view=azure-cli-latest
Then login to Azure CLI using it.
az login --service-principal -u 25922285-eab9-4262-ba61-8083533a929b --password <<pwd>> --tenant 72f988bf-86f1-41af-91ab-2d7cd011db47 --allow-no-subscriptions
Use the --allow-no-subscriptions argument since this service principal may not have access to any subscription.
Now, AzureServiceTokenProvider will get a token using this service principal for local development.
Specify service principal details in an environment variable. AzureServiceTokenProvider will use the specified service principal for local development. Please see the section Running the application using a service principal in local development environment in this sample on how to do that. https://github.com/Azure-Samples/app-service-msi-keyvault-dotnet
Note: Ths is only for local development. AzureServiceTokenProvider will use MSI when deployed to App Service.

APIGEE Application level

I am trying to access a collection using my application client_id, and client secret as follows:
https://api.usergrid.com/my_org/my_app/my_collection?client_id=XXXXXX&client_secret=XXXXXX
I get the following error:
{
"error":"unauthorized",
"timestamp":1416953278007,
"duration":0,
"exception":"org.apache.usergrid.rest.exceptions.SecurityException",
"error_description":"No application access authorized"
}
Is there a setting in my app somewhere that I am not aware of?
You need to create an access code. This is explained here: http://apigee.com/docs/app-services/content/authenticating-users-and-application-clients. To use the access token, follow the instructions on this page: http://apigee.com/docs/app-services/content/authenticating-api-requests

Resources