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
2 changes: 1 addition & 1 deletion 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.234'
ModuleVersion = '1.9.239'

# ID used to uniquely identify this module
GUID = '0a405382-ebc2-445b-8325-541535810193'
Expand Down
8 changes: 8 additions & 0 deletions DomainManagement/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 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
- Upd: Group Policy Permissions - will now by default consistently use the PDC Emulator, instead of the targeted server.
- Upd: Group Policy - will now by default consistently use the PDC Emulator, instead of the targeted server.
- Upd: Group Policy Link - will now by default consistently use the PDC Emulator.
- Upd: Group Policy Owner - will now by default consistently use the PDC Emulator.

## 1.9.234 (2025-10-31)

- Upd: General - Use the shared managed remoting feature, allowing configuring session options.
Expand Down
2 changes: 2 additions & 0 deletions DomainManagement/en-us/strings.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@

'Resolve-GPFilterMapping.Filter.Path.DoesNotExist.SilentlyContinue' = 'Issue resolving filter condition {0}: The specified path {1} could not be found, skipping.' # $Condition.Name, $searchBase

'Resolve-GPTargetServer.Info.ChangingToPDC' = 'Changing target server from "{0}" to the PDC Emulator: "{1}". Group Policy components default to working against the PDC, as most GPO tools (including the default GPMC console) do the same. This behavior can be disabled in the module configuration settings.' # $Server, $domainObject.PDCEmulator

'Resolve-Identity.ParentObject.NoSecurityPrincipal' = 'Error processing parent of {0} : {1} of type {2} is no legal security principal and cannot be assigned permissions!' # $ADObject, $parentObject.Name, $parentObject.ObjectClass

'Resolve-PolicyRevision.Result.ErrorOnConfigImport' = 'Failed to read configuration for {0}: {1}' # $Policy.DisplayName, $result.Error.Exception.Message
Expand Down
3 changes: 2 additions & 1 deletion DomainManagement/functions/gplinks/Invoke-DMGPLink.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

Configures the current domain's group policy links as desired.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")]
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[Parameter(ValueFromPipeline = $true)]
Expand Down Expand Up @@ -209,7 +210,7 @@
}
#endregion Utility Functions

$parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential
$parameters = Resolve-GPTargetServer -Server $Server -Credential $Credential
$parameters['Debug'] = $false
Assert-ADConnection @parameters -Cmdlet $PSCmdlet
Invoke-Callback @parameters -Cmdlet $PSCmdlet
Expand Down
6 changes: 3 additions & 3 deletions DomainManagement/functions/gplinks/Test-DMGPLink.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
)

begin {
$parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential
$parameters = Resolve-GPTargetServer -Server $Server -Credential $Credential
$parameters['Debug'] = $false
Assert-ADConnection @parameters -Cmdlet $PSCmdlet
Invoke-Callback @parameters -Cmdlet $PSCmdlet
Expand Down Expand Up @@ -352,7 +352,7 @@
$ouData = Get-OUData -Parameters $parameters
foreach ($ouDatum in $ouData) {
$resultDefaults = @{
Server = $Server
Server = $parameters.Server
ObjectType = 'GPLink'
Identity = $ouDatum.OrganizationalUnit
Configuration = $ouDatum
Expand Down Expand Up @@ -426,7 +426,7 @@
New-LinkUpdate -Action Delete -PolicyName $linkedObject.DisplayName -PolicyDN $linkedObject.DistinguishedName -Status $linkedObject.Status -OriginalStatus $linkedObject.Status -Identity $adObject.DistinguishedName
}
$configProxy = [PSCustomObject]@{ Definition = $changes }
New-TestResult -ObjectType GPLink -Type 'Delete' -Identity $adObject.DistinguishedName -Server $Server -ADObject $adObject -Configuration $configProxy -Changed $changes
New-TestResult -ObjectType GPLink -Type 'Delete' -Identity $adObject.DistinguishedName -Server $parameters.Server -ADObject $adObject -Configuration $configProxy -Changed $changes
}
#endregion Process Managed Estate
}
Expand Down
2 changes: 1 addition & 1 deletion DomainManagement/functions/gpowner/Invoke-DMGPOwner.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
)

