How to copy AD user attributes to another field with PowerShell


TechMentor

Last week I spent some time working on my presentation for the TechMentor conference coming up in August.  It’s not too late to get in on the action, and this year it’s going to be in the heart of Microsoft country.  I hope to see you there.

Tapioca

Tapioca Goodness

I love tapioca pudding.  What does that have to do with Active Directory and PowerShell?!  Keep reading.

Duplicate Data Dosey Doh!

Have you ever needed to copy data between attributes in Active Directory? Maybe you need to copy an ExtensionAttribute value into a different ExtensionAttribute. Maybe you need to copy email, UPN, or SIP addresses. You may even want to move the EmployeeNumber value into the EmployeeID attribute instead. What if you needed to create a new Description based on a combination from other attributes?

Today’s post is a simple one-liner that could get you into a lot of trouble. Really. But hey, that could be said with any PowerShell code if not tested in a lab first. It could get you in trouble if you mass update the wrong attributes, so I’m warning you up front to keep the safety switch in the “on” position.

The Code

Get-ADUser is a rather handy cmdlet capable of retrieving precisely the target user population you wish to manipulate. For the exact syntax go look at Get-Help Get-ADUser. Also check out Get-Help about_ActiveDirectory_Filter. You can’t hurt too much with simple queries. Again, do this in your lab to craft the right combination of Filter (or LDAPFilter) and SearchBase. The same can be said of Get-ADObject if you want to manipulate any other AD object, so don’t limit yourself to just users here.

Import-Module ActiveDirectory            
            
# Find all accounts with a Department            
# Copy that value into Description            
Get-ADUser -LDAPFilter '(Department=*)' -Properties Description, Department |            
 Select-Object * -First 5 |            
 ForEach-Object {Set-ADObject -Identity $_.DistinguishedName ` 
  -Replace @{Description=$($_.Department)}}

Our one-liner takes the output from Get-ADUser and pipes it to Set-ADObject. This is where we use the two properties we specified with the parameter “-Properties”. Since we are changing a value, we use the “-Replace” switch to copy the value from one attribute to the other. In this case we are copying the Department over to the Description attribute. It’s pretty straight forward.  Note the generous use of $_ , indicating each user object coming through the PowerShell pipeline.

The Safety

Notice how I inserted “Select-Object * -First 5” into the middle of the pipe. That is the safety switch to make sure we only update five users as a test. Remove this part when it comes time for production implementation.

Calculated Values

But what if we want to combine a couple other attributes into a new one? Our second example copies the First Name, Last Name, and Department over into the Description attribute. Notice that we enclose each reference inside $(). This makes sure that the dotted property names get referenced correctly inside the new string value.

# Find all accounts with a Department            
# Copy that value along with the GivenName and SurName into Description            
Get-ADUser -LDAPFilter '(Department=*)' -Properties Description, Department |            
 Select-Object * -First 5 |            
 ForEach-Object {Set-ADObject -Identity $_.DistinguishedName ` 
  -Replace @{Description="$($_.GivenName) $($_.SurName) $($_.Department)"}}            

Seeing Double Yet?

Finally we wrap up with a simple query line to display the results of our effort. If the results don’t look right, then go back and fix your code. Make sure that you include all of the attribute names after the “-Properties” parameter of Get-ADUser. That is an easy one to miss.  Using the code samples above you can change the property names around to meet your needs.

# View the results            
Get-ADUser -LDAPFilter '(Department=*)' -Properties Description, Department |            
 Select-Object * -First 5 |            
 Format-Table Name, Description, Department

And Now A Word From The Soap Box

On a side note let me take this opportunity to explain why you should rarely use * after “-Properties”. If you did this:

Get-ADUser -Filter * -Properties *

Your domain controller would have to grab every single property of every single user. That is terribly inefficient and costly in a production environment with thousands of users. That is like going to the grocery store, buying every flavor of pudding, and then only eating the tapioca when you get home. Don’t buy the vanilla and chocolate if you’re not going to eat it. In other words don’t query every single attribute known to mankind if you’re not going to use them in your code. It’s just not cool. Don’t do it. Really. Just don’t.

There is an art to crafting the correct filter and property parameters. You can learn it.  I would start with Get-Help about_ActiveDirectory_Filter.  Dive into the help and learn how to write LDAP query strings. You can also use the query feature in AD Users & Computers to develop your LDAP query strings in the GUI.

