private/export/Export-GraphEntity.ps1

function Export-GraphEntity {
    [CmdletBinding()]
    param (
        # The entity to export. e.g. /beta/servicePrincipals
        [string]
        [Parameter(Mandatory = $true)]
        $EntityUri,

        # Parameters to include. e.g. $expand=appRoleAssignments&$top=999
        [string]
        [Parameter(Mandatory = $false)]
        $QueryString,

        # The folder for the entity. e.g. ServicePrincipals
        [string]
        [Parameter(Mandatory = $true)]
        $EntityName,

        # The name to show in the progress bar. E.g. Service Principals
        [string]
        [Parameter(Mandatory = $true)]
        $ProgressActivity,

        # The additional properties/relations to be queried for each object. e.g. oauth2PermissionGrants
        [string[]]
        $RelatedPropertyNames,

        # The folder to output the report to.
        [string]
        [Parameter(Mandatory = $true)]
        $ExportPath,

        # Get's count of items to show progress, skip if entity does not support $count
        [switch]
        $ShowCount,

        # The maximum time (in minutes) the assessment should spend on querying this entity.
        [int]
        $MaximumQueryTime
    )
    if ((Get-ZtConfig -ExportPath $ExportPath -Property $EntityName)) {
        Write-PSFMessage "Skipping $EntityName since it was downloaded previously" -Tag Import
        return
    }

    $activity = "Exporting $ProgressActivity"
    Write-ZtProgress $activity

    $totalCount = if ($ShowCount.IsPresent) {
        Get-ZtGraphObjectCount $EntityUri
    }
    else {
        0
    }
    $pageIndex = 0
    $currentCount = 0

    $folderPath = Join-Path $ExportPath $EntityName
    Clear-ZtFolder $folderPath

    $uri = $EntityUri + '?' + $QueryString
    $startTime = Get-Date
    $stopTime = $startTime.AddMinutes($MaximumQueryTime)
    $hasTimeLimit = $MaximumQueryTime -gt 0

    $hasCompleted = $false
    do {
        $results = Invoke-MgGraphRequest -Method GET -Uri $uri -OutputType HashTable
        $currentCount = ExportPage $pageIndex $folderPath $results $RelatedPropertyNames $EntityName $EntityUri $currentCount $totalCount $ProgressActivity $ShowCount

        if (!$results) {
            $uri = $null
        }
        else {
            $uri = Get-ObjectProperty $results '@odata.nextLink'
        }
        $pageIndex++

        if (!$uri) {
            $hasCompleted = $true
        }
        elseif (!$hasCompleted -and $hasTimeLimit -and (Get-Date) -gt $stopTime) {
            Write-PSFMessage "Maximum time limit reached for $EntityName"
            $hasCompleted = $true
        }
    }while (!$hasCompleted)

    Set-ZtConfig -ExportPath $ExportPath -Property $EntityName -Value $true
}

function ExportPage($pageIndex, $path, $results, $relatedPropertyNames, $entityName, $entityUri, $currentCount, $totalCount, $progressActivity, $showCount) {
    Write-PSFMessage "Exporting $entityName page $pageIndex"

    if ($relatedPropertyNames) {
        foreach ($result in $results.value) {
            $currentCount++
            $status = Get-Status $currentCount $totalCount $showCount $entityName $result
            Write-ZtProgress "Exporting $progressActivity" -Status $status
            foreach ($propertyName in $relatedPropertyNames) {
                Add-GraphProperty $result $propertyName $entityName $entityUri
            }
        }
    }
    else {
        $currentCount += $results.value.Count
        $status = Get-Status $currentCount $totalCount $showCount $entityName $null
        Write-ZtProgress "Exporting $progressActivity" -Status $status
    }

    $filePath = Join-Path $path "$entityName-$pageIndex.json"
    $results | ConvertTo-Json -Depth 100 | Out-File -FilePath $filePath -Force
    return $currentCount
}

function Get-Status($currentCount, $totalCount, $showCount, $name, $result) {
    if ($showCount -and $null -ne $result) {
        $name = Get-ObjectProperty $result 'displayName'
        $status = "$currentCount of $totalCount : $name"
    }
    else {
        $status = "Retrieved $currentCount items..."
    }
    return $status
}

function Add-GraphProperty($result, $propertyName, $entityName, $entityUri) {
    $id = Get-ObjectProperty $result 'id'
    Write-PSFMessage "Adding $propertyName to $entityName $id" -Tag Graph
    $propertyResults = Invoke-MgGraphRequest -Uri "$entityUri/$id/$propertyName" -OutputType HashTable
    $result[$propertyName] = Get-ObjectProperty $propertyResults 'value'
}