changed: logging

changed: sort and deduplicate output
changed: "GruppenVerwaltungNEU..." output
This commit is contained in:
Edmond Gebara 2026-07-06 06:50:50 +02:00
parent b84b5a73a1
commit a5e28a7f52
12 changed files with 785 additions and 1280 deletions

View file

@ -2,32 +2,18 @@
function writeHeaders
{
################################################################
# --------------------------------------------------------------
# Header für $DAWGruppenAnlegenOutFilePath anlegen
# --------------------------------------------------------------
"Basket.Import.ADGroup.Create" | Out-File $DAWGruppenAnlegenOutFilePath -Encoding "utf8NoBOM"
"GUIMandator" + ";" + "GUIADGroup.Name" + ";" + "GUIADGroup.Description" + ";" + "GUIMandatorADGroupType" + ";" + "GUIADGroup.DisplayName" + ";" + "GUIProcessingNote" + ";" + "OldName" | Out-File $DAWGruppenAnlegenOutFilePath -Append -Encoding "utf8NoBom"
# --------------------------------------------------------------
# Header für $DAWGruppenverwaltungOutFilePath anlegen
# --------------------------------------------------------------
"Basket.Import.ADGroup.ManageMembers" | Out-File "$DAWGruppenverwaltungOutFilePath" -Encoding "utf8NoBom"
"GUIMandator" + ";" + "GUIMandatorADGroupType" + ";" + "GUIADGroup.LDAP" + ";" + "GUIADGroup.ADGroups.Add.LDAPs" + ";" + "GUIADGroup.ADGroups.Rem.LDAPs" + ";" + "GUIADGroup.Accounts.Add.LDAPs" + ";" + "GUIADGroup.Accounts.Rem.LDAPs" + ";" + "GUIProcessingNote" + ";" + "ADObjectType" + ";" + "Quellgruppe" | Out-File $DAWGruppenverwaltungOutFilePath -Encoding "utf8NoBom" -Append
"Basket.Import.ADGroup.ManageMembers" | Out-File "$DAWGruppenverwaltungNeuOutFilePath" -Encoding "utf8NoBom"
"GUIMandator" + ";" + "GUIMandatorADGroupType" + ";" + "GUIADGroup.LDAP" + ";" + "GUIADGroup.ADGroups.Add.LDAPs" + ";" + "GUIADGroup.ADGroups.Rem.LDAPs" + ";" + "GUIADGroup.Accounts.Add.LDAPs" + ";" + "GUIADGroup.Accounts.Rem.LDAPs" + ";" + "GUIProcessingNote" + ";" + "ADObjectType" + ";" + "Quellgruppe" | Out-File $DAWGruppenverwaltungNeuOutFilePath -Encoding "utf8NoBom" -Append
# --------------------------------------------------------------
# Header für $KURSSollProfile anlegen
# --------------------------------------------------------------
"Profilnummer;Name;Beschreibung;Profilverantwortliche OE/Stelle;Info;InfoKurs" | Out-File $KURSSollProfileOutFilePath -Encoding "utf8NoBom"
# --------------------------------------------------------------
# Header für $IstMitarbeiterProfile anlegen
# --------------------------------------------------------------
"Personalnummer;;Profilnummer;Name Berechtigungsprofil" | Out-File "$KURSIstMitarbeiterProfileOutFilePath" -Encoding "utf8NoBom"
################################################################
}
function getObjectMemberships
@ -51,6 +37,7 @@ function getObjectMemberships
}
else
{
outLogVerbose 'else -> true (if/elseif (?) -> false)'
@($bucket)
}
@ -73,34 +60,20 @@ function getAllUserMemberships
$groupMemberships = @(getObjectMemberships $adObjectEntry.ObjectName "group" "group")
foreach ($groupMembership in $groupMemberships)
{
outLogVerbose 'foreach $groupMembership in $groupMemberships:' $groupMembership.MemberName
$result.AddRange(@(getObjectMemberships $groupMembership.MemberName "user" "group"))
}
# it's the same, bad odd syntax
# return ,$result.ToArray()
return @($result)
}
function getUsersByMemberGroup
{
<#
.SYNOPSIS
Retrieves the user membership entries for a given group via the
prebuilt membership index (O(1) lookup, exact class match).
.PARAMETER GroupName
The group whose user members are requested (matched against GroupName).
.OUTPUTS
Array of matching membership entries (empty array if none).
Object references are preserved from the index.
#>
param
(
[Parameter(Mandatory = $true, Position = 0)]
[string] $GroupName
)
# BUGFIX 2026-07-02: was indexing the flat array $ADObjectMemberships with a string
# key, which fails silently (non-terminating Int32 conversion error -> $null) and
# dropped ALL indirect (group-inherited) user memberships. Must use the hashtable.
$bucket = $ADObjectMembershipsIndexed[(getMembershipIndexKey $GroupName "user")]
$result = if ($null -eq $bucket)
{
@ -108,47 +81,17 @@ function getUsersByMemberGroup
}
else
{
outLogVerbose 'else -> true (if/elseif (?) -> false)'
@($bucket)
}
return $result
}
# function getAllUserMemberships
# {
# param([Parameter(Mandatory, Position = 0)] $adObjectEntry)
# $result = [System.Collections.Generic.List[object]]::new()
# $result.AddRange(@(getObjectMemberships $adObjectEntry.ObjectName "user" "user"))
# $groupMemberships = @(getObjectMemberships $adObjectEntry.ObjectName "group" "group")
# foreach ($groupMembership in $groupMemberships)
# {
# $result.AddRange(@(getObjectMemberships $groupMembership.MemberName "user" "group"))
# }
# # ist beides gleich
# # return ,$result.ToArray()
# return @($result)
# }
# function getObjectMemberships
# {
# param
# (
# [Parameter(Mandatory = $true, Position = 0)]
# [String] $adObjectEntryName,
# [Parameter(Mandatory = $true, Position = 1)]
# [string] $memberShipType,
# [Parameter(Mandatory = $true, Position = 2)]
# [string] $memberShipOriginalType
# )
# $objectMemberships = @($ADObjectMemberships | Where-Object {($_.GroupName -ieq $adObjectEntryName) -and ($_.MemberClass -ieq $memberShipType)})
# # this is not very nice - we should only add members if they don´t exist - not just to set the values
# $objectMemberships | Add-Member -Force "Origin" $memberShipOriginalType
# $objectMemberships | Add-Member -Force "OriginName" $adObjectEntryName
# return $objectMemberships
# }
function getMembershipIndexKey
{
@ -164,16 +107,6 @@ function getMembershipIndexKey
function newObjectMembershipsIndex
{
<#
.SYNOPSIS
Builds a lookup index over membership entries for O(1) retrieval by
GroupName + MemberClass, replacing repeated linear Where-Object scans.
.PARAMETER ADObjectMemberships
The flat collection of membership entries to index.
.OUTPUTS
Hashtable mapping "<groupname>\0<memberclass>" (lowercased) to a
List[object] of the matching entries. Object references are preserved.
#>
param
(
[Parameter(Mandatory = $true, Position = 0)]
@ -185,16 +118,18 @@ function newObjectMembershipsIndex
foreach ($entry in $ADObjectMemberships)
{
# NUL separator avoids key collisions between e.g. "ab"+"c" and "a"+"bc"
outLogVerbose 'foreach $entry in $ADObjectMemberships:' $entry.GroupName
$key = getMembershipIndexKey $($entry.GroupName) $($entry.MemberClass)
$key0 = "$($entry.GroupName)`0$($entry.MemberClass)".ToLowerInvariant()
if (-not($key -eq $key0))
{
outLogVerbose 'if (-not($key -eq $key0)) -> true'
throw "this is BAAAD"
}
$bucket = $index[$key]
if ($null -eq $bucket)
{
outLogVerbose 'if ($null -eq $bucket) -> true'
$bucket = [System.Collections.Generic.List[object]]::new()
$index[$key] = $bucket
}
@ -213,27 +148,23 @@ function getRoleType
[Parameter(Mandatory = $true, Position = 1)]
[String] $typeTag
)
# $FileSystemAccessEntriesNew = @()
$roleType = "unknown"
# BUGFIX 2026-07-02: 'Sort-Object -Property "Order"' does not resolve hashtable keys on
# Windows PowerShell 5.1 (silent no-op, declaration order preserved). The scriptblock
# form works identically on 5.1 and 7, making rule evaluation order engine-independent.
$relevantRoleTypes = $SPK.GroupManagementMappings | Where-Object {$_.OU -ieq $typeTag} | Sort-Object { [int]$_.Order }
foreach ($relevantRoleType in $relevantRoleTypes)
{
outLogVerbose 'foreach $relevantRoleType in $relevantRoleTypes:' $relevantRoleType.TypeName
if ($adObjectEntry.OldName -cmatch $relevantRoleType.TagRegExp)
{
outLogVerbose 'if ($adObjectEntry.OldName -cmatch $relevantRoleType.TagRegExp) -> true'
$roleType = $relevantRoleType.TypeName
break
}
}
# Ausnahmebehandlungen in Absprache mit N.Zimmer am 21.01.2019
#--------------------------------------------------------------
if (($typeTag -ieq $FI.TypeTagSecurity) -and ($roleType -ieq "unknown"))
{
outLogVerbose 'if (($typeTag -ieq $FI.TypeTagSecurity) -and ($roleType -ieq "unknown")) -> true'
$roleType = "GPO"
}
#--------------------------------------------------------------
return $roletype
}
@ -246,18 +177,19 @@ function buildPrefixMatcher
if (-not (Test-Path $CsvPath))
{
outLogVerbose 'if (-not (Test-Path $CsvPath)) -> true'
throw "Prefix CSV not found: $CsvPath"
}
# length -> HashSet[string] of prefixes with exactly that length
$byLength = [System.Collections.Generic.Dictionary[int, System.Collections.Generic.HashSet[string]]]::new()
# foreach ($row in (Import-Csv -Path $CsvPath))
foreach ($row in $SPK.ProfilPrefixes)
{
outLogVerbose 'foreach $row in $SPK.ProfilPrefixes:' $row
$prefix = $row.Prefix
if ([string]::IsNullOrEmpty($prefix))
{
outLogVerbose 'if ([string]::IsNullOrEmpty($prefix)) -> true'
continue
}
@ -265,18 +197,15 @@ function buildPrefixMatcher
$bucket = $null
if (-not $byLength.TryGetValue($len, [ref] $bucket))
{
outLogVerbose 'if (-not $byLength.TryGetValue($len, [ref] $bucket)) -> true'
$bucket = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::Ordinal)
$byLength[$len] = $bucket
}
[void]$bucket.Add($prefix)
}
# Pre-compute the distinct lengths once, sorted descending so the most specific
# (longest) prefix wins first - mirrors the "specific before generic" ordering
# of a well-formed elseif cascade.
$lengths = $byLength.Keys | Sort-Object -Descending
# Return a small state object carrying both structures.
return [pscustomobject]@{
ByLength = $byLength
Lengths = $lengths
@ -294,40 +223,30 @@ function testKnownPrefixes
if ([string]::IsNullOrEmpty($Value))
{
outLogVerbose 'if ([string]::IsNullOrEmpty($Value)) -> true'
return $false
}
$valueLen = $Value.Length
foreach ($len in $Matcher.Lengths)
{
outLogVerbose 'foreach $len in $Matcher.Lengths:' $len
if ($len -gt $valueLen)
{
outLogVerbose 'if ($len -gt $valueLen) -> true'
continue
} # prefix longer than value -> can't match
}
$candidate = $Value.Substring(0, $len)
$bucket = $Matcher.ByLength[$len]
if ($bucket.Contains($candidate))
{
return $true # found a known prefix
outLogVerbose 'if ($bucket.Contains($candidate)) -> true'
return $true
}
}
return $false
}
# function getNormalizedDirectoryPaths
# {
# $analyzedDirectoryEntries = @()
# # $header = "Verzeichnisname", "Endpunktname", "Gruppe Lesen", "Gruppe Aendern", "Benutzer mit Leserecht", "Benutzer mit Aenderungsrecht", "Rollen mit Leserecht", "Rollen mit Aenderungsrecht", "Verzeichnisart", "normierter Pfad"
# # $directoriesFilePathEntries = Get-Content -Path $DirectoriesFilePath | Select-Object -Skip 1 | ConvertFrom-Csv -Delimiter ";" -Header $header
# # $directoriesFilePathEntries = Get-Content -Path $DirectoriesInFilePath | Select-Object -Skip 1 | ConvertFrom-Csv -Delimiter ";"
# $directoriesFilePathEntries = Get-Content -Path $DirectoriesInFilePath | ConvertFrom-Csv -Delimiter ";"
# # $headers = ($directoriesFilePathEntries | Get-Member -MemberType NoteProperty)
# # $interstingHeader = $headers.Name -match "Gruppe .ndern"
# foreach ($FilePathEntry in $directoriesFilePathEntries)
# {
# $analyzedDirectoryEntries += $FilePathEntry.Name
# }
# }
function postProcess
@ -337,41 +256,10 @@ function postProcess
[bool]$convertToAscii = $false
)
#########################################################
# just to be sure that nothing happens: comment it out until you know what`s happening here !
#########################################################
# $tmpFilePath = moveToInstituteTmpPath $DAWGruppenAnlegenOutFilePath $true
# $firstlines = Get-Content -Path $tmpFilePath -TotalCount 2
# Get-Content -Path $tmpFilePath -TotalCount 1 | Out-File -Force $DAWGruppenAnlegenOutFilePath
# Get-Content -Path $tmpFilePath | Select-Object -Skip 2 | ConvertFrom-Csv -Delimiter ";" | select 'GUIMandator', 'GUIADGroup.Name', 'GUIADGroup.Description', 'GUIMandatorADGroupType', 'GUIADGroup.DisplayName', 'GUIProcessingNote' | ConvertTo-Csv -Delimiter ';' -NoTypeInformation | Out-File -FilePath $DAWGruppenAnlegenOutFilePath -Encoding UTF8 -Append
# # Import-Csv $tmpFilePath | select 'GUIMandator','GUIADGroup.Name','GUIADGroup.Description','GUIMandatorADGroupType','GUIADGroup.DisplayName','GUIProcessingNote' | Export-Csv -Path $DAWGruppenAnlegenOutFilePath -Delimiter ";" -Encoding UTF8
# $tmpFilePath = moveToInstituteTmpPath $DAWGruppenverwaltungOutFilePath $true
# $firstlines = Get-Content -Path $tmpFilePath -TotalCount 2
# Get-Content -Path $tmpFilePath -TotalCount 1 | Out-File -Force $DAWGruppenverwaltungOutFilePath
# Get-Content -Path $tmpFilePath | Select-Object -Skip 2 | ConvertFrom-Csv -Delimiter ";" | select "GUIMandator", "GUIMandatorADGroupType", "GUIADGroup.LDAP", "GUIADGroup.ADGroups.Add.LDAPs", "GUIADGroup.ADGroups.Rem.LDAPs", "GUIADGroup.Accounts.Add.LDAPs", "GUIADGroup.Accounts.Rem.LDAPs", "GUIProcessingNote" | ConvertTo-Csv -Delimiter ';' -NoTypeInformation | Out-File -FilePath $DAWGruppenverwaltungOutFilePath -Encoding UTF8 -Append
# # Import-Csv $tmpFilePath | select "GUIMandator","GUIMandatorADGroupType","GUIADGroup.LDAP","GUIADGroup.ADGroups.Add.LDAPs","GUIADGroup.ADGroups.Rem.LDAPs","GUIADGroup.Accounts.Add.LDAPs","GUIADGroup.Accounts.Rem.LDAPs","GUIProcessingNote" | Export-Csv -Path $DAWGruppenverwaltungOutFilePath -Delimiter ";" -Encoding UTF8 -NoTypeInformation
# $tmpFilePath = moveToInstituteTmpPath $DAWGruppenverwaltungNeuOutFilePath $true
# $firstlines = Get-Content -Path $tmpFilePath -TotalCount 2
# Get-Content -Path $tmpFilePath -TotalCount 1 | Out-File -Force $DAWGruppenverwaltungNeuOutFilePath
# Get-Content -Path $tmpFilePath | Select-Object -Skip 2 | ConvertFrom-Csv -Delimiter ";" | select "GUIMandator", "GUIMandatorADGroupType", "GUIADGroup.LDAP", "GUIADGroup.ADGroups.Add.LDAPs", "GUIADGroup.ADGroups.Rem.LDAPs", "GUIADGroup.Accounts.Add.LDAPs", "GUIADGroup.Accounts.Rem.LDAPs", "GUIProcessingNote" | ConvertTo-Csv -Delimiter ';' -NoTypeInformation | Out-File -FilePath $DAWGruppenverwaltungNeuOutFilePath -Encoding UTF8 -Append
####################################################### todo ?
# $KURSIstProfileOutFilePath
# $KURSIstProfileBerechtigungenOutFilePath
# $KURSIstStellenBerechtigungenOutFilePath
# $KURSIstStellenProfileOutFilePath
# $KURSIstOEsBerechtigungenOutFilePath
# $KURSIstOEsProfileOutFilePath
# $KURSIstMitarbeiterProfileOutFilePath
# $KURSSollStellenfunktionenOutFilePath
# $KURSSollProfileOutFilePath
# $KURSSollProfileBerechtigungenOutFilePath
# $KURSSollStellenfunktionenBerechtigungenOutFilePath
# $KURSSollStellenfunktionenProfileOutFilePath
####################################################### todo ?
}