Get-DsAcl

The goal of this PowerShell function is to create a report of permissions assigned to objects in Active Directory in much the same way as DsRevoke.

The names for extended rights and object types are read from the schema allowing the script to display friendly names.

Properties

Property Description
Name The name of the Object; by default this will be an OU
DN The DN of the Object; equivalent to Object (DsRevoke)
ObjectClass Object class
SecurityPrincipal The User or Group (or Computer) the Access Control Entry is for; used as the search using DsRevoke
AccessType Allow or Deny; equivalent to ACE Type
Permissions Same as Permissions list in DsRevoke
AppliesTo What the ACE applies to; equivalent to "ACE inherited by all child objects" entry
AppliesToObjectType The object class the ACE applies to; equivalent to "ACE inherited by all child objects of Class ..."
AppliesToProperty The specific property or Property Set an ACE applies to, or the Extended Right an ACE grants (if defined)
Inherited True if the Access Control Entry was inherited. Only applies with -Inherited switch.

Get-DsAcl

function Get-DsAcl {
    <#
    .SYNOPSIS
        Get directory service access control lists from Active Directory.
    .DESCRIPTION
        Get-DsAcl uses ADSI to retrieve the security descriptor from an account. Descriptions of extended attributes are read from the Schema.
    #>

    [CmdletBinding(DefaultParameterSetName = 'ByObjectClass')]
    param (
          [String]$SearchBase,
          
          [Parameter(ParameterSetName = 'ByObjectClass')]
          [String]$ObjectType = "organizationalUnit",
          
          [Parameter(ParameterSetName = 'UsingLdapFilter')]
          [String]$LdapFilter = "(&(objectClass=$ObjectType)(objectCategory=$ObjectType))",
          
          [Switch]$Inherited
    )
    
    $rootDSE = [ADSI]'LDAP://RootDSE'
    $schema = [ADSI]('LDAP://{0}' -f $rootDSE.Get('schemaNamingContext'))
    $extendedRights = [ADSI]('LDAP://CN=Extended-Rights,{0}' -f $rootDSE.Get('configurationNamingContext'))
    
    $searcher = [ADSISearcher]$LdapFilter
    if ($SearchBase) {
        $searcher.SearchRoot = [ADSI]('LDAP://{0}' -f $SearchBase)
    }
    $searcher.PageSize = 1000
    $searcher.FindAll() | ForEach-Object {
        $Object = $_.GetDirectoryEntry()
        
        # Retrieve all Access Control Entries from the AD Object
        $acl = $Object.PsBase.ObjectSecurity.GetAccessRules($true, $Inherited, [Security.Principal.NTAccount])
        
        $ACL | Select-Object @{n='Name';e={ $Object.Get("name") }},
            @{n='DN';e={ $Object.Get("distinguishedName") }},
            @{n='ObjectClass';e={ $Object.Class }},
            @{n='SecurityPrincipal';e={ $_.IdentityReference.ToString() }}, 
            @{n='AccessType';e={ $_.AccessControlType }},
            @{n='Permissions';e={ $_.ActiveDirectoryRights }},
            @{n='AppliesTo';e={
                # Change the values for InheritanceType to friendly names
                switch ($_.InheritanceType) {
                    "None"            { "This object only" }
                    "Descendents"     { "All child objects" }
                    "SelfAndChildren" { "This object and one level Of child objects" }
                    "Children"        { "One level of child objects" }
                    "All"             { "This object and all child objects" }
                }
            }},
            @{n='AppliesToObjectType';e={
                if ($_.InheritedObjectType.ToString() -notmatch "0{8}.*") {
                    # Search for the Object Type in the Schema
                    $LdapFilter = "(SchemaIDGUID=$(($_.InheritedObjectType.ToByteArray() | ForEach-Object { '{0:X2}' -f $_ }) -join '')"
                    
                    $Result = (New-Object DirectoryServices.DirectorySearcher( $Schema, $LdapFilter)).FindOne()
                    $Result.Properties["ldapdisplayname"]
                } else {
                    "All"
                }
            }},
            @{n='AppliesToProperty';e={
                if ($_.ObjectType.ToString() -notmatch "0{8}.*") {
                    # Search for a possible Extended-Right or Property Set
                    $LdapFilter = "(rightsGuid=$($_.ObjectType.ToString()))"
                    $Result = (New-Object DirectoryServices.DirectorySearcher( $ExtendedRights, $LdapFilter)).FindOne()
                    
                    if ($Result) {
                        $Result.Properties["displayname"]
                    } else {
                        # Search for the attribute name in the Schema
                        $LdapFilter = "(SchemaIDGUID=$(($_.ObjectType.ToByteArray() | ForEach-Object { '{0:X2}' -f $_ }) -join ''))"
                        
                        $Result = (New-Object DirectoryServices.DirectorySearcher( $Schema, $LdapFilter)).FindOne()
                        $Result.Properties["ldapdisplayname"]
                    }
                } else {
                    "All"
                }
            }},
            @{n='Inherited';e={ $_.IsInherited }}
        }
}

Usage examples

Reporting on a sub-OU:

Get-DsAcl "OU=Test,DC=domain,DC=com" # Or Get-DsAcl -SearchRoot "OU=Test,DC=domain,DC=com"

Reporting on contacts in a sub-OU:

Get-DsAcl "OU=Test,DC=domain,DC=com" -ObjectType contact

Using a custom LDAP filter:

Get-DsAcl -LdapFilter "(objectClass=*)"

Including Inherited entries:

Get-DsAcl -LdapFilter "(name=Chris Dent)" -Inherited