Everyday ActiveDirectory module cmdlets for help desk and sysadmin work, with realistic examples for the contoso.com domain. Change names and OUs to match yours.
Getting started
| Task |
Command |
| Install on Windows 10 or 11 (admin) |
Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0 |
| Install on Windows Server |
Install-WindowsFeature RSAT-AD-PowerShell |
| Load the module |
Import-Module ActiveDirectory |
| List every AD cmdlet |
Get-Command -Module ActiveDirectory |
| Domain summary and FSMO roles |
Get-ADDomain | Select-Object DNSRoot, PDCEmulator, RIDMaster, InfrastructureMaster |
| Domain controllers |
Get-ADDomainController -Filter * | Select-Object Name, Site, IPv4Address, IsGlobalCatalog |
| Find the PDC emulator |
Get-ADDomainController -Discover -Service PrimaryDC |
The module talks to Active Directory Web Services on the domain controllers (TCP 9389). Add -Server dc01.contoso.com to target one DC.
Users
| Task |
Command |
| One user (by sAMAccountName, DN, GUID or SID) |
Get-ADUser -Identity jdoe |
| Extra properties |
Get-ADUser jdoe -Properties Title, Department, LastLogonDate, PasswordLastSet |
| Every property (slow, use for one user) |
Get-ADUser jdoe -Properties * |
| Search by name |
Get-ADUser -Filter "Name -like 'John*'" |
| All users in one OU |
Get-ADUser -Filter * -SearchBase "OU=Staff,DC=contoso,DC=com" |
| Users with no email address |
Get-ADUser -Filter 'EmailAddress -notlike "*"' |
| Headcount by department |
Get-ADUser -Filter * -Properties Department | Group-Object Department | Sort-Object Count -Descending |
| Update attributes |
Set-ADUser jdoe -Title 'Help Desk Analyst' -Department IT -Office 'Building 2' |
| Set an attribute with no named parameter |
Set-ADUser jdoe -Replace @{extensionAttribute1 = 'Contractor'} |
| Disable, enable |
Disable-ADAccount jdoe, Enable-ADAccount jdoe |
| Account expiry (expires at 00:00 on that date), or remove it |
Set-ADAccountExpiration jdoe -DateTime '2026-12-31', or Clear-ADAccountExpiration jdoe |
| Move to another OU |
Get-ADUser jdoe | Move-ADObject -TargetPath "OU=Leavers,DC=contoso,DC=com" |
| Delete (asks to confirm) |
Remove-ADUser jdoe |
New user
New-ADUser -Name 'Jane Doe' -GivenName Jane -Surname Doe `
-SamAccountName jdoe -UserPrincipalName jdoe@contoso.com `
-Path 'OU=Staff,DC=contoso,DC=com' `
-AccountPassword (Read-Host -AsSecureString 'Initial password') `
-ChangePasswordAtLogon $true -Enabled $true
Without -AccountPassword and -Enabled $true, the new account is created disabled.
Passwords and lockouts
| Task |
Command |
| Reset a password |
Set-ADAccountPassword jdoe -Reset -NewPassword (Read-Host -AsSecureString 'New password') |
| Force a change at next sign-in |
Set-ADUser jdoe -ChangePasswordAtLogon $true |
| Unlock an account |
Unlock-ADAccount jdoe |
| Is it locked, and when was the last bad password? |
Get-ADUser jdoe -Properties LockedOut, AccountLockoutTime, LastBadPasswordAttempt, BadLogonCount |
| All locked-out users |
Search-ADAccount -LockedOut -UsersOnly | Select-Object Name, SamAccountName |
| When was the password last set? |
Get-ADUser jdoe -Properties PasswordLastSet, PasswordNeverExpires |
| Domain password and lockout policy |
Get-ADDefaultDomainPasswordPolicy |
| Fine-grained policy that applies to a user |
Get-ADUserResultantPasswordPolicy jdoe (no output means the domain policy applies) |
BadLogonCount and LastBadPasswordAttempt are not replicated, so each DC can show different values. Failed passwords are forwarded to the PDC emulator, so it is a good DC to ask with -Server.
Finding disabled, stale and expiring accounts
| Find |
Command |
| Disabled users |
Search-ADAccount -AccountDisabled -UsersOnly or Get-ADUser -Filter 'Enabled -eq $false' |
| No sign-in for 90 days |
Search-ADAccount -AccountInactive -TimeSpan 90.00:00:00 -UsersOnly |
| Enabled users idle 90 days, with dates |
$cutoff = (Get-Date).AddDays(-90), then Get-ADUser -Filter 'Enabled -eq $true -and LastLogonDate -lt $cutoff' -Properties LastLogonDate |
| Stale computers |
Search-ADAccount -AccountInactive -TimeSpan 90.00:00:00 -ComputersOnly |
| Expired accounts |
Search-ADAccount -AccountExpired -UsersOnly |
| Accounts expiring in the next 14 days |
Search-ADAccount -AccountExpiring -TimeSpan 14.00:00:00 -UsersOnly |
| Expired passwords |
Search-ADAccount -PasswordExpired -UsersOnly |
| Passwords set never to expire |
Search-ADAccount -PasswordNeverExpires -UsersOnly |
| Limit any search to one OU |
add -SearchBase "OU=Staff,DC=contoso,DC=com" |
LastLogonDate comes from lastLogonTimestamp, which replicates but can lag by up to about 14 days. Accounts that never signed in have no value and do not match a date filter.
Groups and membership
| Task |
Command |
| One group |
Get-ADGroup 'SG-Finance-Read' -Properties Description, ManagedBy |
| New security group |
New-ADGroup -Name 'SG-Finance-Read' -GroupScope Global -GroupCategory Security -Path 'OU=Groups,DC=contoso,DC=com' -Description 'Read access to the Finance share' |
| Add members |
Add-ADGroupMember -Identity 'SG-Finance-Read' -Members jdoe, asmith |
| Remove a member (asks to confirm) |
Remove-ADGroupMember -Identity 'SG-Finance-Read' -Members jdoe |
| Direct members |
Get-ADGroupMember 'SG-Finance-Read' | Select-Object Name, ObjectClass |
| Members including nested groups |
Get-ADGroupMember 'Domain Admins' -Recursive | Select-Object Name |
| Groups a user belongs to |
Get-ADPrincipalGroupMembership jdoe | Select-Object Name |
| Direct groups as DNs |
(Get-ADUser jdoe -Properties MemberOf).MemberOf |
| Groups with no members |
Get-ADGroup -Filter * -Properties Members | Where-Object { -not $_.Members } |
Group scopes: DomainLocal, Global, Universal. Categories: Security, Distribution. MemberOf does not list the primary group (usually Domain Users).
Computers
| Task |
Command |
| One computer with useful details |
Get-ADComputer PC01 -Properties OperatingSystem, LastLogonDate, IPv4Address |
| Computers by name pattern in an OU |
Get-ADComputer -Filter "Name -like 'LIB-*'" -SearchBase 'OU=Workstations,DC=contoso,DC=com' |
| All servers |
Get-ADComputer -Filter 'OperatingSystem -like "*Server*"' -Properties OperatingSystem | Select-Object Name, OperatingSystem |
| Count by operating system |
Get-ADComputer -Filter * -Properties OperatingSystem | Group-Object OperatingSystem | Sort-Object Count -Descending |
| Disable a computer account |
Get-ADComputer PC01 | Disable-ADAccount |
| Delete a computer (asks to confirm) |
Remove-ADComputer PC01 |
If Remove-ADComputer fails because the object has child objects (for example BitLocker recovery keys), use Get-ADComputer PC01 | Remove-ADObject -Recursive.
Organizational units
| Task |
Command |
| List OUs |
Get-ADOrganizationalUnit -Filter * | Select-Object Name, DistinguishedName |
| New OU (protected from deletion by default) |
New-ADOrganizationalUnit -Name Laptops -Path 'OU=Workstations,DC=contoso,DC=com' |
| Remove protection |
Set-ADOrganizationalUnit 'OU=Old,DC=contoso,DC=com' -ProtectedFromAccidentalDeletion $false |
| Delete an OU and everything in it |
Remove-ADOrganizationalUnit 'OU=Old,DC=contoso,DC=com' -Recursive |
| Move any object |
Move-ADObject 'CN=PC01,OU=Workstations,DC=contoso,DC=com' -TargetPath 'OU=Laptops,OU=Workstations,DC=contoso,DC=com' |
-Filter or -LDAPFilter?
|
-Filter |
-LDAPFilter |
| Syntax |
PowerShell style |
Raw LDAP |
| Operators |
-eq -ne -like -notlike -lt -le -gt -ge -and -or -not |
= >= <= & | ! and * wildcards |
| Property names |
Module names such as Enabled, LastLogonDate, or LDAP names |
LDAP names only, such as sAMAccountName, sn |
| Everything |
-Filter * |
-LDAPFilter '(objectClass=*)' |
| Name starts with Smi |
-Filter "Surname -like 'Smi*'" |
-LDAPFilter '(sn=Smi*)' |
| IT or HR department |
-Filter "Department -eq 'IT' -or Department -eq 'HR'" |
-LDAPFilter '(|(department=IT)(department=HR))' |
| Disabled accounts |
-Filter 'Enabled -eq $false' |
-LDAPFilter '(userAccountControl:1.2.840.113556.1.4.803:=2)' |
| No email set |
-Filter 'EmailAddress -notlike "*"' |
-LDAPFilter '(!(mail=*))' |
| Parameter |
What it does |
-SearchBase 'OU=Staff,DC=contoso,DC=com' |
Start the search at this OU instead of the domain root |
-SearchScope Base | OneLevel | Subtree |
This object only, direct children only, or everything below (default) |
-Properties Name1, Name2 |
Return extra attributes. * returns all of them. |
-ResultSetSize 100 |
Stop after 100 results |
-Server dc01.contoso.com |
Ask a specific domain controller |
Tips: filter on the server with -Filter rather than Where-Object on large directories. Add -WhatIf to any Set-, Add-, Move- or Remove- command to preview it first.