Public/GPO/Set-AdAclCreateDeleteGPO.ps1
function Set-AdAclCreateDeleteGPO { <# .SYNOPSIS Delegates permissions to create and delete Group Policy Objects (GPOs). .DESCRIPTION The Set-AdAclCreateDeleteGPO function grants or removes permissions to create and delete Group Policy Objects (GPOs) for a specified security group within Active Directory. This function performs the following actions: - Locates the Group Policy Container (CN=Policies,CN=System) in the domain - Adds or removes the appropriate Access Control Entries (ACEs) to allow GPO creation/deletion - Configures permissions that work with both GPMC and PowerShell-based GPO management The function requires Domain Admin privileges or equivalent permissions to modify the Group Policy Container ACL. It supports confirmation prompts through -Confirm and can be bypassed with -Force when used in automation scenarios. .PARAMETER Group Specifies the security group that will receive or lose GPO creation and deletion permissions. This parameter accepts various formats: - SAM Account Name (e.g., 'SG_GPO_Admins') - Distinguished Name (e.g., 'CN=SG_GPO_Admins,OU=Groups,DC=contoso,DC=com') - Security Identifier (SID) object or string - Group object from Get-ADGroup .PARAMETER RemoveRule When specified, the function removes GPO creation/deletion permissions instead of granting them. This is useful when decommissioning administrative roles or reducing privileges. .EXAMPLE Set-AdAclCreateDeleteGPO -Group 'SG_GPO_Admins' Grants permissions to create and delete Group Policy Objects to the 'SG_GPO_Admins' security group. .EXAMPLE Set-AdAclCreateDeleteGPO -Group 'SG_GPO_Admins' -RemoveRule Removes permissions to create and delete Group Policy Objects from the 'SG_GPO_Admins' security group. .EXAMPLE 'SG_GPO_Admins' | Set-AdAclCreateDeleteGPO Grants GPO creation and deletion permissions using pipeline input. .EXAMPLE $Splat = @{ Group = 'SG_GPO_Admins' RemoveRule = $true } Set-AdAclCreateDeleteGPO @Splat Removes GPO creation and deletion permissions using splatting. .INPUTS System.String Microsoft.ActiveDirectory.Management.ADGroup You can pipe group names as strings or Group objects from Get-ADGroup to this function. .OUTPUTS System.Void This function does not generate any output. .NOTES Used Functions: Name ║ Module/Namespace ═══════════════════════════════════════════╬══════════════════════════════ Set-AclConstructor5 ║ EguibarIT.DelegationPS Get-AdObjectType ║ EguibarIT.DelegationPS Write-Verbose ║ Microsoft.PowerShell.Utility Write-Error ║ Microsoft.PowerShell.Utility .NOTES Version: 2.0 DateModified: 22/May/2025 LastModifiedBy: Vicente Rodriguez Eguibar vicente@eguibar.com Eguibar IT http://www.eguibarit.com .LINK https://github.com/vreguibar/EguibarIT.DelegationPS .COMPONENT Group Policy .ROLE Security Administration .FUNCTIONALITY GPO Management, Delegation of Control #> [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact = 'Medium', DefaultParameterSetName = 'Default' )] [OutputType([void])] param ( # PARAM1 STRING for the Delegated Group Name [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Group Name which will get the delegation', Position = 0)] [ValidateNotNullOrEmpty()] [Alias('IdentityReference', 'Identity', 'Trustee', 'GroupID')] $Group, # PARAM2 SWITCH If present, the access rule will be removed. [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'If present, the access rule will be removed.', Position = 1)] [ValidateNotNullOrEmpty()] [Switch] $RemoveRule ) Begin { Set-StrictMode -Version Latest # Display function header if variables exist if ($null -ne $Variables -and $null -ne $Variables.HeaderDelegation) { $txt = ($Variables.HeaderDelegation -f (Get-Date).ToString('dd/MMM/yyyy'), $MyInvocation.Mycommand, (Get-FunctionDisplay -HashTable $PsBoundParameters -Verbose:$False) ) Write-Verbose -Message $txt } #end if ############################## # Module imports ############################## # Variables Definition [Hashtable]$Splat = [hashtable]::New([StringComparer]::OrdinalIgnoreCase) # Verify Group exist and return it as Microsoft.ActiveDirectory.Management.AdGroup $CurrentGroup = Get-AdObjectType -Identity $PSBoundParameters['Group'] } #end Begin Process { <# ACE number: 1 -------------------------------------------------------- IdentityReference : XXX ActiveDirectoryRights : CreateChild AccessControlType : Allow ObjectType : GuidNULL InheritanceType : None InheritedObjectType : GuidNULL IsInherited = False #> $Splat = @{ Id = $CurrentGroup LDAPPath = 'CN=Policies,CN=System,{0}' -f $Variables.defaultNamingContext AdRight = 'CreateChild', 'DeleteChild' AccessControlType = 'Allow' ObjectType = $Constants.guidNull AdSecurityInheritance = 'None' } # Check if RemoveRule switch is present. if ($PSBoundParameters['RemoveRule']) { $Splat['RemoveRule'] = $true $ActionDescription = ('Remove GPO management permissions from group {0}' -f $PSBoundParameters['Group']) } else { $ActionDescription = ('Grant GPO management permissions to group {0}' -f $PSBoundParameters['Group']) } #end If-Else # Perform the action with ShouldProcess if ($PSCmdlet.ShouldProcess($PSBoundParameters['Group'], $ActionDescription)) { Set-AclConstructor5 @Splat Write-Verbose -Message ('Successfully completed {0}' -f $ActionDescription) } #end If } #end Process End { # Display function footer if variables exist if ($null -ne $Variables -and $null -ne $Variables.FooterDelegation) { $txt = ($Variables.FooterDelegation -f $MyInvocation.InvocationName, $ActionDescription ) Write-Verbose -Message $txt } #end if } #end END } #end function Set-AdAclCreateDeleteGPO |