I have no problem adding a user to a role using
https://graph.windows.net/{TenantId}/users/{UserId}/appRoleAssignments?api-version=1.5
string data = JsonConvert.SerializeObject(new
{
id = roleId,
principalDisplayName = userEmail,
principalId = userId,
principalType = "User",
resourceId = servicePrincipalId
});
But this is not working for groups by changing:
https://graph.windows.net/{TenantId}/groups/{GroupId}/appRoleAssignments?api-version=1.5
principalDisplayName = GroupDisplayName,
principalId = groupId,
principalType = "Group"
Also tried without the principalDisplayName
I am receiving "Bad Request" and in Fiddler "One or more properties are invalid." with no extra information.
Can you login to azure portal with your tenant credential and check if you can assign AAD group to role? It's most likely related to your permission
Azure Active Directory -> Enterprise applications -> Find your application -> Users and Groups -> Add User
Related
We would like to expose some datasets that are common for all customers and could be imported into their powerbi reports across different workspaces.
We have a design where we use one service principle profile per customer. This in provides securoty around isolating the data of each customer. Is there any way (using service principle profiles) to be able to support the sharing of some common data across workspaces?
Is there any way (using service principal profiles) to be able to support the sharing of some common data across workspaces?
Yes, you can create a security group in Azure AD and add all the service principals that are modelled to your customers in that group and then provide that security group access to your workspace by adding the group as with required permissions on the workspace where the shared dataset exists. If you want to give access to individual service principals, you can do that too.
Your customers can then use the SP’s client ID, Tenant ID and Secret to access the dataset via their app in the browser or even by calling the data via PowerShell. You can directly share your dataset with the security group containing your Service principals.
Created a security group in Azure AD and added my service principal to the group: -
Power BI settings:-
Go to app.powerbi.com Log in to your Power BI workspace > click on Settings > Admin Portal > Tenant Settings > Developer Settings > Allow service principals to use Power BI API’s Enable > and then provide access to your security group
There are 2 ways you can share your data with the Service principal
1. Sharing directly
By directly sharing the workspace dataset with the Security group where the service principals exist.
2. Access permissions on workspace
Provide Access to the workspace so the sp’s can access data set directly via their app or call the workspace via Power shell.
Go to your workspace > Select your data and click on … dots > Manage permissions > Grant people access > Select your Power BI Embed group
You can change the read write permissions later >
Calling Dataset via Application -
Now, We can call this Dataset from our application by adding the Authentication to Service principal and adding the SP’s Client ID, Tenant ID, Client Secret etc. You can refer this document :-
https://learn.microsoft.com/en-us/power-bi/developer/embedded/embed-organization-app
Authentication Code :
This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices (IServiceCollection services) {
services
.AddMicrosoftIdentityWebAppAuthentication(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi(PowerBiServiceApi.RequiredScopes)
.AddInMemoryTokenCaches();
services.AddScoped (typeof (PowerBiServiceApi));
var mvcBuilder = services.AddControllersWithViews (options => {
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add (new AuthorizeFilter (policy));
});
mvcBuilder.AddMicrosoftIdentityUI();
services.AddRazorPages();
}
app.settings:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "xxxx.onmicrosoft.com",
"TenantId": "xxxxxxxxxxxxx",
"ClientId": "xxxxxxxxxxxxx",
"ClientSecret": "xxxxxxxx",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath": "/signout-callback-oidc"
},
"PowerBi": {
"ServiceRootUrl": "https://api.powerbi.com"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
Controller.cs
private PowerBiServiceApi powerBiServiceApi;
public HomeController (PowerBiServiceApi powerBiServiceApi) {
this.powerBiServiceApi = powerBiServiceApi;
}
[AllowAnonymous]
public IActionResult Index() {
return View();
}
public async Task<IActionResult> Embed() {
Guid workspaceId = new Guid("11111111-1111-1111-1111-111111111111");
Guid reportId = new Guid("22222222-2222-2222-2222-222222222222");
var viewModel = await powerBiServiceApi.GetReport(workspaceId, reportId);
return View(viewModel);
}
[AllowAnonymous]
[ResponseCache (Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error() {
return View (new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
Embed your powerbi data set with JS
$(function(){
// 1 - Get DOM object for div that is report container
let reportContainer = document.getElementById("embed-container");
// 2 - Get report embedding data from view model
let reportId = window.viewModel.reportId;
let embedUrl = window.viewModel.embedUrl;
let token = window.viewModel.token
// 3 - Embed report using the Power BI JavaScript API.
let models = window['powerbi-client'].models;
let config = {
type: 'report',
id: reportId,
embedUrl: embedUrl,
accessToken: token,
permissions: models.Permissions.All,
tokenType: models.TokenType.Aad,
viewMode: models.ViewMode.View,
settings: {
panes: {
filters: { expanded: false, visible: true },
pageNavigation: { visible: false }
}
}
};
// Embed the report and display it within the div container.
let report = powerbi.embed(reportContainer, config);
Add such codes depending on the framework of your customer’s App and run the app to access the Power BI data.
Accessing PowerBI workspace with Powershell -
Refer the document here :- https://learn.microsoft.com/en-us/powershell/module/microsoftpowerbimgmt.profile/connect-powerbiserviceaccount?view=powerbi-ps
Powershell commands :-
Install PowerBI Powershell module -
Install-Module -Name MicrosoftPowerBIMgmt
Connect to PowerBI SP -
Connect-PowerBIServiceAccount -ServicePrincipal -Credential (Get-Credential) -Tenant 83331f4e-7f45-4ce4-99ed-af9038592395
In the User name enter the App Id of the SP and in Password add the secret that was created for the SP during app registration.
connected to PowerBI successfully :-
Get the workspace -
Get-PowerBIWorkspace
Reference :-
https://learn.microsoft.com/en-us/power-bi/developer/embedded/embed-service-principal
https://learn.microsoft.com/en-us/power-bi/developer/embedded/embed-organization-app
I have created an application using graph API and I have assigned them permission - both delegated and application...
ServicePrincipal servicePrincipal = graphClient.servicePrincipals(resSerPrinId)
.buildRequest()
.get();
List<AppRole> appRoles = servicePrincipal.appRoles;
List<PermissionScope> scopes = servicePrincipal.oauth2PermissionScopes;
List<ResourceAccess> raList = new ArrayList<ResourceAccess>();
for (AppRole appRole : appRoles) {
ResourceAccess access = new ResourceAccess();
access.id = appRole.id;
access.type = "Role";
raList.add(access);
}
System.out.println("Roles added...");
for (PermissionScope permissionScope : scopes) {
ResourceAccess access = new ResourceAccess();
access.id = permissionScope.id;
access.type = "Scope";
raList.add(access);
}
System.out.println("Scopes added...");
RequiredResourceAccess reqResAccess = new RequiredResourceAccess();
reqResAccess.resourceAccess = raList;
reqResAccess.resourceAppId = resSerPrinAppClientId;
List<RequiredResourceAccess> rraList = new ArrayList<RequiredResourceAccess>();
rraList.add(reqResAccess);
Application application = graphClient.applications(clientAppObjId)
.buildRequest()
.get();
application.requiredResourceAccess = rraList;
graphClient.applications(clientAppObjId)
.buildRequest()
.patch(application);
Here in the code above, resSerPrinId is resource service principal Id which has app roles in manifest and a scope in "expose an api" section...
So I am pulling out appRoles and oauth2Permission from that resource service principal and sending them to client service principal...
In the UI I am seeing that the permissions do not have grant...
Is it possible to give them admin grant using some graph API or or manually loading these permission and then giving them admin grant...or do I need to always use the UI to do it...?
So we have basically 2 types of permissions - roles (application)/scope (delegated)
So to provide "grant admin consent" to your delegated permissions, use the below snippet
GraphServiceClient graphClient = GraphServiceClient.builder().authenticationProvider( authProvider ).buildClient();
OAuth2PermissionGrant oAuth2PermissionGrant = new OAuth2PermissionGrant();
oAuth2PermissionGrant.clientId = "clientId-value";
oAuth2PermissionGrant.consentType = "consentType-value";
oAuth2PermissionGrant.principalId = "principalId-value";
oAuth2PermissionGrant.resourceId = "resourceId-value";
oAuth2PermissionGrant.scope = "scope-value";
graphClient.oauth2PermissionGrants()
.buildRequest()
.post(oAuth2PermissionGrant);
Here is the link to documentation - Read about oAuth2PermissionGrant here
Now to provide "grant admin consent" to application permissions, use the below snippet...
GraphServiceClient graphClient = GraphServiceClient.builder().authenticationProvider( authProvider ).buildClient();
AppRoleAssignment appRoleAssignment = new AppRoleAssignment();
appRoleAssignment.principalId = UUID.fromString("33ad69f9-da99-4bed-acd0-3f24235cb296");
appRoleAssignment.resourceId = UUID.fromString("9028d19c-26a9-4809-8e3f-20ff73e2d75e");
appRoleAssignment.appRoleId = UUID.fromString("ef7437e6-4f94-4a0a-a110-a439eb2aa8f7");
graphClient.servicePrincipals("9028d19c-26a9-4809-8e3f-20ff73e2d75e").appRoleAssignedTo()
.buildRequest()
.post(appRoleAssignment);
Here is the link to documentation - Read more about AppRoleAssignment here
You have to use these two bad boys right here and its done - just use the correct Ids (do not confuse client id with client principal id and likewise)
Basically the flow is - get all permissions (both types loaded up using Required Resource Access - then add admin grants to all the permissions using above code. Hope this helps. comment if you need more help.
I am trying to run the sample at:
https://github.com/Azure-Samples/ms-identity-dotnetcore-b2c-account-management
And am receiving this error:
Enter command, then press ENTER: 7
Create a user with the custom attributes 'FavouriteSeason' (string) and 'LovesPets' (boolean)
Have you created the custom attributes 'FavouriteSeason' (string) and 'LovesPets' (boolean) in your tenant?
Code: Request_BadRequest
Message: One or more property values specified are invalid.
Inner error:
AdditionalData:
date: 2020-06-30T23:24:26
request-id: dad23cee-984b-439c-a943-9e1bc6be4c9b
ClientRequestId: dad23cee-984b-439c-a943-9e1bc6be4c9b
I have created the custom attributes and can clear see them in the tenant...it even returns their ids. I've setup the Graph access level appropriately (I believe).
Any ideas? Thank you!
In Code, you need to update Tenant id, client id, client secret and also give B2cExtensionAppClientId= “your application display name” in appsettings.json
You have to create two custom attributes in the B2C portal
I. FavouriteSeason (string)
II. LovesPets (boolean)
Use below code to create the user with the custom attribute
try
{
//Create user
var result = await graphClient.Users
.Request()
.AddAsync(new User
{
GivenName = "Casey",
Surname = "Jensen",
DisplayName = "Casey Jensen",
Identities = new List<ObjectIdentity>
{
new ObjectIdentity()
{
SignInType = "emailAddress",
Issuer ="AADCxPb2c.onmicrosoft.com",
IssuerAssignedId = "x#AADCxPb2c.onmicrosoft.com",
}
},
PasswordProfile = new PasswordProfile()
{
Password = b2c_ms_graph.Helpers.PasswordHelper.GenerateNewPassword(4, 8, 4)
},
PasswordPolicies = "DisablePasswordExpiration",
AdditionalData = extensionInstance
}) ;
Console.WriteLine(result);
string userId = result.Id;
Console.WriteLine(result.Id);
Console.WriteLine($"Created the new user. Now get the created user with object ID '{userId}'...");
Can find it on your registered App on Azure AD B2C:
Click on "API Permissions" on left panel.
Then Chose "APIs my organization uses".
You can find the value under name "b2c-extensions-app. Do not modify. Used by AADB2C for storing user data.".
Hi I am trying to add extension property to azure ad user using Microsoft.Graph package.
var schema = new SchemaExtension()
{
Id = "Location",
TargetTypes = new List<string> { "User" },
Description = "DescribesLocation",
Properties = new List<ExtensionSchemaProperty>() { new ExtensionSchemaProperty { Name = "LocationCode", Type = "String" } }
};
var result = graphClient.SchemaExtensions.Request().AddAsync(schema).Result;
I have created a daemon application in azure ad registered applications and given below permissions to the application
Directory.Read.All
Directory.ReadWrite.All
User.Invite.All
User.Read
User.Read.All
User.ReadWrite.All
Group.Read.All
Group.ReadWrite.All
I am getting error message as Code: Authorization_RequestDenied
Message: Insufficient privileges to complete the operation. What other permissions are required for this operation and which permissions are not required?
You need the Directory.AccessAsUser.All permission. Here are some test cases that you may find useful.
I have successfully been able to share a OneDrive for Business file with a group using the Sharepoint CMOS API.
Like this:
context.Load(listItem, l => l.HasUniqueRoleAssignments);
context.ExecuteQuery();
if (!listItem.HasUniqueRoleAssignments)
{
listItem.BreakRoleInheritance(true, false);
}
var group = context.Web.SiteGroups.GetByName(groupName);
context.Load(context.Site.RootWeb, w => w.RoleDefinitions);
context.ExecuteQuery();
var roleTypeKind = readOnly ? RoleType.Reader : RoleType.Editor;
var role = context.Site.RootWeb.RoleDefinitions.FirstOrDefault(rd => rd.RoleTypeKind == roleTypeKind);
var roleDefCollection = new RoleDefinitionBindingCollection(context) { role };
listItem.RoleAssignments.Add(group, roleDefCollection);
context.ExecuteQuery();
But the problem is that group belongs to SiteGroups which is really local to one user. So if I have an Azure AD Group I cannot access it using the Sharepoint API.
I can find an Azure AD group using the GraphClient API like this:
var azuregroup = activeDirectoryClient.Groups
.Where(g=> g.DisplayName.Equals("groupname"))
.ExecuteAsync().Result.CurrentPage.ToList().First() as Group;
But how do I grant that group access to the file using an/any API ? It should be possible since it is possible in the OneDrive for Business UI.
It is not possible doing this
listItem.RoleAssignments.Add(azuregroup, roleDefCollection);
because the first parameter to Add has to be a Principal which the azuregroup is not.
Hope anybody can help - I have searched the internet for countless hours.