Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DomainManagement/DomainManagement.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
RootModule = 'DomainManagement.psm1'

# Version number of this module.
ModuleVersion = '1.9.228'
ModuleVersion = '1.9.234'

# ID used to uniquely identify this module
GUID = '0a405382-ebc2-445b-8325-541535810193'
Expand All @@ -26,7 +26,7 @@
# Modules that must be imported into the global environment prior to importing
# this module
RequiredModules = @(
@{ ModuleName = 'PSFramework'; ModuleVersion = '1.12.346' }
@{ ModuleName = 'PSFramework'; ModuleVersion = '1.13.416' }

# Additional Dependencies, cannot declare due to bug in dependency handling in PS5.1
# @{ ModuleName = 'ADSec'; ModuleVersion = '1.0.0' }
Expand Down
9 changes: 9 additions & 0 deletions DomainManagement/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 1.9.234 (2025-10-31)

- Upd: General - Use the shared managed remoting feature, allowing configuring session options.
- Upd: AccessRules - when failing to resolve the identity of a principal on an existing access rule, the warning now reports the DN of the object
- Upd: Exchange - accepts test results as input for invoke
- Upd: GPLink - Supports cherry-picking results within a single OU.
- Fix: GPOwner - Prints red error messages on screen when failing to resovle the owner
- Fix: Groups - Ignores SamAccountName during creation of new groups

## 1.9.228 (2025-10-02)

- Upd: Group Memberships - added option "ConfigOnly", allowing to define the group processing mode, without specifying any actual memberships.
Expand Down
2 changes: 1 addition & 1 deletion DomainManagement/en-us/strings.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
'Get-PermissionGuidMapping.Processing' = 'Processing Permission Guids for domain: {0} (This may take a while)' # $identity

'Get-Principal.Resolution.Failed' = 'Failed to resolve principal: SID {0} | Name {1} | ObjectClass {2} | Domain {3}' # $Sid, $Name, $ObjectClass, $Domain
'Get-Principal.Resolution.FailedWithTarget' = 'Failed to resolve principal: SID {0} | Name {1} | ObjectClass {2} | Domain {3} | Target {4}' # $Sid, $Name, $ObjectClass, $Domain, $Target

'Get-SchemaGuidMapping.Processing' = 'Processing Schema Guids for domain: {0} (This may take a while)' # $identity

Expand Down Expand Up @@ -76,7 +77,6 @@
'Invoke-DMGPLink.Delete.AllEnabled' = 'Removing all ({0}) policy links (all of which are enabled)' # $countActual
'Invoke-DMGPLink.GpoMissing' = 'Skipping GP Link application for {0} - cannot resolve Group Policies "{1}"' # $testItem.ADObject, (($testItem.Changed | Where-Object Action -eq 'GpoMissing').Policy -join ", ")
'Invoke-DMGPLink.New' = 'Linking {0} group policies (all new links)' # $countConfigured
'Invoke-DMGPLink.New.GpoNotFound' = 'Unable to find Group Policy Object: {0}' # (Resolve-String -Text $_.PolicyName)
'Invoke-DMGPLink.New.NewGPLinkString' = 'Finished gPLink string being applied to {0}: {1}' # $ADObject.DistinguishedName, $gpLinkString
'Invoke-DMGPLink.Update.AllEnabled' = 'Updating GPLink - {0} links configured, {1} links present, {2} links present that are not in configuration (All present and undesired links are enabled)' # $countConfigured, $countActual, $countNotInConfig
'Invoke-DMGPLink.Update.Change' = ' Link update: {0} - {1} ({2})' # $change.Action, $change.Policy, $ADObject.DistinguishedName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@
$desiredPermissions = $script:accessRules[$key] | Convert-AccessRule @parameters -ADObject $adObject
}

$delta = Compare-AccessRules @parameters -ADRules ($adAclObject.Access | Convert-AccessRuleIdentity @parameters) -ConfiguredRules $desiredPermissions -DefaultRules $defaultPermissions -ADObject $adObject
$delta = Compare-AccessRules @parameters -ADRules ($adAclObject.Access | Convert-AccessRuleIdentity @parameters -Target $adAclObject.DistinguishedName) -ConfiguredRules $desiredPermissions -DefaultRules $defaultPermissions -ADObject $adObject

