DownloadErrorScript.ps1


<#PSScriptInfo
 
.VERSION 1.0
 
.GUID a026e502-a561-4270-85ec-ae66f02897f1
 
.AUTHOR Microsoft Corporation
 
.COMPANYNAME Microsoft Corporation
 
.COPYRIGHT (c) 2019 Microsoft. All rights reserved.
 
.TAGS Microsoft Graph Search PowerShell
 
.LICENSEURI https://learn.microsoft.com/en-us/legal/mdsa?redirectedfrom=MSDN
 
.PROJECTURI https://learn.microsoft.com/en-us/microsoftsearch/connector-details-errors
 
.ICONURI https://contoso.com/Icon
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
 
#>


#Requires -Module MSAL.PS

<#
.Synopsis
Script to download item errors for a connection
 
.Description
This script downloads the item errors in a Microsoft Graph Connection. An Msal token is generated using the tenant credentials in which the connection is present.
That token is then used to first get all the distinct error codes with which the items failed and then for each error code all the item errors are fetched iteratively.
Getting item errors for a particular error code is a batched API call with deafulat batch size as 500.
 
.Parameter ConnectionId
ConnectionId for which the item errors need to be downloaded.
 
.parameter OutputFile
OutputFile in which item errors should get appended
#>


param(
    [Parameter(Mandatory=$true)][string]$ConnectionId,
    [Parameter(Mandatory=$true)][string]$OutputFile
)

function InitializeOutputFile {
    param (
        $FileName
    )
    "Time,Path,Error Code,Detailed Error Code,Message" | Set-Content -Path $FileName
}

function WriteErrorBatchToOutputFile {
    param (
        $ErrorsBatch,
        $FileName
    )

    Foreach ($Error in $ErrorsBatch.errors) {
        $OutputLine = Get-Date($Error.timeStamp) -UFormat "%m/%d/%Y %T"
        $OutputLine = $OutputLine +","+ $Error.displayText +","+ $ErrorsBatch.Code +","+ $Error.detailedCode +',"'+ $ErrorsBatch.message +'"'
        $OutputLine | Add-Content -Path $FileName
    }
}

function Invoke-RestMethodWithRetries {
    param (
        [string]$ApiName,
        [string]$Uri,
        [hashtable]$Headers,
        [string]$Method,
        [int]$MaxRetries = 3
    )

    $retryCount = 0
    do {
        try {
            Write-Host "Request made for $ApiName."
            return Invoke-WebRequest -Uri $Uri -Headers $Headers -Method $Method 
        } 
        catch {
            $retryCount++
            Write-Warning "Request failed for $ApiName. Retrying (Attempt $retryCount)..."
            Start-Sleep -Seconds (2 * $retryCount)  # Increase the wait time with each retry
        }
    } while ($retryCount -lt $MaxRetries)

    throw "Request failed for $ApiName. All the retries are exhausted"
}

function Get-TokenWithRetries {
    param (
        [string]$ClientId,
        [string]$Authority,
        [string]$RedirectUri,
        [string]$Scopes,
        [bool]$IsRefresh = $false,
        [int]$MaxRetries = 3
    )

    $retryCount = 0
    do {
        try {
            if($IsRefresh)
            {
                Write-Host "Requesting for Msal Refresh Access token."
                return Get-MsalToken -ClientId $ClientId -Authority $Authority -RedirectUri $RedirectUri -Scopes $Scopes -ForceRefresh
            }
            else
            {
                Write-Host "Requesting for Msal Access token."
                return Get-MsalToken -ClientId $ClientId -Authority $Authority -RedirectUri $RedirectUri -Scopes $Scopes
            }
        } 
        catch {
            $retryCount++
            Write-Warning "Request failed for Generating Msal Token. Retrying (Attempt $retryCount)..."
            Start-Sleep -Seconds (2 * $retryCount)  # Increase the wait time with each retry
        }
    } while ($retryCount -lt $MaxRetries)

    throw "Request failed for Generating Msal Token. All the retries are exhausted"
} 

try {
    # Install MSAL.ps using "Install-Module -Name MSAL.PS"

    InitializeOutputFile -FileName $OutputFile

    $clientId = "d3590ed6-52b3-4102-aeff-aad2292ab01c"
    $authority = "https://login.microsoftonline.com/common"
    $redirectUri = "yammer://wefwef"
    $scopes = 'https://gcs.office.com/.default'

    $token = Get-TokenWithRetries -ClientId $clientId -Authority $authority -RedirectUri $redirectUri -Scopes $scopes
    $headers = @{"authorization"="Bearer $($token.AccessToken)"; "Content-Type"="application/json"; "Accept" = "application/json;odata=fullmetadata"}

    # Get the list of Error Codes
    $ErrorCodesResponse = Invoke-RestMethodWithRetries -ApiName "GetAggregatedErrors" -Uri "https://gcs.office.com/v1.0/admin/datasets/$ConnectionId/errors" -Headers $headers -Method Get | ConvertFrom-Json

    # Get all the error items for each error code
    Foreach($ErrorCode in $ErrorCodesResponse.errorCodes)
    {
        $CurrentErrorCode = $ErrorCode.code
        $CurrentOffset = "0"
        while($CurrentOffset -ne "-1") {
            Write-Host "Curent Offset: $CurrentOffset"

            # Refresh the token if it has expired
            $currentTime = Get-Date
            $tokenExpiry = $token.ExpiresOn
            Write-Host "CurrentTime: $currentTime, TokenExpiresOn: $tokenExpiry"

            # Refresh the token if it's about to expire in the next 5 seconds
            if ($tokenExpiry -lt ($currentTime.AddSeconds(5))) {
                Write-Host "Access token is about to expire. Refreshing..."
                $token = Get-TokenWithRetries -ClientId $clientId -Authority $authority -RedirectUri $redirectUri -Scopes $scopes -IsRefresh $true
            }

            $ItemErrorsBatchResponse = Invoke-RestMethodWithRetries -ApiName "GetErrorsByErrorCode" -Uri "https://gcs.office.com/v1.0/admin/datasets/$ConnectionId/errors/${CurrentErrorCode}?offset=$CurrentOffset&limit=2000" -Headers $headers -Method Get | ConvertFrom-Json
            WriteErrorBatchToOutputFile -ErrorsBatch $ItemErrorsBatchResponse -FileName $OutputFile
            $CurrentOffset = $ItemErrorsBatchResponse.metaData.nextOffset
        }
    }
} 
catch {
    Write-Error "Error in running GetErrorItems.ps1 Error message: $_";
}