Private/Get-UTCMCurrentStateSnapshot.ps1
|
function Get-UTCMCurrentStateSnapshot { [CmdletBinding()] param( [Parameter(Mandatory)] [string[]]$Resources, [int]$PollingIntervalSeconds = 10 ) # Build request body — the createSnapshot action REQUIRES a resources array $body = @{ displayName = "UTCM Temp Current State" description = "Temporary snapshot for comparison" resources = $Resources } # Use the correct action endpoint (not the jobs collection) $job = Invoke-GraphRequestWithRetry -Method 'POST' -Uri $script:CreateSnapshotActionUri -Body $body # Poll until terminal status (notStarted|running -> succeeded|failed|partiallySuccessful) do { Start-Sleep -Seconds $PollingIntervalSeconds $status = Invoke-GraphRequestWithRetry -Method 'GET' -Uri "$($script:SnapshotJobsUri)/$($job.id)" Write-Log -Message "Current-state snapshot status: $($status.status)" -Color Gray } while ($status.status -in @('notStarted','running')) if ($status.status -notin @('succeeded','partiallySuccessful')) { $errorInfo = @() try { $ed = $null if ($status -is [System.Collections.IDictionary] -and $status.ContainsKey('errorDetails')) { $ed = $status['errorDetails'] } elseif ($status.PSObject.Properties.Name -contains 'errorDetails') { $ed = $status.errorDetails } if ($ed -and $ed.Count -gt 0) { $errorInfo += "errorDetails: $($ed -join '; ')" } } catch { <# ignore #> } $statusDump = try { $status | ConvertTo-Json -Depth 5 -Compress } catch { $status.ToString() } Write-Warning "Full snapshot job response: $statusDump" throw ("Current-state snapshot job failed with status '{0}'. {1}" -f $status.status, ($errorInfo -join ' | ')) } # Download snapshot items from resourceLocation (not $expand — unsupported on this entity) if (-not $status.resourceLocation -or [string]::IsNullOrWhiteSpace($status.resourceLocation)) { throw "Current-state snapshot job '$($status.id)' does not expose resourceLocation; cannot retrieve items." } $tmp = [System.IO.Path]::GetTempFileName() try { Invoke-MgGraphRequest -Method GET -Uri $status.resourceLocation -OutputFilePath $tmp -ErrorAction Stop $raw = Get-Content -LiteralPath $tmp -Raw $json = $raw | ConvertFrom-Json -ErrorAction Stop $items = $null if ($json.PSObject.Properties.Name -contains 'configurationItems') { $items = $json.configurationItems } elseif ($json.PSObject.Properties.Name -contains 'resources') { $items = $json.resources } elseif ($json -is [System.Collections.IEnumerable]) { $items = $json } else { throw "Downloaded artifact does not contain 'configurationItems'/'resources' and is not an array." } # Normalize to id/displayName/type/data for downstream compare/report consumers $normalizedItems = foreach ($it in $items) { $idVal = $null if ($it.PSObject.Properties.Name -contains 'id' -and $it.id) { $idVal = [string]$it.id } elseif ($it.PSObject.Properties.Name -contains 'resourceInstanceIdentifier' -and $it.resourceInstanceIdentifier) { $idVal = [string]$it.resourceInstanceIdentifier } elseif ($it.PSObject.Properties.Name -contains 'properties' -and $it.properties) { foreach ($p in 'Id','Identity','Guid','ObjectId') { if ($it.properties.PSObject.Properties.Name -contains $p -and $it.properties.$p) { $idVal = [string]$it.properties.$p; break } } } $typeVal = $null if ($it.PSObject.Properties.Name -contains 'resourceType' -and $it.resourceType) { $typeVal = [string]$it.resourceType } elseif ($it.PSObject.Properties.Name -contains 'type' -and $it.type) { $typeVal = [string]$it.type } $dataVal = $null if ($it.PSObject.Properties.Name -contains 'properties') { $dataVal = $it.properties } elseif ($it.PSObject.Properties.Name -contains 'data') { $dataVal = $it.data } $displayVal = $null if ($it.PSObject.Properties.Name -contains 'displayName') { $displayVal = [string]$it.displayName } [pscustomobject]@{ id = $idVal displayName = $displayVal type = $typeVal data = $dataVal } } Add-Member -InputObject $status -NotePropertyName configurationItems -NotePropertyValue $normalizedItems -Force Add-Member -InputObject $status -NotePropertyName rawConfiguration -NotePropertyValue $json -Force } finally { Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue } return $status } |