A PowerShell script to document all attributes of a specified Active Directory object. The script first documents all mandatory attributes for the class of the object, then all optional attributes. The script documents each attribute lDAPDisplayName, the syntax, if it is multi-valued, if it is operational (also called constructed), and the value or values assigned. If the attribute has no value assigned for the object, this is indicated.

The object is identified by distinguished name. Either pass the distinguished name in quotes as a parameter to the script, or the script will prompt for the distinguished name. For example, to document a user:

 

PowerShell
Edit|Remove
DocumentADAttributes.ps1 "cn=Jim Smith,ou=Sales,ou=West,dc=domain,dc=com"
 

The object can be in any naming context. For example, to document a site link:

 

 

PowerShell
Edit|Remove
DocumentADAttributes.ps1 "cn=West-East,cn=IP,cn=Inter-Site Transports,cn=Sites,cn=Configuration,dc=domain,dc=com"
 

The output can be large. It is best to redirect the output to a text file.

 

Notes:

  1. For each attribute the script outputs the attribute lDAPDisplayName, the attribute syntax, and the value.
  2. If the attribute has no value assigned in AD, the value is displayed as "<not set>".
  3. If the attribute is multi-valued, the syntax has the string "[]" appended at the end. For example "OctetString[]"
  4. Multi-valued attributes have each value displayed on a separate line.
  5. If the attribute is operational (also called constructed), the script displays the syntax with the string "<operational> appended at the end. For example "DirectoryString <operational>" or "Oid[] <operational>".
  6. The values of multi-valued Sid attributes that are operational are retrieved using extra code. In addition, the Translate method is used to display the corresponding sAMAccountName in parentheses. An example is the tokenGroups attribute of a security principal.
  7. Integer values are displayed with culture-specific separators (commas or periods) separating digits.
  8. Large Integers (64-bit) are converted into DateTime or TimeSpan values, as appropriate.
  9. Byte arrays that are more than 100 bytes long are not displayed. Instead the number of bytes is shown.
  10. Byte arrays less than 100 bytes are displayed as space delimited decimal values.
  11. The exception is byte arrays for the following attributes, which are converted into friendly formats: all GUIDs, all SIDs, logonHours, schedule.
  12. The logonHours attribute (if a value is assigned) is displayed on 7 lines, one for each day of the week. A pattern of 24 bits each day is displayed where a "1" means the user is allowed to logon, and a "0" means they are not. The values are in the time zone of the local computer.
  13. The schedule attribute is displayed on 7 lines, one for each day of the week. Each line displays 24 hours. For each hour the output shows if replication is scheduled during any of the 15-minute intervals in the hour. A "Y" means replication is scheduled, "n" means it is not. An hour will be similar to "YYnn". The values are in the time zone of the local computer.
  14. The following "flag" attributes are interpreted to display what the value means: userAccountControl, msDS-User-Account-Control-Constructed, groupType, searchFlags, systemFlags, sAMAccountType, instanceType

The PowerShell code follows (or it can be downloaded above).

 

PowerShell
Edit|Remove
# DocumentADAttributes.ps1 
# A PowerShell script to document all attributes of a specified Active Directory object. 
# Author: Richard L. Mueller 
# Version 1.0 - January 302019 
# Version 2.0 - February 82019 - Document multi-valued SID syntax operational attributes. 
 
