Partner Center PowerShell – Secure App Model

Microsoft is introducing a secure and scalable framework for authenticating Cloud Solution Provider (CSP) partners and Control Panel Vendors (CPV) using multi-factor authentication (MFA). This new model will elevate security for operations involving the Partner Center API. This will help to protect the partner’s infrastructure and customer data from security risk. With this new model partners will now need to perform a consent process. Through that process an access token will be requested from Azure Active Directory using an authorization code. The result returned from that request will include an access token, refresh token and additional information. The refresh token value should be stored in a secure repository such as Azure Key Vault. It will be used when requesting an access token to interact with the Partner Center API. 

Guidance and sample code has been provided by Microsoft for the Partner Center .NET SDK and Partner Center Java SDK, so I would like to take this opportunity to provide the same for the Partner Center PowerShell module. With the latest release of the module you can now utilize the New-PartnerAccessToken cmdlet to perform the partner consent process. To do this you will need an Azure AD application that has urn:ietf:wg:oauth:2.0:oob configured as one of the reply URLs. The following PowerShell script can be used to create and configure the required Azure AD application

        This script will create the require Azure AD application. 
        .\Create-AzureADApplication.ps1 -ConfigurePreconsent $true -DisplayName "Partner Center Web App" 
        .\Create-AzureADApplication.ps1 -ConfigurePreconsent $true -DisplayName "Partner Center Web App" -TenantId eb210c1e-b697-4c06-b4e3-8b104c226b9a

        .\Create-AzureADApplication.ps1 -ConfigurePreconsent $true -DisplayName "Partner Center Web App" -TenantId
    .PARAMETER ConfigurePreconsent
        Flag indicating whether or not the Azure AD application should be configured for preconsent.
    .PARAMETER DisplayName
        Display name for the Azure AD application that will be created.
    .PARAMETER TenantId
        [OPTIONAL] The domain or tenant identifier for the Azure AD tenant that should be utilized to create the various resources. 

    [Parameter(Mandatory = $true)]
    [Parameter(Mandatory = $true)]
    [Parameter(Mandatory = $false)]

$ErrorActionPreference = "Stop"

