Private/ConvertTo-IntuneAccessSnapshot.ps1

function Get-IntuneAccessSnapshotHash {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [AllowEmptyString()] [string] $Value
    )

    $bytes = [Text.Encoding]::UTF8.GetBytes($Value)
    $hash = [Security.Cryptography.SHA256]::HashData($bytes)
    ([Convert]::ToHexString($hash)).ToLowerInvariant()
}

function Copy-IntuneAccessSnapshotValue {
    [CmdletBinding()]
    [OutputType([object], [string], [object[]])]
    param(
        [AllowNull()] [object] $Value,
        [string] $PropertyName = '',
        [string] $RedactionKey = ''
    )

    if ($null -eq $Value) { return $null }

    if ($Value -is [string]) {
        if (-not [string]::IsNullOrWhiteSpace($RedactionKey) -and
            $PropertyName -match '(?i)(^id$|ids$|name$|displayname$|userprincipalname$|mail$|account$|serialnumber$|ipaddress$|oldvalue$|newvalue$|description$|tenant)') {
            $digest = Get-IntuneAccessSnapshotHash -Value "$RedactionKey`n$Value"
            return "redacted-$($digest.Substring(0, 16))"
        }
        return $Value
    }

    if ($Value -is [ValueType]) { return $Value }
    if ($Value -is [Collections.IDictionary]) {
        $copy = [ordered] @{}
        foreach ($key in $Value.Keys) {
            $copy[[string] $key] = Copy-IntuneAccessSnapshotValue -Value $Value[$key] -PropertyName ([string] $key) -RedactionKey $RedactionKey
        }
        return [PSCustomObject] $copy
    }
    if ($Value -is [Collections.IEnumerable]) {
        return @($Value | ForEach-Object { Copy-IntuneAccessSnapshotValue -Value $_ -PropertyName $PropertyName -RedactionKey $RedactionKey })
    }

    $copy = [ordered] @{}
    foreach ($property in $Value.PSObject.Properties | Where-Object MemberType -In @('NoteProperty', 'Property', 'AliasProperty', 'ScriptProperty')) {
        $copy[$property.Name] = Copy-IntuneAccessSnapshotValue -Value $property.Value -PropertyName $property.Name -RedactionKey $RedactionKey
    }
    [PSCustomObject] $copy
}

function ConvertTo-IntuneAccessSnapshot {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)] [object] $TenantRbac,
        [switch] $RedactIdentity
    )

    if ('IntuneAccess.TenantRbac' -notin $TenantRbac.PSObject.TypeNames) {
        throw 'InputObject must be an IntuneAccess tenant RBAC collection.'
    }

    $tenant = Get-IntuneAccessProperty $TenantRbac 'Tenant'
    $tenantId = [string] (Get-IntuneAccessProperty $tenant 'Id' (Get-IntuneAccessProperty $tenant 'DisplayName' 'IntuneAccess'))
    $redactionKey = if ($RedactIdentity) { Get-IntuneAccessSnapshotHash -Value "IntuneAccess`n$tenantId" } else { '' }
    $data = [ordered] @{}
    foreach ($collectionName in @(
        'Administrators', 'AdminGroups', 'RoleAssignments', 'RoleDefinitions', 'ScopeGroups', 'ScopeTags',
        'Permissions', 'Memberships', 'WorkloadObjects', 'WorkloadAssignments', 'WorkloadGroups',
        'AssignmentFilters', 'ManagedDevices', 'ManagedUsers', 'DeploymentOutcomes', 'PolicySettings', 'PolicyConflictFindings', 'AuditEvents'
    )) {
        $data[$collectionName] = @(Get-IntuneAccessProperty $TenantRbac $collectionName @())
    }
    $data['WorkloadCollectionStatus'] = @(Get-IntuneAccessProperty $TenantRbac 'WorkloadCollectionStatus' @())
    $data['OutcomeCollectionStatus'] = @(Get-IntuneAccessProperty $TenantRbac 'OutcomeCollectionStatus' @())

    $snapshotTenant = [PSCustomObject] [ordered] @{
        Id          = [string] (Get-IntuneAccessProperty $tenant 'Id' '')
        DisplayName = [string] (Get-IntuneAccessProperty $tenant 'DisplayName' '')
    }
    $snapshotData = [PSCustomObject] $data
    if ($RedactIdentity) {
        $snapshotTenant = Copy-IntuneAccessSnapshotValue -Value $snapshotTenant -RedactionKey $redactionKey
        $snapshotData = Copy-IntuneAccessSnapshotValue -Value $snapshotData -RedactionKey $redactionKey
    }

    $dataJson = $snapshotData | ConvertTo-Json -Depth 40 -Compress
    [PSCustomObject] [ordered] @{
        PSTypeName      = 'IntuneAccess.Snapshot'
        Schema          = 'https://controlaltdeletetechbits.github.io/intune-access/schemas/snapshot-1.0.json'
        SchemaVersion   = '1.0'
        ToolVersion     = $script:IntuneAccessVersion
        ExportedAt      = [DateTimeOffset]::Now
        Tenant          = $snapshotTenant
        IdentityMode    = if ($RedactIdentity) { 'Pseudonymised' } else { 'Full' }
        IntegritySha256 = Get-IntuneAccessSnapshotHash -Value $dataJson
        Data            = $snapshotData
    }
}