If ($Args.Count -eq 1{ 
    # Retrieve the Active Directory object distinguished name from the command line. 
    $ObjectDN = $Args[0} 
ElseIf ($Args.Count -gt 1{ 
    Write-Host "Only one command line parameter, an object distinguished name, is allowed." ` 
         -ForegroundColor Red -BackgroundColor Black 
    Write-Host "Script aborted." -ForegroundColor Red -BackgroundColor Black 
    Break 
} 
Else 
{ 
    # Prompt for the Active Directory object distinguished name. 
    $ObjectDN = Read-Host "Enter the AD object distinguished name" 
} 
 
# Bind to the Active Directory object specified. 
$ADObject = [ADSI]"LDAP://$ObjectDN" 
 
If (-Not $ADObject.Name) 
{ 
    # Object not found. 
    Write-Host "Error: Active Directory object $ObjectDN not found." ` 
        -ForegroundColor Red -BackgroundColor Black 
    Write-Host "Script aborted." -ForegroundColor Red -BackgroundColor Black 
    Break 
} 
 
# Determine the Naming Context for the object. 
$Domain = [System.DirectoryServices.DirectoryEntry]([ADSI]"LDAP://rootDSE") 
$NCs = $Domain.Get("NamingContexts") 
 
# The correct Naming Context will be the longest one that matches the specified DN. 
$Max = 0 
ForEach ($NC In $NCs) 
{ 
    If ($ObjectDN -Like "*$NC") 
    { 
        $Length = $NC.Length 
        If ($Length -gt $Max) 
        { 
            $Max = $Length 
            $Base = $NC 
        } 
    } 
} 
 
# Search the naming context. 
$Searcher = New-Object System.DirectoryServices.DirectorySearcher 
$Searcher.SearchScope = "subtree" 
$Searcher.SearchRoot = "LDAP://$Base" 
 
# Retrieve local Time Zone bias from machine registry in hours. 
# This bias does not change with Daylight Savings Time. 
# This bias is used with the logonHours attribute. 
$LHBias = [Math]::Round((Get-ItemProperty ` 
    -Path HKLM:\System\CurrentControlSet\Control\TimeZoneInformation).Bias/60, ` 
    0, [MidpointRounding]::AwayFromZero) 
 
# Retrieve local Time Zone bias from machine registry in hours. 
# This bias changes with Daylight Savings Time. 
# This bias is used with the schedule attribute. 
$SchedBias = (Get-ItemProperty -Path ` 
    HKLM:\System\CurrentControlSet\Control\TimeZoneInformation).ActiveTimeBias 
# Account for negative bias. 
If ($SchedBias -gt 10080){$SchedBias = $SchedBias - 4294967296} 
$SchedBias = [Math]::Round($SchedBias/600, [MidpointRounding]::AwayFromZero) 
 
Function OctetToGUID ($Octet) 
{ 
    # Function to convert Octet value (byte array) into string GUID value. 
    $GUID = [GUID]$Octet 
    Return $GUID.ToString("B"} 
 
Function HexToGUID ($Hex) 
{ 
    If ($Hex.Length -eq 32) 
    { 
    # Function to convert hex string into string GUID value. 
        $GUID = "{" + $Hex.SubString(6,2) + $Hex.SubString(4,2) + $Hex.SubString(2,2) ` 
            + $Hex.SubString(0,2) + "-" + $Hex.SubString(10,2) + $Hex.SubString(8,2) + "-" ` 
            + $Hex.SubString(14,2) + $Hex.SubString(12,2) + "-" + $Hex.SubString(16,2) ` 
            + $Hex.SubString(18,2) + "-" + $Hex.SubString(20,12) + "}" 
    } 
    Else {$GUID = "{" + $Hex + "}"} 
    Return $GUID.ToLower() 
} 
 
# Create an array of 168 bytes, representing the hours in a week, 
# for the logonHours attribute. 
$LH = New-Object 'object[]' 168 
 
Function OctetToHours ($Octet) 
{ 
    # Function to convert Octet value (byte array) into binary string 
    # representing logonHours attribute. The 168 bits represent 24 hours 
    # per day for 7 days, Sunday through Saturday. The values are converted 
    # into local time. If the bit is "1", the user is allowed to logon 
    # during that hour. If the bit is "0", the user is not allowed to logon. 
    # Modified to display each day on a separate line. 
    For ($j = 0; $j -le 20; $j = $j + 1) 
    { 
        For ($k = 7; $k -ge 0; $k = $k - 1) 
        { 
            $m = 8*$j + $k - $LHBias 
            If ($m -lt 0{$m = $m + 168} 
            If ($Octet[$j] -band [Math]::Pow(2, $k)) {$LH[$m] = "1"} 
            Else {$LH[$m] = "0"} 
        } 
    } 
    $Hours = "" 
    For ($j = 0; $j -le 6; $j = $j + 1) 
    { 
        For ($k = 0; $k -le 2; $k = $k + 1) 
        { 
            $n = 8*($j + $k) 
            If ($k -eq 0{$Hours =  $Hours + [String]::Join("", $LH[$n..($n + 7)])} 
            Else {$Hours = $Hours + "-" + [String]::Join("", $LH[$n..($n + 7)])} 
            If (($k -eq 2) -And ($j -ne 6)) {$Hours = $Hours + "`r`n                            "} 
        } 
    } 
    Return $Hours 
} 
 
Function OctetToSchedule ($Sched) 
{ 
    # Function to document the schedule attribute of a connection object. 
    # Ignore first 20 bytes of schedule array (index 0 through 19). 
    # This is a header line that is always the same. 
    $Start = 20 
 
    # Loop through the last 168 bytes, from index 20 to 187. These bytes 
    # represent the 168 hours in a week, Sunday through Saturday, in UTC. 
    # Loop first through each day of the week (index 0 through 6). 
    $Output = "" 
    For ($j = 0; $j -le 6; $j = $j + 1) 
    { 
        # Line to be output for this day of the week. 
        If ($j -eq 0{$Line = ""} 
        Else {$Line = "`r`n                          "} 
        $L = "" 
        $First = $True 
 
        # Loop through the 24 hours in the day (index 0 through 23). 
        For ($k = 0; $k -le 23; $k = $k + 1) 
        { 
            # Adjust for time zone bias. The values in the schedule attribute are 
            # in UTC, so they must be adjusted for the local time zone bias. 
            # $m is the index into the byte array $Sched. 
            $m = $start + $k + $SchedBias 
 
            # If the time zone adjusted index is less than 20, add 168. 
            If ($m -lt 20{$m = $m + 168} 
            # If the time zone adjusted index is greater than 187, subtract 168. 
            If ($m -gt 187{$m = $m - 168} 
 
            # Each byte indicates when replication is scheduled in the hour. 
            # Default output indicating which 15 minute interval 
            # in each hour when replication scheduled. 
            Switch ($Sched[$m]) 
            { 
                0 {$X = "nnnn"} 
                1 {$X = "Ynnn"} 
                2 {$X = "nYnn"} 
                3 {$X = "YYnn"} 
                4 {$X = "nnYn"} 
                5 {$X = "YnYn"} 
                6 {$X = "nYYn"} 
                7 {$X = "YYYn"} 
                8 {$X = "nnnY"} 
                9 {$X = "YnnY"} 
                10 {$X = "nYnY"} 
                11 {$X = "YYnY"} 
                12 {$X = "nnYY"} 
                13 {$X = "YnYY"} 
                14 {$X = "nYYY"} 
                15 {$X = "YYYY"} 
            } # End Switch statement for default output. 
            # Construct the line for this day. 
            $Line = "$Line$L$X" 
            If ($First) 
            { 
                $First = $False 
                $L = " " 
            } 
        } # End of loop for the 24 hours in a day. 
 
        # Output for this day. 
        $Output = "$Output$Line" 
 
        # Advance 24 hours (bytes) to the next day. 
        $Start = $Start + 24 
    } # End of loop for the 7 days in a week. 
    Return $Output 
} 
 
Function UAC ($Flag) 
{ 
    # Function to evaluate the userAccountControl 
    # and msDS-User-Account-Control-Computed attributes. 
    $Setting = "" 
    If ($Flag -band 0x02) {$Setting = $Setting + "AccountDisabled "} 
    If ($Flag -band 0x08) {$Setting = $Setting + "HomeDirReqd "} 
    If ($Flag -band 0x10) {$Setting = $Setting + "LockedOut "} 
    If ($Flag -band 0x20) {$Setting = $Setting + "PwdNotReqd "} 
    If ($Flag -band 0x40) {$Setting = $Setting + "PwdCannotChg "} 
    If ($Flag -band 0x80) {$Setting = $Setting + "EncryptedTextPwdAllowed "} 
    If ($Flag -band 0x100) {$Setting = $Setting + "TempDuplAccount "} 
    If ($Flag -band 0x200) {$Setting = $Setting + "NormalAccount "} 
    If ($Flag -band 0x800) {$Setting = $Setting + "InterdomnainTrustAcct "} 
    If ($Flag -band 0x1000) {$Setting = $Setting + "WorkstationTrustAcct "} 
    If ($Flag -band 0x2000) {$Setting = $Setting + "ServerTrustAcct "} 
    If ($Flag -band 0x10000) {$Setting = $Setting + "PwdDoesNotExpire "} 
    If ($Flag -band 0x20000) {$Setting = $Setting + "MNSLogonAcct "} 
    If ($Flag -band 0x40000) {$Setting = $Setting + "SmartcardReqd "} 
    If ($Flag -band 0x80000) {$Setting = $Setting + "TrustedForDelgation "} 
    If ($Flag -band 0x100000) {$Setting = $Setting + "NotDelegated "} 
    If ($Flag -band 0x200000) {$Setting = $Setting + "UseDESKeyOnly "} 
    If ($Flag -band 0x400000) {$Setting = $Setting + "RequirePreauth "} 
    If ($Flag -band 0x800000) {$Setting = $Setting + "PwdExpired "} 
    If ($Flag -band 0x1000000) {$Setting = $Setting + "TrustedToAuthForDelegation "} 
    If ($Flag -band 0x4000000) {$Setting = $Setting + "PartialSecretsAcct "} 
    If ($Flag -band 0x8000000) {$Setting = $Setting + "UseAESKeysOnly "} 
    Return " (" + $Setting.Trim() + ")" 
} 
 
Function GroupType ($Flag) 
{ 
    $GT = "" 
    # Function to retrieve group type from the groupType attribute. 
    If ($Flag -band 0x01) {$GT = $GT + "Built-in "} 
    If ($Flag -band 0x02) {$GT = $GT + "Global "} 
    If ($Flag -band 0x04) {$GT = $GT + "Local "} 
    If ($Flag -band 0x08) {$GT = $GT + "Universal "} 
    If ($Flag -band 0x10) {$GT = $GT + "APP_BASIC "} 
    If ($Flag -band 0x20) {$GT = $GT + "APP_QUERY "} 
    If ($Flag -band 0x80000000) {$GT = $GT.Trim() + "/Security"} 
    Else {$GT = $GT.Trim() + "/Distribution"} 
    Return " ($GT)" 
} 
 
Function SearchFlags ($Flag) 
{ 
    $SF= "" 
    # Function to evaluate the searchFlags attribute. 
    If ($Flag -band 0x01) {$SF = $SF + "Indexed "} 
    If ($Flag -band 0x02) {$SF = $SF + "IndexedEachContainer "} 
    If ($Flag -band 0x04) {$SF = $SF + "InANRSet "} 
    If ($Flag -band 0x08) {$SF = $SF + "PreservedInTombstone "} 
    If ($Flag -band 0x10) {$SF = $SF + "CopiedWhenObjectCopied "} 
    If ($Flag -band 0x20) {$SF = $SF + "TupleIndex "} 
    If ($Flag -band 0x40) {$SF = $SF + "VLVIndex "} 
    Return " (" + $SF.Trim() + ")" 
} 
 
Function SystemFlags ($Flag) 
{ 
    $SysF = "" 
    # Function to evaluate the systemFlags attribute. 
    If ($Flag -band 0x01) {$SysF = $SysF + "AttrReplicated/NTDSCrossRefObj "} 
    If ($Flag -band 0x02) {$SysF = $SysF + "ReplToGC/DomainCrossRefObj "} 
    If ($Flag -band 0x04) {$SysF = $SysF + "AttrConstructed "} 
    If ($Flag -band 0x10) {$SysF = $SysF + "AttrInBaseSchema "} 
    If ($Flag -band 0x02000000) {$SysF = $SysF + "DelImmediately "} 
    If ($Flag -band 0x04000000) {$SysF = $SysF + "CannotBeMoved "} 
    If ($Flag -band 0x08000000) {$SysF = $SysF + "CannotBeRenamed "} 
    If ($Flag -band 0x10000000) {$SysF = $SysF + "CanBeMovedWithRestrictions "} 
    If ($Flag -band 0x20000000) {$SysF = $SysF + "CanBeMoved "} 
    If ($Flag -band 0x40000000) {$SysF = $SysF + "CanBeRenamed "} 
    If ($Flag -band 0x80000000) {$SysF = $SysF + "CannotBeDeleted "} 
    Return " (" + $SysF.Trim() + ")" 
} 
 
Function SAMType ($Flag) 
{ 
    # Function to evaluate the sAMAccountType attribute. 
    Switch ($Flag) 
    { 
        0x10000000 {$ST = "GroupObject"} 
        0x10000001 {$ST = "NonSecurityGroupObject"} 
        0x20000000 {$ST = "AliasObject"} 
        0x20000001 {$ST = "NonSecurityAliasObject"} 
        0x30000000 {$ST = "UserAccount"} 
        0x30000001 {$ST = "MachineAccount"} 
        0x30000002 {$ST = "TrustAccount"} 
        0x40000000 {$ST = "AppBasicAccount"} 
        0x40000001 {$ST = "AppQueryAccount"} 
    } 
    Return " ($ST)" 
} 
 
Function InstanceType ($Flag) 
{ 
    $IT= "" 
    # Function to evaluate the searchFlags attribute. 
    If ($Flag -band 0x01) {$IT = $IT + "NCHead "} 
    If ($Flag -band 0x02) {$IT = $IT + "ReplicaNotInstantiated "} 
    If ($Flag -band 0x04) {$IT = $IT + "Writeable "} 
    If ($Flag -band 0x08) {$IT = $IT + "NCAboveHeld "} 
    If ($Flag -band 0x10) {$IT = $IT + "NCBeingConstructed "} 
    If ($Flag -band 0x20) {$IT = $IT + "NCBeingRemoved "} 
    Return " (" + $IT.Trim() + ")" 
} 
 
# Determine the object class. 
$Schema = [DirectoryServices.ActiveDirectory.ActiveDirectorySchema]::GetCurrentSchema() 
 
$Class = $ADObject.objectClass.ToString().Split(" ")[-1"Object DN: $ObjectDN" 
"Object class: $Class" 
 
# Filter on the object. 
$Searcher.Filter = "(distinguishedName=$ObjectDN)" 
 
# Retrieve attributes for this class from the Schema. 
$ManAttributes = $Schema.FindClass("$Class").MandatoryProperties | Select Name, Syntax, IsSingleValued, CommonName 
$OptAttributes = $Schema.FindClass("$Class").OptionalProperties | Select Name, Syntax, IsSingleValued, CommonName 
 
$ManCount = $ManAttributes.Count 
$OptCount = $OptAttributes.Count 
 
ForEach ($Attribute In $ManAttributes) 
{ 
    # It is assumed that no mandatory attributes are multi-valued Sid syntax and operational. 
    $AttrName = $Attribute.Name 
    $Searcher.PropertiesToLoad.Add($AttrName) > $Null 
} 
 
$Result = $Searcher.FindOne() 
 
"Mandatory Attributes ($ManCount)" 
ForEach ($Attribute In $ManAttributes) 
{ 
    $AttrName = $Attribute.Name 
    $AttrSyntax = $Attribute.Syntax 
    $Single = $Attribute.IsSingleValued 
    If (-Not $Single) {$AttrSyntax = "$AttrSyntax[]"} 
    $Name = $Attribute.CommonName 
    # Retrieve systemFlags attribute of the attribute. 
    $Attr = [ADSI]"LDAP://cn=$Name,$Schema" 
    $Flags = $Attr.psbase.Properties.Item("systemFlags")[0] 
    If ($Flags -band 4{$AttrSyntax = "$AttrSyntax <operational>"} 
    $Values = $Result.Properties.Item($AttrName) 
    If ($Values[0] -eq $Null) 
    { 
        "  $AttrName ($AttrSyntax): <not set>" 
    } 
    Else 
    { 
        ForEach ($Value In $Values) 
        { 
            Switch ($Value.GetType().Name) 
            { 
                "Int64" 
                { 
                    If (($Value -ge [TimeSpan]::MaxValue.Ticks) ` 
                        -Or ($Value -le [TimeSpan]::MinValue.Ticks)) 
                    { 
                        "  $AttrName ($AttrSyntax): <never>" 
                    } 
                    Else 
                    { 
                        If (($Value -gt 120000000000000000) ` 
                            -And ($Value -le [DateTime]::MaxValue.Ticks)) 
                        { 
                            $Date = [DateTime]$Value 
                            "  $AttrName ($AttrSyntax): " + '{0:n0}' -f $Value ` 
                                + "(" + $Date.AddYears(1600).ToLocalTime() + ")" 
                        } 
                        Else 
                        { 
                            If ($Value -lt 0) 
                            { 
                                $Span = [TimeSpan](-$Value) 
                                "  $AttrName ($AttrSyntax): " + '{0:n0}' -f $Value ` 
                                    + "($Span [Days.Hours:Minutes:Seconds])" 
                            } 
                            Else {"  $AttrName ($AttrSyntax): " + '{0:n0}' -f $Value} 
                        } 
                    } 
                } 
                "Byte[]" 
                { 
                    If (($Value.Length -eq 16) ` 
                        -And ($AttrName.ToUpper().Contains("GUID") -eq $True)) 
                    { 
                        "  $AttrName ($AttrSyntax): " + $(OctetToGUID($Value)) 
                    } 
                    Else 
                    { 
                        If (($Value.Length -eq 21) -And ($AttrName -eq "logonHours")) 
                        { 
                            "  $AttrName ($AttrSyntax): " + $(OctetToHours($Value)) 
                        } 
                        Else 
                        { 
                            If (($Value[0] -eq 1) -And (` 
                                (($Value[1] -eq 1) -And ($Value.Length -eq 12)) ` 
                                -Or (($Value[1] -eq 2) -And ($Value.Length -eq 16)) ` 
                                -Or (($Value[1] -eq 4) -And ($Value.Length -eq 24)) ` 
                                -Or (($Value[1] -eq 5) -And ($Value.Length -eq 28)))) 
                            { 
                                $SID = New-Object System.Security.Principal.SecurityIdentifier $Value, 0 
                                "  $AttrName ($AttrSyntax): $SID" 
                            } 
                            Else 
                            { 
                                If ($AttrName -eq "schedule") 
                                { 
                                    "  $AttrName ($AttrSyntax): " + $(OctetToSchedule $Value) 
                                } 
                                Else 
                                { 
                                    $Size = $Value.Count 
                                    If ($Size -gt 100) 
                                    { 
                                        "  $AttrName ($AttrSyntax) <$Size bytes>" 
                                    } 
                                    Else 
                                    { 
                                        "  $AttrName ($AttrSyntax): $Value" 
                                    } 
                                } 
                            } 
                        } 
                    } 
                } 
                "Int32" 
                { 
                    Switch ($AttrName.ToLower()) 
                    { 
                        "useraccountcontrol" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(UAC($Value)) 
                        } 
                        "msds-user-account-control-computed" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(UAC($Value)) 
                        } 
                        "grouptype" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(GroupType($Value)) 
                        } 
                        "searchflags" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(SearchFlags($Value)) 
                        } 
                        "systemflags" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(SystemFlags($Value)) 
                        } 
                        "samaccounttype" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(SAMType($Value)) 
                        } 
                        "instancetype" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(InstanceType($Value)) 
                        } 
                        Default 
                        { 
                            "  $AttrName ($AttrSyntax): " + '{0:n0}' -f $Value 
                        } 
                    } 
                } 
                "Boolean" 
                { 
                    If ($Value -eq 0{$Boolean = "TRUE"} 
                    Else {$Boolean = "FALSE"} 
                    "  $AttrName ($AttrSyntax): $Boolean" 
                } 
                "String" 
                { 
                    If ($AttrSyntax -Like "DNWithBinary*") 
                    { 
                        $Items = $Value.Split(":") 
                        $DN = $Items[3] 
                        $GUID = HexToGUID $Items[2] 
                        "  $AttrName ($AttrSyntax): $DN $GUID" 
                    } 
                    Else 
                    { 
                        "  $AttrName ($AttrSyntax): $Value" 
                    } 
                } 
                Default 
                { 
                    "  $AttrName ($AttrSyntax): $Value" 
                } 
            } # End Switch ($Value.GetType().Name). 
        } # End ForEach ($Value In $Values). 
    } # End $Values not Null and not operational. 
} # End ForEach $ManAttributes. 
 
$Searcher.Dispose() 
$Searcher = New-Object System.DirectoryServices.DirectorySearcher 
$Searcher.SearchScope = "subtree" 
$Searcher.SearchRoot = "LDAP://$Base" 
 
# Filter on the object. 
$Searcher.Filter = "(distinguishedName=$ObjectDN)" 
 
"Optional Attributes ($OptCount)" 
 
ForEach ($Attribute In $OptAttributes) 
{ 
    $AttrName = $Attribute.Name 
    $AttrSyntax = $Attribute.Syntax 
    $Single = $Attribute.IsSingleValued 
    If (-Not $Single) {$AttrSyntax = "$AttrSyntax[]"} 
    $Name = $Attribute.CommonName 
    # Retrieve systemFlags attribute of the attribute. 
    $Attr = [ADSI]"LDAP://cn=$Name,$Schema" 
    $Flags = $Attr.psbase.Properties.Item("systemFlags")[0] 
    If ($Flags -band 4{$AttrSyntax = "$AttrSyntax <operational>"} 
 
    # A error is raised if we attempt to retrieve multi-valued Sid operational attributes. 
    If ($AttrSyntax -ne "Sid[] <operational>") 
    { 
        $Searcher.PropertiesToLoad.Add($AttrName) > $Null 
    } 
 
    $Result = $Searcher.FindOne() 
 
    $Values = $Result.Properties.Item($AttrName) 
    If ($AttrSyntax -eq "Sid[] <operational>") 
    { 
        $ADObject.psbase.RefreshCache("$AttrName") 
        $SIDs = $ADObject.psbase.Properties.Item("$AttrName") 
        ForEach ($Value In $SIDs) 
        { 
            $SID = New-Object System.Security.Principal.SecurityIdentifier $Value, 0 
            $Group = $SID.Translate([System.Security.Principal.NTAccount]) 
            $GrpNTName = $Group.Value.Split("\")[1] 
            "  $AttrName ($AttrSyntax): $SID ($GrpNTName)" 
        } 
    } 
    ElseIf ($Values[0] -eq $Null) 
    { 
        "  $AttrName ($AttrSyntax): <not set>" 
    } 
    Else 
    { 
        ForEach ($Value In $Values) 
        { 
            Switch ($Value.GetType().Name) 
            { 
                "Int64" 
                { 
                    If (($Value -ge [TimeSpan]::MaxValue.Ticks) ` 
                        -Or ($Value -le [TimeSpan]::MinValue.Ticks)) 
                    { 
                        "  $AttrName ($AttrSyntax): <never>" 
                    } 
                    Else 
                    { 
                        If (($Value -gt 120000000000000000) ` 
                            -And ($Value -le [DateTime]::MaxValue.Ticks)) 
                        { 
                            $Date = [DateTime]$Value 
                            "  $AttrName ($AttrSyntax): " + '{0:n0}' -f $Value ` 
                                + "(" + $Date.AddYears(1600).ToLocalTime() + ")" 
                        } 
                        Else 
                        { 
                            If ($Value -lt 0) 
                            { 
                                $Span = [TimeSpan](-$Value) 
                                "  $AttrName ($AttrSyntax): " + '{0:n0}' -f $Value ` 
                                    + "($Span [Days.Hours:Minutes:Seconds])" 
                            } 
                            Else {"  $AttrName ($AttrSyntax): " + '{0:n0}' -f $Value} 
                        } 
                    } 
                } 
                "Byte[]" 
                { 
                    If (($Value.Length -eq 16) ` 
                        -And ($AttrName.ToUpper().Contains("GUID") -eq $True)) 
                    { 
                        "  $AttrName ($AttrSyntax): " + $(OctetToGUID($Value)) 
                    } 
                    Else 
                    { 
                        If (($Value.Length -eq 21) -And ($AttrName -eq "logonHours")) 
                        { 
                            "  $AttrName ($AttrSyntax): " + $(OctetToHours($Value)) 
                        } 
                        Else 
                        { 
                            If (($Value[0] -eq 1) -And (` 
                                (($Value[1] -eq 1) -And ($Value.Length -eq 12)) ` 
                                -Or (($Value[1] -eq 2) -And ($Value.Length -eq 16)) ` 
                                -Or (($Value[1] -eq 4) -And ($Value.Length -eq 24)) ` 
                                -Or (($Value[1] -eq 5) -And ($Value.Length -eq 28)))) 
                            { 
                                $SID = New-Object System.Security.Principal.SecurityIdentifier $Value, 0 
                                "  $AttrName ($AttrSyntax): $SID" 
                            } 
                            Else 
                            { 
                                If ($AttrName -eq "schedule") 
                                { 
                                    "  $AttrName ($AttrSyntax): " + $(OctetToSchedule $Value) 
                                } 
                                Else 
                                { 
                                    $Size = $Value.Count 
                                    If ($Size -gt 100) 
                                    { 
                                        "  $AttrName ($AttrSyntax) <$Size bytes>" 
                                    } 
                                    Else 
                                    { 
                                        "  $AttrName ($AttrSyntax): $Value" 
                                    } 
                                } 
                            } 
                        } 
                    } 
                } 
                "Int32" 
                { 
                    Switch ($AttrName.ToLower()) 
                    { 
                        "useraccountcontrol" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(UAC($Value)) 
                        } 
                        "msds-user-account-control-computed" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(UAC($Value)) 
                        } 
                        "grouptype" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(GroupType($Value)) 
                        } 
                        "searchflags" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(SearchFlags($Value)) 
                        } 
                        "systemflags" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(SystemFlags($Value)) 
                        } 
                        "samaccounttype" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(SAMType($Value)) 
                        } 
                        "instancetype" 
                        { 
                            "  $AttrName ($AttrSyntax): $Value " + $(InstanceType($Value)) 
                        } 
                        Default 
                        { 
                            "  $AttrName ($AttrSyntax): " + '{0:n0}' -f $Value 
                        } 
                    } 
                } 
                "Boolean" 
                { 
                    If ($Value -eq 0{$Boolean = "TRUE"} 
                    Else {$Boolean = "FALSE"} 
                    "  $AttrName ($AttrSyntax): $Boolean" 
                } 
                "String" 
                { 
                    If ($AttrSyntax -Like "DNWithBinary*") 
                    { 
                        $Items = $Value.Split(":") 
                        $DN = $Items[3] 
                        $GUID = HexToGUID $Items[2] 
                        "  $AttrName ($AttrSyntax): $DN $GUID" 
                    } 
                    Else 
                    { 
                        "  $AttrName ($AttrSyntax): $Value" 
                    } 
                } 
                Default 
                { 
                    "  $AttrName ($AttrSyntax): $Value" 
                } 
            } # End Switch ($Value.GetType().Name). 
        } # End ForEach ($Value In $Values). 
    } # End $Values not Null and not operational. 
} # End ForEach $OptAttributes.