# Check if the Azure AD PowerShell module has already been loaded. 
if ( ! ( Get-Module AzureAD ) ) {
    # Check if the Azure AD PowerShell module is installed.
    if ( Get-Module -ListAvailable -Name AzureAD ) {
        # The Azure AD PowerShell module is not load and it is installed. This module 
        # must be loaded for other operations performed by this script.
        Write-Host -ForegroundColor Green "Loading the Azure AD PowerShell module..."
        Import-Module AzureAD
    } else {
        Install-Module AzureAD

try {
    Write-Host -ForegroundColor Green "When prompted please enter the appropriate credentials..."
    if([string]::IsNullOrEmpty($TenantId)) {
        Connect-AzureAD | Out-Null

        $TenantId = $(Get-AzureADTenantDetail).ObjectId 
    } else {
        Connect-AzureAD -TenantId $TenantId | Out-Null
} catch [Microsoft.Azure.Common.Authentication.AadAuthenticationCanceledException] {
    # The authentication attempt was canceled by the end-user. Execution of the script should be halted.
    Write-Host -ForegroundColor Yellow "The authentication attempt was canceled. Execution of the script will be halted..."
} catch {
    # An unexpected error has occurred. The end-user should be notified so that the appropriate action can be taken. 
    Write-Error "An unexpected error has occurred. Please review the following error message and try again." `

$adAppAccess = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]@{
    ResourceAppId = "00000002-0000-0000-c000-000000000000";
    ResourceAccess = 
        Id = "5778995a-e1bf-45b8-affa-663a9f3f4d04";
        Type = "Role"},
        Id = "a42657d6-7f20-40e3-b6f0-cee03008a62a";
        Type = "Scope"},
        Id = "311a71cc-e848-46a1-bdf8-97ff7156d8e6";
        Type = "Scope"}

$graphAppAccess = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]@{
    ResourceAppId = "00000003-0000-0000-c000-000000000000";
    ResourceAccess = 
            Id = "bf394140-e372-4bf9-a898-299cfc7564e5";
            Type = "Role"}, 
            Id = "7ab1d382-f21e-4acd-a863-ba3e13f7da61";
            Type = "Role"}

$partnerCenterAppAccess = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]@{
    ResourceAppId = "fa3d9a0c-3fb0-42cc-9193-47c7ecd2edbd";
    ResourceAccess = 
            Id = "1cebfa2a-fb4d-419e-b5f9-839b4383e05a";
            Type = "Scope"}

$SessionInfo = Get-AzureADCurrentSessionInfo

Write-Host -ForegroundColor Green "Creating the Azure AD application and related resources..."

$app = New-AzureADApplication -AvailableToOtherTenants $true -DisplayName $DisplayName -IdentifierUris "https://$($SessionInfo.TenantDomain)/$((New-Guid).ToString())" -RequiredResourceAccess $adAppAccess, $graphAppAccess, $partnerCenterAppAccess -ReplyUrls @("urn:ietf:wg:oauth:2.0:oob")
$password = New-AzureADApplicationPasswordCredential -ObjectId $app.ObjectId
$spn = New-AzureADServicePrincipal -AppId $app.AppId -DisplayName $DisplayName

if($ConfigurePreconsent) {
    $adminAgentsGroup = Get-AzureADGroup -Filter "DisplayName eq 'AdminAgents'"
    Add-AzureADGroupMember -ObjectId $adminAgentsGroup.ObjectId -RefObjectId $spn.ObjectId

Write-Host "ApplicationId       = $($app.AppId)"
Write-Host "ApplicationSecret   = $($password.Value)"

Next you will perform the partner consent process by performing the following 

$credential = Get-Credential
$token = New-PartnerAccessToken -Consent -Credential $credential -Resource -ServicePrincipal

When the Get-Credential cmdlet is invoked you will be prompted to enter a username and password. Specify the application identifier as the username and the application secret as the password. When then New-PartnerAccessToken cmdlet is invoked you will be prompted for credentials once again. This time you will need to specify the credentials for the service account that you will be using. Please note that this should be a partner account with the appropriate permissions. After the successfully execution of the New-PartnerAccesToken cmdlet you will find that the $token variable contains the response from Azure Active Directory for a token when the resource of  Included in this response is a refresh token, you will want to store this value in a secure repository such as Azure Key Vault or a similar service.

When you are ready to connect to the Partner Center PowerShell module you need to retrieve the refresh token value that was stored in a secure location. Then you will need to execute the New-PartnerAccessToken and Connect-PartnerCenter cmdlets as shown below 

$credential = Get-Credential
$pcToken = New-PartnerAccessToken -RefreshToken $refreshToken -Resource -Credential $credential -ServicePrincipal

Connect-PartnerCenter -AccessToken $pcToken.AccessToken -AccessTokenExpiresOn $pcToken.ExpiresOn -ApplicationId $appId

When you are prompted for credentials you will want to specify the application identifier and application secret, for the Azure AD application used when generating the refresh token. Since the refresh token parameter is specified the cmdlet will perform the refresh token flow to request a new access token. Finally, I wanted to mention this approach can be used with other PowerShell modules such as MSOnline. Use the following to use this approach to establish the connection 

$credential = Get-Credential
$aadGraphToken = New-PartnerAccessToken -RefreshToken $refreshToken -Resource -Credential $credential -ServicePrincipal
$graphToken =  New-PartnerAccessToken -RefreshToken $refreshToken -Resource -Credential $credential -ServicePrincipal

Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken

Through this you can successfully perform operations using the MSOnline PowerShell module and comply with the new security requirements.

Leave a Reply

Your email address will not be published. Required fields are marked *