begin {
$parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential
$parameters = Resolve-GPTargetServer -Server $Server -Credential $Credential
$parameters['Debug'] = $false
Assert-ADConnection @parameters -Cmdlet $PSCmdlet
Invoke-Callback @parameters -Cmdlet $PSCmdlet
Expand Down
6 changes: 3 additions & 3 deletions DomainManagement/functions/gpowner/Test-DMGPOwner.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
)

begin {
$parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential
$parameters = Resolve-GPTargetServer -Server $Server -Credential $Credential
$parameters['Debug'] = $false
Assert-ADConnection @parameters -Cmdlet $PSCmdlet
Invoke-Callback @parameters -Cmdlet $PSCmdlet
Expand Down Expand Up @@ -88,7 +88,7 @@
try { $desiredOwner = Resolve-Principal @parameters -Name (Resolve-String -Text $ownerCfg.Identity) -OutputType ADObject -ErrorAction Stop }
catch {
Write-PSFMessage -Level Warning -String 'Test-DMGPOwner.Identity.NotFound' -StringValues $ownerCfg.Identity, $gpoADObject.DisplayName -Target $ownerCfg
New-TestResult -ObjectType GPOwner -Type IdentityNotFound -Identity $gpoADObject.DisplayName -Server $Server -Configuration $ownerCfg -ADObject $gpoADObject
New-TestResult -ObjectType GPOwner -Type IdentityNotFound -Identity $gpoADObject.DisplayName -Server $parameters.Server -Configuration $ownerCfg -ADObject $gpoADObject
continue
}
$actualAcl = Get-AdsAcl @parameters -Path $gpoADObject
Expand All @@ -112,7 +112,7 @@
'{0} -> {1}' -f $this.Old, $this.New
} -Force

New-TestResult -ObjectType GPOwner -Type Update -Identity $gpoADObject.DisplayName -Changed $change -Server $Server -Configuration $ownerCfg -ADObject $gpoADObject
New-TestResult -ObjectType GPOwner -Type Update -Identity $gpoADObject.DisplayName -Changed $change -Server $parameters.Server -Configuration $ownerCfg -ADObject $gpoADObject
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,16 @@

