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 } #region Utility FUnctions function Export-Page { [CmdletBinding()] param ( $PageIndex, $Path, $Results, $RelatedPropertyNames, $EntityName, $EntityUri, $CurrentCount, $TotalCount, $ProgressActivity, [switch] $ShowCount ) Write-PSFMessage "Exporting $EntityName page $PageIndex" if ($RelatedPropertyNames) { foreach ($result in $Results.value) { $CurrentCount++ $status = Get-Status -CurrentCount $CurrentCount -TotalCount $totalCount -ShowCount:$ShowCount -Result $result Write-ZtProgress "Exporting $progressActivity" -Status $status foreach ($propertyName in $RelatedPropertyNames) { Add-GraphProperty -Result $result -PropertyName $propertyName -EntityName $EntityName -EntityUri $EntityUri } } } else { $CurrentCount += $results.value.Count $status = Get-Status -CurrentCount $CurrentCount Write-ZtProgress "Exporting $progressActivity" -Status $status } $filePath = Join-Path -Path $Path -ChildPath "$EntityName-$PageIndex.json" $results | Export-PSFJson -Path $filePath -Depth 100 -Encoding UTF8NoBom $CurrentCount } function Get-Status { [CmdletBinding()] param ( $CurrentCount, $TotalCount, [switch] $ShowCount, $Result ) if ($ShowCount -and $null -ne $result) { "$CurrentCount of $TotalCount : $($result.displayName)" } else { "Retrieved $CurrentCount items..." } } function Add-GraphProperty { [CmdletBinding()] param ( $Result, [string] $PropertyName, [string] $EntityName, [string] $EntityUri ) $id = Get-ObjectProperty $result 'id' Write-PSFMessage "Adding $propertyName to $entityName $id" -Tag Graph try { $propertyResults = Invoke-MgGraphRequest -Uri "$entityUri/$id/$propertyName" -OutputType HashTable $result[$propertyName] = Get-ObjectProperty $propertyResults 'value' } catch { $errorMessage = $_.Exception.Message # Check for known timeout errors that should be silently logged if ($entityName -eq "SignIn" -and $errorMessage -like "*The request was canceled due to the configured HttpClient.Timeout*") { Write-PSFMessage "Timeout occurred while adding $propertyName to $entityName $id - silently continuing" -Tag Graph -Level Verbose } else { # Log unknown errors and show warning Write-PSFMessage "Failed to add $propertyName to $entityName $id. Error: $errorMessage" -Tag Graph -Level Warning Write-Warning "Failed to retrieve property '$propertyName' for $entityName '$id': $errorMessage" } } } #endregion Utility FUnctions $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 do { $results = Invoke-MgGraphRequest -Method GET -Uri $uri -OutputType HashTable $currentCount = Export-Page -PageIndex $pageIndex -Path $folderPath -Results $results -RelatedPropertyNames $RelatedPropertyNames -EntityName $EntityName -EntityUri $EntityUri -CurrentCount $currentCount -TotalCount $totalCount -ProgressActivity $ProgressActivity -ShowCount:$ShowCount if (-not $results) { $uri = $null } else { $uri = $results.'@odata.nextLink' } $pageIndex++ if (-not $uri) { break } elseif ($hasTimeLimit -and (Get-Date) -gt $stopTime) { Write-PSFMessage "Maximum time limit reached for $EntityName" break } } while ($true) Set-ZtConfig -ExportPath $ExportPath -Property $EntityName -Value $true } |