if ($delta) {
New-TestResult @resultDefaults -Type Update -Changed $delta -ADObject $adAclObject
Expand Down
14 changes: 12 additions & 2 deletions DomainManagement/functions/exchange/Invoke-DMExchange.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
.DESCRIPTION
Apply the desired exchange domain content update.
Use Register-DMExchange to define the exchange update.

.PARAMETER InputObject
Test results provided by the associated test command.
Only the provided changes will be executed, unless none were specified, in which ALL pending changes will be executed.

.PARAMETER Server
The server / domain to work with.
Expand All @@ -32,6 +36,9 @@
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseUsingScopeModifierInNewRunspaces', '')]
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[Parameter(ValueFromPipeline = $true)]
$InputObject,

[Parameter(Mandatory = $true)]
[PSFComputer]
$Server,
Expand Down Expand Up @@ -133,14 +140,17 @@
}
process
{
$testResult = Test-DMExchange @parameters
if (-not $InputObject) {
$testResult = Test-DMExchange @parameters
}
else { $testResult = $InputObject }

if (-not $testResult) { return }

#region PS Remoting
$psParameter = $PSBoundParameters | ConvertTo-PSFHashtable -Include Credential
$psParameter.ComputerName = $Server
try { $session = New-PSSession @psParameter -ErrorAction Stop }
try { $session = New-AdcPSSession @psParameter -ErrorAction Stop }
catch {
Stop-PSFFunction -String 'Invoke-DMExchange.WinRM.Failed' -StringValues $Server -ErrorRecord $_ -EnableException $EnableException -Cmdlet $PSCmdlet -Target $Server
return
Expand Down
96 changes: 65 additions & 31 deletions DomainManagement/functions/gplinks/Invoke-DMGPLink.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,29 @@
$ADObject,

[bool]
$Disable
$Disable,

$Changes,

$Definition
)
$parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential

if (-not $Disable) {
$newChanges = foreach ($change in $Definition) {
if ($change.Policy -notin $Changes.Policy) {
$change.ToLink()
continue
}
if (-not $Disable) { continue }

'[LDAP://{0};1]' -f $change.PolicyDN
}

if (-not $newChanges) {
Set-ADObject @parameters -Identity $ADObject -Clear gPLink -ErrorAction Stop
return
}
Set-ADObject @parameters -Identity $ADObject -Replace @{ gPLink = ($ADObject.gPLink -replace ";\d\]", ";1]") } -ErrorAction Stop -Confirm:$false
Set-ADObject @parameters -Identity $ADObject -Replace @{ gPLink = $newChanges -join '' } -ErrorAction Stop -Confirm:$false
}

function New-Link {
Expand All @@ -103,24 +117,11 @@

$ADObject,

$Configuration,

[Hashtable]
$GpoNameMapping
$Changes
)
$parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential

$gpLinkString = ($Configuration.Include | Sort-Object -Property @{ Expression = { $_.Tier }; Descending = $false }, Precedence -Descending | ForEach-Object {
$gpoDN = $GpoNameMapping[(Resolve-String -Text $_.PolicyName)]
if (-not $gpoDN) {
Write-PSFMessage -Level Warning -String 'Invoke-DMGPLink.New.GpoNotFound' -StringValues (Resolve-String -Text $_.PolicyName) -Target $ADObject -FunctionName Invoke-DMGPLink
return
}
$stateID = "0"
if ($_.State -eq 'Enforced') { $stateID = "2" }
if ($_.State -eq 'Disabled') { $stateID = "1" }
"[LDAP://$gpoDN;$stateID]"
}) -Join ""
$gpLinkString = @($Changes | Sort-Object -Property @{ Expression = { $_.Tier }; Descending = $false }, Precedence -Descending).ForEach{ $_.ToLink() } -Join ""
Write-PSFMessage -Level Debug -String 'Invoke-DMGPLink.New.NewGPLinkString' -StringValues $ADObject.DistinguishedName, $gpLinkString -Target $ADObject -FunctionName Invoke-DMGPLink
Set-ADObject @parameters -Identity $ADObject -Replace @{ gPLink = $gpLinkString } -ErrorAction Stop -Confirm:$false
}
Expand All @@ -145,21 +146,54 @@
[Hashtable]
$GpoNameMapping,


[AllowNull()]
$Changes
)
$parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential

