Public/func_Grant-MSPApplication.ps1

function Grant-MSPApplication {
    [CmdletBinding()]
    param (
        [string]$CustomerTenantID
    )

    # ignore list... for now (with reasons)
    $ignoreRoleList = @("user_impersonation", "profile", "offline_access", "openid") # no CSP tenant needs these permissions in any case
    $ignoreAppList = @("fc780465-2017-40d4-a0c5-307022471b92", "00000002-0000-0000-c000-000000000000") #defender ATP & AAD Graph (defender ATP gives an error on tenants that do not have it, will look into it. AAD Graph will de discontinued)

    New-DebugLine "Starting the Application Granter"
    if (-not $CustomerTenantID) {
        [array]$CustomerTenantID = Get-MSPToolboxPartnerList | Select-Object -ExpandProperty customerId
    }

    # Get the session data and get CSP token
    $sessionData = $script:mspToolBoxSession

    $cspTokenSplat = @{
        Uri     = "https://login.microsoftonline.com/$($sessionData.TenantID)/oauth2/v2.0/token"
        Method  = "Post"
        Headers = @{
            "Content-Type" = "application/x-www-form-urlencoded"
        }
        Body    = @{
            client_id     = $sessionData.ApplicationID
            client_secret = $sessionData.ApplicationSecret
            refresh_token = $sessionData.Refreshtoken
            grant_type    = "refresh_token"
            scope         = "https://api.partnercenter.microsoft.com/user_impersonation"
        }
    }
    try {  
        $cspAccessToken = (Invoke-RestMethod @cspTokenSplat).access_token
        if ((Read-JwtTokenPermissions $cspAccessToken) -eq "user_impersonation") {
            $cspAuthHeader = @{
                Authorization  = "Bearer $cspAccessToken"
                "Content-Type" = "application/json"
            }
            New-DebugLine "Got CSP token and required permission"
        }
        else {
            throw "MSPToolbox | No 'user_impersonation' permission found, please add the permissions and try again"
        }
    }
    catch {
        Write-Error (Format-ErrorCode $_)
        return
    }
    
    if ($CustomerTenantID.count -gt 1 -and $PSBoundParameters.Keys -notcontains "Debug") {
        $progress = $true
    }
    # get application permissions and build the consent body
    try {
        if ($progress -eq $true) { Write-Progress -Activity "Granting Application Permissions" -Status "Getting and building application consent body..." }

        $appInformation = Invoke-MSPGraphRequest -Endpoint ("applications(appId='{0}')" -f $script:mspToolBoxSession.ApplicationId)
        New-DebugLine "Got application information"

        $requiredResource = Invoke-MSPGraphRequest -Endpoint "applications/$($appInformation.id)/?`$select=requiredResourceAccess" | Select-Object -ExpandProperty requiredResourceAccess
        New-DebugLine "Required resources collected"

        $bodyBuilder = @()
        foreach ($resourceApp in $requiredResource) {
            if ($progress -eq $true) { Write-Progress -Activity "Granting Application Permissions" -Status ("Getting enterprise application permissions for app {0}" -f $resourceApp.resourceAppId) -PercentComplete (([array]::IndexOf($requiredResource, $resourceApp) / $requiredResource.Count) * 100) }
            New-DebugLine ("Getting enterprise application permissions for app {0}" -f $resourceApp.resourceAppId)
            $enterPriseAppPermissions = Invoke-MSPGraphRequest -Endpoint ("servicePrincipals?`$filter=appid eq '{0}'&`$select=oauth2PermissionScopes" -f $resourceApp.resourceAppId) | Select-Object -ExpandProperty oauth2PermissionScopes
            [pscustomobject]$applicationAndRoles = @{
                enterpriseApplicationId = $resourceApp.resourceAppId
                scope                   = ($enterPriseAppPermissions | Where-Object { $_.id -in $resourceApp.resourceAccess.id } | Select-Object -ExpandProperty value | Where-Object { $_ -notin $ignoreRoleList }) -join ","
            }
            if (($applicationAndRoles.scope) -and ($applicationAndRoles.enterpriseApplicationId -notin $ignoreAppList)) {
                $bodyBuilder += $applicationAndRoles
            }
        }
        New-DebugLine "Application consent body:"
        New-DebugLine ($bodyBuilder | Out-String)
    }
    catch {
        Write-Error (Format-ErrorCode $_)
        return
    }

    # consent body for all permissions
    $applicationGrant = $bodyBuilder | foreach { @{enterpriseApplicationId = $_.enterpriseApplicationId; scope = $_.scope } }
    $applicationConsentBody = @{
        applicationId     = $script:mspToolBoxSession.ApplicationID
        applicationGrants = @($applicationGrant)
    } | ConvertTo-Json -Depth 4

    # output table
    $outputTable = [PSCustomObject]@{
        Success = 0
        Failed  = 0
    }

    # loop through all customer(s)
    foreach ($customer in $CustomerTenantID) {
        if ($progress -eq $true) { Write-Progress -Activity "Granting Application Permissions" -Status "Granting $([array]::IndexOf($CustomerTenantID, $customer))/$($CustomerTenantID.Count)..." -PercentComplete ((([array]::IndexOf($CustomerTenantID, $customer)) / $CustomerTenantID.Count) * 100) }
        # Remove any existing Grants
        New-DebugLine "Processing tenantID $customer"
        try {
            $applicationRevokeSplat = @{
                Method = "Delete"
                Uri    = "https://api.partnercenter.microsoft.com/v1/customers/$($customer)/applicationconsents/$($script:mspToolBoxSession.ApplicationId)"
                Header = $cspAuthHeader
            }
            Invoke-RestMethod @applicationRevokeSplat | Out-Null
            New-DebugLine "Removed application grant successfully"
        }
        catch {
            $outputTable.Failed++
            New-DebugLine "Failed to remove application grant"
            Write-Error "Customer Tenant ID: $customer .$(Format-ErrorCode $_)"
            continue
        }

        # Grant application
        $applicationConsentSplat = @{
            Method  = "Post"
            Uri     = "https://api.partnercenter.microsoft.com/v1/customers/$($customer)/applicationconsents"
            Body    = $applicationConsentBody
            Header  = $cspAuthHeader
            Content = "application/json"
        }
        try {
            Invoke-RestMethod @applicationConsentSplat | Out-Null
            New-DebugLine "Application granted successfully"
        }
        catch {
            $outputTable.Failed++
            New-DebugLine "Failed to grant application successfully"
            Write-Error "Customer Tenant ID: $customer .$(Format-ErrorCode $_)"
            continue
        }
        $outputTable.Success++
    }
    return $outputTable
}