SEC536: Adversarial AI - Penetration Testing AI Systems


Experience SANS training through course previews.
Learn MoreLet us help.
Contact usBecome a member for instant access to our free resources.
Sign UpWe're here to help.
Contact UsJoin me as we answer the question: Where are all of the user properties for Active Directory users for Get-ADUSer?

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 : 107The 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:

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.


SANS Faculty Fellow Joshua Wright is the author of SEC504 and Dynamic Incident Response (DAIR). A longtime RSA Conference panelist and open-source developer, he brings over two decades of research, offensive security, and teaching experience.
Read more about Joshua Wright