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

<#
    .SYNOPSIS
        This script will create the require Azure AD application. 
    .EXAMPLE
        .\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 tenant01.onmicrosoft.com
    .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. 
#>

Param
(
    [Parameter(Mandatory = $true)]
    [switch]$ConfigurePreconsent, 
    [Parameter(Mandatory = $true)]
    [string]$DisplayName,
    [Parameter(Mandatory = $false)]
    [string]$TenantId
)

$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..."
    Exit 
} 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." `
        "$($Error[0].Exception)"
}

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

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

$partnerCenterAppAccess = = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]@{
    ResourceAppId = "fa3d9a0c-3fb0-42cc-9193-47c7ecd2edbd";
    ResourceAccess = 
        [Microsoft.Open.AzureAD.Model.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 https://api.partnercenter.microsoft.com -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 https://api.partnercenter.microsoft.com.  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 https://api.partnercenter.microsoft.com -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 https://graph.windows.net -Credential $credential -ServicePrincipal
$graphToken =  New-PartnerAccessToken -RefreshToken $refreshToken -Resource https://graph.microsoft.com -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.

Partner Smart Office

Office 365 customers are provided with many tools to help improve their security practices; however, many do not take advantage of the tools. To help customers manage security threats, Microsoft partners need a way to streamline and aggregate security information across their entire customer base. A new open source solution called Partner Smart Office facilitates aggregation of information on customers with subscriptions obtained through an Enterprise Agreement or the Cloud Solution Provider (CSP) program.

Partner Smart Office imports and aggregates information obtained from the Intelligent Security Graph and Office 365 Secure Score, enabling partners to take advantage of advanced analytics. These analytics are able to link threat intelligence and security data to provide insights that can strengthen a customer’s organizational security. Now, partners can also view security data across all of their customers at once.

Secure Score is a numerical summary of a given customer’s security posture within Office 365 based on system configurations, user behavior, and other security-related measurements. It represents the extent to which the customer has adopted security controls available in Office 365, which can help offset the risk of being breached. (No online service is completely immune from security breaches; Secure Score should not be interpreted as a guarantee against security breach in any manner).

In addition to Secure Score information, data from the Microsoft Intelligent Security Graph for each configured environment is aggregated into a central repository. This provides access to  alerts from Azure Security Center and Azure Active Directory Identity Protection. With this information partners are able to gain complete insights into the security posture for their customers.

If you are interested in gaining additional security insights for your customers, that allow you to craft targeted offers the I would like to encourage you to deploy Partner Smart Office. To learn more about deploying this solution check out the deployment documentation.