With Patrick Liang help on MSDN forums, finally I've come up with a solution: enable Pre-consent
feature for a partner's AAD app to grant access to customers subscriptions. Let me describe it:
1. Partner Center Explorer project
https://github.com/Microsoft/Partner-Center-Explorer/
It's a web application similar to partnercenter.microsoft.com and it's a good example of various Microsoft APIs usage. Most important, this project is a complete example of accessing customer's subscription from partner AAD app. However, it suggests user interaction (OAuth authentification to login.live.com as a partner) and I faced some issues when tried to avoid it. Below I describe how to connect to customer's subscription with all credentials already in code.
2. Partner AAD app
Create native AAD app instead of web AAD app but configure its "Permissions to other applications" the same way. Skip steps which are not applicable to native app (for example, skip client_secret
obtaining and skip manifest update).
3. PowerShell script
Last step of app configuring is to run this script:
Connect-MsolService
$g = Get-MsolGroup | ? {$_.DisplayName -eq 'AdminAgents'}
$s = Get-MsolServicePrincipal | ? {$_.AppPrincipalId -eq 'INSERT-CLIENT-ID-HERE'}
Add-MsolGroupMember -GroupObjectId $g.ObjectId -GroupMemberType ServicePrincipal -GroupMemberObjectId $s.ObjectId
It's required to install several modules to execute these comandlets. If you get an error during "Microsoft Online Services Sign-In Assistant for IT Professionals" install, try to install BETA module:
Microsoft Online Services Sign-In Assistant for IT Professionals BETA
And you probably will need this one:
Microsoft Online Services Module for Windows PowerShell 64-bit
4. Code
Finally we are ready to authenticate and create a role assignment:
public async void AssignRoleAsync()
{
var token = await GetTokenAsync();
var response = await AssignRoleAsync(token.AccessToken);
}
public async Task<AuthenticationResult> GetTokenAsync()
{
var authContext = new AuthenticationContext($"https://login.windows.net/{CustomerId}");
return await authContext.AcquireTokenAsync(
"https://management.core.windows.net/"
, ApplicationId
, new UserCredential(PartnerUserName, PartnerPassword));
}
public async Task<HttpResponseMessage> AssignRoleAsync(string accessToken)
{
string newAssignmentId = Guid.NewGuid().ToString();
string subSegment = $"subscriptions/{CustomerSubscriptionId}/providers/Microsoft.Authorization";
string requestUri = $"https://management.azure.com/{subSegment}/roleAssignments/{newAssignmentId}?api-version=2015-07-01";
string roleDefinitionId = "INSERT_ROLE_GUID_HERE";
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
var body = new AssignRoleRequestBody();
body.properties.principalId = UserToAssignId;
body.properties.roleDefinitionId = $"/{subSegment}/roleDefinitions/{roleDefinitionId}";
var httpRequest = HttpHelper.CreateJsonRequest(body, HttpMethod.Put, requestUri);
return await client.SendAsync(httpRequest);
}
}
To obtain role definition IDs, just make a request to get all roles per subscription scope.
Useful links:
MSDN: How to manage customer's usage-based subscription programmatically?
MSDN: When will auto-stamping/implicit consent be available for CREST customers?
Managing Role-Based Access Control with the REST API