0
votes

Let me first describe a "manual" scenario. I login to Partner Center as a partner and go to customer list (https://partnercenter.microsoft.com/en-us/pcv/customers/list). For any customer it is possible to manage all its usage-based subscriptions in Azure portal using All resources (Azure portal) link:

enter image description here

In particular, I can add a co-admin to subscription (i.e. add a user with role Owner):

enter image description here

How to automate this management of customer's subscriptions?

My efforts: I have some experience of CREST API and RBAC API. This is limitation of an Azure Active Directory (AAD) application described in docs:

You can only grant access to resource in your subscription for applications in the same directory as your subscription.

Due to each customer's subscription exists in separate customer's AAD, it seems RBAC API cann't help:

  1. It requires an AAD application-based token (i.e. based on TenantId, ClientId, ClientSecret), and there is no way to programmatically create an AAD application in customer's directory.

  2. An application located in partner's AAD cann't get access to customer's subscription.

Does any way to programmatically add an admin/co-admin/owner to customer's subscription exist?

1

1 Answers

0
votes

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