begin
{
$parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential
$parameters = Resolve-GPTargetServer -Server $Server -Credential $Credential
$parameters['Debug'] = $false
Assert-ADConnection @parameters -Cmdlet $PSCmdlet
Invoke-Callback @parameters -Cmdlet $PSCmdlet
Assert-Configuration -Type GroupPolicyPermissions -Cmdlet $PSCmdlet
Set-DMDomainContext @parameters
$computerName = (Get-ADDomain @parameters).PDCEmulator
$psParameter = $PSBoundParameters | ConvertTo-PSFHashtable -Include ComputerName, Credential -Inherit
$psParameter = Resolve-GPTargetServer -Server $Server -Credential $Credential -ForRemoting
try { $session = New-AdcPSSession @psParameter -ErrorAction Stop }
catch {
Stop-PSFFunction -String 'Invoke-DMGPPermission.WinRM.Failed' -StringValues $computerName -ErrorRecord $_ -EnableException $EnableException -Cmdlet $PSCmdlet -Target $computerName
Stop-PSFFunction -String 'Invoke-DMGPPermission.WinRM.Failed' -StringValues $parameters.Server -ErrorRecord $_ -EnableException $EnableException -Cmdlet $PSCmdlet -Target $parameters.Server
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,16 @@
)

begin {
$parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential
$parameters = Resolve-GPTargetServer -Server $Server -Credential $Credential
$parameters['Debug'] = $false
Assert-ADConnection @parameters -Cmdlet $PSCmdlet
Invoke-Callback @parameters -Cmdlet $PSCmdlet
Assert-Configuration -Type GroupPolicyPermissions -Cmdlet $PSCmdlet
Set-DMDomainContext @parameters
$computerName = (Get-ADDomain @parameters).PDCEmulator
$psParameter = $PSBoundParameters | ConvertTo-PSFHashtable -Include ComputerName, Credential -Inherit
$psParameter = Resolve-GPTargetServer -Server $Server -Credential $Credential -ForRemoting
try { $session = New-AdcPSSession @psParameter -ErrorAction Stop }
catch {
Stop-PSFFunction -String 'Test-DMGPPermission.WinRM.Failed' -StringValues $computerName -ErrorRecord $_ -EnableException $EnableException -Cmdlet $PSCmdlet -Target $computerName
Stop-PSFFunction -String 'Test-DMGPPermission.WinRM.Failed' -StringValues $parameters.Server -ErrorRecord $_ -EnableException $EnableException -Cmdlet $PSCmdlet -Target $parameters.Server
return
}

Expand Down Expand Up @@ -272,7 +271,7 @@
}

$resultDefaults = @{
Server = $Server
Server = $parameters.Server
ObjectType = 'GPPermission'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,24 +66,23 @@

begin
{
$parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential
$parameters = Resolve-GPTargetServer -Server $Server -Credential $Credential
$parameters['Debug'] = $false
Assert-ADConnection @parameters -Cmdlet $PSCmdlet
Invoke-Callback @parameters -Cmdlet $PSCmdlet
Assert-Configuration -Type GroupPolicyObjects -Cmdlet $PSCmdlet
$computerName = (Get-ADDomain @parameters).PDCEmulator
$psParameter = $PSBoundParameters | ConvertTo-PSFHashtable -Include ComputerName, Credential -Inherit
$psParameter = Resolve-GPTargetServer -Server $Server -Credential $Credential -ForRemoting
try { $session = New-AdcPSSession @psParameter -ErrorAction Stop }
catch {
Stop-PSFFunction -String 'Invoke-DMGroupPolicy.WinRM.Failed' -StringValues $computerName -ErrorRecord $_ -EnableException $EnableException -Cmdlet $PSCmdlet -Target $computerName
Stop-PSFFunction -String 'Invoke-DMGroupPolicy.WinRM.Failed' -StringValues $parameters.Server -ErrorRecord $_ -EnableException $EnableException -Cmdlet $PSCmdlet -Target $parameters.Server
return
}
Set-DMDomainContext @parameters

try { $gpoRemotePath = New-GpoWorkingDirectory -Session $session -ErrorAction Stop }
catch {
Remove-PSSession -Session $session -WhatIf:$false -Confirm:$false -ErrorAction SilentlyContinue
Stop-PSFFunction -String 'Invoke-DMGroupPolicy.Remote.WorkingDirectory.Failed' -StringValues $computerName -Target $computerName -ErrorRecord $_ -EnableException $EnableException
Stop-PSFFunction -String 'Invoke-DMGroupPolicy.Remote.WorkingDirectory.Failed' -StringValues $parameters.Server -Target $parameters.Server -ErrorRecord $_ -EnableException $EnableException
return
}
}
Expand Down
11 changes: 5 additions & 6 deletions DomainManagement/functions/grouppolicies/Test-DMGroupPolicy.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -38,35 +38,34 @@
)

begin {
$parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential
$parameters = Resolve-GPTargetServer -Server $Server -Credential $Credential
$parameters['Debug'] = $false
Assert-ADConnection @parameters -Cmdlet $PSCmdlet
Invoke-Callback @parameters -Cmdlet $PSCmdlet
Assert-Configuration -Type GroupPolicyObjects -Cmdlet $PSCmdlet
Set-DMDomainContext @parameters
$computerName = (Get-ADDomain @parameters).PDCEmulator

# DomainData retrieval
$domainDataNames = ((Get-DMGroupPolicy).DisplayName | Get-DMGPRegistrySetting | Where-Object DomainData).DomainData | Select-Object -Unique
try { $null = $domainDataNames | Invoke-DMDomainData @parameters -EnableException }
catch {
Stop-PSFFunction -String 'Test-DMGroupPolicy.DomainData.Failed' -StringValues ($domainDataNames -join ",") -ErrorRecord $_ -EnableException $EnableException -Cmdlet $PSCmdlet -Target $computerName
Stop-PSFFunction -String 'Test-DMGroupPolicy.DomainData.Failed' -StringValues ($domainDataNames -join ",") -ErrorRecord $_ -EnableException $EnableException -Cmdlet $PSCmdlet -Target $parameters.Server
return
}

# PS Remoting
$psParameter = $PSBoundParameters | ConvertTo-PSFHashtable -Include ComputerName, Credential -Inherit
$psParameter = Resolve-GPTargetServer -Server $Server -Credential $Credential -ForRemoting
try { $session = New-AdcPSSession @psParameter -ErrorAction Stop }
catch {
Stop-PSFFunction -String 'Test-DMGroupPolicy.WinRM.Failed' -StringValues $computerName -ErrorRecord $_ -EnableException $EnableException -Cmdlet $PSCmdlet -Target $computerName
Stop-PSFFunction -String 'Test-DMGroupPolicy.WinRM.Failed' -StringValues $parameters.Server -ErrorRecord $_ -EnableException $EnableException -Cmdlet $PSCmdlet -Target $parameters.Server
return
}
}
process {
if (Test-PSFFunctionInterrupt) { return }

$resultDefaults = @{
Server = $Server
Server = $parameters.Server
ObjectType = 'GroupPolicy'
}

Expand Down
1 change: 1 addition & 0 deletions DomainManagement/internal/configurations/configuration.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Set-PSFConfig -Module 'DomainManagement' -Name 'Import.IndividualFiles' -Value $

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.'
Set-PSFConfig -Module 'DomainManagement' -Name 'GroupPolicy.AlwaysUsePDC' -Value $true -Initialize -Validation bool -Description 'All Group Policy Consoles use by default the PDC Emulator. To make operations and visual verification match, Group Policy Components also use the PDC. This setting allows overriding that behavior.'

#$coreCount = $env:NUMBER_OF_PROCESSORS
#if (-not $coreCount) { $coreCount = 4 }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
function Resolve-GPTargetServer {
<#
.SYNOPSIS
Resolve the target to use for GPO Operations.

.DESCRIPTION
Resolve the target to use for GPO Operations.
By default, all GPO Components work with the PDC Emulator, as it is the server targeted by all GPMC consoles.

It will also - once per session - remind the user, that the target was redirected to the PDC.
It returns a hashtable ready to use for splatting with AD commands, including Server and - if originally provided - Credential.

.PARAMETER Server
The original server to target.

.PARAMETER Credential
The credentials to use.

.PARAMETER ForRemoting
We require the connection parameters for PowerShell Remoting.
Replaces "Server" with "ComputerName" in the resulting hashtable.

.EXAMPLE
PS C:\> Resolve-GPTargetServer -Server dc1.contoso.com -Credential $cred

Returns a hashtable with the data required to execute AD commands (Keys "Server" & "Credential").
If the original target server was not the PDC emulator, it was replaced with the PDC from contoso.com.
#>
[outputType([hashtable])]
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[PSFComputer]
$Server,

[AllowNull()]
[PSCredential]
$Credential,

[switch]
$ForRemoting
)
begin {
if (-not $script:cache_GPDomainTarget) {
$script:cache_GPDomainTarget = @{}
}
$isEnabled = Get-PSFConfigValue -FullName 'DomainManagement.GroupPolicy.AlwaysUsePDC' -Fallback $true
}
process {
$adParam = @{ Server = $Server }
if ($Credential) { $adParam.Credential = $Credential }

if (-not $isEnabled) {
if ($ForRemoting) {
$adParam.ComputerName = $adParam.Server
$adParam.Remove("Server")
}
return $adParam
}

if (-not $script:cache_GPDomainTarget["$Server"]) {
$script:cache_GPDomainTarget["$Server"] = Get-ADDomain @adParam
}
$domainObject = $script:cache_GPDomainTarget["$Server"]

if ($domainObject.PDCEmulator -ne $Server) {
if ($domainObject.DnsRoot -ne $Server) {
Write-PSFMessage -Level Host -String 'Resolve-GPTargetServer.Info.ChangingToPDC' -StringValues $Server, $domainObject.PDCEmulator -Target $domainObject.DnsRoot -Once "$($Server)->$($domainObject.PDCEmulator)"
}
$adParam.Server = $domainObject.PDCEmulator
}
if ($ForRemoting) {
$adParam.ComputerName = $adParam.Server
$adParam.Remove("Server")
}
return $adParam
}
}
Loading