diff --git a/DomainManagement/DomainManagement.psd1 b/DomainManagement/DomainManagement.psd1 index bb8d628..be9a6f0 100644 --- a/DomainManagement/DomainManagement.psd1 +++ b/DomainManagement/DomainManagement.psd1 @@ -3,7 +3,7 @@ RootModule = 'DomainManagement.psm1' # Version number of this module. - ModuleVersion = '1.9.239' + ModuleVersion = '1.9.248' # ID used to uniquely identify this module GUID = '0a405382-ebc2-445b-8325-541535810193' diff --git a/DomainManagement/changelog.md b/DomainManagement/changelog.md index 115693a..6301f2c 100644 --- a/DomainManagement/changelog.md +++ b/DomainManagement/changelog.md @@ -1,5 +1,15 @@ # Changelog +## 1.9.248 (2026-02-12) + +- Upd: AccessRules - stopped removing "Protect from Accidental deletion" from containers +- Fix: AccessRules - invocation fails when access rule ordering is inconsistent. Now reports an error and skips the broken entry. +- Fix: AccessRules - fails some identity resolution scenarios for the builtin administrators group, when it is in a domain originally setup in German +- Fix: Groups - when renaming a group, the change details will include the wrong Identity +- Fix: GPPermission - when removing access for an identity that cannot be resolved, the change shows only a `\`. +- Fix: GPOwner - fails if a GPO exists in AD that does not contain a DisplayName (e.g. a highly corrupted GPO) +- Fix: Service Accounts - tests generate an error for gMSA whose principals cannot be resolved. + ## 1.9.239 (2025-11-13) - New: Configuration "DomainManagement.GroupPolicy.AlwaysUsePDC" - can be used to disable the Group Policy commands to prefer the PDC over the specified DC diff --git a/DomainManagement/en-us/strings.psd1 b/DomainManagement/en-us/strings.psd1 index 386419b..470e705 100644 --- a/DomainManagement/en-us/strings.psd1 +++ b/DomainManagement/en-us/strings.psd1 @@ -49,6 +49,7 @@ 'Invoke-DMAccessRule.AccessRule.Create' = 'Adding access rule for {0}, granting {1} ({2})' # $changeEntry.Configuration.IdentityReference, $changeEntry.Configuration.ActiveDirectoryRights, $changeEntry.Configuration.AccessControlType 'Invoke-DMAccessRule.AccessRule.Creation.Failed' = 'Failed to create accessrule at {0} for {1}' # $testItem.Identity, $changeEntry.Configuration.IdentityReference 'Invoke-DMAccessRule.AccessRule.Remove' = 'Removing access rule for {0}, granting {1} ({2}) from {3}' # $changeEntry.ADObject.IdentityReference, $changeEntry.ADObject.ActiveDirectoryRights, $changeEntry.ADObject.AccessControlType, $changeEntry.DistinguishedName + 'Invoke-DMAccessRule.AccessRule.Remove.Error.Consistency' = 'Failed to remove access rule from {0}! This may be due to a consistency error. Investigate the object, it may be resolvable via the dsa GUI in the security tab' # $testItem.Identity 'Invoke-DMAccessRule.AccessRule.Remove.Failed' = 'Failed to removing access rule for {0}, granting {1} ({2}) from {3} for unknown reasons (sorry). If this persists, consider enabling the alternative deletion mode through the "Domainmanagement.AccessRules.Remove.Option2" configuration setting.' # $changeEntry.ADObject.IdentityReference, $changeEntry.ADObject.ActiveDirectoryRights, $changeEntry.ADObject.AccessControlType, $changeEntry.DistinguishedName 'Invoke-DMAccessRule.AccessRule.Restore' = 'Restoring access rule from schema default for {0}, granting {1} ({2})' # $changeEntry.Configuration.IdentityReference, $changeEntry.Configuration.ActiveDirectoryRights, $changeEntry.Configuration.AccessControlType 'Invoke-DMAccessRule.ADObject.Missing' = 'Cannot process access rules, due to missing AD object: {0}. Please ensure the domain object is created before trying to apply rules to it!' # $testItem.Identity @@ -235,8 +236,11 @@ 'Test-DMServiceAccount.Computer.NotFound' = 'Error processing service account {1}: Cannot find computer {0}, will be unable to assign this access permission to the service account.' # $name, $resolvedName 'Test-DMServiceAccount.Computer.Optional.NotFound' = 'Error processing service account {1}: Cannot find computer {0}, will be unable to assign this access permission to the service account.' # $name, $resolvedName + 'Test-DMServiceAccount.Error.PrincipalNotFound' = 'Error resolving principals allowed to access the password of gMSA {0}: {1} cannot be resolved. This might be a previously deleted principal. Invoking principal changes against this gMSA will likely remove this assignment.' # $adObject.SamAccountName, $fail.TargetObject 'Test-DMServiceAccount.Group.NotFound' = 'Error processing service account {1}: Cannot find group {0}, will be unable to assign this access permission to the service account.' # $name, $resolvedName + 'Test-GPPermissionFilter.Error.BadAdGpoConfiguration.DisplayName' = 'Bad Configuration state in Active Directory: A corrupt Group Policy object was found that contains no displayname. You can search for the object with this command: "Get-ADObject -LdapFilter ''(&(objectCategory=groupPolicyContainer)(!(DisplayName=*)))''"' # + 'Test-KdsRootKey.Adding' = 'Adding KDS Root Key. It is backdated by 10 hours and thus instantly available. Until this has fully replicated however, you may still be unable to reliably use any created group Managed Service Accounts. This is purely to facilitate gMSA creation.' # 'Test-KdsRootKey.Failed' = 'Failed to add a KDS Root Key. Make sure you have sufficient permissions to complete the task.' # diff --git a/DomainManagement/functions/AccessRule/Invoke-DMAccessRule.ps1 b/DomainManagement/functions/AccessRule/Invoke-DMAccessRule.ps1 index 78b697c..9adb5b4 100644 --- a/DomainManagement/functions/AccessRule/Invoke-DMAccessRule.ps1 +++ b/DomainManagement/functions/AccessRule/Invoke-DMAccessRule.ps1 @@ -80,7 +80,11 @@ #region Remove Access Rules if ($changeEntry.Type -eq 'Delete') { Write-PSFMessage -Level InternalComment -String 'Invoke-DMAccessRule.AccessRule.Remove' -StringValues $changeEntry.ADObject.IdentityReference, $changeEntry.ADObject.ActiveDirectoryRights, $changeEntry.ADObject.AccessControlType, $changeEntry.DistinguishedName -Target $changeEntry - $aclObject.RemoveAccessRuleSpecific($changeEntry.ADObject.OriginalRule) + try { $aclObject.RemoveAccessRuleSpecific($changeEntry.ADObject.OriginalRule) } + catch { + Write-PSFMessage -Level Warning -String 'Invoke-DMAccessRule.AccessRule.Remove.Error.Consistency' -StringValues $testItem.Identity -Target $changeEntry + continue + } Remove-RedundantAce -AccessControlList $aclObject -IdentityReference $changeEntry.ADObject.OriginalRule.IdentityReference $stillThere = $false diff --git a/DomainManagement/functions/gppermissions/Test-DMGPPermission.ps1 b/DomainManagement/functions/gppermissions/Test-DMGPPermission.ps1 index b6e99fe..d24ee92 100644 --- a/DomainManagement/functions/gppermissions/Test-DMGPPermission.ps1 +++ b/DomainManagement/functions/gppermissions/Test-DMGPPermission.ps1 @@ -115,6 +115,7 @@ Action = $null ADObject = $ADObject } + if ($result.DisplayName -eq '\') { $result.DisplayName = $inputItem.Trustee.Sid -as [string] } $result | Add-Member -MemberType ScriptMethod -Name ToString -Force -PassThru -Value { '{0}: {1}' -f $this.Action, $this.DisplayName } diff --git a/DomainManagement/functions/groups/Test-DMGroup.ps1 b/DomainManagement/functions/groups/Test-DMGroup.ps1 index b144756..73765f2 100644 --- a/DomainManagement/functions/groups/Test-DMGroup.ps1 +++ b/DomainManagement/functions/groups/Test-DMGroup.ps1 @@ -79,7 +79,7 @@ #region Case: One old version present 1 { - New-TestResult @resultDefaults -Type Rename -ADObject $oldGroups -Changed (New-AdcChange -Identity $adObject -Property Name -OldValue $oldGroups.Name -NewValue $resolvedName) + New-TestResult @resultDefaults -Type Rename -ADObject $oldGroups -Changed (New-AdcChange -Identity $oldGroups -Property Name -OldValue $oldGroups.Name -NewValue $resolvedName) $oldNamesFound += $oldGroups.Name $noNameUpdate = $true $adObject = $oldGroups diff --git a/DomainManagement/functions/serviceaccounts/Test-DMServiceAccount.ps1 b/DomainManagement/functions/serviceaccounts/Test-DMServiceAccount.ps1 index 8618c2a..56d6d06 100644 --- a/DomainManagement/functions/serviceaccounts/Test-DMServiceAccount.ps1 +++ b/DomainManagement/functions/serviceaccounts/Test-DMServiceAccount.ps1 @@ -173,8 +173,11 @@ #region PrincipalsAllowedToRetrieveManagedPassword # Use SamAccountName rather than DistinguishedName as accounts may not yet have been moved to their correct container so DN might fail - $currentPrincipals = ($adObject.PrincipalsAllowedToRetrieveManagedPassword | Get-ADObject @parameters -Properties SamAccountName).SamAccountName - + $currentPrincipals = ($adObject.PrincipalsAllowedToRetrieveManagedPassword | Get-ADObject @parameters -Properties SamAccountName -ErrorAction SilentlzContinue -ErrorVariable failed).SamAccountName + foreach ($fail in $failed) { + Write-PSFMessage -Level Warning -String 'Test-DMServiceAccount.Error.PrincipalNotFound' -StringValues $adObject.SamAccountName, $fail.TargetObject -Target $adObject + } + # Object Category $desiredPrincipals = @() foreach ($category in $serviceAccountDefinition.ObjectCategory) { diff --git a/DomainManagement/functions/users/Test-DMUser.ps1 b/DomainManagement/functions/users/Test-DMUser.ps1 index ac3ebff..549f086 100644 --- a/DomainManagement/functions/users/Test-DMUser.ps1 +++ b/DomainManagement/functions/users/Test-DMUser.ps1 @@ -86,7 +86,7 @@ #region Case: One old version present 1 { - New-TestResult @resultDefaults -Type Rename -ADObject $oldUsers + New-TestResult @resultDefaults -Type Rename -ADObject $oldUsers -Changed (New-AdcChange -Identity $oldUsers -Property Name -OldValue $oldUsers.Name -NewValue $resolvedSamAccName) continue main } #endregion Case: One old version present diff --git a/DomainManagement/internal/functions/acl_ace/Compare-AccessRules.ps1 b/DomainManagement/internal/functions/acl_ace/Compare-AccessRules.ps1 index 263ba3f..da898d9 100644 --- a/DomainManagement/internal/functions/acl_ace/Compare-AccessRules.ps1 +++ b/DomainManagement/internal/functions/acl_ace/Compare-AccessRules.ps1 @@ -95,7 +95,7 @@ $relevantADRules = :outer foreach ($adRule in $ADRules) { if ($adRule.OriginalRule.IsInherited) { continue } #region Skip OUs' "Protect from Accidential Deletion" ACE - if (($adRule.AccessControlType -eq 'Deny') -and ($ADObject.ObjectClass -eq 'organizationalUnit')) { + if (($adRule.AccessControlType -eq 'Deny') -and ($ADObject.ObjectClass -in 'organizationalUnit','container')) { if ($adRule.IdentityReference -eq 'everyone') { continue } $eSid = [System.Security.Principal.SecurityIdentifier]'S-1-1-0' $eName = $eSid.Translate([System.Security.Principal.NTAccount]) diff --git a/DomainManagement/internal/functions/groupPolicy/Test-GPPermissionFilter.ps1 b/DomainManagement/internal/functions/groupPolicy/Test-GPPermissionFilter.ps1 index fb96b15..89895e6 100644 --- a/DomainManagement/internal/functions/groupPolicy/Test-GPPermissionFilter.ps1 +++ b/DomainManagement/internal/functions/groupPolicy/Test-GPPermissionFilter.ps1 @@ -30,6 +30,7 @@ [CmdletBinding()] param ( [Parameter(Mandatory = $true)] + [AllowEmptyString()] [string] $GpoName, @@ -50,6 +51,11 @@ $FilterHash ) + if (-not $GpoName) { + Write-PSFMessage -Level Warning -String 'Test-GPPermissionFilter.Error.BadAdGpoConfiguration.DisplayName' + return + } + if (-not $Filter.Trim()) { return $false } $testResults = @{ } diff --git a/DomainManagement/internal/scripts/variables2.ps1 b/DomainManagement/internal/scripts/variables2.ps1 index ea8f6b0..0f8ee48 100644 --- a/DomainManagement/internal/scripts/variables2.ps1 +++ b/DomainManagement/internal/scripts/variables2.ps1 @@ -1,6 +1,7 @@ # File for variables that should NOT be reset on context changes $script:builtInSidMapping = @{ # English + 'BUILTIN\Administrators' = [System.Security.Principal.SecurityIdentifier]'S-1-5-32-544' 'BUILTIN\Account Operators' = [System.Security.Principal.SecurityIdentifier]'S-1-5-32-548' 'BUILTIN\Server Operators' = [System.Security.Principal.SecurityIdentifier]'S-1-5-32-549' 'BUILTIN\Print Operators' = [System.Security.Principal.SecurityIdentifier]'S-1-5-32-550' @@ -15,6 +16,7 @@ $script:builtInSidMapping = @{ 'BUILTIN\Storage Replica Administrators' = [System.Security.Principal.SecurityIdentifier]'S-1-5-32-582' # Deutsch + 'BUILTIN\Administratoren' = [System.Security.Principal.SecurityIdentifier]'S-1-5-32-544' 'BUILTIN\Konten-Operatoren' = [System.Security.Principal.SecurityIdentifier]'S-1-5-32-548' 'BUILTIN\Server-Operatoren' = [System.Security.Principal.SecurityIdentifier]'S-1-5-32-549' 'BUILTIN\Druck-Operatoren' = [System.Security.Principal.SecurityIdentifier]'S-1-5-32-550'