$gpLinkString = ''
if ($Disable) {
$desiredDNs = $Configuration.ExtendedInclude.PolicyName | Resolve-String | ForEach-Object { $GpoNameMapping[$_] }
$gpLinkString += ($ADobject.LinkedGroupPolicyObjects | Where-Object DistinguishedName -NotIn $desiredDNs | Sort-Object -Property Precedence -Descending | ForEach-Object {
"[LDAP://$($_.DistinguishedName);1]"
}) -join ""
$allItems = $Configuration.Definition | Sort-Object -Property @{ Expression = { $_.Tier }; Descending = $false }, Precedence -Descending
$itemsToInclude = $allItems | Where-Object {
($_.Action -in 'None', 'State', 'Reorder') -or
(
($_.Action -eq 'Add') -and
($_.Policy -in $Changes.Policy)
) -or
(
($_.Action -eq 'Delete') -and
($_.Policy -notin $Changes.Policy) -and
(-not $Disable)
)
}

$gpLinkString += ($Configuration.ExtendedInclude | Where-Object DistinguishedName | Sort-Object -Property @{ Expression = { $_.Tier }; Descending = $false }, Precedence -Descending | ForEach-Object {
$_.ToLink()
}) -Join ""

$dontReorder = $allItems | Where-Object {
($_.Action -eq 'Reorder') -and
($_.Policy -notin $Changes.Policy)
}
foreach ($noReorderItem in $dontReorder | Sort-Object OriginalPosition) {
$below = $null
$above = $null
if ($noReorderItem.OriginalPosition -gt 1) { $below = $allItems | Where-Object OriginalPosition -eq ($noReorderItem.OriginalPosition - 1) }
else { $above = $allItems | Where-Object OriginalPosition -eq ($noReorderItem.OriginalPosition + 1) }

$allOtherItems = $itemsToInclude | Where-Object { $_ -ne $noReorderItem }
if ($above) {
$index = $allOtherItems.IndexOf($above)
$itemsAbove = @()
if ($index -gt 0) { $itemsAbove = @($allOtherItems[0..($index-1)]) }
$itemsBelow = @($allOtherItems[$index..(@($allOtherItems).Count - 1)])
}
else {
$index = $allOtherItems.IndexOf($below)
$itemsAbove = @($allOtherItems[0..($index-1)])
$itemsBelow = @()
if ($index -lt ($allOtherItems.Count - 1)) { $itemsBelow = @($allOtherItems[($index)..($allOtherItems.Count - 1)])}
}
$itemsToInclude = $itemsAbove + $noReorderItem + $itemsBelow
}

$gpLinkString = @($itemsToInclude).ForEach{ $_.ToLink() } -join ""

$msgParam = @{
Level = 'SomewhatVerbose'
Tag = 'change'
Expand Down Expand Up @@ -200,19 +234,19 @@
Stop-PSFFunction -String 'General.Invalid.Input' -StringValues 'Test-DMGPLink', $testItem -Target $testItem -Continue -EnableException $EnableException
}

$countConfigured = ($testItem.Configuration | Measure-Object).Count
$countConfigured = ($testItem.Changed | Measure-Object).Count
$countActual = ($testItem.ADObject.LinkedGroupPolicyObjects | Measure-Object).Count
$countNotInConfig = ($testItem.ADObject.LinkedGroupPolicyObjects | Where-Object DistinguishedName -NotIn ($testItem.Configuration.PolicyName | Remove-PSFNull | Resolve-String | ForEach-Object { $gpoDisplayToDN[$_] }) | Measure-Object).Count

switch ($testItem.Type) {
'Delete' {
Invoke-PSFProtectedCommand -ActionString 'Invoke-DMGPLink.Delete.AllEnabled' -ActionStringValues $countActual -Target $testItem -ScriptBlock {
Clear-Link @parameters -ADObject $testItem.ADObject -Disable $Disable -ErrorAction Stop
Clear-Link @parameters -ADObject $testItem.ADObject -Disable $Disable -Changes $testItem.Changed -Definition $testItem.Configuration.Definition -ErrorAction Stop
} -EnableException $EnableException.ToBool() -PSCmdlet $PSCmdlet -Continue
}
'Create' {
Invoke-PSFProtectedCommand -ActionString 'Invoke-DMGPLink.New' -ActionStringValues $countConfigured -Target $testItem -ScriptBlock {
New-Link @parameters -ADObject $testItem.ADObject -Configuration $testItem.Configuration -GpoNameMapping $gpoDisplayToDN -ErrorAction Stop
New-Link @parameters -ADObject $testItem.ADObject -Changes $testItem.Changed -ErrorAction Stop
} -EnableException $EnableException.ToBool() -PSCmdlet $PSCmdlet -Continue
}
'Update' {
Expand Down
Loading