Private/Normalize.ps1
|
# Every object stored in a snapshot is rebuilt from an explicit allowlist of properties. This keeps secret # material out of snapshots (a passwordCredential's 'hint' holds the first characters of the secret; Graph never # returns 'secretText' on read, but it is dropped here regardless) and keeps the snapshot schema stable when # Microsoft adds properties. function Test-AgentIdProperty { param($InputObject, [Parameter(Mandatory)][string]$Name) return ($null -ne $InputObject -and $null -ne $InputObject.PSObject.Properties[$Name]) } function ConvertTo-AgentIdDateString { param($Value) if ($null -eq $Value -or "$Value" -eq '') { return $null } $date = ConvertTo-AgentIdDate $Value if ($null -eq $date) { return "$Value" } return $date.UtcDateTime.ToString('o') } function ConvertTo-AgentIdDate { # Accepts DateTime, DateTimeOffset or an ISO 8601 string; returns a DateTimeOffset or $null. param($Value) if ($null -eq $Value) { return $null } if ($Value -is [datetimeoffset]) { return $Value } if ($Value -is [datetime]) { $utc = if ($Value.Kind -eq [DateTimeKind]::Unspecified) { [datetime]::SpecifyKind($Value, [DateTimeKind]::Utc) } else { $Value.ToUniversalTime() } return [datetimeoffset]$utc } $parsed = [datetimeoffset]::MinValue $styles = [System.Globalization.DateTimeStyles]::AssumeUniversal if ([datetimeoffset]::TryParse("$Value", [System.Globalization.CultureInfo]::InvariantCulture, $styles, [ref]$parsed)) { return $parsed } return $null } function ConvertTo-AgentIdCredential { param($InputObject, [Parameter(Mandatory)][ValidateSet('Secret', 'Certificate')][string]$Kind) foreach ($c in @($InputObject)) { if ($null -eq $c) { continue } [ordered]@{ keyId = $c.keyId kind = $Kind displayName = $c.displayName type = $c.type usage = $c.usage startDateTime = ConvertTo-AgentIdDateString $c.startDateTime endDateTime = ConvertTo-AgentIdDateString $c.endDateTime } } } function ConvertTo-AgentIdFederatedCredential { param($InputObject) foreach ($f in @($InputObject)) { if ($null -eq $f) { continue } [ordered]@{ id = $f.id name = $f.name issuer = $f.issuer subject = $f.subject audiences = @($f.audiences) } } } function ConvertTo-AgentIdDirectoryObjectRef { # Sponsors and owners: users, groups or service principals. param($InputObject) foreach ($o in @($InputObject)) { if ($null -eq $o) { continue } $ref = [ordered]@{ id = $o.id odataType = $o.'@odata.type' displayName = $o.displayName userPrincipalName = $o.userPrincipalName } # Only record accountEnabled when Graph returned it: $null means unknown, not disabled. $ref.accountEnabled = if (Test-AgentIdProperty $o 'accountEnabled') { $o.accountEnabled } else { $null } $ref } } function ConvertTo-AgentIdSecurityAttributeName { # Reduces a customSecurityAttributes value ({ Set = { Attribute = value } }) to the 'Set.Attribute' names that # hold a value. The values themselves are not kept: the audit only needs to know that one is defined. param($InputObject) if ($null -eq $InputObject) { return } foreach ($set in $InputObject.PSObject.Properties) { if ($set.Name -like '*@*' -or $set.Value -isnot [pscustomobject]) { continue } foreach ($attribute in $set.Value.PSObject.Properties) { if ($attribute.Name -like '*@*') { continue } # OData annotations such as 'Project@odata.type' $v = $attribute.Value $hasValue = if ($null -eq $v) { $false } elseif ($v -is [string]) { $v.Trim() -ne '' } elseif ($v -is [System.Collections.IEnumerable]) { @($v).Count -gt 0 } else { $true } if ($hasValue) { "$($set.Name).$($attribute.Name)" } } } } function ConvertTo-AgentIdAppRoleAssignment { param($InputObject) foreach ($a in @($InputObject)) { if ($null -eq $a) { continue } [ordered]@{ id = $a.id principalId = $a.principalId principalType = $a.principalType principalDisplayName = $a.principalDisplayName resourceId = $a.resourceId resourceDisplayName = $a.resourceDisplayName appRoleId = $a.appRoleId createdDateTime = ConvertTo-AgentIdDateString $a.createdDateTime } } } function ConvertTo-AgentIdPermissionGrant { param($InputObject) foreach ($g in @($InputObject)) { if ($null -eq $g) { continue } [ordered]@{ id = $g.id clientId = $g.clientId consentType = $g.consentType principalId = $g.principalId resourceId = $g.resourceId scope = $g.scope } } } function ConvertTo-AgentIdInheritablePermission { param($InputObject) foreach ($p in @($InputObject)) { if ($null -eq $p) { continue } $scopes = $p.inheritableScopes $kind = $scopes.kind if (-not $kind -and $scopes.'@odata.type') { # e.g. '#microsoft.graph.enumeratedScopes' -> 'enumerated' $kind = ($scopes.'@odata.type' -replace '^#?microsoft\.graph\.', '') -replace 'Scopes$', '' } [ordered]@{ resourceAppId = $p.resourceAppId kind = $kind scopes = @($scopes.scopes | Where-Object { $_ }) } } } function ConvertTo-AgentIdResource { param($InputObject) [ordered]@{ id = $InputObject.id appId = $InputObject.appId displayName = $InputObject.displayName appRoles = @(foreach ($r in @($InputObject.appRoles)) { if ($r) { [ordered]@{ id = $r.id; value = $r.value } } }) } } |