Shore Up
A clerk updating the printed cards in an office rolodex, pencilling in a new job title and phone extension on each card before slotting it back.
WindowsSecurity

Update Active Directory User Details with PowerShell

Ketan Aagja8 min read
No ratings yet

Before you run this

This guide changes existing Active Directory user objects: it sets the email address, job title, and department on one account or a batch of accounts. It does not create or delete anything. But it overwrites whatever those attributes held before, and Active Directory keeps no built-in undo — if you set the wrong department on 200 people, the only way back is to set it again with the correct value. So treat the write as permanent unless you record the old values first (I show how below).

You need an elevated PowerShell session running as an account with rights to modify the target user objects — normally a delegated account with write permission on the OU, or a Domain Admin. You also need the ActiveDirectory module, which ships with RSAT (on a member workstation) or is present on a domain controller. Confirm it loads:

Import-Module ActiveDirectory

If that errors, install RSAT ("Active Directory Domain Services Tools") from Windows optional features first.

Test on one throwaway object before you touch anyone real. Make a test user — say testuser01 — in a test OU, run the single-user command against it, and confirm the result. Every command below supports -WhatIf, which prints what would change without changing it. Use it. Read the script, understand the CSV it consumes, and never paste a bulk update into a live domain without a dry run first.

My assumptions: a member server or admin workstation joined to an AD domain, Windows PowerShell 5.1 (the version bundled with Windows Server and Windows 10/11), and the ActiveDirectory module available. The commands work the same in PowerShell 7 as long as the module is imported.

The cmdlet and the three attributes

The tool is Set-ADUser. It has dedicated parameters that map to the three attributes we care about:

  • -EmailAddress writes the mail attribute
  • -Title writes the title attribute
  • -Department writes the department attribute

These are real, documented parameters — see Microsoft Learn for Set-ADUser. Because they exist as named parameters, you don't need the more general -Replace hashtable syntax for these fields, which keeps the command readable.

You identify the user with -Identity, which accepts a sAMAccountName, a distinguished name, a GUID, or a SID. I'll use the sAMAccountName (the logon name) throughout because it's the value most admins have on hand.

Update a single user

Start here. This is also your test-object command.

Set-ADUser -Identity "jsmith" `
    -EmailAddress "jsmith@example.com" `
    -Title "Senior Analyst" `
    -Department "Finance" `
    -WhatIf   # dry run: shows what would change, changes nothing

Replace jsmith with the real logon name and the three values with the real ones. example.com is a placeholder — use your actual mail domain.

With -WhatIf present, PowerShell prints a line like "Performing the operation 'Set' on target 'CN=John Smith,…'" and stops. When you're satisfied, remove -WhatIf and run it again to apply.

If you only want to change one field, pass only that parameter. Omitting a parameter leaves that attribute untouched:

# Change the department only, leave email and title alone
Set-ADUser -Identity "jsmith" -Department "Operations"

Clearing a value

To empty an attribute rather than set it, use -Clear with the LDAP attribute name (not the friendly parameter name):

# Remove the title entirely
Set-ADUser -Identity "jsmith" -Clear title

The attribute names for our three fields are mail, title, and department.

Record the old values first (your rollback)

Before a bulk change, export the current state so you can restore it. This is the closest thing to an undo you get.

# Export current values for the users you're about to touch
Import-Csv "C:\path\to\updates.csv" |
    ForEach-Object { Get-ADUser -Identity $_.SamAccountName `
        -Properties mail,title,department } |
    Select-Object SamAccountName, mail, title, department |
    Export-Csv "C:\path\to\backup-before.csv" -NoTypeInformation

Keep backup-before.csv somewhere safe. If the change goes wrong, that file is your recovery source — you re-run the bulk update against it. Note that Get-ADUser only returns mail, title, and department when you ask for them with -Properties; they aren't in the default property set.

Bulk update from a CSV

The standard approach for many users is a CSV plus a loop. Build a file updates.csv with a header row and one row per user:

SamAccountName,EmailAddress,Title,Department
jsmith,jsmith@example.com,Senior Analyst,Finance
awong,awong@example.com,Team Lead,Operations
bpatel,bpatel@example.com,Analyst,Finance

Then loop over it. This script does a dry run by default — you flip one switch to apply:

Import-Module ActiveDirectory

$csvPath = "C:\path\to\updates.csv"   # your CSV
$Apply   = $false                     # set to $true to actually write changes

Import-Csv $csvPath | ForEach-Object {
    $params = @{
        Identity     = $_.SamAccountName
        EmailAddress = $_.EmailAddress
        Title        = $_.Title
        Department   = $_.Department
    }

    if ($Apply) {
        Set-ADUser @params
        Write-Host "Updated $($_.SamAccountName)"
    }
    else {
        # -WhatIf prints the intended change without making it
        Set-ADUser @params -WhatIf
    }
}

Run it once as-is ($Apply is $false) and read every -WhatIf line. If a sAMAccountName is wrong, Set-ADUser throws for that row — you'll see it in the dry run, which is exactly when you want to catch it. When the output looks right, set $Apply = $true and run again.

A note on the splat (@params): I build a hashtable of parameters and pass it with the @ sigil. It keeps the loop readable and means the same block feeds both the dry run and the real run.

If some rows change only one field, the fixed hashtable above will overwrite the others with whatever the CSV cell holds — including blanks. If your CSV has empty cells you don't intend to write, either split the work into separate CSVs per field, or build the hashtable conditionally so empty values are skipped. Keep the CSV columns fully populated and this isn't a concern.

Verify the change

Read the attributes straight back from AD. Don't trust the script's own output — check the source.

Get-ADUser -Identity "jsmith" -Properties mail,title,department |
    Select-Object SamAccountName, mail, title, department

For the whole batch at once:

Import-Csv "C:\path\to\updates.csv" |
    ForEach-Object { Get-ADUser -Identity $_.SamAccountName `
        -Properties mail,title,department } |
    Select-Object SamAccountName, mail, title, department |
    Format-Table -AutoSize

Compare that table to your CSV. Every row should match. Be aware of replication lag: if you write on one domain controller and read from another, you may briefly see the old value. Query the same DC you wrote to (Get-ADUser -Server dc01.example.com …) if you need certainty immediately after the change.

Undo

There's no rollback button, so recovery means re-applying the old values from the backup you exported earlier:

Import-Csv "C:\path\to\backup-before.csv" | ForEach-Object {
    Set-ADUser -Identity $_.SamAccountName `
        -EmailAddress $_.mail `
        -Title $_.title `
        -Department $_.department -WhatIf   # dry run first, as always
}

Check the -WhatIf output, then drop the switch to restore. If a field was originally empty, the backup cell will be empty too — writing an empty string is not identical to clearing the attribute, so for a true "make it blank again" restore, use Set-ADUser -Clear with mail, title, or department as shown earlier.

That's the whole loop: back up the current values, dry-run the change, apply it, read it back, and keep the backup until you're sure. It's boring on purpose — boring is what you want when the target is your live directory.

Written by
Ketan Aagja

Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.

More about the author →

Was this article helpful?

Tap a star — no sign-in needed.

Be the first to rate this article.