From c44f389b6985ba11a3b1cb8cf0fde70afe6e68f6 Mon Sep 17 00:00:00 2001 From: FriedrichWeinmann Date: Fri, 12 Jan 2024 08:12:25 +0100 Subject: [PATCH 1/2] adding parallelization --- DomainManagement/DomainManagement.psd1 | 4 +- DomainManagement/changelog.md | 4 + DomainManagement/en-us/strings.psd1 | 1 + .../AccessRule/Test-DMAccessRule.ps1 | 574 +++++++----------- .../internal/configurations/configuration.ps1 | 9 +- .../functions/acl_ace/Compare-AccessRules.ps1 | 183 ++++++ .../acl_ace/Convert-AccessRuleIdentity.ps1 | 68 +++ .../acl_ace/Get-CategoryBasedRules.ps1 | 75 +++ .../functions/acl_ace/Resolve-Identity.ps1 | 72 +++ .../internal/scripts/variables2.ps1 | 5 +- 10 files changed, 638 insertions(+), 357 deletions(-) create mode 100644 DomainManagement/internal/functions/acl_ace/Compare-AccessRules.ps1 create mode 100644 DomainManagement/internal/functions/acl_ace/Convert-AccessRuleIdentity.ps1 create mode 100644 DomainManagement/internal/functions/acl_ace/Get-CategoryBasedRules.ps1 create mode 100644 DomainManagement/internal/functions/acl_ace/Resolve-Identity.ps1 diff --git a/DomainManagement/DomainManagement.psd1 b/DomainManagement/DomainManagement.psd1 index af0c4d8..fb1f11f 100644 --- a/DomainManagement/DomainManagement.psd1 +++ b/DomainManagement/DomainManagement.psd1 @@ -3,7 +3,7 @@ RootModule = 'DomainManagement.psm1' # Version number of this module. - ModuleVersion = '1.8.201' + ModuleVersion = '1.8.202' # ID used to uniquely identify this module GUID = '0a405382-ebc2-445b-8325-541535810193' @@ -26,7 +26,7 @@ # Modules that must be imported into the global environment prior to importing # this module RequiredModules = @( - @{ ModuleName = 'PSFramework'; ModuleVersion = '1.7.270' } + @{ ModuleName = 'PSFramework'; ModuleVersion = '1.10.318' } # Additional Dependencies, cannot declare due to bug in dependency handling in PS5.1 # @{ ModuleName = 'ADSec'; ModuleVersion = '1.0.0' } diff --git a/DomainManagement/changelog.md b/DomainManagement/changelog.md index 6005809..4557523 100644 --- a/DomainManagement/changelog.md +++ b/DomainManagement/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 1.8.202 (2024-01-12) + +- Upd: Access Rules - added option to parallelize tests (experimental) + ## 1.8.201 (2023-12-08) - Upd: Group Policy - will now detect group policies that have been created but not yet linked as created. diff --git a/DomainManagement/en-us/strings.psd1 b/DomainManagement/en-us/strings.psd1 index f15d016..961dd32 100644 --- a/DomainManagement/en-us/strings.psd1 +++ b/DomainManagement/en-us/strings.psd1 @@ -194,6 +194,7 @@ 'Test-DMAccessRule.DefaultPermission.Failed' = 'Failed to retrieve default permissions from Schema when connecting to {0}' # $Server 'Test-DMAccessRule.NoAccess' = 'Failed to access {0}' # $resolvedPath + 'Test-DMAccessRule.Parallel.Error' = 'Failed to process {0}' # $fail.ADObject 'Test-DMAcl.ADObjectNotFound' = 'The target object could not be found: {0}' # $resolvedPath 'Test-DMAcl.NoAccess' = 'Failed to access Acl on {0}' # $resolvedPath diff --git a/DomainManagement/functions/AccessRule/Test-DMAccessRule.ps1 b/DomainManagement/functions/AccessRule/Test-DMAccessRule.ps1 index 228e4fa..b5c928a 100644 --- a/DomainManagement/functions/AccessRule/Test-DMAccessRule.ps1 +++ b/DomainManagement/functions/AccessRule/Test-DMAccessRule.ps1 @@ -1,5 +1,4 @@ -function Test-DMAccessRule -{ +function Test-DMAccessRule { <# .SYNOPSIS Validates the targeted domain's Access Rule configuration. @@ -34,6 +33,7 @@ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "")] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")] [CmdletBinding()] param ( [PSFComputer] @@ -43,159 +43,8 @@ $Credential ) - begin - { + begin { #region Utility Functions - function Compare-AccessRules { - [CmdletBinding()] - param ( - [AllowEmptyCollection()] - $ADRules, - [AllowEmptyCollection()] - $ConfiguredRules, - [AllowEmptyCollection()] - $DefaultRules, - $ADObject, - - [PSFComputer] - $Server, - - [PSCredential] - $Credential - ) - - $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential - - # Resolve the mode under which it will be evaluated. Either 'Additive' or 'Constrained' - $processingMode = Resolve-DMAccessRuleMode @parameters -ADObject $adObject - - function Write-Result { - [CmdletBinding()] - param ( - [ValidateSet('Create', 'Delete', 'FixConfig', 'Restore')] - [Parameter(Mandatory = $true)] - $Type, - - $Identity, - - [AllowNull()] - $ADObject, - - [AllowNull()] - $Configuration, - - [string] - $DistinguishedName - ) - - $item = [PSCustomObject]@{ - PSTypeName = 'DomainManagement.AccessRule.Change' - Type = $Type - ACT = $ADObject.AccessControlType - Identity = $Identity - Rights = $ADObject.ActiveDirectoryRights - DistinguishedName = $DistinguishedName - ADObject = $ADObject - Configuration = $Configuration - } - if (-not $ADObject) { - $item.ACT = $Configuration.AccessControlType - $item.Rights = $Configuration.ActiveDirectoryRights - } - Add-Member -InputObject $item -MemberType ScriptMethod ToString -Value { '{0}: {1}' -f $this.Type, $this.Identity } -Force -PassThru - } - - $defaultRulesPresent = [System.Collections.ArrayList]::new() - $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.IdentityReference -eq 'everyone') { continue } - $eSid = [System.Security.Principal.SecurityIdentifier]'S-1-1-0' - $eName = $eSid.Translate([System.Security.Principal.NTAccount]) - if ($adRule.IdentityReference -eq $eName) { continue } - if ($adRule.IdentityReference -eq $eSid) { continue } - } - #endregion Skip OUs' "Protect from Accidential Deletion" ACE - - foreach ($defaultRule in $DefaultRules) { - if (Test-AccessRuleEquality -Parameters $parameters -Rule1 $adRule -Rule2 $defaultRule) { - $null = $defaultRulesPresent.Add($defaultRule) - continue outer - } - } - $adRule - } - - #region Foreach non-default AD Rule: Check whether configured and delete if not so - :outer foreach ($relevantADRule in $relevantADRules) { - foreach ($configuredRule in $ConfiguredRules) { - if (Test-AccessRuleEquality -Parameters $parameters -Rule1 $relevantADRule -Rule2 $configuredRule) { - # If explicitly defined for deletion, do so - if ('False' -eq $configuredRule.Present) { - Write-Result -Type Delete -Identity $relevantADRule.IdentityReference -ADObject $relevantADRule -DistinguishedName $ADObject - } - continue outer - } - } - - # Don't generate delete changes - if ($processingMode -eq 'Additive') { continue } - # Don't generate delete changes, unless we have configured a permission level for the affected identity - if ($processingMode -eq 'Defined') { - if (-not ($relevantADRule.IdentityReference | Compare-Identity -Parameters $parameters -ReferenceIdentity $ConfiguredRules.IdentityReference -IncludeEqual -ExcludeDifferent)) { - continue - } - } - - Write-Result -Type Delete -Identity $relevantADRule.IdentityReference -ADObject $relevantADRule -DistinguishedName $ADObject - } - #endregion Foreach non-default AD Rule: Check whether configured and delete if not so - - #region Foreach configured rule: Check whether it exists as defined or make it so - :outer foreach ($configuredRule in $ConfiguredRules) { - foreach ($defaultRule in $DefaultRules) { - if ('True' -ne $configuredRule.Present) { break } - if ($configuredRule.NoFixConfig) { break } - if (Test-AccessRuleEquality -Parameters $parameters -Rule1 $defaultRule -Rule2 $configuredRule) { - Write-Result -Type FixConfig -Identity $defaultRule.IdentityReference -ADObject $defaultRule -Configuration $configuredRule -DistinguishedName $ADObject - continue outer - } - } - foreach ($relevantADRule in $relevantADRules) { - if (Test-AccessRuleEquality -Parameters $parameters -Rule1 $relevantADRule -Rule2 $configuredRule) { - continue outer - } - } - # Do not generate Create rules for any rule not configured for creation - if ('True' -ne $configuredRule.Present) { continue } - Write-Result -Type Create -Identity $configuredRule.IdentityReference -Configuration $configuredRule -DistinguishedName $ADObject - } - #endregion Foreach configured rule: Check whether it exists as defined or make it so - - #region Foreach non-existent default rule: Create unless configured otherwise - $domainControllersOUFilter = '*{0}' -f ('OU=Domain Controllers,%DomainDN%' | Resolve-String) - :outer foreach ($defaultRule in $DefaultRules | Where-Object { $_ -notin $defaultRulesPresent.ToArray() }) { - # Do not apply restore to Domain Controllers OU, as it is already deployed intentionally diverging from the OU defaults - if ($ADObject -like $domainControllersOUFilter) { break } - - # Skip 'CREATOR OWNER' Rules, as those should never be restored. - # When creating an AD object that has this group as default permissions, it will instead - # Translate those to the identity creating the object - if ('S-1-3-0' -eq $defaultRule.SID) { continue } - - foreach ($configuredRule in $ConfiguredRules) { - if (Test-AccessRuleEquality -Parameters $parameters -Rule1 $defaultRule -Rule2 $configuredRule) { - # If we explicitly don't want the rule: Skip and do NOT create a restoration action - if ('True' -ne $configuredRule.Present) { continue outer } - } - } - - Write-Result -Type Restore -Identity $defaultRule.IdentityReference -Configuration $defaultRule -DistinguishedName $ADObject - } - #endregion Foreach non-existent default rule: Create unless configured otherwise - } - function Convert-AccessRule { [CmdletBinding()] param ( @@ -227,24 +76,24 @@ try { $identity = Resolve-Identity @parameters -IdentityReference $ruleObject.IdentityReference -ADObject $ADObject } catch { - if ('True' -ne $ruleObject.Present) { continue } - Stop-PSFFunction -String 'Convert-AccessRule.Identity.ResolutionError' -Target $ruleObject -ErrorRecord $_ -Continue - } + if ('True' -ne $ruleObject.Present) { continue } + Stop-PSFFunction -String 'Convert-AccessRule.Identity.ResolutionError' -Target $ruleObject -ErrorRecord $_ -Continue + } [PSCustomObject]@{ - PSTypeName = 'DomainManagement.AccessRule.Converted' - IdentityReference = $identity - AccessControlType = $ruleObject.AccessControlType - ActiveDirectoryRights = $ruleObject.ActiveDirectoryRights - InheritanceFlags = $ruleObject.InheritanceFlags - InheritanceType = $ruleObject.InheritanceType - InheritedObjectType = $inheritedObjectTypeGuid + PSTypeName = 'DomainManagement.AccessRule.Converted' + IdentityReference = $identity + AccessControlType = $ruleObject.AccessControlType + ActiveDirectoryRights = $ruleObject.ActiveDirectoryRights + InheritanceFlags = $ruleObject.InheritanceFlags + InheritanceType = $ruleObject.InheritanceType + InheritedObjectType = $inheritedObjectTypeGuid InheritedObjectTypeName = $inheritedObjectTypeName - ObjectFlags = $ruleObject.ObjectFlags - ObjectType = $objectTypeGuid - ObjectTypeName = $objectTypeName - PropagationFlags = $ruleObject.PropagationFlags - Present = $ruleObject.Present + ObjectFlags = $ruleObject.ObjectFlags + ObjectType = $objectTypeGuid + ObjectTypeName = $objectTypeName + PropagationFlags = $ruleObject.PropagationFlags + Present = $ruleObject.Present } } } @@ -257,146 +106,6 @@ $convertCmdGuid.End() } } - - function Convert-AccessRuleIdentity { - [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingEmptyCatchBlock', '')] - [CmdletBinding()] - param ( - [Parameter(ValueFromPipeline = $true)] - [System.DirectoryServices.ActiveDirectoryAccessRule[]] - $InputObject, - - [PSFComputer] - $Server, - - [PSCredential] - $Credential - ) - begin { - $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential - $domainObject = Get-Domain2 @parameters - } - process { - :main foreach ($accessRule in $InputObject) { - if ($accessRule.IdentityReference -is [System.Security.Principal.NTAccount]) { - Add-Member -InputObject $accessRule -MemberType NoteProperty -Name OriginalRule -Value $accessRule -PassThru - continue main - } - - if (-not $accessRule.IdentityReference.AccountDomainSid) { - try { $identity = Get-Principal @parameters -Sid $accessRule.IdentityReference -Domain $domainObject.DNSRoot -OutputType NTAccount } - catch { - # Empty Catch is OK here, warning happens in command - } - } - else { - try { $identity = Get-Principal @parameters -Sid $accessRule.IdentityReference -Domain $accessRule.IdentityReference -OutputType NTAccount } - catch { - # Empty Catch is OK here, warning happens in command - } - } - if (-not $identity) { - $identity = $accessRule.IdentityReference - } - - $newRule = [System.DirectoryServices.ActiveDirectoryAccessRule]::new($identity, $accessRule.ActiveDirectoryRights, $accessRule.AccessControlType, $accessRule.ObjectType, $accessRule.InheritanceType, $accessRule.InheritedObjectType) - # Include original object as property in order to facilitate removal if needed. - Add-Member -InputObject $newRule -MemberType NoteProperty -Name OriginalRule -Value $accessRule -PassThru - } - } - } - - function Resolve-Identity { - [CmdletBinding()] - param ( - [string] - $IdentityReference, - - $ADObject, - - [PSFComputer] - $Server, - - [PSCredential] - $Credential - ) - - #region Parent Resolution - if ($IdentityReference -eq '') { - $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential - $domainObject = Get-Domain2 @parameters - $parentPath = ($ADObject.DistinguishedName -split ",",2)[1] - $parentObject = Get-ADObject @parameters -Identity $parentPath -Properties SamAccountName, Name, ObjectSID - if (-not $parentObject.ObjectSID) { - Stop-PSFFunction -String 'Resolve-Identity.ParentObject.NoSecurityPrincipal' -StringValues $ADObject, $parentObject.Name, $parentObject.ObjectClass -EnableException $true -Cmdlet $PSCmdlet - } - if ($parentObject.SamAccountName) { return [System.Security.Principal.NTAccount]('{0}\{1}' -f $domainObject.Name, $parentObject.SamAccountName) } - else { return [System.Security.Principal.NTAccount]('{0}\{1}' -f $domainObject.Name, $parentObject.Name) } - } - #endregion Parent Resolution - - #region Default Resolution - $identity = Resolve-String -Text $IdentityReference - if ($identity -as [System.Security.Principal.SecurityIdentifier]) { - $identity = $identity -as [System.Security.Principal.SecurityIdentifier] - } - else { - $identity = $identity -as [System.Security.Principal.NTAccount] - } - if ($null -eq $identity) { $identity = (Resolve-String -Text $IdentityReference) -as [System.Security.Principal.NTAccount] } - - $identity - #endregion Default Resolution - } - - function Get-CategoryBasedRules { - [CmdletBinding()] - param ( - [Parameter(Mandatory = $true)] - $ADObject, - - [PSFComputer] - $Server, - - [PSCredential] - $Credential, - - $ConvertNameCommand, - - $ConvertGuidCommand - ) - - $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include ADObject, Server, Credential - - $resolvedCategories = Resolve-DMObjectCategory @parameters - foreach ($resolvedCategory in $resolvedCategories) { - foreach ($ruleObject in $script:accessCategoryRules[$resolvedCategory.Name]) { - $objectTypeGuid = $ConvertGuidCommand.Process($ruleObject.ObjectType)[0] - $objectTypeName = $ConvertNameCommand.Process($ruleObject.ObjectType)[0] - $inheritedObjectTypeGuid = $ConvertGuidCommand.Process($ruleObject.InheritedObjectType)[0] - $inheritedObjectTypeName = $ConvertNameCommand.Process($ruleObject.InheritedObjectType)[0] - - try { $identity = Resolve-Identity @parameters -IdentityReference $ruleObject.IdentityReference } - catch { Stop-PSFFunction -String 'Convert-AccessRule.Identity.ResolutionError' -Target $ruleObject -ErrorRecord $_ -Continue } - - [PSCustomObject]@{ - PSTypeName = 'DomainManagement.AccessRule.Converted' - IdentityReference = $identity - AccessControlType = $ruleObject.AccessControlType - ActiveDirectoryRights = $ruleObject.ActiveDirectoryRights - InheritanceFlags = $ruleObject.InheritanceFlags - InheritanceType = $ruleObject.InheritanceType - InheritedObjectType = $inheritedObjectTypeGuid - InheritedObjectTypeName = $inheritedObjectTypeName - ObjectFlags = $ruleObject.ObjectFlags - ObjectType = $objectTypeGuid - ObjectTypeName = $objectTypeName - PropagationFlags = $ruleObject.PropagationFlags - Present = $ruleObject.Present - } - } - } - } #endregion Utility Functions $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential @@ -412,8 +121,7 @@ return } } - process - { + process { if (Test-PSFFunctionInterrupt) { return } #region Process Configured Objects @@ -421,9 +129,9 @@ $resolvedPath = Resolve-String -Text $key $resultDefaults = @{ - Server = $Server - ObjectType = 'AccessRule' - Identity = $resolvedPath + Server = $Server + ObjectType = 'AccessRule' + Identity = $resolvedPath Configuration = $script:accessRules[$key] } @@ -435,7 +143,7 @@ try { $adAclObject = Get-AdsAcl @parameters -Path $resolvedPath -EnableException } catch { if ($script:accessRules[$key].Optional -notcontains $false) { continue } - Write-PSFMessage -String 'Test-DMAccessRule.NoAccess' -StringValues $resolvedPath -Tag 'panic','failed' -Target $script:accessRules[$key] -ErrorRecord $_ + Write-PSFMessage -String 'Test-DMAccessRule.NoAccess' -StringValues $resolvedPath -Tag 'panic', 'failed' -Target $script:accessRules[$key] -ErrorRecord $_ New-TestResult @resultDefaults -Type 'NoAccess' Continue } @@ -444,14 +152,14 @@ if ($adObject.adminCount) { $defaultPermissions = @() - $desiredPermmissions = Get-AdminSDHolderRules @parameters + $desiredPermissions = Get-AdminSDHolderRules @parameters } else { $defaultPermissions = Get-DMObjectDefaultPermission @parameters -ObjectClass $adObject.ObjectClass - $desiredPermmissions = $script:accessRules[$key] | Convert-AccessRule @parameters -ADObject $adObject + $desiredPermissions = $script:accessRules[$key] | Convert-AccessRule @parameters -ADObject $adObject } - $delta = Compare-AccessRules @parameters -ADRules ($adAclObject.Access | Convert-AccessRuleIdentity @parameters) -ConfiguredRules $desiredPermmissions -DefaultRules $defaultPermissions -ADObject $adObject + $delta = Compare-AccessRules @parameters -ADRules ($adAclObject.Access | Convert-AccessRuleIdentity @parameters) -ConfiguredRules $desiredPermissions -DefaultRules $defaultPermissions -ADObject $adObject if ($delta) { New-TestResult @resultDefaults -Type Update -Changed $delta -ADObject $adAclObject @@ -460,22 +168,186 @@ } #endregion Process Configured Objects - #region Process Non-Configured AD Objects - $resolvedConfiguredObjects = $script:accessRules.Keys | Resolve-String + $doParallelize = Get-PSFConfigValue -FullName 'DomainManagement.AccessRules.Parallelize' + #region Process Non-Configured AD Objects - Serial + if (-not $doParallelize) { + $resolvedConfiguredObjects = $script:accessRules.Keys | Resolve-String + + $foundADObjects = foreach ($searchBase in (Resolve-ContentSearchBase @parameters -NoContainer)) { + Get-ADObject @parameters -LDAPFilter '(objectCategory=*)' -SearchBase $searchBase.SearchBase -SearchScope $searchBase.SearchScope -Properties adminCount + } + + $resultDefaults = @{ + Server = $Server + ObjectType = 'AccessRule' + } + + $convertCmdName = { Convert-DMSchemaGuid @parameters -OutType Name }.GetSteppablePipeline() + $convertCmdName.Begin($true) + $convertCmdGuid = { Convert-DMSchemaGuid @parameters -OutType Guid }.GetSteppablePipeline() + $convertCmdGuid.Begin($true) + + $processed = @{ } + foreach ($foundADObject in $foundADObjects) { + # Prevent duplicate processing + if ($processed[$foundADObject.DistinguishedName]) { continue } + $processed[$foundADObject.DistinguishedName] = $true + + # Skip items that were defined in configuration, they were already processed + if ($foundADObject.DistinguishedName -in $resolvedConfiguredObjects) { continue } + + $adAclObject = Get-AdsAcl @parameters -Path $foundADObject.DistinguishedName + $compareParam = @{ + ADRules = $adAclObject.Access | Convert-AccessRuleIdentity @parameters + DefaultRules = Get-DMObjectDefaultPermission @parameters -ObjectClass $foundADObject.ObjectClass + ConfiguredRules = Get-CategoryBasedRules -ADObject $foundADObject @parameters -ConvertNameCommand $convertCmdName -ConvertGuidCommand $convertCmdGuid + ADObject = $foundADObject + } + + # Protected Objects + if ($foundADObject.AdminCount) { + $compareParam.DefaultRules = @() + $compareParam.ConfiguredRules = Get-AdminSDHolderRules @parameters + } + + $compareParam += $parameters + $delta = Compare-AccessRules @compareParam + + if ($delta) { + New-TestResult @resultDefaults -Type Update -Changed $delta -ADObject $adAclObject -Identity $foundADObject.DistinguishedName + continue + } + } + + $convertCmdName.End() + $convertCmdGuid.End() - $foundADObjects = foreach ($searchBase in (Resolve-ContentSearchBase @parameters -NoContainer)) { - Get-ADObject @parameters -LDAPFilter '(objectCategory=*)' -SearchBase $searchBase.SearchBase -SearchScope $searchBase.SearchScope -Properties adminCount + return } + #endregion Process Non-Configured AD Objects - Serial - $resultDefaults = @{ - Server = $Server - ObjectType = 'AccessRule' + #region Process Non-Configured AD Objects - Parallel + #region Prepare Runspace Environment + $variables = @{ + resultDefaults = @{ + Server = $Server + ObjectType = 'AccessRule' + } + parameters = $parameters + adminSDHolderRules = Get-AdminSDHolderRules @parameters + schemaDefaultPermissions = $script:schemaObjectDefaultPermission["$Server"] + accessRuleConfiguration = @{ + accessRules = $script:accessRules + accessCategoryRules = $script:accessCategoryRules + } + objectCategorySettings = $script:objectCategories + stringTable = $script:nameReplacementTable + } + $modules = @( + (Get-Module DomainManagement).ModuleBase + (Get-Module ADSec).ModuleBase + ) + $functions = @{ + 'New-TestResult' = [ScriptBlock]::Create((Get-Command -Name New-TestResult).Definition) + } + + $begin = { + $null = Get-Acl -Path . + & (Get-Module DomainManagement) { + $script:schemaObjectDefaultPermission["$($global:parameters.Server)"] = $global:schemaDefaultPermissions.Clone() + $script:accessRules = $global:accessRuleConfiguration.accessRules.Clone() + $script:accessCategoryRules = $global:accessRuleConfiguration.accessCategoryRules.Clone() + $script:nameReplacementTable = $global:stringTable.Clone() + $script:objectCategories = $global:objectCategorySettings.Clone() + foreach ($__category in $script:objectCategories.Values) { + if ($__category.TestScript -is [scriptblock]) { + $__category.TestScript = ([PsfScriptBlock]$__category.TestScript).ToGlobal() + } + } + } + + $global:convertCmdName = { Convert-DMSchemaGuid @parameters -OutType Name }.GetSteppablePipeline() + $global:convertCmdName.Begin($true) + $global:convertCmdGuid = { Convert-DMSchemaGuid @parameters -OutType Guid }.GetSteppablePipeline() + $global:convertCmdGuid.Begin($true) + + $global:cmdCompareAccessRules = & (Get-Module DomainManagement) { Get-Command Compare-AccessRules } + $global:cmdConvertAccessRuleIdentity = & (Get-Module DomainManagement) { Get-Command Convert-AccessRuleIdentity } + $global:cmdGetCategoryBasedRules = & (Get-Module DomainManagement) { Get-Command Get-CategoryBasedRules } } + $process = { + $count = 0 + do { + try { + $foundADObject = $_ + $adAclObject = Get-AdsAcl @parameters -Path $foundADObject.DistinguishedName + $compareParam = @{ + ADRules = & $global:cmdConvertAccessRuleIdentity -InputObject $adAclObject.Access @parameters + DefaultRules = Get-DMObjectDefaultPermission @parameters -ObjectClass $foundADObject.ObjectClass + ConfiguredRules = & $global:cmdGetCategoryBasedRules -ADObject $foundADObject @parameters -ConvertNameCommand $convertCmdName -ConvertGuidCommand $convertCmdGuid + ADObject = $foundADObject + } - $convertCmdName = { Convert-DMSchemaGuid @parameters -OutType Name }.GetSteppablePipeline() - $convertCmdName.Begin($true) - $convertCmdGuid = { Convert-DMSchemaGuid @parameters -OutType Guid }.GetSteppablePipeline() - $convertCmdGuid.Begin($true) + # Protected Objects + if ($foundADObject.AdminCount) { + $compareParam.DefaultRules = @() + $compareParam.ConfiguredRules = $adminSDHolderRules + } + + $compareParam += $parameters + $delta = & $global:cmdCompareAccessRules @compareParam + } + catch { + $count++ + if ($count -lt 10) { continue } + + $fail = [PSCustomObject]@{ + ADObject = $foundADObject + Acl = $adAclObject + Error = $_ + } + Write-PSFRunspaceQueue -Name fails -Value $fail + break + } + + if ($delta) { + New-TestResult @resultDefaults -Type Update -Changed $delta -ADObject $adAclObject -Identity $foundADObject.DistinguishedName + } + + break + } + while ($true) + } + $end = { + $global:convertCmdName.End() + $global:convertCmdGuid.End() + } + + $param = @{ + Name = 'AccessRuleProcessor' + Count = (Get-PSFConfigValue -FullName 'DomainManagement.AccessRules.Threads' -Fallback 4) + InQueue = 'input' + OutQueue = 'results' + Functions = $functions + Modules = $modules + Variables = $variables + + Begin = $begin + Process = $process + End = $end + + CloseOutQueue = $true + } + #endregion Prepare Runspace Environment + + $workflow = New-PSFRunspaceWorkflow -Name 'DomainManagement.AccessRules' -Force + $null = $workflow | Add-PSFRunspaceWorker @param + + $resolvedConfiguredObjects = $script:accessRules.Keys | Resolve-String + + $foundADObjects = foreach ($searchBase in (Resolve-ContentSearchBase @parameters -NoContainer)) { + Get-ADObject @parameters -LDAPFilter '(objectCategory=*)' -SearchBase $searchBase.SearchBase -SearchScope $searchBase.SearchScope -Properties adminCount + } $processed = @{ } foreach ($foundADObject in $foundADObjects) { @@ -486,31 +358,27 @@ # Skip items that were defined in configuration, they were already processed if ($foundADObject.DistinguishedName -in $resolvedConfiguredObjects) { continue } - $adAclObject = Get-AdsAcl @parameters -Path $foundADObject.DistinguishedName - $compareParam = @{ - ADRules = $adAclObject.Access | Convert-AccessRuleIdentity @parameters - DefaultRules = Get-DMObjectDefaultPermission @parameters -ObjectClass $foundADObject.ObjectClass - ConfiguredRules = Get-CategoryBasedRules -ADObject $foundADObject @parameters -ConvertNameCommand $convertCmdName -ConvertGuidCommand $convertCmdGuid - ADObject = $foundADObject - } - - # Protected Objects - if ($foundADObject.AdminCount) { - $compareParam.DefaultRules = @() - $compareParam.ConfiguredRules = Get-AdminSDHolderRules @parameters - } - - $compareParam += $parameters - $delta = Compare-AccessRules @compareParam - - if ($delta) { - New-TestResult @resultDefaults -Type Update -Changed $delta -ADObject $adAclObject -Identity $foundADObject.DistinguishedName - continue + Write-PSFRunspaceQueue -Name input -InputObject $workflow -Value $foundADObject + } + $workflow.Queues.input.Closed = $true + + try { + $workflow | Start-PSFRunspaceWorkflow + $workflow | Wait-PSFRunspaceWorkflow -WorkerName AccessRuleProcessor -Closed -PassThru | Stop-PSFRunspaceWorkflow + $fails = Read-PSFRunspaceQueue -InputObject $workflow -Name fails -All + foreach ($fail in $fails) { + Write-PSFMessage -Level Warning -String 'Test-DMAccessRule.Parallel.Error' -StringValues $fail.ADObject -ErrorRecord $fail.Error -Target $fail } + + $results = Read-PSFRunspaceQueue -InputObject $workflow -Name results -All + # Fix String Presentation for objects from a background runspace + $results | Add-Member -MemberType ScriptMethod -Name ToString -Value { $this.Identity } -Force + $results.Changed | Add-Member -MemberType ScriptMethod ToString -Value { '{0}: {1}' -f $this.Type, $this.Identity } -Force + $results } - - $convertCmdName.End() - $convertCmdGuid.End() - #endregion Process Non-Configured AD Objects + finally { + $workflow | Remove-PSFRunspaceWorkflow + } + #endregion Process Non-Configured AD Objects - Parallel } } diff --git a/DomainManagement/internal/configurations/configuration.ps1 b/DomainManagement/internal/configurations/configuration.ps1 index d1afeb4..c68f4ba 100644 --- a/DomainManagement/internal/configurations/configuration.ps1 +++ b/DomainManagement/internal/configurations/configuration.ps1 @@ -15,4 +15,11 @@ Set-PSFConfig -Module 'DomainManagement' -Name 'Import.DoDotSource' -Value $fals Set-PSFConfig -Module 'DomainManagement' -Name 'Import.IndividualFiles' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be imported individually. During the module build, all module code is compiled into few files, which are imported instead by default. Loading the compiled versions is faster, using the individual files is easier for debugging and testing out adjustments." Set-PSFConfig -Module 'DomainManagement' -Name 'ServiceAccount.SkipKdsCheck' -Value $false -Initialize -Validation bool -Description 'Whether the check for a KDS Root Key should be skipped. By default, Invoke-DMServiceAccount will validate the necessary key exists before creating gMSA. However, reading the key requires Domain Admin privileges, which may not always be available. Skipping the check will cause gMSA creation to fail with an error, if the KDSRootKey does not yet exist.' -Set-PSFConfig -Module 'DomainManagement' -Name 'AccessRules.Remove.Option2' -Value $false -Initialize -Validation bool -Description 'In some environments, the default way of removing access rules have proved to not work out. Using this option enables a second way for removing access rules.' \ No newline at end of file +Set-PSFConfig -Module 'DomainManagement' -Name 'AccessRules.Remove.Option2' -Value $false -Initialize -Validation bool -Description 'In some environments, the default way of removing access rules have proved to not work out. Using this option enables a second way for removing access rules.' + +#$coreCount = $env:NUMBER_OF_PROCESSORS +#if (-not $coreCount) { $coreCount = 4 } +# Should not use more than 4 for now, until retries are configurable +Set-PSFConfig -Module 'DomainManagement' -Name 'AccessRules.Threads' -Value 4 -Initialize -Validation integerpositive -Description 'The number of runspaces to use for access rule processing. This is an in-memory operation, using more cores than available is disadviced.' +# Should be disabled by default, as runspace feature not quite stable yet +Set-PSFConfig -Module 'DomainManagement' -Name 'AccessRules.Parallelize' -Value $false -Initialize -Validation bool -Description 'Whether to process AccessRules with multiple runspaces (recommended for performance reasons).' \ No newline at end of file diff --git a/DomainManagement/internal/functions/acl_ace/Compare-AccessRules.ps1 b/DomainManagement/internal/functions/acl_ace/Compare-AccessRules.ps1 new file mode 100644 index 0000000..bf596b5 --- /dev/null +++ b/DomainManagement/internal/functions/acl_ace/Compare-AccessRules.ps1 @@ -0,0 +1,183 @@ +function Compare-AccessRules { + <# + .SYNOPSIS + Compare actual access rules to the system default and configured rules. + + .DESCRIPTION + Compare actual access rules to the system default and configured rules. + + .PARAMETER ADRules + The Access Rules actually deployed on the AD Object + + .PARAMETER ConfiguredRules + The Access Rules configured for the AD Object + + .PARAMETER DefaultRules + The default Access Rules for objects of this type + + .PARAMETER ADObject + The AD Object for which Access Rules are being compared + + .PARAMETER Server + The server / domain to work with. + + .PARAMETER Credential + The credentials to use for this operation. + + .EXAMPLE + PS C:\> Compare-AccessRules @parameters -ADRules ($adAclObject.Access | Convert-AccessRuleIdentity @parameters) -ConfiguredRules $desiredPermissions -DefaultRules $defaultPermissions -ADObject $adObject + + Compare actual access rules on the specified AD Object to the system default and configured rules. + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")] + [CmdletBinding()] + param ( + [AllowEmptyCollection()] + $ADRules, + + [AllowEmptyCollection()] + $ConfiguredRules, + + [AllowEmptyCollection()] + $DefaultRules, + + $ADObject, + + [PSFComputer] + $Server, + + [PSCredential] + $Credential + ) + + $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential + + # Resolve the mode under which it will be evaluated. Either 'Additive' or 'Constrained' + $processingMode = Resolve-DMAccessRuleMode @parameters -ADObject $adObject + + function Write-Result { + [CmdletBinding()] + param ( + [ValidateSet('Create', 'Delete', 'FixConfig', 'Restore')] + [Parameter(Mandatory = $true)] + $Type, + + $Identity, + + [AllowNull()] + $ADObject, + + [AllowNull()] + $Configuration, + + [string] + $DistinguishedName + ) + + $item = [PSCustomObject]@{ + PSTypeName = 'DomainManagement.AccessRule.Change' + Type = $Type + ACT = $ADObject.AccessControlType + Identity = $Identity + Rights = $ADObject.ActiveDirectoryRights + DistinguishedName = $DistinguishedName + ADObject = $ADObject + Configuration = $Configuration + } + if (-not $ADObject) { + $item.ACT = $Configuration.AccessControlType + $item.Rights = $Configuration.ActiveDirectoryRights + } + Add-Member -InputObject $item -MemberType ScriptMethod ToString -Value { '{0}: {1}' -f $this.Type, $this.Identity } -Force -PassThru + } + + $defaultRulesPresent = [System.Collections.ArrayList]::new() + $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.IdentityReference -eq 'everyone') { continue } + $eSid = [System.Security.Principal.SecurityIdentifier]'S-1-1-0' + $eName = $eSid.Translate([System.Security.Principal.NTAccount]) + if ($adRule.IdentityReference -eq $eName) { continue } + if ($adRule.IdentityReference -eq $eSid) { continue } + } + #endregion Skip OUs' "Protect from Accidential Deletion" ACE + + foreach ($defaultRule in $DefaultRules) { + if (Test-AccessRuleEquality -Parameters $parameters -Rule1 $adRule -Rule2 $defaultRule) { + $null = $defaultRulesPresent.Add($defaultRule) + continue outer + } + } + $adRule + } + + #region Foreach non-default AD Rule: Check whether configured and delete if not so + :outer foreach ($relevantADRule in $relevantADRules) { + foreach ($configuredRule in $ConfiguredRules) { + if (Test-AccessRuleEquality -Parameters $parameters -Rule1 $relevantADRule -Rule2 $configuredRule) { + # If explicitly defined for deletion, do so + if ('False' -eq $configuredRule.Present) { + Write-Result -Type Delete -Identity $relevantADRule.IdentityReference -ADObject $relevantADRule -DistinguishedName $ADObject + } + continue outer + } + } + + # Don't generate delete changes + if ($processingMode -eq 'Additive') { continue } + # Don't generate delete changes, unless we have configured a permission level for the affected identity + if ($processingMode -eq 'Defined') { + if (-not ($relevantADRule.IdentityReference | Compare-Identity -Parameters $parameters -ReferenceIdentity $ConfiguredRules.IdentityReference -IncludeEqual -ExcludeDifferent)) { + continue + } + } + + Write-Result -Type Delete -Identity $relevantADRule.IdentityReference -ADObject $relevantADRule -DistinguishedName $ADObject + } + #endregion Foreach non-default AD Rule: Check whether configured and delete if not so + + #region Foreach configured rule: Check whether it exists as defined or make it so + :outer foreach ($configuredRule in $ConfiguredRules) { + foreach ($defaultRule in $DefaultRules) { + if ('True' -ne $configuredRule.Present) { break } + if ($configuredRule.NoFixConfig) { break } + if (Test-AccessRuleEquality -Parameters $parameters -Rule1 $defaultRule -Rule2 $configuredRule) { + Write-Result -Type FixConfig -Identity $defaultRule.IdentityReference -ADObject $defaultRule -Configuration $configuredRule -DistinguishedName $ADObject + continue outer + } + } + foreach ($relevantADRule in $relevantADRules) { + if (Test-AccessRuleEquality -Parameters $parameters -Rule1 $relevantADRule -Rule2 $configuredRule) { + continue outer + } + } + # Do not generate Create rules for any rule not configured for creation + if ('True' -ne $configuredRule.Present) { continue } + Write-Result -Type Create -Identity $configuredRule.IdentityReference -Configuration $configuredRule -DistinguishedName $ADObject + } + #endregion Foreach configured rule: Check whether it exists as defined or make it so + + #region Foreach non-existent default rule: Create unless configured otherwise + $domainControllersOUFilter = '*{0}' -f ('OU=Domain Controllers,%DomainDN%' | Resolve-String) + :outer foreach ($defaultRule in $DefaultRules | Where-Object { $_ -notin $defaultRulesPresent.ToArray() }) { + # Do not apply restore to Domain Controllers OU, as it is already deployed intentionally diverging from the OU defaults + if ($ADObject -like $domainControllersOUFilter) { break } + + # Skip 'CREATOR OWNER' Rules, as those should never be restored. + # When creating an AD object that has this group as default permissions, it will instead + # Translate those to the identity creating the object + if ('S-1-3-0' -eq $defaultRule.SID) { continue } + + foreach ($configuredRule in $ConfiguredRules) { + if (Test-AccessRuleEquality -Parameters $parameters -Rule1 $defaultRule -Rule2 $configuredRule) { + # If we explicitly don't want the rule: Skip and do NOT create a restoration action + if ('True' -ne $configuredRule.Present) { continue outer } + } + } + + Write-Result -Type Restore -Identity $defaultRule.IdentityReference -Configuration $defaultRule -DistinguishedName $ADObject + } + #endregion Foreach non-existent default rule: Create unless configured otherwise +} \ No newline at end of file diff --git a/DomainManagement/internal/functions/acl_ace/Convert-AccessRuleIdentity.ps1 b/DomainManagement/internal/functions/acl_ace/Convert-AccessRuleIdentity.ps1 new file mode 100644 index 0000000..03830cf --- /dev/null +++ b/DomainManagement/internal/functions/acl_ace/Convert-AccessRuleIdentity.ps1 @@ -0,0 +1,68 @@ +function Convert-AccessRuleIdentity { + <# + .SYNOPSIS + Converts the identity on the specified access rule to NT Account. + + .DESCRIPTION + Converts the identity on the specified access rule to NT Account. + + .PARAMETER InputObject + The Access Rules for which to convert the Identity. + + .PARAMETER Server + The server / domain to work with. + + .PARAMETER Credential + The credentials to use for this operation. + + .EXAMPLE + PS C:\> $adAclObject.Access | Convert-AccessRuleIdentity @parameters + + Converts the identity on all Access Rules in $adAclObject to NT Account. + #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingEmptyCatchBlock', '')] + [CmdletBinding()] + param ( + [Parameter(ValueFromPipeline = $true)] + [System.DirectoryServices.ActiveDirectoryAccessRule[]] + $InputObject, + + [PSFComputer] + $Server, + + [PSCredential] + $Credential + ) + begin { + $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential + $domainObject = Get-Domain2 @parameters + } + process { + :main foreach ($accessRule in $InputObject) { + if ($accessRule.IdentityReference -is [System.Security.Principal.NTAccount]) { + Add-Member -InputObject $accessRule -MemberType NoteProperty -Name OriginalRule -Value $accessRule -PassThru + continue main + } + + if (-not $accessRule.IdentityReference.AccountDomainSid) { + try { $identity = Get-Principal @parameters -Sid $accessRule.IdentityReference -Domain $domainObject.DNSRoot -OutputType NTAccount } + catch { + # Empty Catch is OK here, warning happens in command + } + } + else { + try { $identity = Get-Principal @parameters -Sid $accessRule.IdentityReference -Domain $accessRule.IdentityReference -OutputType NTAccount } + catch { + # Empty Catch is OK here, warning happens in command + } + } + if (-not $identity) { + $identity = $accessRule.IdentityReference + } + + $newRule = [System.DirectoryServices.ActiveDirectoryAccessRule]::new($identity, $accessRule.ActiveDirectoryRights, $accessRule.AccessControlType, $accessRule.ObjectType, $accessRule.InheritanceType, $accessRule.InheritedObjectType) + # Include original object as property in order to facilitate removal if needed. + Add-Member -InputObject $newRule -MemberType NoteProperty -Name OriginalRule -Value $accessRule -PassThru + } + } +} \ No newline at end of file diff --git a/DomainManagement/internal/functions/acl_ace/Get-CategoryBasedRules.ps1 b/DomainManagement/internal/functions/acl_ace/Get-CategoryBasedRules.ps1 new file mode 100644 index 0000000..fd78f69 --- /dev/null +++ b/DomainManagement/internal/functions/acl_ace/Get-CategoryBasedRules.ps1 @@ -0,0 +1,75 @@ +function Get-CategoryBasedRules { + <# + .SYNOPSIS + Returns all access rules applicable to an ad object via category rules. + + .DESCRIPTION + Returns all access rules applicable to an ad object via category rules. + + .PARAMETER ADObject + The AD Object for which to resolve access rules by category. + + .PARAMETER Server + The server / domain to work with. + + .PARAMETER Credential + The credentials to use for this operation. + + .PARAMETER ConvertNameCommand + A steppable pipeline wrapping Convert-DMSchemaGuid converting to name. + + .PARAMETER ConvertGuidCommand + A steppable pipeline wrapping Convert-DMSchemaGuid converting to guid. + + .EXAMPLE + PS C:\> Get-CategoryBasedRules -ADObject $foundADObject @parameters -ConvertNameCommand $convertCmdName -ConvertGuidCommand $convertCmdGuid + + Returns all access rules applicable to $foundADObject via category rules. + #> + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + $ADObject, + + [PSFComputer] + $Server, + + [PSCredential] + $Credential, + + $ConvertNameCommand, + + $ConvertGuidCommand + ) + + $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include ADObject, Server, Credential + + $resolvedCategories = Resolve-DMObjectCategory @parameters + foreach ($resolvedCategory in $resolvedCategories) { + foreach ($ruleObject in $script:accessCategoryRules[$resolvedCategory.Name]) { + $objectTypeGuid = $ConvertGuidCommand.Process($ruleObject.ObjectType)[0] + $objectTypeName = $ConvertNameCommand.Process($ruleObject.ObjectType)[0] + $inheritedObjectTypeGuid = $ConvertGuidCommand.Process($ruleObject.InheritedObjectType)[0] + $inheritedObjectTypeName = $ConvertNameCommand.Process($ruleObject.InheritedObjectType)[0] + + try { $identity = Resolve-Identity @parameters -IdentityReference $ruleObject.IdentityReference } + catch { Stop-PSFFunction -String 'Convert-AccessRule.Identity.ResolutionError' -Target $ruleObject -ErrorRecord $_ -Continue } + + [PSCustomObject]@{ + PSTypeName = 'DomainManagement.AccessRule.Converted' + IdentityReference = $identity + AccessControlType = $ruleObject.AccessControlType + ActiveDirectoryRights = $ruleObject.ActiveDirectoryRights + InheritanceFlags = $ruleObject.InheritanceFlags + InheritanceType = $ruleObject.InheritanceType + InheritedObjectType = $inheritedObjectTypeGuid + InheritedObjectTypeName = $inheritedObjectTypeName + ObjectFlags = $ruleObject.ObjectFlags + ObjectType = $objectTypeGuid + ObjectTypeName = $objectTypeName + PropagationFlags = $ruleObject.PropagationFlags + Present = $ruleObject.Present + } + } + } +} \ No newline at end of file diff --git a/DomainManagement/internal/functions/acl_ace/Resolve-Identity.ps1 b/DomainManagement/internal/functions/acl_ace/Resolve-Identity.ps1 new file mode 100644 index 0000000..aa3bfb1 --- /dev/null +++ b/DomainManagement/internal/functions/acl_ace/Resolve-Identity.ps1 @@ -0,0 +1,72 @@ +function Resolve-Identity { + <# + .SYNOPSIS + Resolve an Identity Reference with special rules. + + .DESCRIPTION + Resolve an Identity Reference with special rules. + Resolves to a SID (preferred) or NT Account (Fallback). + + Special Rules: + resolves to the parent object in AD + + This is a helper tool to resolve Identities on Access Rules applied to (or ointended for) AD objects only. + + .PARAMETER IdentityReference + The Identity to resolve. + + .PARAMETER ADObject + The AD Object from which the access rules has been read where the Identity is being resolved. + + .PARAMETER Server + The server / domain to work with. + + .PARAMETER Credential + The credentials to use for this operation. + + .EXAMPLE + PS C:\> Resolve-Identity -Identity $name -ADObject %adObject @parameters + + Resolve the Identity in $name + #> + [CmdletBinding()] + param ( + [string] + $IdentityReference, + + $ADObject, + + [PSFComputer] + $Server, + + [PSCredential] + $Credential + ) + + #region Parent Resolution + if ($IdentityReference -eq '') { + $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential + $domainObject = Get-Domain2 @parameters + $parentPath = ($ADObject.DistinguishedName -split ",",2)[1] + $parentObject = Get-ADObject @parameters -Identity $parentPath -Properties SamAccountName, Name, ObjectSID + if (-not $parentObject.ObjectSID) { + Stop-PSFFunction -String 'Resolve-Identity.ParentObject.NoSecurityPrincipal' -StringValues $ADObject, $parentObject.Name, $parentObject.ObjectClass -EnableException $true -Cmdlet $PSCmdlet + } + if ($parentObject.SamAccountName) { return [System.Security.Principal.NTAccount]('{0}\{1}' -f $domainObject.Name, $parentObject.SamAccountName) } + else { return [System.Security.Principal.NTAccount]('{0}\{1}' -f $domainObject.Name, $parentObject.Name) } + } + #endregion Parent Resolution + + #region Default Resolution + $identity = Resolve-String -Text $IdentityReference + if ($identity -as [System.Security.Principal.SecurityIdentifier]) { + $identity = $identity -as [System.Security.Principal.SecurityIdentifier] + } + else { + $identity = $identity -as [System.Security.Principal.NTAccount] + } + if ($null -eq $identity) { $identity = (Resolve-String -Text $IdentityReference) -as [System.Security.Principal.NTAccount] } + + $identity + #endregion Default Resolution +} \ No newline at end of file diff --git a/DomainManagement/internal/scripts/variables2.ps1 b/DomainManagement/internal/scripts/variables2.ps1 index f89c833..ea8f6b0 100644 --- a/DomainManagement/internal/scripts/variables2.ps1 +++ b/DomainManagement/internal/scripts/variables2.ps1 @@ -27,4 +27,7 @@ $script:builtInSidMapping = @{ # 'BUILTIN\RDS Endpoint Servers' = [System.Security.Principal.SecurityIdentifier]'S-1-5-32-576' # 'BUILTIN\RDS Management Servers' = [System.Security.Principal.SecurityIdentifier]'S-1-5-32-577' # 'BUILTIN\Storage Replica Administrators' = [System.Security.Principal.SecurityIdentifier]'S-1-5-32-582' -} \ No newline at end of file +} + +# Persistent Cache for Default Permissions +$script:schemaObjectDefaultPermission = @{ } \ No newline at end of file From e540cf23ef2bd52f448699150d6574540f095f89 Mon Sep 17 00:00:00 2001 From: FriedrichWeinmann Date: Fri, 12 Jan 2024 08:23:00 +0100 Subject: [PATCH 2/2] making PSSA happy --- DomainManagement/functions/AccessRule/Test-DMAccessRule.ps1 | 1 + .../internal/functions/acl_ace/Get-CategoryBasedRules.ps1 | 1 + DomainManagement/internal/functions/acl_ace/Resolve-Identity.ps1 | 1 + 3 files changed, 3 insertions(+) diff --git a/DomainManagement/functions/AccessRule/Test-DMAccessRule.ps1 b/DomainManagement/functions/AccessRule/Test-DMAccessRule.ps1 index b5c928a..f904510 100644 --- a/DomainManagement/functions/AccessRule/Test-DMAccessRule.ps1 +++ b/DomainManagement/functions/AccessRule/Test-DMAccessRule.ps1 @@ -34,6 +34,7 @@ [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseDeclaredVarsMoreThanAssignments", "")] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidGlobalVars", "")] [CmdletBinding()] param ( [PSFComputer] diff --git a/DomainManagement/internal/functions/acl_ace/Get-CategoryBasedRules.ps1 b/DomainManagement/internal/functions/acl_ace/Get-CategoryBasedRules.ps1 index fd78f69..12c3bbd 100644 --- a/DomainManagement/internal/functions/acl_ace/Get-CategoryBasedRules.ps1 +++ b/DomainManagement/internal/functions/acl_ace/Get-CategoryBasedRules.ps1 @@ -26,6 +26,7 @@ Returns all access rules applicable to $foundADObject via category rules. #> + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] diff --git a/DomainManagement/internal/functions/acl_ace/Resolve-Identity.ps1 b/DomainManagement/internal/functions/acl_ace/Resolve-Identity.ps1 index aa3bfb1..0c0c48a 100644 --- a/DomainManagement/internal/functions/acl_ace/Resolve-Identity.ps1 +++ b/DomainManagement/internal/functions/acl_ace/Resolve-Identity.ps1 @@ -29,6 +29,7 @@ Resolve the Identity in $name #> + [OutputType([System.Security.Principal.NTAccount])] [CmdletBinding()] param ( [string]