DSACL.psm1

#Region '.\_ModuleVariables.ps1' 0
$Script:GuidTable = @{
    'Account Restrictions'                      = [guid]'4c164200-20c0-11d0-a768-00aa006e0529'
    'All'                                       = [guid]'00000000-0000-0000-0000-000000000000'
    'CN'                                        = [guid]'bf96793f-0de6-11d0-a285-00aa003049e2'
    'Computer'                                  = [guid]'bf967a86-0de6-11d0-a285-00aa003049e2'
    'Contact'                                   = [guid]'5cb41ed0-0e4c-11d0-a286-00aa003049e2'
    'distinguishedName'                         = [guid]'bf9679e4-0de6-11d0-a285-00aa003049e2'
    'dNSHostName'                               = [guid]'72e39547-7b18-11d1-adef-00c04fd8d5cd'
    'DNS Host Name Attributes'                  = [guid]'72e39547-7b18-11d1-adef-00c04fd8d5cd'
    'gPLink'                                    = [guid]'f30e3bbe-9ff0-11d1-b603-0000f80367c1'
    'Group'                                     = [guid]'bf967a9c-0de6-11d0-a285-00aa003049e2'
    'GroupManagedServiceAccount'                = [guid]'7b8b558a-93a5-4af7-adca-c017e67f1057'
    'ManagedServiceAccount'                     = [guid]'ce206244-5827-4a86-ba1c-1c0c386c1b64'
    'member'                                    = [guid]'bf9679c0-0de6-11d0-a285-00aa003049e2'
    'name'                                      = [guid]'bf967a0e-0de6-11d0-a285-00aa003049e2'
    'OrganizationalUnit'                        = [guid]'bf967aa5-0de6-11d0-a285-00aa003049e2'
    'PwdLastSet'                                = [guid]'bf967a0a-0de6-11d0-a285-00aa003049e2'
    'ResetPassword'                             = [guid]'00299570-246d-11d0-a768-00aa006e0529'
    'sAMAccountName'                            = [guid]'3e0abfd0-126a-11d0-a060-00aa006c33ed'
    'self-membership'                           = [guid]'bf9679c0-0de6-11d0-a285-00aa003049e2'
    'servicePrincipalName'                      = [guid]'f3a64788-5306-11d1-a9c5-0000f80367c1'
    'User'                                      = [guid]'bf967aba-0de6-11d0-a285-00aa003049e2'
    'userParameters'                            = [guid]'bf967a6d-0de6-11d0-a285-00aa003049e2'
    'Validated write to DNS host name'          = [guid]'72e39547-7b18-11d1-adef-00c04fd8d5cd'
    'Validated write to service principal name' = [guid]'f3a64788-5306-11d1-a9c5-0000f80367c1'
}

$Script:DefaultContainersPatternTable = @{
    Computers = '^(?<prefix>B:32:AA312825768811D1ADED00C04FD8D5CD:)(?<DN>.+)$'
    Users     = '^(?<prefix>B:32:A9D1CA15768811D1ADED00C04FD8D5CD:)(?<DN>.+)$'
}

#$Script:RightsMapLoaded = $false
#EndRegion '.\_ModuleVariables.ps1' 33
#Region '.\Classes\DefaultContainerConfig.ps1' 0
class DSACLDefaultContainerConfig {
    [string] $Name
    [string] $DistinguishedName
    hidden [string] $Prefix
    hidden [int] $Index
    hidden [string] $DomainDN

    DSACLDefaultContainerConfig($Name,$DistinguishedName,$Prefix,$Index,$DomainDN) {
        $this.Name = $Name
        $this.DistinguishedName = $DistinguishedName
        $this.Prefix = $Prefix
        $this.Index = $Index
        $this.DomainDN = $DomainDN
    }
}
#EndRegion '.\Classes\DefaultContainerConfig.ps1' 15
#Region '.\Private\ConvertTo-LDAPGuidFilter.ps1' 0
function ConvertTo-LDAPGuidFilter {
    [CmdletBinding()]
    param (
        [guid]$Guid
    )
    process {
        '\{6}{7}\{4}{5}\{2}{3}\{0}{1}\{11}{12}\{9}{10}\{16}{17}\{14}{15}\{19}{20}\{21}{22}\{24}{25}\{26}{27}\{28}{29}\{30}{31}\{32}{33}\{34}{35}'-f([string[]]$Guid.ToString().ToCharArray())
    }
}
#EndRegion '.\Private\ConvertTo-LDAPGuidFilter.ps1' 9
#Region '.\Private\Find-LDAPObject.ps1' 0
function Find-LDAPObject {
    [CmdletBinding()]
    param (
        [System.DirectoryServices.SearchScope]
        $SearchScope = [System.DirectoryServices.SearchScope]::Subtree,

        [string]
        $SearchBase,

        [string]
        $Server,

        [String[]]
        $Property,

        [Parameter(Mandatory)]
        [string]
        $LDAPFilter,

        [switch]
        $Raw
    )
    process {
        try {
            if($Property.Count -gt 0) {
                $Properties = $Property
            } else {
                $Properties = $null
            }
            if(-not $PSBoundParameters.ContainsKey('SearchBase')) {
                $SearchBase = Get-LdapObject -DistinguishedName RootDse | Select-Object -ExpandProperty defaultNamingContext
            }
            if ([string]::IsNullOrWhiteSpace($Server)) {
                $SearchRoot = "LDAP://$SearchBase"
            } else {
                $SearchRoot = "LDAP://$Server/$SearchBase"
            }

            $DirectoryEntry = New-Object -TypeName 'System.DirectoryServices.DirectoryEntry' -ArgumentList $SearchRoot
            $Searcher = New-Object -TypeName 'System.DirectoryServices.DirectorySearcher' -ArgumentList $DirectoryEntry, $LDAPFilter, $Properties, $SearchScope
            $Searcher.PageSize = 1000
            $Result = $Searcher.FindAll()
            if($Raw.IsPresent) {
                Write-Output $Result
            } else {
                foreach($Object in $Result) {
                    $ObjectData = @{}
                    foreach($prop in $Object.Properties.Keys) {
                        if($Object.Properties[$prop].Count -eq 1) {
                            $Data = $Object.Properties[$prop].Item(0)
                        } else {
                            $Data = for ($i = 0; $i -lt $Object.Properties[$prop].Count; $i++) {
                                $Object.Properties[$prop].Item($i)
                            }
                        }
                        $ObjectData.Add($prop,$Data)
                    }
                    [PSCustomObject]$ObjectData
                }
            }
        }
        catch {
            throw
        }
        finally {
            try {
                $Searcher.Dispose()
            }
            catch {
                # Don't care about errors
            }
        }
    }
}
#EndRegion '.\Private\Find-LDAPObject.ps1' 74
#Region '.\Private\Get-LDAPObject.ps1' 0
function Get-LDAPObject {
    [CmdletBinding()]
    param (
        # DistinguishedName of LDAP object to bind to
        [Parameter(Mandatory)]
        [string]
        $DistinguishedName,

        # Set domain controller to use
        [Parameter()]
        [string]
        $Server,

        # Set Credentials to use when connecting
        [Parameter()]
        [pscredential]
        $Credential
    )
    try {
        $ArgumentList = $(
            if($PSBoundParameters.ContainsKey('Server')) {
                "LDAP://$Server/$DistinguishedName"
            }
            else {
                "LDAP://$DistinguishedName"
            }
            if($PSBoundParameters.ContainsKey('Credential')) {
                $Credential.UserName
                $Credential.GetNetworkCredential().Password
            }
        )
        $DirectoryEntry = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $ArgumentList
        $null = try {
            # Try to read the object to force an exception if no object was found.
            $DirectoryEntry | Format-List
        }
        catch {
            throw "Object not found: $DistinguishedName"
        }
        return $DirectoryEntry
    }
    catch {
        throw
    }
}
#EndRegion '.\Private\Get-LDAPObject.ps1' 45
#Region '.\Private\Get-SID.ps1' 0
function Get-SID {
    [CmdletBinding()]
    param (
        # DistinguishedName of LDAP object to get SID from
        [Parameter(Mandatory)]
        [string]
        $DistinguishedName,

        # Set domain controller to use
        [Parameter()]
        [string]
        $Server,

        # Set Credentials to use when connecting
        [Parameter()]
        [pscredential]
        $Credential
    )

    process {
        $Object = Get-LDAPObject @PSBoundParameters
        New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $Object.ObjectSID.Value, 0
    }

}
#EndRegion '.\Private\Get-SID.ps1' 25
#Region '.\Private\Set-DSACLAccessRule.ps1' 0
function Set-DSACLAccessRule {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [System.DirectoryServices.DirectoryEntry]
        $Target,

        [Parameter(Mandatory,ValueFromPipeline)]
        [System.DirectoryServices.ActiveDirectoryAccessRule]
        $ACE
    )
    process {
        try {
            $Target.psbase.ObjectSecurity.AddAccessRule($ACE)
        }
        catch {
            throw
        }
    }
    end {
        Set-DSACLObject -DirectoryEntry $Target
    }
}
#EndRegion '.\Private\Set-DSACLAccessRule.ps1' 24
#Region '.\Private\Set-DSACLObject.ps1' 0
function Set-DSACLObject {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [System.DirectoryServices.DirectoryEntry]
        $DirectoryEntry
    )
    try {
        $DirectoryEntry.psbase.CommitChanges()
    } catch {
        throw
    }
}
#EndRegion '.\Private\Set-DSACLObject.ps1' 14
#Region '.\Private\Set-Owner.ps1' 0
function Set-Owner {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNullOrEmpty()]
        [System.DirectoryServices.DirectoryEntry]
        $Target,

        [Parameter(Mandatory)]
        [String]
        $OwnerDN
    )
    process {
        try {
            $Owner = Get-SID -DistinguishedName $OwnerDN
            $Target.psbase.ObjectSecurity.SetOwner($Owner)
            Set-DSACLObject -DirectoryEntry $Target
        }
        catch {
            throw
        }
    }
}
#EndRegion '.\Private\Set-Owner.ps1' 23
#Region '.\Public\Add-DSACLCreateChild.ps1' 0
<#
.SYNOPSIS
Give Delegate rights to create objects of selected type in target (usually an OU)
 
