Using PowerShell to Update an AD User from a CSV file

I needed to update a bunch of AD Users with their new job titles. A new year has started and some made promotions and got a new job title. So I received an Excel file with their full name and their new title from HR. Because this is happening every year, is it time to create a PowerShell script for it.

As a bonus, we are also going to send the user an email to notify them that their job title is changed. So they don’t have to contact IT for it anymore.

Importing the CSV File in PowerShell

To update the AD User we are going to use a CSV file. This allows us to use the Import-CSV cmdlet in PowerShell. I have used the following Excel table that I have saved a CSV.

namejobtitle
John Doesenior accountmanager
Jane Doeaccountmanager
Salina Scottengineer
Jasper Rempeljunior engineer

With the parameter csvPath, we can specify the location of the CSV file that we want to import.

Set-UserPromotions.ps1 -csvPath c:\temp\promotions.csv

Now to import the CSV file into PowerShell we use the following command later on:

$promotions = Import-Csv -Delimiter ";" -Path $csvPath

Tip

By default, the Delimiter is a comma ( , ), but you can change to by using the -Delimiter parameter. As you can see I am using the $rootPath to reference to the script location. You can also fill in the absolute path to the file here.

The result of $promotions is:

name    jobtitle
-------------
John Doe        senior accountmanager
Jane Doe        accountmanager
Salina Scott    Engineer
Jasper Rempel   junior engineer

Finding the AD user to update

The next step is to find the user in the Active Directory. We need to find the users based on their full names. We are going to use the Get-ADUser cmdlet for this and filter the results on the display name.

We don’t have one user, but a whole list. So we are going to use a foreach loop to walk through the list of users.

foreach($user in $promotions){
    # Find user
    $ADUser = Get-ADUser -Filter "displayname -eq '$($user.name)'"
}

So we are trying to get each user in the table promotions. Now a good practice is to implement a catch in case the user doesn’t exist. Maybe HR made a typo which can result in the user not being found.

Also, we need the user’s email address later on to send him the notification. Add the property mail to the Get-ADUser cmd.

foreach($user in $promotions){
    #find user
    $ADUser = Get-ADUser -Filter "displayname -eq '$($user.user)'" -Properties mail

    if ($ADUser){
        # <update the user>
    }else{
        Write-Warning ("Failed to update " + $($user.user))
    }
}

Updating the AD User

If the users exist in the Active Directory we can use the Set-ADUser to update an attribute. In this case the title attribute, but that can be any AD User-related attribute.

Tip

Read more about the Set-ADUser cmdlet in this article.
foreach($user in $promotions){

    # Find user
    $ADUser = Get-ADUser -Filter "displayname -eq '$($user.name)'" -Properties mail

    if ($ADUser){
        Set-ADUser -Identity $ADUser -Title $user.jobtitle
    }else{
        Write-Warning ("Failed to update " + $($user.name))
    }
}

If you only want to update AD User from a CSV file then you are done. If you also want to send them an email to notify them about the change, then keep reading.

Sending the User an Email

I am using an email template to send the user an email.  The template is an HTML file that you can download here on GitHub along with this script. Before we can the email we need to replace some placeholders with the user’s name and a new title. Also, we need an SMTP server for sending the email.

Setting the STMP details

Below is a simple array with the SMTP details, like the server and from address.

#SMPT Details to send the email
$smtp = @{
	"address" = "stonegrovebank.mail.protection.outlook.com"
	"from" = "itdept@stonegrovebank.com>"
	"subject" = "Jobtitle updated."
}

Creating the Email body

I have a function that I use for creating the email body. Using functions allows me to reuse parts of code in different scripts. The first function will get the email template and replace the placeholders with the correct data.

