Private/Get-SPMScanObject.ps1
|
function Get-SPMScanObject { <# .SYNOPSIS Loads a scan (path, FileInfo or object) and normalizes it: legacy permission codes are mapped to FC/W/C/R (X* for custom levels) and limited-access noise is stripped. #> [CmdletBinding()] param( [Parameter(Mandatory)] $InputObject ) $scan = $null if ($InputObject -is [System.IO.FileInfo]) { $scan = Get-Content -Path $InputObject.FullName -Raw -Encoding utf8 | ConvertFrom-Json } elseif ($InputObject -is [string]) { if (-not (Test-Path -Path $InputObject)) { throw "Scan file not found: $InputObject" } $scan = Get-Content -Path $InputObject -Raw -Encoding utf8 | ConvertFrom-Json } else { # round-trip live objects so mutation never touches the caller's data $scan = $InputObject | ConvertTo-Json -Depth 64 | ConvertFrom-Json } $legacy = @{ V = 'FC'; B = 'W'; M = 'C'; L = 'R'; E = 'W' } $limitedRole = '(?i)(limited access|beschr.nkter zugriff)' foreach ($site in @($scan.sites)) { $dropIds = [System.Collections.Generic.HashSet[string]]::new() foreach ($p in @($site.principals)) { if ([string]$p.name -like 'Limited Access System Group*') { [void]$dropIds.Add([string]$p.id) } } $site.principals = @($site.principals | Where-Object { -not $dropIds.Contains([string]$_.id) }) $dropRoles = [System.Collections.Generic.HashSet[string]]::new() $newRoles = [ordered]@{} if ($site.customRoles) { foreach ($prop in $site.customRoles.PSObject.Properties) { if ([string]$prop.Value -match $limitedRole) { [void]$dropRoles.Add($prop.Name); continue } $key = if ($prop.Name -match '^C(\d+)$') { 'X' + $Matches[1] } else { $prop.Name } $newRoles[$key] = $prop.Value } } $site.customRoles = [pscustomobject]$newRoles foreach ($n in @($site.nodes)) { $n.assignments = @($n.assignments | Where-Object { -not $dropIds.Contains([string]$_.principalId) -and -not $dropRoles.Contains([string]$_.permission) }) foreach ($a in @($n.assignments)) { if ($legacy.ContainsKey([string]$a.permission)) { $a.permission = $legacy[[string]$a.permission] } elseif ([string]$a.permission -match '^C(\d+)$') { $a.permission = 'X' + $Matches[1] } } } if ($site.stats) { $site.stats.principalCount = @($site.principals).Count $site.stats.sharingLinkCount = @($site.principals | Where-Object { $_.type -eq 'SharingLink' }).Count } } return $scan } function Get-SPMPermRank { <# .SYNOPSIS Rank of a permission code: FC=0, W=1, C=2, R=3, custom=4 (lower is stronger). #> [CmdletBinding()] param([string]$Permission) switch ($Permission) { 'FC' { return 0 } 'W' { return 1 } 'C' { return 2 } 'R' { return 3 } default { return 4 } } } function Get-SPMPersonMap { <# .SYNOPSIS Resolves the effective people of a site: per principal the set of contained person keys (UPN, case-insensitive), plus a global person index. #> [CmdletBinding()] param( [Parameter(Mandatory)] $Site ) $groupy = @('SharePointGroup', 'EntraGroup', 'M365Group', 'SecurityGroup', 'SharingLink') $perPrincipal = @{} $personIndex = [ordered]@{} foreach ($p in @($Site.principals)) { $set = [System.Collections.Generic.HashSet[string]]::new() if ($p.type -eq 'User') { $key = ([string]($p.upn ?? $p.name)).Trim().ToLowerInvariant() if ($key) { [void]$set.Add($key) if (-not $personIndex.Contains($key)) { $personIndex[$key] = [pscustomobject]@{ key = $key; name = [string]$p.name; upn = $p.upn } } } } elseif ($p.type -in $groupy -and $p.members) { $stack = [System.Collections.Generic.Stack[object]]::new() foreach ($m in @($p.members)) { $stack.Push($m) } while ($stack.Count -gt 0) { $m = $stack.Pop() if ($null -eq $m) { continue } if ($m.type -eq 'User') { $key = ([string]($m.upn ?? $m.name)).Trim().ToLowerInvariant() if (-not $key) { continue } [void]$set.Add($key) if (-not $personIndex.Contains($key)) { $personIndex[$key] = [pscustomobject]@{ key = $key; name = [string]($m.name ?? $m.upn); upn = $m.upn } } elseif (-not $personIndex[$key].upn -and $m.upn) { $personIndex[$key].upn = $m.upn } } elseif ($m.PSObject.Properties['members'] -and $m.members) { foreach ($c in @($m.members)) { $stack.Push($c) } } } } $perPrincipal[[string]$p.id] = $set } return @{ PerPrincipal = $perPrincipal Persons = @($personIndex.Values | Sort-Object name) } } |