.EXAMPLE
Add-DSACLCreateChild -TargetDN $UsersOU -DelegateDN $UserAdminGroup -ObjectTypeName User
Will give the group with DistinguishedName in $UserAdminGroup access to create user objects in
the OU with DistinguishedName in $UsersOU and all sub-OUs. Add -NoInheritance do disable inheritance.
 
#>

function Add-DSACLCreateChild {
    [CmdletBinding(DefaultParameterSetName='ByTypeName')]
    param (
        # DistinguishedName of object to modify ACL on. Usually an OU.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [String]
        $TargetDN,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipelineByPropertyName)]
        [String]
        $DelegateDN,

        # Object type to give full control over
        [Parameter(Mandatory,ParameterSetName='ByTypeName')]
        [ValidateSet('Computer', 'Contact', 'Group', 'ManagedServiceAccount', 'GroupManagedServiceAccount', 'User','All')]
        [String]
        $ObjectTypeName,

        # ObjectType guid, used for custom object types
        [Parameter(Mandatory,ParameterSetName='ByGuid')]
        [Guid]
        $ObjectTypeGuid,

        # Allow or Deny
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [System.Security.AccessControl.AccessControlType]
        $AccessType = 'Allow',

        # Sets access right to "This object only"
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [Switch]
        $NoInheritance
    )

    process {
        try {
            if ($NoInheritance.IsPresent) {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'None'
            }
            else {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'All'
            }
            switch ($PSCmdlet.ParameterSetName) {
                'ByTypeName' { $ObjectType = $Script:GuidTable[$ObjectTypeName]}
                'ByGuid'     { $ObjectType = $ObjectTypeGuid }
            }

            $Params = @{
                TargetDN              = $TargetDN
                DelegateDN            = $DelegateDN
                ActiveDirectoryRights = 'CreateChild'
                AccessControlType     = $AccessType
                ObjectType            = $ObjectType
                InheritanceType       = $InheritanceType
            }
            Add-DSACLCustom @Params

        }
        catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLCreateChild.ps1' 78
#Region '.\Public\Add-DSACLCustom.ps1' 0
<#
.SYNOPSIS
Give Delegate custom rights in target (usually an OU)
 
.DESCRIPTION
Used to delegate any custom rights in Active Directory.
Requires knowledge of creating ActiveDirectoryAccessRules, please use with caution.
 
#>

function Add-DSACLCustom {
    [CmdletBinding(DefaultParameterSetName='Delegate')]
    param (
        # DistinguishedName of object to modify ACL on. Usually an OU.
        [Parameter(Mandatory,ParameterSetName='Delegate')]
        [Parameter(Mandatory,ParameterSetName='Self')]
        [Parameter(Mandatory,ParameterSetName='Sid')]
        [String]
        $TargetDN,

        # Give access to "Self" instead of a user or group
        [Parameter(Mandatory,ParameterSetName='Self')]
        [Switch]
        $Self,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory,ParameterSetName='Delegate')]
        [String]
        $DelegateDN,

        [Parameter(Mandatory,ParameterSetName='Sid')]
        [String]
        $SID,

        # List of access rights that should be applied
        [Parameter(Mandatory,ParameterSetName='Delegate')]
        [Parameter(Mandatory,ParameterSetName='Self')]
        [Parameter(Mandatory,ParameterSetName='Sid')]
        [System.DirectoryServices.ActiveDirectoryRights[]]
        $ActiveDirectoryRights,

        # Sets allow or deny
        [Parameter(Mandatory,ParameterSetName='Delegate')]
        [Parameter(Mandatory,ParameterSetName='Self')]
        [Parameter(Mandatory,ParameterSetName='Sid')]
        [System.Security.AccessControl.AccessControlType]
        $AccessControlType,

        # Sets guid where access right should apply
        [Parameter(ParameterSetName='Delegate')]
        [Parameter(ParameterSetName='Self')]
        [Parameter(ParameterSetName='Sid')]
        [Guid]
        $ObjectType,

        # Sets if and how this rule should be inherited
        [Parameter(ParameterSetName='Delegate')]
        [Parameter(ParameterSetName='Self')]
        [Parameter(ParameterSetName='Sid')]
        [System.DirectoryServices.ActiveDirectorySecurityInheritance]
        $InheritanceType,

        # Sets guid of object types that should inherit this rule
        [Parameter(ParameterSetName='Delegate')]
        [Parameter(ParameterSetName='Self')]
        [Parameter(ParameterSetName='Sid')]
        [Guid]
        $InheritedObjectType

    )

    process {
        try {
            $Target = Get-LDAPObject -DistinguishedName $TargetDN -ErrorAction Stop
            switch ($PSCmdlet.ParameterSetName) {
                'Delegate' {
                    $DelegateSID = Get-SID -DistinguishedName $DelegateDN
                }
                'Self' { $DelegateSID = New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList 'S-1-5-10' }
                'Sid' { $DelegateSID = New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $SID }
            }

            $null = $PSBoundParameters.Remove('Self')
            $null = $PSBoundParameters.Remove('TargetDN')
            $null = $PSBoundParameters.Remove('DelegateDN')
            $null = $PSBoundParameters.Remove('SID')
            $PSBoundParameters.Add('Identity',$DelegateSID)

            $ACE = New-DSACLAccessRule @PSBoundParameters

            Set-DSACLAccessRule -Target $Target -ACE $ACE
        }
        catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLCustom.ps1' 96
#Region '.\Public\Add-DSACLDeleteChild.ps1' 0
<#
.SYNOPSIS
Give Delegate rights to delete objects of selected type in target (usually an OU)
 
.EXAMPLE
Add-DSACLDeleteChild -TargetDN $UsersOU -DelegateDN $UserAdminGroup -ObjectTypeName User
Will give the group with DistinguishedName in $UserAdminGroup access to delete user objects in
the OU with DistinguishedName in $UsersOU and all sub-OUs. Add -NoInheritance do disable inheritance.
 
#>

function Add-DSACLDeleteChild {
    [CmdletBinding(DefaultParameterSetName='ByTypeName')]
    param (
        # DistinguishedName of object to modify ACL on. Usually an OU.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [String]
        $TargetDN,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipelineByPropertyName)]
        [String]
        $DelegateDN,

        # Object type to give full control over
        [Parameter(Mandatory,ParameterSetName='ByTypeName')]
        [ValidateSet('Computer', 'Contact', 'Group', 'ManagedServiceAccount', 'GroupManagedServiceAccount', 'User','All')]
        [String]
        $ObjectTypeName,

        # ObjectType guid, used for custom object types
        [Parameter(Mandatory,ParameterSetName='ByGuid')]
        [Guid]
        $ObjectTypeGuid,

        # Allow or Deny
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [System.Security.AccessControl.AccessControlType]
        $AccessType = 'Allow',

        # Sets access right to "This object only"
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [Switch]
        $NoInheritance,

        # Adds DeleteTree right allowing to delete an object and all its child objects in one operation.
        # This is often required for deleting computer objects
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [Switch]
        $IncludeChildren
    )

    process {
        try {
            if ($NoInheritance.IsPresent) {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]::Children
            }
            else {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]::Descendents
            }
            switch ($PSCmdlet.ParameterSetName) {
                'ByTypeName' { $ObjectType = $Script:GuidTable[$ObjectTypeName]}
                'ByGuid'     { $ObjectType = $ObjectTypeGuid }
            }

            if ($IncludeChildren.IsPresent) {
                $ActiveDirectoryRights = 'Delete', 'DeleteTree'
            }
            else {
                $ActiveDirectoryRights = 'Delete'
            }

            $Params = @{
                TargetDN              = $TargetDN
                DelegateDN            = $DelegateDN
                ActiveDirectoryRights = $ActiveDirectoryRights
                AccessControlType     = $AccessType
                ObjectType            = $Script:GuidTable['All']
                InheritanceType       = $InheritanceType
                InheritedObjectType   = $ObjectType
            }
            Add-DSACLCustom @Params

        }
        catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLDeleteChild.ps1' 93
