Public/Get-ADUserPermission.ps1
function Get-ADUserPermission { <# .SYNOPSIS Retrieves permissions assigned to Active Directory users. .DESCRIPTION This function retrieves and displays the effective permissions assigned to Active Directory users. It can retrieve permissions for a single user specified by their distinguished name or process multiple users via pipeline input. The function handles both direct permissions and those inherited through group memberships. The permissions are retrieved using the .NET DirectoryServices namespace, which provides a comprehensive way to access the security descriptor of AD objects. .PARAMETER Identity Distinguished Name of the Active Directory user to query. This parameter accepts pipeline input by value or by property name. Use this to specify which user's permissions to retrieve. .PARAMETER TargetObject Distinguished Name of the Active Directory object to check permissions against. If not specified, the function retrieves permissions on all objects the user has access to. .PARAMETER IncludeInherited When specified, includes inherited permissions in the results. By default, only direct permissions are displayed. .PARAMETER ExcludeGenericAll When specified, excludes objects where the user has GenericAll permissions to reduce output volume. .EXAMPLE Get-ADUserPermission -Identity "CN=John Doe,OU=Users,DC=contoso,DC=com" Retrieves all direct permissions for the user John Doe in the contoso.com domain. .EXAMPLE Get-ADUserPermission -Identity "CN=John Doe,OU=Users,DC=contoso,DC=com" -IncludeInherited Retrieves both direct and inherited permissions for the user John Doe. .EXAMPLE Get-ADUser -Filter {Department -eq "IT"} | Get-ADUserPermission Retrieves permissions for all users in the IT department. .EXAMPLE Get-ADUserPermission -Identity "CN=John Doe,OU=Users,DC=contoso,DC=com" -TargetObject "OU=Sales,DC=contoso,DC=com" Retrieves permissions that John Doe has on the Sales OU. .INPUTS [Microsoft.ActiveDirectory.Management.ADUser] [String] .OUTPUTS [PSCustomObject] containing the following properties: - User: The user account name - TargetObject: The AD object the permission applies to - ObjectClass: The class of the target object - AccessType: The type of access (Allow/Deny) - Permission: The permission type (e.g., ReadProperty, WriteProperty, etc.) - IsInherited: Whether the permission is inherited - InheritedFrom: The object from which the permission is inherited (if applicable) .NOTES Used Functions: Name ║ Module/Namespace ═══════════════════════════════════════════╬══════════════════════════════ Get-ADObject ║ ActiveDirectory Get-ADUser ║ ActiveDirectory Get-FunctionDisplay ║ EguibarIT Test-IsValidDN ║ EguibarIT Write-Verbose ║ Microsoft.PowerShell.Utility Write-Debug ║ Microsoft.PowerShell.Utility Write-Error ║ Microsoft.PowerShell.Utility .NOTES Version: 1.1 DateModified: 22/May/2025 LastModifiedBy: Vicente Rodriguez Eguibar vicente@eguibar.com Eguibar IT http://www.eguibarit.com .LINK https://github.com/vreguibar/EguibarIT/blob/main/Public/Get-ADUserPermission.ps1 .COMPONENT Active Directory .ROLE Security Administrator .FUNCTIONALITY Permission Management #> [CmdletBinding( SupportsShouldProcess = $false, ConfirmImpact = 'Low', DefaultParameterSetName = 'Default' )] [OutputType([PSCustomObject[]])] param ( [Parameter( Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ValueFromRemainingArguments = $false, HelpMessage = 'Distinguished Name of the AD user', Position = 0, ParameterSetName = 'Default' )] [ValidateNotNullOrEmpty()] [ValidateScript( { Test-IsValidDN -ObjectDN $_ }, ErrorMessage = 'DistinguishedName provided is not valid! Please Check.' )] [Alias('DN', 'DistinguishedName')] [string]$Identity, [Parameter( Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, HelpMessage = 'Distinguished Name of the target AD object to check permissions against', Position = 1, ParameterSetName = 'Default' )] [ValidateScript( { Test-IsValidDN -ObjectDN $_ }, ErrorMessage = 'Target DistinguishedName provided is not valid! Please Check.' )] [string]$TargetObject, [Parameter( Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, HelpMessage = 'Include inherited permissions in the output', Position = 2, ParameterSetName = 'Default' )] [switch]$IncludeInherited, [Parameter( Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false, ValueFromRemainingArguments = $false, HelpMessage = 'Exclude objects where the user has GenericAll permissions', Position = 3, ParameterSetName = 'Default' )] [switch]$ExcludeGenericAll ) Begin { # Set strict mode Set-StrictMode -Version Latest # Display function header if variables exist if ($null -ne $Variables -and $null -ne $Variables.Header) { $txt = ($Variables.Header -f (Get-Date).ToString('dd/MMM/yyyy'), $MyInvocation.Mycommand, (Get-FunctionDisplay -HashTable $PsBoundParameters -Verbose:$False) ) Write-Verbose -Message $txt } #end if ############################## # Module imports Import-Module -Name ActiveDirectory -Force -ErrorAction Stop ############################## # Variables Definition # Create dictionary to map Active Directory rights to human-readable permissions [hashtable]$ADRightsMapping = @{ 'GenericRead' = 'Read' 'GenericWrite' = 'Write' 'GenericExecute' = 'Execute' 'GenericAll' = 'Full Control' 'ReadProperty' = 'Read Property' 'WriteProperty' = 'Write Property' 'CreateChild' = 'Create Child Object' 'DeleteChild' = 'Delete Child Object' 'ListObject' = 'List Contents' 'DeleteTree' = 'Delete Tree' 'ListContents' = 'List Contents' 'ExtendedRight' = 'Extended Right' 'Delete' = 'Delete' 'ReadControl' = 'Read Control' 'WriteDacl' = 'Write DACL' 'WriteOwner' = 'Take Ownership' 'AccessSystemSecurity' = 'Access System Security' 'Synchronize' = 'Synchronize' 'CreateDirectoryService' = 'Create Directory Service' 'CreateDirectoryServiceObject' = 'Create Directory Service Object' 'DeleteDirectoryServiceObject' = 'Delete Directory Service Object' 'ReadDirectoryServiceData' = 'Read Directory Service Data' 'WriteDirectoryServiceData' = 'Write Directory Service Data' 'ControlDirectoryServiceAccess' = 'Control Directory Service Access' } # Create a hashtable for splatting [hashtable]$Splat = [hashtable]::New([StringComparer]::OrdinalIgnoreCase) # Create an array to store results [System.Collections.ArrayList]$Results = @() Write-Verbose -Message 'Starting process' } #end Begin Process { try { # Get user object from AD $UserObject = Get-ADUser -Identity $Identity -Properties ObjectSID, SamAccountName -ErrorAction Stop Write-Debug -Message ('Found user: {0}' -f $UserObject.SamAccountName) # Get groups the user is a member of (direct and nested) $Groups = Get-ADGroup -LDAPFilter "(member:1.2.840.113556.1.4.1941:=$($UserObject.DistinguishedName))" -Properties ObjectSID Write-Debug -Message ('User is a member of {0} groups' -f $Groups.Count) # Build collection of security principals to check permissions for [System.Collections.ArrayList]$SecurityPrincipals = @() [void]$SecurityPrincipals.Add($UserObject) if ($null -ne $Groups) { foreach ($Group in $Groups) { [void]$SecurityPrincipals.Add($Group) } } # If TargetObject is specified, only check that object if ($PSBoundParameters.ContainsKey('TargetObject')) { Write-Verbose -Message ('Checking permissions on target object: {0}' -f $TargetObject) $TargetObjects = Get-ADObject -Identity $TargetObject -Properties nTSecurityDescriptor -ErrorAction Stop } else { # Otherwise search the entire domain for objects with ACEs for the user or their groups Write-Verbose -Message 'Searching for objects with ACEs for the user or their groups' # Build LDAP filter for security principal SIDs [System.Text.StringBuilder]$LDAPFilter = [System.Text.StringBuilder]::new() [void]$LDAPFilter.Append('(|') foreach ($Principal in $SecurityPrincipals) { [void]$LDAPFilter.Append("(nTSecurityDescriptor:1.2.840.113556.1.4.803:=*$($Principal.ObjectSID)*)") } [void]$LDAPFilter.Append(')') Write-Debug -Message ('LDAP filter: {0}' -f $LDAPFilter.ToString()) # Get objects with matching security descriptors $TargetObjects = Get-ADObject -LDAPFilter $LDAPFilter.ToString() -Properties nTSecurityDescriptor -ResultSetSize 5000 Write-Verbose -Message ('Found {0} objects with ACEs for the user or their groups' -f $TargetObjects.Count) } # Process each target object foreach ($Target in $TargetObjects) { Write-Debug -Message ('Processing target object: {0}' -f $Target.DistinguishedName) # Get security descriptor $SecurityDescriptor = $Target.nTSecurityDescriptor # Check DACL entries if ($null -ne $SecurityDescriptor -and $null -ne $SecurityDescriptor.DiscretionaryAcl) { foreach ($Ace in $SecurityDescriptor.DiscretionaryAcl) { # Check if ACE applies to one of our security principals $AceSid = $Ace.SecurityIdentifier $MatchingPrincipal = $SecurityPrincipals | Where-Object { $_.ObjectSID.Value -eq $AceSid.Value } if ($null -ne $MatchingPrincipal) { # Skip inherited permissions if not requested if (-not $IncludeInherited -and $Ace.IsInherited) { continue } # Skip GenericAll if requested if ($ExcludeGenericAll -and $Ace.ActiveDirectoryRights -like '*GenericAll*') { continue } # Map permissions to human-readable format [System.Collections.ArrayList]$MappedPermissions = @() foreach ($Right in $ADRightsMapping.Keys) { if ($Ace.ActiveDirectoryRights -like "*$Right*") { [void]$MappedPermissions.Add($ADRightsMapping[$Right]) } } # If no mapped permissions found, use the raw value if ($MappedPermissions.Count -eq 0) { [void]$MappedPermissions.Add($Ace.ActiveDirectoryRights.ToString()) } # Create result object $ResultObject = [PSCustomObject]@{ User = $UserObject.SamAccountName TargetObject = $Target.DistinguishedName ObjectClass = $Target.ObjectClass AccessType = $Ace.AccessControlType.ToString() Permission = ($MappedPermissions -join ', ') IsInherited = $Ace.IsInherited InheritedFrom = if ($Ace.IsInherited) { $Ace.GetInheritanceSource() } else { 'N/A' } AppliesVia = if ($MatchingPrincipal.ObjectClass -eq 'user') { 'Direct' } else { "Group: $($MatchingPrincipal.SamAccountName)" } } # Add to results [void]$Results.Add($ResultObject) } } } } } catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { Write-Error -Message ('Identity not found: {0}' -f $Identity) } catch [System.UnauthorizedAccessException] { Write-Error -Message ('Access denied when querying permissions: {0}' -f $_.Exception.Message) } catch { Write-Error -Message ('Error: {0}' -f $_.Exception.Message) } #end Try-Catch } #end Process End { # Return the results $Results # Display function footer if variables exist if ($null -ne $Variables -and $null -ne $Variables.Footer) { $txt = ($Variables.Footer -f $MyInvocation.InvocationName, 'processing AD user permissions.' ) Write-Verbose -Message $txt } #end if } #end End } #end function Get-ADUserPermission |