Function Get-EmailTemplate {
  <#
    .SYNOPSIS
    Get the eamil template which is located in the same location as the script
  #>
	PARAM(
		[parameter(Mandatory=$true)]
		$user,
		[parameter(Mandatory=$true)]
		$jobtitle
	)

	PROCESS
	{
		#Get the mailtemplate
		$mailTemplate = (Get-Content ($rootPath + '\MailTemplate.html')) | ForEach-Object {
			$_ 	-replace '{{user.jobtitle}}', $jobtitle`
			-replace '{{user.firstname}}', $user.givenName
		} | Out-String	
		
		return $mailTemplate
	}
}

In the foreach loop, after the Set-ADuser cmd we add the following line to call the function and build the email body:

 $emailBody = Get-EmailTemplate -user $ADUser -JobTitle $user.jobtitle

Sending the email

The last function is for sending the email. It takes the SMTP details from the SMTP array and uses the supplied information to send the email. I added a what-if variable in it so you can run a test before sending the actual email.

Function Send-Mail {
	<#
    .SYNOPSIS
    Send the user a mail.
  #>
	PARAM(
		[parameter(Mandatory=$true)]
		$emailBody,
		[parameter(Mandatory=$true)]
		$user,
		[parameter(Mandatory=$false)]
		[bool]$whatIf
	)
	
    PROCESS
	{
		#Set encoding
		$encoding = [System.Text.Encoding]::UTF8

		Try 
		{
			if ($whatIf -ne $true)
			{
				send-MailMessage -SmtpServer $smtp.address -To $user.mail -From $smtp.from -Subject $smtp.subject -Encoding $encoding -Body $emailBody -BodyAsHtml
			}
			else
			{
				Write-host ("Send mail to -SmtpServer " + $smtp.address + " -To " + $user.mail + " -From " + $smtp.from + " -Subject $smtp.subject")
			}
		}
		Catch
		{
			Write-Error "Failed to send email to, $_"
		}

	}
}

We call this function with the following cmd that we add below the email body:

Send-Mail -user $ADUser -EmailBody $emailBody

So the complete foreach loop now looks like this:

foreach($user in $promotions){
    #find user
    $ADUser = Get-ADUser -Filter "displayname -eq '$($user.user)'" -Properties mail

    if ($ADUser){
        Set-ADUser -Identity $ADUser -Title $user.jobtitle -WhatIf

        $emailBody = Get-EmailTemplate -user $ADUser -JobTitle $user.jobtitle
        Send-Mail -user $ADUser -EmailBody $emailBody -whatIf $true
    }else{
        Write-Warning ("Failed to update " + $($user.user))
    }
}

Notice that I added two WhatIf flags. One for the Set-ADUser and one for the Send-mail. This way you can run your script to test it before actually changing or sending anything.

Conclusion

I hope the code above helped you update your AD User from a CSV file with PowerShell. You can find the complete script and email template here in my GitHub repository.

If you have any questions just add a comment below.

20 thoughts on “Using PowerShell to Update an AD User from a CSV file”

  1. I have the script setup like so:

    $changes = import-csv c:\resources\script.csv
    foreach($user in $changes){
    $ADUser = Get-ADUser -Filter “user -eq ‘$($user.user)'” -properties ExtensionAttribute8
    if ($ADUser){
    Set-ADUser $ADUser -ExtensionAttribute8 $user.ExtensionAttribute8
    }

    else{
    Write-Warning (“Failed to update ” + $($user.user))
    }
    }

    However the output of this doesn’t give any errors, other than “WARNING: Failed to update John.Doe”, I’m a bit stumped, any thoughts?

    • I think your filter is incorrect, the field user doesn’t exist in Get-ADuser. Try filtering on Displayname

      $ADUser = Get-ADUser -Filter “displayname -eq ‘$($user.user)'” -properties ExtensionAttribute8

      • Thanks for your help, i managed to get this working in the end. I had to update a few attributes for 300+ users, namely the Manager attribute, the L (location) attribute and the customer specifically asked for ExtensionAttribute8 to be updated:

        #set csv location
        $changes = import-csv c:\resources\users.csv -delimiter “,”
        #make changes to “manger” attribute
        foreach($user in $changes){
        $username = $user.user
        $ADUser = Get-ADUser -Filter {SamAccountName -like $username} -properties manager
        if ($ADUser){
        Set-ADUser -identity $ADUser -manager $user.manager
        }
        else{
        Write-Warning (“Failed to update ” + $($user.user))
        }
        }

        However I encountered a weird side effect. When updating ExtensionAttribute8 it also changed the “Logon Workstations” to whatever the ExtensionAttribute8 was set to. Given that the attribute name isn’t a logon device it meant people could’t logon to anything, there where no restrictions on this before..
        Not entirely sure how the Logon items and ExtensionAttribute8 are linked but a quick powershell script reverted the logon item without changing ExtensionAttribute8:

        $changes = import-csv c:\resources\users.csv -delimiter “,”
        foreach($user in $changes){
        $username = $user.user
        $ADUser = Get-ADUser -Filter {SamAccountName -like $username}
        Set-ADUser $user.user -LogonWorkstations $null }

        I used the same “users” .csv for the above.

        Anyway, thank you for your help this saved me a huge amount of time updating 3 different attributes for 300+ users

  2. I am having trouble locating the script, csv-file and email template here on your TechNet Gallery. Have they been removed or I am not seeing them?

  3. Hi,
    This will help a lot.
    But, one question, what if we need to update user’s managers, is there an easy way to build in to this script?
    thanks for your reply

  4. Correction to my previous question:

    I would like to update company field in AD for only those employees with NO information in the company field. Thus, if company field information already exist for an employee, I would the script to skip with a message to say ” User already has company information”. Can I use this script to do so ?

  5. I would like to update company field in AD for only those employees with information in the company field. Thus, if company field already, I would the script to skip with a message to say ” User already company information”. Can I use this script to do so ?

    • Hi Josh,

      Sorry, I should have added a note that the delimiter can be different than my example. Here in the Netherlands, Excel save csv files be default with the ; delimiter. I will add it to the article.
      Thanks for reaching out

  6. Great post and I’m trying to run the script but keep getting the following error:
    Get-ADUser : The search filter cannot be recognized
    At C:\Script\UpdateADUser.ps1:5 char:15
    + $ADUser = Get-ADUser -Filter “displayname -eq ‘$($user.name)'”
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Get-ADUser], ADException
    + FullyQualifiedErrorId : ActiveDirectoryServer:8254,Microsoft.ActiveDirectory.Management.Commands.GetADUser

    WARNING: Failed to update

    • In the post, I have an example table with the fields User and Jobtitle, but the scripts are looking for name and jobtitle. Make sure you refer to the correct field names, I will update the article.

Leave a Comment

0 Shares
Tweet
Pin
Share
Share