Private/Set-AclConstructor5.ps1

# Constructor W/5 attributes https://msdn.microsoft.com/en-us/library/cawwkf0x.aspx

function Set-AclConstructor5 {
    <#
        .Synopsis
        Modifies ACLs on Active Directory objects.

        .DESCRIPTION
            This function adds or removes access rules to an Active Directory object
            using a constructor with 5 parameters to specify the access rule details.

        .PARAMETER Id
            Specifies the SamAccountName of the delegated group or user. This is the identity for which the access rule will be modified.
            It can be a variable containing the AD group.

        .PARAMETER LDAPPath
            Specifies the LDAP path of the target Active Directory object.

        .PARAMETER AdRight
            Specifies the Active Directory rights. Valid options include CreateChild, DeleteChild, and others.

        .PARAMETER AccessControlType
            Specifies whether the access control is to Allow or Deny.

        .PARAMETER ObjectType
            Specifies the object type GUID. Use for specific property access or extended rights.

        .PARAMETER RemoveRule
            If specified, the access rule will be removed. If omitted, the access rule will be added.

        .PARAMETER AdSecurityInheritance
            Security inheritance of the new right (All, Children, Descendent, None, SelfAndChildren)

        .EXAMPLE
            Set-AclConstructor5 -Id "SG_SiteAdmins_XXXX"
            -LDAPPath "OU=Users,OU=XXXX,OU=Sites,DC=EguibarIT,DC=local"
            -AdRight "CreateChild,DeleteChild"
            -AccessControlType "Allow"
            -ObjectType 12345678-abcd-1234-abcd-0123456789012
            -AdSecurityInheritance "All"

        .EXAMPLE
            $Splat = @{
                Id = "SG_SiteAdmins_XXXX"
                LDAPPath = "OU=Users,OU=XXXX,OU=Sites,DC=EguibarIT,DC=local"
                AdRight = "CreateChild,DeleteChild"
                AccessControlType = "Allow"
                ObjectType = '12345678-abcd-1234-abcd-0123456789012'
                AdSecurityInheritance = "All"
            }
            Set-AclConstructor5 @Splat

        .EXAMPLE
            $group = Get-AdGroup "SG_SiteAdmins_XXXX"

            $Splat = @{
                Id = $group
                LDAPPath = "OU=Users,OU=XXXX,OU=Sites,DC=EguibarIT,DC=local"
                AdRight = "CreateChild,DeleteChild"
                AccessControlType = "Allow"
                ObjectType = '12345678-abcd-1234-abcd-0123456789012'
                AdSecurityInheritance = "All"
            }
            Set-AclConstructor5 @Splat

        .NOTES
                Used Functions:
                    Name | Module
                    ---------------------------------------|--------------------------
                    Get-ADObject | ActiveDirectory
                    Get-Acl | Microsoft.Powershell.Security
                    Set-Acl | Microsoft.Powershell.Security
                    New-Object | Microsoft.Powershell.Utility
                    Get-AdObjectType | EguibarIT.DelegationPS
                    Test-IsValidDN | EguibarIT.DelegationPS
                    Get-CurrentErrorToDisplay | EguibarIT.DelegationPS

        .NOTES
        Version: 2.0
        DateModified: 09/Feb/2024
        LasModifiedBy: Vicente Rodriguez Eguibar
            vicente@eguibar.com
            Eguibar Information Technology S.L.
            http://www.eguibarit.com
  #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Low')]
    [OutputType([void])]

    param (
        # PARAM1 STRING for the Delegated Identity
        # An IdentityReference object that identifies the trustee of the access rule.
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Identity of the Delegated Group',
            Position = 0)]
        [ValidateNotNullOrEmpty()]
        [Alias('IdentityReference', 'Identity', 'Trustee', 'GroupID', 'Group')]
        $Id,

        # PARAM2 STRING for the object's LDAP path
        # The LDAP path to the object where the ACL will be changed
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Distinguished Name of the object',
            Position = 1)]
        [ValidateNotNullOrEmpty()]
        [ValidateScript({ Test-IsValidDN -ObjectDN $_ }, ErrorMessage = 'DistinguishedName provided is not valid! Please Check.')]
        [Alias('DN', 'DistinguishedName')]
        [String]
        $LDAPpath,

        # PARAM3 STRING representing AdRight
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Active Directory Right',
            Position = 2)]
        [ValidateNotNullOrEmpty()]
        [ValidateSet([ActiveDirectoryRights])]
        [Alias('ActiveDirectoryRights')]
        [String[]]
        $AdRight,

        # PARAM4 STRING representing Access Control Type
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Allow or Deny access to the given object',
            Position = 3)]
        #[ValidateSet('Allow', 'Deny')]
        [ValidateSet([AccessControlType])]
        [String]
        $AccessControlType,

        # PARAM5 STRING representing Object GUID
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'GUID of the object',
            Position = 4)]
        [AllowNull()]
        [Guid]
        $ObjectType,

        # PARAM6 STRING representing ActiveDirectory Security Inheritance
        [Parameter(Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'Security inheritance of the new right (All, Children, Descendents, None, SelfAndChildren)',
            Position = 5)]
        #[ValidateSet('All', 'Children', 'Descendents', 'None', 'SelfAndChildren')]
        [ValidateSet([ActiveDirectorySecurityInheritance], ErrorMessage = "Value '{0}' is invalid. Try one of: {1}")]
        [Alias('InheritanceType', 'ActiveDirectorySecurityInheritance')]
        [String]
        $AdSecurityInheritance,

        # PARAM7 SWITCH if $false (default) will add the rule. If $true, it will remove the rule
        [Parameter(Mandatory = $False,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            HelpMessage = 'If present, the access rule will be removed.',
            Position = 6)]
        [Switch]
        $RemoveRule
    )

    Begin {
        $txt = ($Variables.HeaderDelegation -f
            (Get-Date).ToShortDateString(),
            $MyInvocation.Mycommand,
            (Get-FunctionDisplay -HashTable $PsBoundParameters -Verbose:$False)
        )
        Write-Verbose -Message $txt

        ##############################
        # Module imports

        ##############################
        # Variables Definition

        $groupObject, $groupSID, $TmpSid, $acl, $Arg1, $Arg2, $Arg3, $Arg4, $Arg5, $RuleArguments = $null

    } #end Begin

    Process {

        # Collect the SID for the trustee we will be delegating to.
        # NULL will be returned if ID is a WellKnownSid
        #
        # Check if Identity is a WellKnownSID
        # Looking in var $Variables.WellKnownSIDs by Value (ej. 'authenticated users')
        # must be in lowercase to work
        If ($Variables.WellKnownSIDs.Values -Contains $PSBoundParameters['Id']) {

            # return SID of the WellKnownSid
            #$groupSID = $Variables.WellKnownSIDs.keys.where{ $Variables.WellKnownSIDs[$_].Contains($Id) }
            $TmpSid = ($Variables.WellKnownSIDs.GetEnumerator() | Where-Object { $_.value -eq $PSBoundParameters['Id'] }).Name

            $groupSID = [System.Security.Principal.SecurityIdentifier]::New($TmpSid)

            Write-Verbose -Message 'Identity is Well-Known SID. Retrieving the Well-Known SID'
        } else {
            $GroupObject = Get-AdObjectType -Identity $PSBoundParameters['Id']

            Write-Verbose -Message 'Identity is already a Group Object. Retrieving the Group'
        }

        # $groupObject will be NULL if ID is a WellKnownSid
        If ($null -ne $GroupObject) {

            # If identity is NOT a WellKnownSID, the function will translate to existing Object SID.
            $groupSID = [System.Security.Principal.SecurityIdentifier]::New($groupObject.SID)

            Write-Verbose -Message 'Retrieving SID of Identity'
        }

        #Get a reference to the Object we want to delegate
        try {

            #
            $object = Get-ADObject -Identity $PSBoundParameters['LDAPPath']

            Write-Verbose -Message ('Accessing the object from given LdapPath {0}.' -f $PSBoundParameters['LDAPPath'])

        } Catch {
            Write-Error -Message ('Error while trying to access LDAP object {0}' -f $PSBoundParameters['LDAPPath'])
            #Get-ErrorDetail -ErrorRecord $_
            throw
        } #end Try-Catch


        #Get a copy of the current DACL on the object
        try {
            $acl = Get-Acl -Path ('AD:\{0}' -f $object.DistinguishedName)

            Write-Verbose -Message ('Get a copy of the current DACL on the object DN {0}.' -f $object.DistinguishedName)

        } Catch {
            Write-Error -Message ('Error while trying to Get a copy of the current DACL {0}' -f $object.DistinguishedName)
            #Get-ErrorDetail -ErrorRecord $_
            throw
        } #end Try-Catch




        # Start creating the Access Rule Arguments
        # Provide the trustee identity (Group who gets the permissions)
        $Arg1 = [Security.Principal.IdentityReference] $groupSID


        # Set what to do (AD Rights http://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectoryrights(v=vs.110).aspx)
        $Arg2 = [DirectoryServices.ActiveDirectoryRights] $PSBoundParameters['AdRight']

        # Define if allowed or denied (AccessControlType - Allow/Denied)
        $Arg3 = [Security.AccessControl.AccessControlType] $PSBoundParameters['AccessControlType']

        # Set the object GUID
        $Arg4 = $PSBoundParameters['ObjectType']

        # Set the scope (AdSecurityInheritance - http://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectorysecurityinheritance(v=vs.110).aspx)
        $Arg5 = [DirectoryServices.ActiveDirectorySecurityInheritance] $PSBoundParameters['AdSecurityInheritance']

        $RuleArguments = $Arg1, $Arg2, $Arg3, $Arg4, $Arg5



        # If parameter RemoveRule is False (default when omitted) it will ADD the Access Rule
        # if TRUE then will REMOVE the access rule
        If ($PSBoundParameters['RemoveRule']) {
            # Action when TRUE is REMOVE

            if ($Force -or $PSCmdlet.ShouldProcess($object.DistinguishedName, "Removing access rule for $($PSBoundParameters['Id'])")) {

                #Create an Access Control Entry for new permission we wish to remove
                $null = $acl.RemoveAccessRule(([System.DirectoryServices.ActiveDirectoryAccessRule]::New($Arg1, $Arg2, $Arg3, $Arg4, $Arg5)))

                Write-Verbose -Message ('Removed access rule from {0}' -f $objectDN.DistinguishedName)
            } #end If
        } else {
            # Action when FALSE is ADD

            if ($Force -or $PSCmdlet.ShouldProcess($object.DistinguishedName, "Adding access rule for $($PSBoundParameters['Id'])")) {

                #Create an Access Control Entry for new permission we wish to add
                [void]$acl.AddAccessRule(([System.DirectoryServices.ActiveDirectoryAccessRule]::New($Arg1, $Arg2, $Arg3, $Arg4, $Arg5)))
                #$null = $acl.AddAccessRule((New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $RuleArguments))

                Write-Verbose -Message ('Added access rule to {0}' -f $objectDN.DistinguishedName)
            } #end If
        } #end If-Else



        try {
            #Re-apply the modified DACL to the OU
            Set-Acl -AclObject $acl -Path ('AD:\{0}' -f $object.DistinguishedName)

            Write-Verbose -Message ('Re-apply the modified DACL to the {0}' -f $objectDN.DistinguishedName)

        } Catch {
            Write-Error -Message ('Error when trying to re-apply the modified DACL to the {0}' -f $objectDN.DistinguishedName)
            #Get-ErrorDetail -ErrorRecord $_
            throw
        } #end Try-Catch

    } #end Process

    End {
        $txt = ($Variables.FooterDelegation -f $MyInvocation.InvocationName,
            'adding access rule with 5 arguments (Private Function).'
        )
        Write-Verbose -Message $txt
    } #end END
}