image

The End

Be careful out there. Don’t update the wrong attributes. Test in a safe place first.  Make sure you’ve got a good backup before you turn this loose.

So now you know what tapioca pudding has to do with Active Directory… GoateePFE likes both of them.  Now I’m headed to the refrigerator to get some.  Yum!  What’s your favorite pudding?

Comments (32)

  1. @Tech, On the Get-ADUser example above you can use the -SearchBase parameter to target your OU. Then specify the name of the extension attribute name in the -Replace parameter of the Set-ADObject cmdlets. Your value can either be a static string or reference
    another attribute as above.

    @DeaconZ, For logging errors I would recommend using the -ErrorVariable parameter with the Set-ADObject cmdlet. You can put all your errors specific to the action in a single variable, then dump the error object at the end of your script to your desired reporting
    format. See "help about_CommonParameters" for more information. For logging successful actions trying using the -Verbose switch on Set-ADObject. If you are using PowerShell v3 or above you can then use this redirector at the end of the command line to send
    all output from all streams to a text file like this: [pipeline commands here] *> output.txt

  2. Hello Petar,
    You can use the Set-ADObject or Set-ADUser cmdlets with the -Clear parameter to erase a value.

    See example 4 in the help for Set-ADUser:

    ————————– EXAMPLE 4 ————————–
    C:PS>Set-ADUser GlenJohn -Remove @{otherMailbox="glen.john"} -Add @{url="fabrikam.com"} -Replace @{title="manager"} -Clear description

    Modify the user with samAccountName GlenJohn’s object by removing glen.john from the otherMailbox property, adding fabrikam.com to the url property, replacing the title property with manager and clearing the description property.

    Ashley
    @GoateePFE

  3. Hi Ben,
    Try changing the last bit as follows:
    @{Company=$_.Description}}
    Let me know if that works.

    In the future if I were you I would edit sample output so as not to expose private company data. Now everyone reading this post knows that Craig Brunet works at DIFC. Cool for hackers. Not cool for you or Craig. Would you like me to delete the comment to protect
    your data, or did you already make these changes before you posted it?
    Ashley
    @GoateePFE

  4. Rob Stagg says:

    Thanks!  I've been looking all over for this…

  5. Roger Zhang says:

    @{Description=$($_.Department)}}

    I prefer in this way:

    @{Description=$_.Department}}

    I don't know why your code is not working for me, so I changed a little bit. It's working now

  6. Jacob Hahn says:

    I had a project where I had to copy the SamAccountName attribute to the EmployeeNumber attribute. This is what it looked like when I was done. This page was a great help. Get-ADUser -LDAPFilter ‘(SamAccountName=*)’ -Properties SamAccountName, EmployeeNumber
    | Select-Object * -First 10|ForEach-Object {Set-ADObject -Identity $_.DistinguishedName ` -replace @{EmployeeNumber="$($_.SamAccountName)"}}

  7. Ben says:

    This code is not working for me.. Here’s what I’m doing:

    get-aduser -searchbase "ou=distributors,ou=people,dc=difc,dc=root01,dc=org" -ldapfilter ‘(Description=*)’ | foreach-object {set-aduser -identity $_.DistinguishedName -replace @{Company="$($_.Description)"}}

    The error I get is:

    set-aduser : replace
    At line:1 char:125
    + get-aduser -searchbase "ou=distributors,ou=people,dc=difc,dc=root01,dc=org" -lda …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (CN=Craig Brunet…C=root01,DC=org:ADUser) [Set-ADUser], ADInvalidOperationException
    + FullyQualifiedErrorId : ActiveDirectoryServer:0,Microsoft.ActiveDirectory.Management.Commands.SetADUser

  8. tech4 says:

    I’m looking for a powershell script to add a value to extensionattribute for all users in a certain OU. Any suggestions?

  9. DeaconZ says:

    Awesome scripting! I’m a novice with Powershell, so any advice on the preferred way to output it to a log file? Especially errors such as when copying from/to an attribute that has a 15 character limit? We have another app that creates accounts with the
    real employee ID as the sAMAccountName.
    So my script is such:

    Get-ADUser -searchbase "OU=corporate,DC=domain,DC=local" -LDAPFilter ‘(sAMAccountName=*)’ -Properties sAMAccountName, employeeID | Select-Object * | ForEach-Object {Set-ADObject -Identity $_.DistinguishedName -replace @{employeeID="$($_.sAMAccountName)"}}

    I want to run this daily and log any output. Can you help please?

  10. Anonymous says:

    Welcome! Today’s post includes demo scripts and links from the Microsoft Virtual Academy event: Using PowerShell for Active Directory . We had a great time creating this for you, and I hope you will share it with anyone needing to ramp up their

  11. Petar Tomic says:

    I need to swap 2 values, and I do that with your script.
    After that I need to clear one of this 2 value.

  12. Brandon Charpentier says:

    I’m trying to use your scripting to replace the UPN value with the value from the Mail attribute. Using this method, how can I do this? I try to replace the department and description attributes with ‘userprinciplename’ and ‘mail’ but I get the error stating
    that the userprinciplename parameter is invalid.

  13. Guillaume Cloutier says:

    Hi I’m trying to run this command: Get-ADUser -searchbase "CN=Users,DC=domain,DC=com" -LDAPFilter ‘(streetAddress=*)’ -Properties wWWHomePage, streetAddress | Select-Object * -First 5 | ForEach-Object {Set-ADObject -Identity $_.DistinguishedName -Replace
    @{wWWHomePage=$_.streetAddress}}

    I don’t get any error but it does not seem to copy the infor from the Street to the Web Site address.

    Thanks,

  14. Hello Guillaume,
    I tested your syntax, and it worked perfectly in my lab. Let me know if there is something else I need to try to help you.
    Thanks,
    GoateePFE

  15. Hi Brandon,
    This sounds like a syntax error with your command. Can you paste in what you typed?
    Thanks,
    GoateePFE

  16. goten tee says:

    Hello, I am trying to copy values in extensionAttribute1 to FacsimilieTelephoneNumber attribute here is my command. It runs without error but the values do not get copied in the fax attribute, any ideas?
    Get-ADUser -searchbase "ou=xxx,ou=Administrative,dc=oimdev,dc=xxx,dc=edu"
    -LDAPFilter ‘($_.extensionAttribute1=*)’ | Select-Object * -First 5 |
    ForEach-Object {Set-ADObject -Identity $_.DistinguishedName`
    -Replace @{Description=$_.FacsimileTelephoneNumber}}

  17. Hello Tee,
    Try changing this:
    -LDAPFilter ‘($_.extensionAttribute1=*)’
    to this:
    -LDAPFilter ‘(extensionAttribute1=*)’
    and this:
    -Replace @{Description=$_.FacsimileTelephoneNumber}}
    to this:
    -Replace @{FacsimileTelephoneNumber=$_.extensionAttribute1}}
    That should get you closer to success. Let me know if that fixes it.
    GoateePFE

  18. greg says:

    Is there a way to copy the 1 number in the otherTelephone array to the ipPhone?

  19. lucy says:

    Great post from your hands again. I loved the complete article.
    By the way nice writing style you have. I never felt like boring while reading this article.

    I will come back & read all your posts soon. Regards, Lucy.

  20. Rayman says:

    First off great post thanks Tapioca man!

    Second, my struggle trying it on one user

    My script:

    PS C:Windowssystem32> Get-ADUser usertest1 -Properties Department, Description | Select-Object usertest1 | ForEach-Object
    {Set-ADObject -Identify $_.DistinguishedName -Replace @{Department=$($_.Description)}}

    My Error:

    Set-ADObject : Cannot validate argument on parameter ‘Replace’. The argument is null or an element of the argument
    collection contains a null value.
    At line:1 char:152
    + … Name -Replace @{Department=$($_.Description)}}
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidData: (:) [Set-ADObject], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.SetADObject

  21. Rayman says:

    oops I misspelled Identity but I am still getting this error

    PS C:Windowssystem32> Get-ADUser raytest -Properties Department, Description | Select-Object raytest | ForEach-Object
    {Set-ADObject -Identity $_.DistinguishedName ` -Replace @{Department=$_.Description}}

    Set-ADObject : Cannot validate argument on parameter ‘Identity’. The argument is null. Provide a valid value for the
    argument, and then try running the command again.
    At line:1 char:121
    + … ject -Identity $_.DistinguishedName ` -Replace @{Department=$_.Description}}
    + ~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidData: (:) [Set-ADObject], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.ActiveDirectory.Management.Commands.SetADObject

  22. Hello Rayman,

    Take this out of your pipeline and it will work: Select-Object raytest |

    Ashley
    GoateePFE

  23. Michael says:

    Get-ADgroup -LDAPFilter ‘(authOrig=*)’ -Properties xxx, yyy|
    Select-Object * -First 1 |
    ForEach-Object {Set-ADObject -Identity $_.DistinguishedName -Replace @{xxx=$($_.yyy)}

    How about if you are trying to add a multiple-value attribute?
    I have error “Set-ADObject : Multiple values were specified for an attribute that can have only one value”

  24. Mona says:

    @GoateePFE
    Hi I am trying to use your script to replace the description field with a “Word” and the employeeNumber from the Attribute tab. Currently I have it such as:
    Get-ADUser ‘myuser’ -Properties Name, Description | ForEach-Object {Set-ADObject -Identity $_.DistinguishedName -Replace @{Description=”Word, $($_.employeenumber)”}}

    It is placing the “Word” into the description field, but not the employeenumber.

    If I remove the “Word” and run the following:

    Get-ADUser ‘myuser’ -Properties Name, Description | ForEach-Object {Set-ADObject -Identity $_.DistinguishedName -Replace @{Description=$_.EmployeeNumber}}

    I get the following error:
    Set-ADObject : Cannot validate argument on parameter ‘Replace’. The argument is null or an element of the argument collection contains a null value.

    I’d like the script to retrieve the employee number and with the “Word” place into the description field. It should look like this: Word, 1234567

    Any help would be appreciated!

    1. Make sure that you include “employeenumber” in the Properties list for Get-ADUser. That should do it.

  25. Christopher says:

    Thank you for your Blog Ashley, been a great leaning tool for a newbie!

    I have successfully been able to replace the extensionAttribute1 with the Description using the below script.

    But what I would like to do if possible is do say an if statement maybe.

    Such as,
    if Description = ‘1’ add ‘Word1’ to extensionAttribute1 field.
    if Description = ‘2’ add ‘Word2’ to extensionAttribute1 field.
    if Description = ‘3’ add ‘Word3’ to extensionAttribute1 field.

    Get-ADUser ‘sampleuser’ -Properties Name, Description, employeenumber, extensionAttribute1 | ForEach-Object {Set-ADObject -Identity $_.DistinguishedName -Replace @{extensionAttribute1=”$($_. Description)”}}

    1. Christopher says:

      Okay so I have figured it out for one user.

      Get-ADUser ‘xxxxxxx’ -Properties Name, State, extensionAttribute1 | ForEach-Object {
      $newAttribute = $null
      if($_.State -eq ‘NSW’) {
      $newAttribute = ‘10.224.68.101’
      }
      if($_.State -eq ‘ACT’) {
      $newAttribute = ‘10.224.68.101’
      }
      elseif($_.State -eq ‘TAS’) {
      $newAttribute = ‘10.224.68.103’
      }
      elselseif($_.State -eq ‘VIC’) {
      $newAttribute = ‘10.224.68.103’
      }
      elseif($_.State -eq ‘WA’) {
      $newAttribute = ‘10.224.68.107’
      }
      elseif($_.State -eq ‘SA’) {
      $newAttribute = ‘10.224.68.107’
      }
      elseif($_.State -eq ‘NT’) {
      $newAttribute = ‘10.224.68.107’
      }
      elseif($_.State -eq ‘QLD’) {
      $newAttribute = ‘10.224.68.105’
      }

      if($newAttribute -ne $null) {
      Set-ADObject -Identity $_.DistinguishedName -Replace @{ extensionAttribute1=$newAttribute }
      }
      }

      That works but when i try the below for every user in an OU it doesnt work

      Get-ADUser -searchbase “OU=Admin Users,DC=xxx,DC=com,DC=au” -LDAPFilter ‘(State=*)’ -Properties Name, State, extensionAttribute1 | ForEach-Object {
      $newAttribute = $null
      if($_.State -eq ‘NSW’) {
      $newAttribute = ‘10.224.68.101’
      }
      elseif($_.State -eq ‘ACT’) {
      $newAttribute = ‘10.224.68.101’
      }
      elseif($_.State -eq ‘TAS’) {
      $newAttribute = ‘10.224.68.103’
      }
      elseif($_.State -eq ‘VIC’) {
      $newAttribute = ‘10.224.68.103’
      }
      elseif($_.State -eq ‘WA’) {
      $newAttribute = ‘10.224.68.107’
      }
      elseif($_.State -eq ‘SA’) {
      $newAttribute = ‘10.224.68.107’
      }
      elseif($_.State -eq ‘NT’) {
      $newAttribute = ‘10.224.68.107’
      }
      elseif($_.State -eq ‘QLD’) {
      $newAttribute = ‘10.224.68.105’
      }

      if($newAttribute -ne $null) {
      Set-ADObject -Identity $_.DistinguishedName -Replace @{ extensionAttribute1=$newAttribute }
      }
      }

      And ideas??

      Much appreciated

      1. Hi Christopher,
        As a PowerShell beginner I think you will like using the Switch statement instead of the cumbersome ElseIf logic. Look at “get-help about_switch”. That will simplify your logic and perhaps give you some clarity on what is failing. Without specific error message text that is all I can offer.
        Hope this helps,
        Ashley
        GoateePFE

        1. Chris says:

          Hi Ashley, I would appreciate your expertise with the following: I’m trying to read a .csv or text file into Powershell based on DistinguishedName via: Import-CSV .\UNIX_Attributes.csv and then using For Each, looping through all the accounts with Set-ADObject to populate 3 UNIX Attributes that will not populate through Set-ADUser.

          For one account at a time, this works: Get-ADObject “CN=Lastname\, Firstname,OU=Users,DC=abcd,DC=com”| Set-ADObject -Add @{gidNumber=”8000″;uid=”3599″;unixHomeDirectory=”/home/foldername”}

          For a bulk update, this doesn’t work so was wondering if you can suggest any corrections?
          Import-CSV .\UNIX_Attributes.csv | ForEach-Object {Get-ADUser -Filter “DistinguishedName -eq `”$($_.DistinguishedName)`”” | Set-ADObject -gidNumber $_.gidNumber -uid $_.uid -unixHomeDirectory $_.unixHomeDirectory}

          This also won’t cooperate:
          Import-CSV .\UNIX_Attributes.csv | ForEach-Object {Get-ADObject -Filter “DistinguishedName -eq `”$($_.DistinguishedName)`”” | Set-ADObject -gidNumber $_.gidNumber -uid $_.uid -unixHomeDirectory $_.unixHome
          Directory}

          In each case I get: Set-ADObject : A parameter cannot be found that matches parameter name ‘gidNumber’.
          At line:1 char:138
          + … me -eq `”$($_.DistinguishedName)`”” | Set-ADObject -gidNumber $_.gidN …
          + ~~~~~~~~~~
          + CategoryInfo : InvalidArgument: (:) [Set-ADObject], ParameterBindingException
          + FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.ActiveDirectory.Management.Commands.SetADObject

          So I guess that’s illegal syntax I’m using but not sure what works there instead? b/t/w, I do bulk updates all the time but never had to address the UNIX Attributes in the sample. My testing was on user accounts which are not populated with any values for the aforementioned UNIX Attributes so I’m not replacing anything if that helps?

          1. Hi Chris,
            You are really close. Try this:

            Import-CSV .\UNIX_Attributes.csv | ForEach-Object {
            Set-ADObject -Identity $_.DistinguishedName -Add @{
            gidNumber=$_.gidNumber;
            uid=$_.uid;
            unixHomeDirectory=$_.unixHomeDirectory
            }
            }

            Let me know if that works.
            Ashley
            GoateePFE

  26. Jake Parnell says:

    Hi Ashley,

    I’ve tried the following script to try and copy the description of a user to the job title, I get no errors although nothing happens.

    Import-Module ActiveDirectory

    Get-ADUser -LDAPFilter ‘(Department=*)’ -Properties Description, Title |
    Select-Object * -First 2 |
    ForEach-Object {Set-ADObject -Identity $_.DistinguishedName `
    -Replace @{Title=$($_.Description

    Any Ideas would be much appreciated

    Many Thanks

    1. Jake Parnell says:

      Excuse my fat fingers, here’s the code without the bit chopped off at the end.

      Import-Module ActiveDirectory

      Get-ADUser -LDAPFilter ‘(Department=*)’ -Properties Description, Title |
      Select-Object * -First 2 |
      ForEach-Object {Set-ADObject -Identity $_.DistinguishedName `
      -Replace @{Title=$($_.Description)}}