Talk With an Expert

Month of PowerShell - The Curious Case of AD User Properties

Join me as we answer the question: Where are all of the user properties for Active Directory users for Get-ADUSer?

Authored byJoshua Wright
Joshua Wright

#monthofpowershell

In my first Month of PowerShell getting started article I talked about a common PowerShell process: running a cmdlet and piping the results to [code]Get-Member[/code] or [code]Select-Object -Property *[/code] to get a list of the object properties.

It's standard PowerShell practice, and I've done it hundreds of times throughout the Month of PowerShell. That is, until I started working with Active Directory user properties:

PS C:\Users\jwright> Get-ADUser -Identity jwright | Select-Object -Property *


DistinguishedName  : CN=jwright,CN=Users,DC=falsimentis,DC=local
Enabled            : True
GivenName          :
Name               : jwright
ObjectClass        : user
ObjectGUID         : f69b19e6-61ac-4cae-a7ac-ec88ec3bb564
SamAccountName     : jwright
SID                : S-1-5-21-1850091285-130397106-3068105436-500
Surname            :
UserPrincipalName  :
PropertyNames      : {DistinguishedName, Enabled, GivenName, Name...}
AddedProperties    : {}
RemovedProperties  : {}
ModifiedProperties : {}
PropertyCount      : 10
PS C:\Users\jwright>

Normally, I'd expect to see all of the properties for the AD User object, but lots of properties are missing. This is not consistent with other PowerShell cmdlet behavior.

Looking at the [code]Get-Help Get-ADUser[/code] output, there is an option [code]-Properties[/code] that accepts a string or list or strings, similar to what we see in the help for [code]Select-Object[/code]. When we use that option with a wildcard, we get all the AD user properties:

PS C:\Users\jwright> Get-ADUser -Identity jwright -Properties * | Select-Object -Property *


AccountExpirationDate                :
accountExpires                       : 0
AccountLockoutTime                   :
AccountNotDelegated                  : False
AllowReversiblePasswordEncryption    : False
AuthenticationPolicy                 : {}
AuthenticationPolicySilo             : {}
BadLogonCount                        : 0
badPasswordTime                      : 0
badPwdCount                          : 0
CannotChangePassword                 : False
CanonicalName                        : falsimentis.local/Users/jwright
Certificates                         : {}
City                                 :
CN                                   : jwright
codePage                             : 0
Company                              :
CompoundIdentitySupported            : {}
Country                              :
countryCode                          : 0
Created                              : 7/16/2022 7:08:49 PM
createTimeStamp                      : 7/16/2022 7:08:49 PM
Deleted                              :
Department                           :
Description                          : Built-in account for administering the computer/domain
DisplayName                          :
DistinguishedName                    : CN=jwright,CN=Users,DC=falsimentis,DC=local
Division                             :
DoesNotRequirePreAuth                : False
dSCorePropagationData                : {7/16/2022 7:09:34 PM, 1/1/1601 12:00:01 AM}
EmailAddress                         :
EmployeeID                           :
EmployeeNumber                       :
Enabled                              : True
Fax                                  :
GivenName                            :
HomeDirectory                        :
HomedirRequired                      : False
HomeDrive                            :
HomePage                             :
HomePhone                            :
Initials                             :
instanceType                         : 4
isCriticalSystemObject               : True
isDeleted                            :
KerberosEncryptionType               : {}
LastBadPasswordAttempt               :
LastKnownParent                      :
lastLogoff                           : 0
lastLogon                            : 133024722383629408
LastLogonDate                        : 7/16/2022 7:10:23 PM
lastLogonTimestamp                   : 133024722231721433
LockedOut                            : False
logonCount                           : 25
logonHours                           : {255, 255, 255, 255...}
LogonWorkstations                    :
Manager                              :
MemberOf                             : {CN=Group Policy Creator Owners,CN=Users,DC=falsimentis,DC=local, CN=Domain
                                       Admins,CN=Users,DC=falsimentis,DC=local, CN=Enterprise
                                       Admins,CN=Users,DC=falsimentis,DC=local, CN=Schema
                                       Admins,CN=Users,DC=falsimentis,DC=local...}