#Region '.\Public\Add-DSACLFullControl.ps1' 0
<#
.SYNOPSIS
Give Delegate FullControl rights on objects of selected type in target (usually an OU)
 
.EXAMPLE
Add-DSACLFullControl -TargetDN $UsersOU -DelegateDN $UserAdminGroup -ObjectTypeName User -AccessType Allow
Will give the group with DistinguishedName in $UserAdminGroup FullControl of user objects in
the OU with DistinguishedName in $UsersOU and all sub-OUs. Add -NoInheritance do disable inheritance.
#>

function Add-DSACLFullControl {
    [CmdletBinding(DefaultParameterSetName='ByTypeName')]
    param (
        # DistinguishedName of object to modify ACL on. Usually an OU.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [String]
        $TargetDN,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipelineByPropertyName)]
        [String]
        $DelegateDN,

        # Object type to give full control over
        [Parameter(Mandatory,ParameterSetName='ByTypeName')]
        [ValidateSet('Computer', 'Contact', 'Group', 'ManagedServiceAccount', 'GroupManagedServiceAccount', 'User', 'All')]
        [String]
        $ObjectTypeName,

        # ObjectType guid, used for custom object types
        [Parameter(Mandatory,ParameterSetName='ByGuid')]
        [Guid]
        $ObjectTypeGuid,

        # Allow or Deny
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [System.Security.AccessControl.AccessControlType]
        $AccessType = 'Allow',

        # Sets access right to "This object only"
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [Switch]
        $NoInheritance
    )

    process {
        try {
            if ($NoInheritance.IsPresent) {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'Children'
            }
            else {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'Descendents'
            }
            switch ($PSCmdlet.ParameterSetName) {
                'ByTypeName' { $ObjectType = $Script:GuidTable[$ObjectTypeName]}
                'ByGuid'     { $ObjectType = $ObjectTypeGuid }
            }

            $Params = @{
                TargetDN              = $TargetDN
                DelegateDN            = $DelegateDN
                ActiveDirectoryRights = 'GenericAll'
                AccessControlType     = $AccessType
                InheritedObjectType   = $ObjectType
                InheritanceType       = $InheritanceType
            }
            Add-DSACLCustom @Params

        }
        catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLFullControl.ps1' 77
#Region '.\Public\Add-DSACLJoinDomain.ps1' 0
<#
    .SYNOPSIS
    Give DelegateDN rights to join computers in target (usually an OU).
 
    .EXAMPLE
    Add-DSACLJoinDomain -TargetDN $ComputersOU -DelegateDN $JoinDomainAccounts -AccessType Allow
    Will give the group with DistinguishedName in $JoinDomainAccounts rights to join computers to
    the domain. Requires a computer account to be created already.
 
    Use switch -AllowCreate to allow to create computer objects in OU and join without a
    pre-existing computer object. Add -NoInheritance do disable the access to ineherit to sub-OUs.
#>

function Add-DSACLJoinDomain {
    [CmdletBinding()]
    param (
        # DistinguishedName of object to modify ACL on. Usually an OU.
        [Parameter(Mandatory)]
        [String]
        $TargetDN,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory)]
        [String]
        $DelegateDN,

        # Allow creating computer objects, this allows to join computers without a pre-staged computer account
        [Parameter()]
        [Switch]
        $AllowCreate,

        # Sets access right to "This object only"
        [Parameter()]
        [Switch]
        $NoInheritance
    )

    process {
        try {

            $WriteParams = @{
                TargetDN       = $TargetDN
                DelegateDN     = $DelegateDN
                ObjectTypeName = 'Computer'
                AccessType     = 'Allow'
                NoInheritance  = $NoInheritance
            }
            Add-DSACLResetPassword @WriteParams
            Add-DSACLWriteAccountRestrictions @WriteParams
            Add-DSACLWriteServicePrincipalName @WriteParams
            Add-DSACLWriteDNSHostName @WriteParams

            if($AllowCreate.IsPresent) {
                Add-DSACLCreateChild -TargetDN $TargetDN -DelegateDN $DelegateDN -ObjectTypeName Computer -NoInheritance:$NoInheritance
            }

        }
        catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLJoinDomain.ps1' 61
#Region '.\Public\Add-DSACLLinkGPO.ps1' 0
<#
.SYNOPSIS
Delegate rights to link GPO on target (usually an OU)
 
.EXAMPLE
Add-DSACLLinkGPO -TargetDN $UsersOU -DelegateDN $GPAdmin -AccessType Allow
Will give the group with DistinguishedName in $GPAdmin rights to link GPOs on
the OU with DistinguishedName in $UsersOU and all sub-OUs. Add -NoInheritance to disable inheritance.
#>

function Add-DSACLLinkGPO {
    [CmdletBinding()]
    param (
        # DistinguishedName of object to modify ACL on. Usually an OU.
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [String]
        $TargetDN,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [String]
        $DelegateDN,

        # Allow or Deny
        [Parameter()]
        [System.Security.AccessControl.AccessControlType]
        $AccessType = 'Allow',

        # Sets access right to "This object only"
        [Parameter()]
        [Switch]
        $NoInheritance
    )

    process {
        try {
            $Params = @{
                TargetDN              = $TargetDN
                DelegateDN            = $DelegateDN
                ActiveDirectoryRights = 'WriteProperty'
                AccessControlType     = $AccessType
                ObjectType            = $Script:GuidTable['gPLink']
            }

            if ($NoInheritance.IsPresent) {
                $Params['InheritanceType'] = [System.DirectoryServices.ActiveDirectorySecurityInheritance]::None
            }
            else {
                $Params['InheritanceType'] = [System.DirectoryServices.ActiveDirectorySecurityInheritance]::All
                $Params['InheritedObjectType'] = $Script:GuidTable['OrganizationalUnit']
            }

            Add-DSACLCustom @Params

        }
        catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLLinkGPO.ps1' 59
#Region '.\Public\Add-DSACLManageGroupMember.ps1' 0
<#
.SYNOPSIS
Give Delegate rights to manage members in group(s).
 
.EXAMPLE
Add-DSACLManageGroupMember -TargetDN $GroupsOU -DelegateDN $AccessAdminGroup -AccessType Allow
Will give the group with DistinguishedName in $AccessAdminGroup access to manage members
of any group in the OU with DistinguishedName in $GroupsOU and all sub-OUs. Add -NoInheritance do disable inheritance.
 
.EXAMPLE
Add-DSACLManageGroupMember -TargetDN $GroupsOU -DelegateDN $AccessAdminGroup -AccessType Allow -NoInheritance
Will give the group with DistinguishedName in $AccessAdminGroup access to manage members
of any group in the OU with DistinguishedName in $GroupsOU. Will not effect groups in sub-OUs.
 
.EXAMPLE
Add-DSACLManageGroupMember -TargetDN $SpecialGroup -DelegateDN $AccessAdminGroup -AccessType Allow -DirectOnGroup
Will give the group with DistinguishedName in $AccessAdminGroup access to manage members
of the group in with DistinguishedName in $SpecialGroup.
 
#>

function Add-DSACLManageGroupMember {
    [CmdletBinding(DefaultParameterSetName='OnContainer')]
    param (
        # DistinguishedName of object to modify ACL on. Usually an OU.
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [String]
        $TargetDN,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [String]
        $DelegateDN,

        # Allow or Deny
        [Parameter()]
        [System.Security.AccessControl.AccessControlType]
        $AccessType = 'Allow',

        # Sets access right to "Children". Use this to effect all groups in OU but not subOUs
        [Parameter(ParameterSetName='OnContainer')]
        [Switch]
        $NoInheritance,

        # Sets access right to "This object only", use this when TargetDN is a group.
        [Parameter(ParameterSetName='OnGroup')]
        [Switch]
        $DirectOnGroup
    )

    process {
        try {
            if ($NoInheritance.IsPresent) {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'Children'
            } elseif ($DirectOnGroup.IsPresent) {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'None'
            } else {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'Descendents'
            }

            $Params = @{
                TargetDN              = $TargetDN
                DelegateDN            = $DelegateDN
                ActiveDirectoryRights = 'WriteProperty'
                AccessControlType     = $AccessType
                ObjectType            = $Script:GuidTable['member']
                InheritanceType       = $InheritanceType
                InheritedObjectType   = $Script:GuidTable['group']
            }
            Add-DSACLCustom @Params

        }
        catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLManageGroupMember.ps1' 76
#Region '.\Public\Add-DSACLManagerCanUpdateGroupMember.ps1' 0
<#
.SYNOPSIS
Give Delegate rights to groups manager to manage members in group(s).
Note that this access stays with the user if the manager changes.
 
.EXAMPLE
Add-DSACLManagerCanUpdateGroupMember -TargetDN $Group
Will give the current manager of the group in $Group access to manage members.
Note that this access stays with the user if the manager changes.
 
#>

function Add-DSACLManagerCanUpdateGroupMember {
    [CmdletBinding(DefaultParameterSetName='OnContainer')]
    param (
        # DistinguishedName of object to modify ACL on. Has to be a group.
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [String]
        $TargetDN
    )

    process {
        try {
            $Group = Get-LDAPObject -DistinguishedName $TargetDN
            if($Group.objectClass -notcontains 'group') {
                throw 'Target has to be a group.'
            }
            $DelegateDN = $Group.managedBy

            Add-DSACLManageGroupMember -TargetDN $TargetDN -DelegateDN $DelegateDN -DirectOnGroup
        }
        catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLManagerCanUpdateGroupMember.ps1' 35
#Region '.\Public\Add-DSACLMoveObjectFrom.ps1' 0
<#
.SYNOPSIS
Delegates right to move object of type ObjectTypeName from TargetDN.
Moving also requires create-child rights in target container.
 
.DESCRIPTION
Delegates the rights to rename and delete objects in TargetDN.
#>


function Add-DSACLMoveObjectFrom {
    [CmdletBinding()]
    param (
        # Object type to allow being moved
        [Parameter(Mandatory)]
        [ValidateSet('Computer', 'Contact', 'Group', 'ManagedServiceAccount', 'GroupManagedServiceAccount', 'User','All')]
        [String]
        $ObjectTypeName,

        # DistinguishedName of object to modify ACL on. Usually an OU.
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        $TargetDN,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        $DelegateDN,

        # Sets access right to "This object only"
        [Switch]
        $NoInheritance
    )

    process {
        try {
            $ErrorActionPreference = 'Stop'
            Add-DSACLRenameObject @PSBoundParameters
            Add-DSACLDeleteChild @PSBoundParameters
        } catch {
            throw
        }
    }

}
#EndRegion '.\Public\Add-DSACLMoveObjectFrom.ps1' 42
#Region '.\Public\Add-DSACLRenameObject.ps1' 0
<#
.SYNOPSIS
Give Delegate rights to rename objects in target (usually an OU)
 
.EXAMPLE
Add-DSACLRenameObject -ObjectTypeName Computer -TargetDN $ComputersOU -DelegateDN $ComputerAdminGroup -AccessType Allow
Will give the group with DistinguishedName in $ComputerAdminGroup rights to rename computers in
the OU with DistinguishedName in $ComputersOU and all sub-OUs. Add -NoInheritance do disable inheritance.
#>

function Add-DSACLRenameObject {
    [CmdletBinding(DefaultParameterSetName='Delegate')]
    param (
        # Object type to allow being renamed
        [Parameter(Mandatory,ParameterSetName='Delegate')]
        [ValidateSet('Computer', 'Contact', 'Group', 'ManagedServiceAccount', 'GroupManagedServiceAccount', 'User','All')]
        [String]
        $ObjectTypeName,

        # DistinguishedName of object to modify ACL on. Usually an OU.
        [Parameter(Mandatory,ParameterSetName='Delegate')]
        [String]
        $TargetDN,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory,ParameterSetName='Delegate')]
        [String]
        $DelegateDN,

        # Sets access right to "This object only"
        [Parameter(ParameterSetName='Delegate')]
        [Switch]
        $NoInheritance
    )

    process {
        try {

            $null = $PSBoundParameters.Remove('ObjectTypeName')
            $null = $PSBoundParameters.Remove('NoInheritance')

            if ($NoInheritance.IsPresent) {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]::Children
            }
            else {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]::Descendents
            }

            $AceParams = @{
                ActiveDirectoryRights = 'WriteProperty'
                AccessControlType     = 'Allow'
                InheritanceType       = $InheritanceType
                InheritedObjectType   = $Script:GuidTable[$ObjectTypeName]
            }

            'distinguishedName', 'name', 'CN' | ForEach-Object -Process {
                Add-DSACLCustom -ObjectType $Script:GuidTable[$_] @AceParams @PSBoundParameters
            }

            if($ObjectTypeName -eq 'Computer') {

                'Account Restrictions' ,'sAMAccountName' | ForEach-Object -Process {
                    Add-DSACLCustom -ObjectType $Script:GuidTable[$_] @AceParams @PSBoundParameters
                }

                $WriteParams = @{
                    TargetDN       = $TargetDN
                    DelegateDN     = $DelegateDN
                    ObjectTypeName = 'Computer'
                    AccessType     = 'Allow'
                    NoInheritance  = $NoInheritance.IsPresent
                }
                Add-DSACLWriteDNSHostName @WriteParams
                Add-DSACLWriteServicePrincipalName @WriteParams

            }

        }
        catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLRenameObject.ps1' 82
#Region '.\Public\Add-DSACLReplicatingDirectoryChanges.ps1' 0
<#
.SYNOPSIS
Give Delegate "Replicating Directory Changes" rights on domain with DistinguishedName in target
 
.EXAMPLE
Add-DSACLReplicatingDirectoryChanges -DelegateDN $AADCServiceAccount
Will give the service account with DistinguishedName in $AADCServiceAccount the right "Replicating Directory Changes".
Add -AllowReplicateSecrets to grant "Replicating Directory Changes All" instead..
 
#>

function Add-DSACLReplicatingDirectoryChanges {
    [CmdletBinding(DefaultParameterSetName='ByTypeName')]
    param (
        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory)]
        [String]
        $DelegateDN,

        # Allow replicating secrets, like passwords (Corresponds to "Replicating Directory Changes All")
        [Parameter()]
        [Switch]
        $AllowReplicateSecrets
    )

    process {
        try {

            $TargetDN = Get-LdapObject -DistinguishedName RootDse | Select-Object -ExpandProperty defaultNamingContext

            if ($AllowReplicateSecrets.IsPresent) {
                # Replicating Directory Changes All
                $ObjectType = '1131f6ad-9c07-11d1-f79f-00c04fc2dcd2'
            }
            else {
                # Replicating Directory Changes
                $ObjectType = '1131f6aa-9c07-11d1-f79f-00c04fc2dcd2'
            }
            $Params = @{
                TargetDN              = $TargetDN
                DelegateDN            = $DelegateDN
                ActiveDirectoryRights = 'ExtendedRight'
                AccessControlType     = 'Allow'
                ObjectType            = $ObjectType
                InheritanceType       = 'None'
            }
            Add-DSACLCustom @Params

        }
        catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLReplicatingDirectoryChanges.ps1' 53
#Region '.\Public\Add-DSACLResetPassword.ps1' 0
<#
.SYNOPSIS
Delegate ResetPassword rights on objects of selected type in target (usually an OU)
 
.EXAMPLE
Add-DSACLResetPassword -TargetDN $UsersOU -DelegateDN $UserAdminGroup -ObjectTypeName User -AccessType Allow
Will give the group with DistinguishedName in $UserAdminGroup ResetPassword rights of user objects in
the OU with DistinguishedName in $UsersOU and all sub-OUs. Add -NoInheritance to disable inheritance.
#>

function Add-DSACLResetPassword {
    [CmdletBinding(DefaultParameterSetName='ByTypeName')]
    param (
        # DistinguishedName of object to modify ACL on. Usually an OU.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [String]
        $TargetDN,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipelineByPropertyName)]
        [String]
        $DelegateDN,

        # Object type to give full control over
        [Parameter(Mandatory,ParameterSetName='ByTypeName')]
        [ValidateSet('User', 'Computer', 'ManagedServiceAccount', 'GroupManagedServiceAccount')]
        [String]
        $ObjectTypeName,

        # ObjectType guid, used for custom object types
        [Parameter(Mandatory,ParameterSetName='ByGuid')]
        [Guid]
        $ObjectTypeGuid,

        # Allow or Deny
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [System.Security.AccessControl.AccessControlType]
        $AccessType = 'Allow',

        # Sets access right to "This object only"
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [Switch]
        $NoInheritance
    )

    process {
        try {
            if ($NoInheritance.IsPresent) {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'Children'
            }
            else {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'Descendents'
            }
            switch ($PSCmdlet.ParameterSetName) {
                'ByTypeName' { $ObjectType = $Script:GuidTable[$ObjectTypeName]}
                'ByGuid'     { $ObjectType = $ObjectTypeGuid }
            }

            $Params = @{
                TargetDN              = $TargetDN
                DelegateDN            = $DelegateDN
                ActiveDirectoryRights = 'ExtendedRight'
                AccessControlType     = $AccessType
                ObjectType            = $Script:GuidTable['ResetPassword']
                InheritanceType       = $InheritanceType
                InheritedObjectType   = $ObjectType
            }
            Add-DSACLCustom @Params

        }
        catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLResetPassword.ps1' 78
#Region '.\Public\Add-DSACLWriteAccountRestrictions.ps1' 0
<#
.SYNOPSIS
Delegate rights to write to the property set "Account Restrictions" on objects of selected type in target (usually an OU)
 
.DESCRIPTION
Delegate rights to write to the property set "Account Restrictions" on objects of selected type in target (usually an OU)
 
A property set is a set of attributes that can be used to minimize the amount of ACE's to create. The property set
"Account Restrictions". More information about this set can be found here: https://docs.microsoft.com/en-us/windows/desktop/adschema/r-user-account-restrictions
 
.EXAMPLE
Add-DSACLWriteAccountRestrictions -TargetDN $UsersOU -DelegateDN $UserAdminGroup -ObjectTypeName User -AccessType Allow
Will give the group with DistinguishedName in $UserAdminGroup rights to SET SPN of user objects in
the OU with DistinguishedName in $UsersOU and all sub-OUs. Add -NoInheritance to disable inheritance.
#>


function Add-DSACLWriteAccountRestrictions {
    [CmdletBinding(DefaultParameterSetName='ByTypeName')]
    param (
        # DistinguishedName of object to modify ACL on. Usually an OU.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [String]
        $TargetDN,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipelineByPropertyName)]
        [String]
        $DelegateDN,

        # Object type to give full control over
        [Parameter(Mandatory,ParameterSetName='ByTypeName')]
        [ValidateSet('User', 'Computer', 'ManagedServiceAccount','GroupManagedServiceAccount')]
        [String]
        $ObjectTypeName,

        # ObjectType guid, used for custom object types
        [Parameter(Mandatory,ParameterSetName='ByGuid')]
        [Guid]
        $ObjectTypeGuid,

        # Allow or Deny
        [Parameter(Mandatory,ParameterSetName='ByTypeName')]
        [Parameter(Mandatory,ParameterSetName='ByGuid')]
        [System.Security.AccessControl.AccessControlType]
        $AccessType,

        # Sets access right to "This object only"
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [Switch]
        $NoInheritance
    )

    process {
        try {

            if ($NoInheritance.IsPresent) {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'Children'
            } else {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'Descendents'
            }

            switch ($PSCmdlet.ParameterSetName) {
                'ByTypeName' { $InheritanceObjectType = $Script:GuidTable[$ObjectTypeName]}
                'ByGuid'     { $InheritanceObjectType = $ObjectTypeGuid }
            }

            $AceParams = @{
                TargetDN              = $TargetDN
                DelegateDN            = $DelegateDN
                ActiveDirectoryRights = 'WriteProperty'
                AccessControlType     = 'Allow'
                ObjectType            = $Script:GuidTable['Account Restrictions']
                InheritanceType       = $InheritanceType
                InheritedObjectType   = $InheritanceObjectType
            }
            Add-DSACLCustom @AceParams

        } catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLWriteAccountRestrictions.ps1' 85
#Region '.\Public\Add-DSACLWriteDNSHostName.ps1' 0
<#
.SYNOPSIS
Delegate rights to SET DNSHostName on objects of selected type in target (usually an OU)
 
.EXAMPLE
Add-DSACLWriteDNSHostName -TargetDN $ComputersOU -DelegateDN $ComputerAdminGroup -ObjectTypeName Computer -AccessType Allow
Will give the group with DistinguishedName in $ComputerAdminGroup rights to SET DNSHostName of computer objects in
the OU with DistinguishedName in $ComputersOU and all sub-OUs. Add -NoInheritance to disable inheritance.
#>

function Add-DSACLWriteDNSHostName {
    [CmdletBinding(DefaultParameterSetName='ByTypeName')]
    param (
        # DistinguishedName of object to modify ACL on. Usually an OU.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [String]
        $TargetDN,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipelineByPropertyName)]
        [String]
        $DelegateDN,

        # Object type to give full control over
        [Parameter(Mandatory,ParameterSetName='ByTypeName')]
        [ValidateSet('Computer', 'ManagedServiceAccount','GroupManagedServiceAccount')]
        [String]
        $ObjectTypeName,

        # ObjectType guid, used for custom object types
        [Parameter(Mandatory,ParameterSetName='ByGuid')]
        [Guid]
        $ObjectTypeGuid,

        # Allow or Deny
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [System.Security.AccessControl.AccessControlType]
        $AccessType = 'Allow',

        # Sets access right to "This object only"
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [Switch]
        $NoInheritance,

        # Only effects validated writes
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [Switch]
        $ValidatedOnly
    )

    process {
        try {
            if ($NoInheritance.IsPresent) {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'Children'
            } else {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'Descendents'
            }

            switch ($PSCmdlet.ParameterSetName) {
                'ByTypeName' { $InheritanceObjectType = $Script:GuidTable[$ObjectTypeName]}
                'ByGuid'     { $InheritanceObjectType = $ObjectTypeGuid }
            }

            if($ValidatedOnly.IsPresent) {
                $ObjectType = $Script:GuidTable['Validated write to DNS host name']
                $ActiveDirectoryRights = [System.DirectoryServices.ActiveDirectoryRights]::Self
            } else {
                $ObjectType = $Script:GuidTable['DNS Host Name Attributes']
                $ActiveDirectoryRights = [System.DirectoryServices.ActiveDirectoryRights]::WriteProperty
            }

            $AceParams = @{
                TargetDN              = $TargetDN
                DelegateDN            = $DelegateDN
                ActiveDirectoryRights = $ActiveDirectoryRights
                AccessControlType     = 'Allow'
                ObjectType            = $ObjectType
                InheritanceType       = $InheritanceType
                InheritedObjectType   = $InheritanceObjectType
            }
            Add-DSACLCustom @AceParams

        } catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLWriteDNSHostName.ps1' 91
#Region '.\Public\Add-DSACLWriteServicePrincipalName.ps1' 0
<#
.SYNOPSIS
Delegate rights to SET ServicePrincipalName (SPN) on objects of selected type in target (usually an OU)
 
.EXAMPLE
Add-DSACLWriteServicePrincipalName -TargetDN $UsersOU -DelegateDN $UserAdminGroup -ObjectTypeName User -AccessType Allow
Will give the group with DistinguishedName in $UserAdminGroup rights to SET SPN of user objects in
the OU with DistinguishedName in $UsersOU and all sub-OUs. Add -NoInheritance to disable inheritance.
#>

function Add-DSACLWriteServicePrincipalName {
    [CmdletBinding(DefaultParameterSetName='ByTypeName')]
    param (
        # DistinguishedName of object to modify ACL on. Usually an OU.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [String]
        $TargetDN,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory,ParameterSetName='ByTypeName',ValueFromPipelineByPropertyName)]
        [Parameter(Mandatory,ParameterSetName='ByGuid',ValueFromPipelineByPropertyName)]
        [String]
        $DelegateDN,

        # Object type to give full control over
        [Parameter(Mandatory,ParameterSetName='ByTypeName')]
        [ValidateSet('User', 'Computer', 'ManagedServiceAccount','GroupManagedServiceAccount')]
        [String]
        $ObjectTypeName,

        # ObjectType guid, used for custom object types
        [Parameter(Mandatory,ParameterSetName='ByGuid')]
        [Guid]
        $ObjectTypeGuid,

        # Allow or Deny
        [Parameter(Mandatory,ParameterSetName='ByTypeName')]
        [Parameter(Mandatory,ParameterSetName='ByGuid')]
        [System.Security.AccessControl.AccessControlType]
        $AccessType,

        # Sets access right to "This object only"
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [Switch]
        $NoInheritance,

        # Only effects validated writes
        [Parameter(ParameterSetName='ByTypeName')]
        [Parameter(ParameterSetName='ByGuid')]
        [Switch]
        $ValidatedOnly
    )

    process {
        try {

            if ($NoInheritance.IsPresent) {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'Children'
            } else {
                $InheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance]'Descendents'
            }

            switch ($PSCmdlet.ParameterSetName) {
                'ByTypeName' { $InheritanceObjectType = $Script:GuidTable[$ObjectTypeName]}
                'ByGuid'     { $InheritanceObjectType = $ObjectTypeGuid }
            }

            if($ValidatedOnly.IsPresent) {
                $ActiveDirectoryRights = 'Self'
            } else {
                $ActiveDirectoryRights = 'WriteProperty'
            }

            $AceParams = @{
                TargetDN              = $TargetDN
                DelegateDN            = $DelegateDN
                ActiveDirectoryRights = $ActiveDirectoryRights
                AccessControlType     = 'Allow'
                ObjectType            = $Script:GuidTable['servicePrincipalName']
                InheritanceType       = $InheritanceType
                InheritedObjectType   = $InheritanceObjectType
            }
            Add-DSACLCustom @AceParams

        } catch {
            throw
        }
    }
}
#EndRegion '.\Public\Add-DSACLWriteServicePrincipalName.ps1' 90
#Region '.\Public\ConvertFrom-DSACLInheritedObjectTypeGuid.ps1' 0
function ConvertFrom-DSACLInheritedObjectTypeGuid {
    [CmdletBinding(DefaultParameterSetName='Access')]
    param (
        [Parameter(Mandatory, ParameterSetName='Access', ValueFromPipeline)]
        [Alias('ACE')]
        [Alias('Access')]
        [System.DirectoryServices.ActiveDirectoryAccessRule]
        $AccessRule,

        [Parameter(Mandatory, ParameterSetName='Audit', ValueFromPipeline)]
        [Alias('Audit')]
        [System.DirectoryServices.ActiveDirectoryAuditRule]
        $AuditRule
    )

    begin {
        try {
            $null = Get-Variable -Name DSACLAttributeGuid -Scope Script -ErrorAction Stop
        }
        catch {
            $null = Register-DSACLRightsMapVariable -Scope Script
        }
    }

    process {

        switch($PSCmdlet.ParameterSetName) {
            'Access' {
                $ObjectFlags = $AccessRule.ObjectFlags
                $InheritedObjectType = $AccessRule.InheritedObjectType
            }

            'Audit' {
                $ObjectFlags = $AuditRule.ObjectFlags
                $InheritedObjectType = $AuditRule.InheritedObjectType
            }
        }
        if ($ObjectFlags.HasFlag([System.Security.AccessControl.ObjectAceFlags]::InheritedObjectAceTypePresent)) {

            if ($Script:DSACLClassGuid.ContainsKey($InheritedObjectType.ToString())) {
                return $Script:DSACLClassGuid[$InheritedObjectType.ToString()]
            }

            if ($Script:DSACLAttributeGuid.ContainsKey($InheritedObjectType.ToString())) {
                return $Script:DSACLAttributeGuid[$InheritedObjectType.ToString()]
            }

            return $InheritedObjectType.ToString()
        }
        else {
            return 'All'
        }

    }
}
#EndRegion '.\Public\ConvertFrom-DSACLInheritedObjectTypeGuid.ps1' 55
#Region '.\Public\ConvertFrom-DSACLObjectTypeGuid.ps1' 0
function ConvertFrom-DSACLObjectTypeGuid {
    [CmdletBinding(DefaultParameterSetName='Access')]
    param (
        [Parameter(Mandatory, ParameterSetName='Access', ValueFromPipeline)]
        [Alias('ACE')]
        [Alias('Access')]
        [System.DirectoryServices.ActiveDirectoryAccessRule]
        $AccessRule,

        [Parameter(Mandatory, ParameterSetName='Audit', ValueFromPipeline)]
        [Alias('Audit')]
        [System.DirectoryServices.ActiveDirectoryAuditRule]
        $AuditRule
    )

    begin {
        try {
            $null = Get-Variable -Name DSACLAttributeGuid -Scope Script -ErrorAction Stop
        }
        catch {
            $null = Register-DSACLRightsMapVariable -Scope Script
        }
    }

    process {
        switch($PSCmdlet.ParameterSetName) {
            'Access' {
                $ObjectFlags = $AccessRule.ObjectFlags
                $ActiveDirectoryRights = $AccessRule.ActiveDirectoryRights
                $ObjectType = $AccessRule.ObjectType
            }

            'Audit' {
                $ObjectFlags = $AuditRule.ObjectFlags
                $ActiveDirectoryRights = $AuditRule.ActiveDirectoryRights
                $ObjectType = $AuditRule.ObjectType
            }
        }
        if ($ObjectFlags.HasFlag([System.Security.AccessControl.ObjectAceFlags]::ObjectAceTypePresent)) {

            # Check for Extended Access Rights
            if ( $ActiveDirectoryRights.HasFlag([System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight) ) {
                if($Script:DSACLExtendedGuid.ContainsKey($ObjectType.ToString())) {
                    return $Script:DSACLExtendedGuid[$ObjectType.ToString()]
                }
            }

            # Validated (Self) and not WriteProperty, check in extended map
            if (
                -not $ActiveDirectoryRights.HasFlag([System.DirectoryServices.ActiveDirectoryRights]::WriteProperty) -and
                $ActiveDirectoryRights.HasFlag([System.DirectoryServices.ActiveDirectoryRights]::Self)
            ) {
                if ($Script:DSACLValidatedWriteGuid.ContainsKey($ObjectType.ToString())) {
                    return $Script:DSACLValidatedWriteGuid[$ObjectType.ToString()]
                }
            }

            # Search for Classes if AccessRight is CreateChild or DeleteChild
            if(
                $ActiveDirectoryRights.HasFlag([System.DirectoryServices.ActiveDirectoryRights]::CreateChild) -or
                $ActiveDirectoryRights.HasFlag([System.DirectoryServices.ActiveDirectoryRights]::DeleteChild)
            ) {
                if ($Script:DSACLClassGuid.ContainsKey($ObjectType.ToString())) {
                    return $Script:DSACLClassGuid[$ObjectType.ToString()]
                }
            }

            if(
                $ActiveDirectoryRights.HasFlag([System.DirectoryServices.ActiveDirectoryRights]::ReadProperty) -or
                $ActiveDirectoryRights.HasFlag([System.DirectoryServices.ActiveDirectoryRights]::WriteProperty)
            ) {
                # Search for Attribute-Sets
                if ($Script:DSACLPropertySetGuid.ContainsKey($ObjectType.ToString())) {
                    return $Script:DSACLPropertySetGuid[$ObjectType.ToString()]
                }

                # Search for Attributes
                if ($Script:DSACLAttributeGuid.ContainsKey($ObjectType.ToString())) {
                    return $Script:DSACLAttributeGuid[$ObjectType.ToString()]
                }
            }

            # TODO: Add more scenarios

            # Fallback to return guid
            return $ObjectType.ToString()
        }
        else {
            return 'All'
        }
    }
}
#EndRegion '.\Public\ConvertFrom-DSACLObjectTypeGuid.ps1' 92
#Region '.\Public\Get-DSACLDefaultContainer.ps1' 0
function Get-DSACLDefaultContainer {
    [CmdletBinding()]
    param (
        $DomainDN,
        [ValidateSet('Users','Computers')]
        [string]
        $Type = 'Computers'
    )
    if(-not $PSBoundParameters.ContainsKey('DomainDN')) {
        $LDAPFilter = '(objectclass=domain)'
    } else {
        $LDAPFilter = "(distinguishedname=$DomainDN)"
    }

    $Domains = Find-LDAPObject -LDAPFilter $LDAPFilter
    if($null -eq $Domains) {
        throw 'Domain not found, please specify correct DomainDN'
    }
    if($Domains.Count -gt 1) {
        throw 'More than one domain found, please specify DomainDN'
    }

    $Domains.wellknownobjects | Where-Object -FilterScript {$_ -match $Script:DefaultContainersPatternTable[$Type]} | ForEach-Object -Process {
        if($Matches.ContainsKey('DN')) {
            [DSACLDefaultContainerConfig]::new(
                'Default{0}Container' -f $Type,
                $Matches['DN'],
                $Matches['prefix'],
                $Domains.wellknownobjects.IndexOf($_),
                $Domains.distinguishedname
            )
        }
    }
}
#EndRegion '.\Public\Get-DSACLDefaultContainer.ps1' 34
#Region '.\Public\Get-DSACLMachineAccountQuota.ps1' 0
function Get-DSACLMachineAccountQuota  {
    [CmdletBinding()]
    param ()
    try {
        $DefaultNamingContextDN = Get-LdapObject -DistinguishedName RootDse | Select-Object -ExpandProperty defaultNamingContext
        $DefaultNamingContext = Get-LdapObject -DistinguishedName $DefaultNamingContextDN
        $DefaultNamingContext.'ms-DS-MachineAccountQuota'.Value
    } catch {
        throw
    }
}
#EndRegion '.\Public\Get-DSACLMachineAccountQuota.ps1' 11
#Region '.\Public\New-DSACLAccessRule.ps1' 0
<#
.Synopsis
   Create Access Control Entry for Active Directory ACL
.DESCRIPTION
   Create Access Control Entry for Active Directory ACL
.EXAMPLE
   New-ADAccessRule -Identity $SID -ActiveDirectoryRights 'CreateChild', 'DeleteChild' -AccessControlType Allow -ObjectType $TypeGuid -InheritanceType None
   Create access rule that gives the object with SID $SID access to create and delete objects of type $TypeGuid on "this object only"
#>

function New-DSACLAccessRule {
    [CmdletBinding()]
    param (
        # SID of principal that will rule will apply to
        [Parameter(Mandatory = $true, ParameterSetName = '1', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '2', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '3', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '4', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '5', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '6', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [System.Security.Principal.SecurityIdentifier]
        $Identity,

        # List of access rights that should be applied
        [Parameter(Mandatory = $true, ParameterSetName = '1', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '2', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '3', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '4', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '5', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '6', ValueFromPipelineByPropertyName = $true)]
        [System.DirectoryServices.ActiveDirectoryRights[]]
        $ActiveDirectoryRights,

        # Sets allow or deny
        [Parameter(Mandatory = $true, ParameterSetName = '1', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '2', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '3', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '4', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '5', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '6', ValueFromPipelineByPropertyName = $true)]
        [System.Security.AccessControl.AccessControlType]
        $AccessControlType,

        # Sets guid where access right should apply
        [Parameter(Mandatory = $true, ParameterSetName = '4', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '5', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '6', ValueFromPipelineByPropertyName = $true)]
        [Guid]
        $ObjectType,

        # Sets if and how this rule should be inherited
        [Parameter(Mandatory = $true, ParameterSetName = '2', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '3', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '5', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '6', ValueFromPipelineByPropertyName = $true)]
        [System.DirectoryServices.ActiveDirectorySecurityInheritance]
        $InheritanceType,

        # Sets guid of object types that should inherit this rule
        [Parameter(Mandatory = $true, ParameterSetName = '3', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '6', ValueFromPipelineByPropertyName = $true)]
        [Guid]
        $InheritedObjectType
    )
    process {
        switch ($PSCmdlet.ParameterSetName) {
            '1' {$ArgumentList = $Identity, $ActiveDirectoryRights, $AccessControlType}
            '2' {$ArgumentList = $Identity, $ActiveDirectoryRights, $AccessControlType, $InheritanceType}
            '3' {$ArgumentList = $Identity, $ActiveDirectoryRights, $AccessControlType, $InheritanceType, $InheritedObjectType}
            '4' {$ArgumentList = $Identity, $ActiveDirectoryRights, $AccessControlType, $ObjectType}
            '5' {$ArgumentList = $Identity, $ActiveDirectoryRights, $AccessControlType, $ObjectType, $InheritanceType}
            '6' {$ArgumentList = $Identity, $ActiveDirectoryRights, $AccessControlType, $ObjectType, $InheritanceType, $InheritedObjectType}
        }
        New-Object -TypeName System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $ArgumentList
    }
}
#EndRegion '.\Public\New-DSACLAccessRule.ps1' 75
#Region '.\Public\New-DSACLAuditRule.ps1' 0
<#
.Synopsis
   Create Access Control Entry for Active Directory ACL
.DESCRIPTION
   Create Access Control Entry for Active Directory ACL
.EXAMPLE
   New-ADAccessRule -Identity $SID -ActiveDirectoryRights 'CreateChild', 'DeleteChild' -AccessControlType Allow -ObjectType $TypeGuid -InheritanceType None
   Create access rule that gives the object with SID $SID access to create and delete objects of type $TypeGuid on "this object only"
#>

function New-DSACLAuditRule {
    [CmdletBinding()]
    param (
        # SID of principal that will rule will apply to
        [Parameter(Mandatory = $true, ParameterSetName = '1', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '2', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '3', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '4', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '5', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '6', ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [System.Security.Principal.SecurityIdentifier]
        $Identity,

        # List of access rights that should be applied
        [Parameter(Mandatory = $true, ParameterSetName = '1', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '2', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '3', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '4', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '5', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '6', ValueFromPipelineByPropertyName = $true)]
        [System.DirectoryServices.ActiveDirectoryRights[]]
        $ActiveDirectoryRights,

        # Sets allow or deny
        [Parameter(Mandatory = $true, ParameterSetName = '1', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '2', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '3', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '4', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '5', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '6', ValueFromPipelineByPropertyName = $true)]
        [System.Security.AccessControl.AuditFlags]
        $AuditFlags,

        # Sets guid where access right should apply
        [Parameter(Mandatory = $true, ParameterSetName = '4', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '5', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '6', ValueFromPipelineByPropertyName = $true)]
        [Guid]
        $ObjectType,

        # Sets if and how this rule should be inherited
        [Parameter(Mandatory = $true, ParameterSetName = '2', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '3', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '5', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '6', ValueFromPipelineByPropertyName = $true)]
        [System.DirectoryServices.ActiveDirectorySecurityInheritance]
        $InheritanceType,

        # Sets guid of object types that should inherit this rule
        [Parameter(Mandatory = $true, ParameterSetName = '3', ValueFromPipelineByPropertyName = $true)]
        [Parameter(Mandatory = $true, ParameterSetName = '6', ValueFromPipelineByPropertyName = $true)]
        [Guid]
        $InheritedObjectType
    )
    process {
        switch ($PSCmdlet.ParameterSetName) {
            '1' {$ArgumentList = $Identity, $ActiveDirectoryRights, $AuditFlags}
            '2' {$ArgumentList = $Identity, $ActiveDirectoryRights, $AuditFlags, $InheritanceType}
            '3' {$ArgumentList = $Identity, $ActiveDirectoryRights, $AuditFlags, $InheritanceType, $InheritedObjectType}
            '4' {$ArgumentList = $Identity, $ActiveDirectoryRights, $AuditFlags, $ObjectType}
            '5' {$ArgumentList = $Identity, $ActiveDirectoryRights, $AuditFlags, $ObjectType, $InheritanceType}
            '6' {$ArgumentList = $Identity, $ActiveDirectoryRights, $AuditFlags, $ObjectType, $InheritanceType, $InheritedObjectType}
        }
        New-Object -TypeName System.DirectoryServices.ActiveDirectoryAuditRule -ArgumentList $ArgumentList
    }
}
#EndRegion '.\Public\New-DSACLAuditRule.ps1' 75
#Region '.\Public\Register-DSACLRightsMapVariable.ps1' 0
function Register-DSACLRightsMapVariable {
    [CmdletBinding()]
    param(
        [Parameter(DontShow)]
        [String]
        $Scope = 'Global'
    )
    $rootDSE = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList 'LDAP://RootDSE'

    # Create empty hash-tables
    $ClassName = @{}
    $ClassGuid = @{}
    $AttributeName = @{}
    $AttributeGuid = @{}
    $ExtendedName = @{}
    $ExtendedGuid = @{}
    $ValidatedWriteName = @{}
    $ValidatedWriteGuid = @{}
    $PropertySetName = @{}
    $PropertySetGuid = @{}

    # Locate Classes
    $Params = @{
        SearchBase  = $rootDSE.SchemaNamingContext.Value
        LDAPFilter  = '(&(objectClass=classSchema)(schemaIDGUID=*))'
        Property  = @('lDAPDisplayName', 'schemaIDGUID')
    }
    Find-LDAPObject @Params | ForEach-Object {
        $ClassName[$_.lDAPDisplayName]=[System.GUID]$_.schemaIDGUID
        $ClassGuid[[string][guid]$_.schemaIDGUID]=$_.lDAPDisplayName
    }

    # Locate Attributes
    $Params = @{
        SearchBase  = $rootDSE.SchemaNamingContext.Value
        LDAPFilter  = '(&(objectClass=attributeSchema)(schemaIDGUID=*))'
        Property  = @('lDAPDisplayName', 'schemaIDGUID')
    }
    Find-LDAPObject @Params | ForEach-Object {
        $AttributeName[$_.lDAPDisplayName]=[System.GUID]$_.schemaIDGUID
        $AttributeGuid[[string][guid]$_.schemaIDGUID]=$_.lDAPDisplayName
    }

    # Info on AccessRights found here: https://docs.microsoft.com/en-us/windows/desktop/ad/creating-a-control-access-right
    # Locate Extended Rights
    $Params = @{
        SearchBase  = $rootDSE.ConfigurationNamingContext
        LDAPFilter  = '(&(objectclass=controlAccessRight)(rightsGUID=*)(validAccesses=256))'
        Property  = @('displayName','rightsGUID')
    }
    Find-LDAPObject @Params | ForEach-Object {
        $ExtendedName[$_.displayName]=[System.GUID]$_.rightsGUID
        $ExtendedGuid[$_.rightsGUID]=$_.displayName
    }

    # Locate Validated Writes
    $Params = @{
        SearchBase  = $rootDSE.ConfigurationNamingContext
        LDAPFilter  = '(&(objectclass=controlAccessRight)(rightsGUID=*)(validAccesses=8))'
        Property  = @('displayName','rightsGUID')
    }
    Find-LDAPObject @Params | ForEach-Object {
        $ValidatedWriteName[$_.displayName]=[System.GUID]$_.rightsGUID
        $ValidatedWriteGuid[$_.rightsGUID]=$_.displayName
    }

    # Locate Property Sets
    $Params = @{
        SearchBase  = $rootDSE.ConfigurationNamingContext
        LDAPFilter  = '(&(objectclass=controlAccessRight)(rightsGUID=*)(validAccesses=48))'
        Property  = @('displayName','rightsGUID')
    }
    Find-LDAPObject @Params | ForEach-Object {
        $PropertySetName[$_.displayName]=[System.GUID]$_.rightsGUID
        $PropertySetGuid[$_.rightsGUID]=$_.displayName
    }

    $(
        New-Variable -Scope $Scope -Name DSACLClassName -Value $ClassName -Description 'Maps Active Directory Class names to GUIDs' -Option ReadOnly -Force -PassThru
        New-Variable -Scope $Scope -Name DSACLClassGuid -Value $ClassGuid -Description 'Maps Active Directory Class GUIDs to names' -Option ReadOnly -Force -PassThru
        New-Variable -Scope $Scope -Name DSACLAttributeName -Value $AttributeName -Description 'Maps Active Directory Attribute names to GUIDs' -Option ReadOnly -Force -PassThru
        New-Variable -Scope $Scope -Name DSACLAttributeGuid -Value $AttributeGuid -Description 'Maps Active Directory Attribute GUIDs to names' -Option ReadOnly -Force -PassThru
        New-Variable -Scope $Scope -Name DSACLExtendedName -Value $ExtendedName -Description 'Maps Active Directory Extended Right names to GUIDs' -Option ReadOnly -Force -PassThru
        New-Variable -Scope $Scope -Name DSACLExtendedGuid -Value $ExtendedGuid -Description 'Maps Active Directory Extended Right GUIDs to names' -Option ReadOnly -Force -PassThru
        New-Variable -Scope $Scope -Name DSACLValidatedWriteName -Value $ValidatedWriteName -Description 'Maps Active Directory ValidatedWrite names to GUIDs' -Option ReadOnly -Force -PassThru
        New-Variable -Scope $Scope -Name DSACLValidatedWriteGuid -Value $ValidatedWriteGuid -Description 'Maps Active Directory ValidatedWrite GUIDs to names' -Option ReadOnly -Force -PassThru
        New-Variable -Scope $Scope -Name DSACLPropertySetName -Value $PropertySetName -Description 'Maps Active Directory Property Set names to GUIDs' -Option ReadOnly -Force -PassThru
        New-Variable -Scope $Scope -Name DSACLPropertySetGuid -Value $PropertySetGuid -Description 'Maps Active Directory Property Set GUIDs to names' -Option ReadOnly -Force -PassThru
    ) | Select-Object -Property Name, Description
}
#EndRegion '.\Public\Register-DSACLRightsMapVariable.ps1' 90
#Region '.\Public\Resolve-DSACLGuid.ps1' 0
function Resolve-DSACLGuid {
    [CmdletBinding()]
    param (
        [guid]$Guid
    )

    begin {
        try {
            $null = Get-Variable -Name DSACLAttributeGuid -Scope Script -ErrorAction Stop
        }
        catch {
            $null = Register-DSACLRightsMapVariable -Scope Script
        }
    }

    process {
        $Result = [ordered]@{}
        if($DSACLAttributeGuid.ContainsKey($Guid.ToString())) {
            $Result.Add('Attribute',$DSACLAttributeGuid[$Guid.ToString()])
        }
        if($DSACLClassGuid.ContainsKey($Guid.ToString())) {
            $Result.Add('Class',$DSACLClassGuid[$Guid.ToString()])
        }
        if($DSACLExtendedGuid.ContainsKey($Guid.ToString())) {
            $Result.Add('ExtendedRight',$DSACLExtendedGuid[$Guid.ToString()])
        }
        if($DSACLPropertySetGuid.ContainsKey($Guid.ToString())) {
            $Result.Add('PropertySet',$DSACLPropertySetGuid[$Guid.ToString()])
        }
        if($DSACLValidatedWriteName.ContainsKey($Guid.ToString())) {
            $Result.Add('ValidatedWrite',$DSACLValidatedWriteName[$Guid.ToString()])
        }
        [pscustomobject]$Result
    }

}
#EndRegion '.\Public\Resolve-DSACLGuid.ps1' 36
#Region '.\Public\Resolve-DSACLObjectName.ps1' 0
function Resolve-DSACLObjectName {
    [CmdletBinding()]
    param (
        [string]$Name
    )

    begin {
        try {
            $null = Get-Variable -Name DSACLAttributeGuid -Scope Script -ErrorAction Stop
        }
        catch {
            $null = Register-DSACLRightsMapVariable -Scope Script
        }
    }

    process {
        $Result = [ordered]@{}
        if($DSACLAttributeName.ContainsKey($Name)) {
            $Result.Add('Attribute',$DSACLAttributeName[$Name])
        }
        if($DSACLClassName.ContainsKey($Name)) {
            $Result.Add('Class',$DSACLClassName[$Name])
        }
        if($DSACLExtendedName.ContainsKey($Name)) {
            $Result.Add('ExtendedRight',$DSACLExtendedName[$Name])
        }
        if($DSACLPropertySetName.ContainsKey($Name)) {
            $Result.Add('PropertySet',$DSACLPropertySetName[$Name])
        }
        if($DSACLValidatedWriteName.ContainsKey($Name)) {
            $Result.Add('ValidatedWrite',$DSACLValidatedWriteName[$Name])
        }
        [pscustomobject]$Result
    }

}
#EndRegion '.\Public\Resolve-DSACLObjectName.ps1' 36
#Region '.\Public\Set-DSACLDefaultContainer.ps1' 0
function Set-DSACLDefaultContainer {
    [CmdletBinding()]
    param (
        [string]
        $DomainDN,

        [ValidateSet('Users','Computers')]
        [string]
        $Type = 'Computers',

        [string]
        $NewValue
    )

    $null = $PSBoundParameters.Remove('NewValue')
    $ContainerObject = Get-DSACLDefaultContainer @PSBoundParameters

    if($ContainerObject.Index -ge 0 ) {
        $FullNewValue = '{0}{1}' -f $ContainerObject.Prefix, $NewValue
        $DirectoryEntry = Get-LDAPObject -DistinguishedName $ContainerObject.DomainDN
        $DirectoryEntry.wellKnownObjects.RemoveAt($ContainerObject.Index)
        $null = $DirectoryEntry.wellKnownObjects.Add($FullNewValue)
        Set-DSACLObject -DirectoryEntry $DirectoryEntry
    } else {
        throw 'Failed to locate wellknown container.'
    }
}
#EndRegion '.\Public\Set-DSACLDefaultContainer.ps1' 27
#Region '.\Public\Set-DSACLMachineAccountQuota.ps1' 0
function Set-DSACLMachineAccountQuota  {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [int]
        $Quota
    )
    try {
        $DefaultNamingContextDN = Get-LdapObject -DistinguishedName RootDse | Select-Object -ExpandProperty defaultNamingContext
        $DefaultNamingContext = Get-LdapObject -DistinguishedName $DefaultNamingContextDN
        $DefaultNamingContext.'ms-DS-MachineAccountQuota' = $Quota
        Set-DSACLObject -DirectoryEntry $DefaultNamingContext
    } catch {
        throw
    }
}
#EndRegion '.\Public\Set-DSACLMachineAccountQuota.ps1' 16
#Region '.\Public\Set-DSACLOwner.ps1' 0
<#
.SYNOPSIS
Set owner on any object in Active Directory
 
#>

function Set-DSACLOwner {
    [CmdletBinding(SupportsShouldProcess)]
    [Alias('chown')]
    [Alias('setowner')]
    param (
        # DistinguishedName of object to modify ACL on.
        [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [String]
        $TargetDN,

        # DistinguishedName of group or user to give permissions to.
        [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
        [String]
        $OwnerDN
    )

    process {
        try {
            $Target = Get-LDAPObject -DistinguishedName $TargetDN -ErrorAction Stop
            $Owner = Get-LDAPObject -DistinguishedName $OwnerDN -ErrorAction Stop
            if($PSCmdlet.ShouldProcess($TargetDN,'Setting owner')) {
                Set-Owner -Target $Target -Owner $Owner.DistinguishedName
            }
        }
        catch {
            throw
        }
    }
}
#EndRegion '.\Public\Set-DSACLOwner.ps1' 34