MNSLogonAccount                      : False
MobilePhone                          :
Modified                             : 7/16/2022 7:10:23 PM
modifyTimeStamp                      : 7/16/2022 7:10:23 PM
msDS-User-Account-Control-Computed   : 0
Name                                 : jwright
nTSecurityDescriptor                 : System.DirectoryServices.ActiveDirectorySecurity
ObjectCategory                       : CN=Person,CN=Schema,CN=Configuration,DC=falsimentis,DC=local
ObjectClass                          : user
ObjectGUID                           : f69b19e6-61ac-4cae-a7ac-ec88ec3bb564
objectSid                            : S-1-5-21-1850091285-130397106-3068105436-500
Office                               :
OfficePhone                          :
Organization                         :
OtherName                            :
PasswordExpired                      : False
PasswordLastSet                      : 7/16/2022 6:42:54 PM
PasswordNeverExpires                 : False
PasswordNotRequired                  : False
POBox                                :
PostalCode                           :
PrimaryGroup                         : CN=Domain Users,CN=Users,DC=falsimentis,DC=local
primaryGroupID                       : 513
PrincipalsAllowedToDelegateToAccount : {}
ProfilePath                          :
ProtectedFromAccidentalDeletion      : False
pwdLastSet                           : 133024705746844315
SamAccountName                       : jwright
sAMAccountType                       : 805306368
ScriptPath                           :
sDRightsEffective                    : 15
ServicePrincipalNames                : {}
SID                                  : S-1-5-21-1850091285-130397106-3068105436-500
SIDHistory                           : {}
SmartcardLogonRequired               : False
State                                :
StreetAddress                        :
Surname                              :
Title                                :
TrustedForDelegation                 : False
TrustedToAuthForDelegation           : False
UseDESKeyOnly                        : False
userAccountControl                   : 512
userCertificate                      : {}
UserPrincipalName                    :
uSNChanged                           : 12579
uSNCreated                           : 8196
whenChanged                          : 7/16/2022 7:10:23 PM
whenCreated                          : 7/16/2022 7:08:49 PM
PropertyNames                        : {AccountExpirationDate, accountExpires, AccountLockoutTime,
                                       AccountNotDelegated...}
AddedProperties                      : {}
RemovedProperties                    : {}
ModifiedProperties                   : {}
PropertyCount                        : 107

The question is, why? Why does [code]Get-ADUser[/code] break from convention like other PowerShell cmdlets and not send all of the available properties to the next command in the pipeline? I think I found the answer in Mike Robbins's PowerShell 101:

This seems like a reasonable assertion for why, and it's an important precedent for PowerShell users to keep in mind: PowerShell cmdlets may not include all available properties by default.

How do you know if the absence of a property you want to access (e.g., the parent process ID in [code]Get-Process[/code]) is available but omitted by default, or if it's not available at all? The only reasonable way to know is to look at the [code]Get-Help[/code] output for a given cmdlet, and see if there is an option for [code]-Properties[/code], and experiment with [code]CMDLETNAME -Properties * | Select-Object -Property *[/code].

In practice, you will rarely need all of the properties from any cmdlet in the pipeline. This brings us to the lesson Blake Regan @crash0ver1d3 tried to tell me a few weeks ago but I wasn't ready to listen yet:

Tweet from Blake Regan reads

For example, if I want to build a CSV file of all domain users that includes the name, SID, password expired (Boolean), and password last set (date) elements, I can run this command:

PS C:\Users\jwright> Get-ADUser -Filter * -Properties * | Select-Object -Property Name, SID, PasswordExpired, PasswordLastSet | ConvertTo-Csv | Out-File userpassset.csv
PS C:\Users\jwright>

However, this creates an unnecessarily large collection of user objects. It might not be noticeable with tens or hundreds of users, but on a domain with hundreds of thousands of users this command will create a lot of unnecessary overhead. Here's a better solution:

PS C:\Users\jwright> Get-ADUser -Filter * -Properties Name, SID, PasswordExpired, PasswordLastSet | Select-Object -Property Name, SID, PasswordExpired, PasswordLastSet | ConvertTo-Csv | Out-File userpassset.csv
PS C:\Users\jwright> Get-Content -first 3 .\userpassset.csv
#TYPE Selected.Microsoft.ActiveDirectory.Management.ADUser
"Name","SID","PasswordExpired","PasswordLastSet"
"jwright","S-1-5-21-1850091285-130397106-3068105436-500","False","7/16/2022 6:42:54 PM"
PS C:\Users\jwright>

It's a little redundant to specify the object properties twice (and don't get me started on the inconsistent use of [code]Get-ADUSer -Properties[/code] and [code]Select-Object -Property[/code]), but it improves performance without a lot of added effort.

If you have other questions about PowerShell things, let me know! Reach out on Twitter (my DMs are open), or email josh@willhackforsushi.com.

Thank you for reading!

-Joshua Wright

Return to Getting Started With PowerShell


Joshua Wright is the author of SANS SEC504: Hacker Tools, Techniques, and Incident Handling, a faculty fellow for the SANS Institute, and a senior technical director at Counter Hack.