none
PowerShell script for AD name change RRS feed

  • Question

  • I need to change all users logon name in AD to their first name.last name    Server 2003

    Wednesday, June 20, 2012 5:32 PM

Answers

  • The best method by far is to add clauses to the filter, so the resultset retrieved from AD only includes users with values assigned to both givenName and sn. For example:


    $Searcher.Filter = "(&(objectCategory=person)(objectClass=user)(givenName=*)(sn=*))"

    -----

    Note you don't need the clause to exclude computers, because they do not have objectCategory person. Also, all users must have value assigned to sAMAccountName, so it doesn't matter what the value is. It is not possible to filter sAMAccountName on any combination of givenName and sn.


    Richard Mueller - MVP Directory Services

    • Proposed as answer by Bigteddy Sunday, June 24, 2012 6:21 AM
    • Marked as answer by IamMred Friday, June 29, 2012 7:22 PM
    Friday, June 22, 2012 5:39 PM
    Moderator

All replies

  • If you're looking to change the sAMAccountName, here's the code. In it's current state it will only show a preview of what's to come. If you really want to run it, change the value of the $previewMode variable from $true to $false. I would NOT recommend doing that out of the gate. Run this script as-is to verify that the name changes are what you want and expect!!!

    #-----------------------------------------------------------------------------------------
    $previewMode = $true #change value to $false ONLY after you have verified the preview!!!
    
    $CurrentDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
    $Domain = [System.DirectoryServices.DirectoryEntry]"LDAP://$CurrentDomain"
    $Searcher = New-Object System.DirectoryServices.DirectorySearcher
    $Searcher.PageSize = 1000
    $Searcher.SearchScope = "Subtree"
    
    $Searcher.Filter = "(&(objectCategory=person)(objectClass=user)(!(objectClass=computer)))"
    $Searcher.PropertiesToLoad.Add("userPrincipalName") > $Null
    $Searcher.PropertiesToLoad.Add("givenName") > $Null
    $Searcher.PropertiesToLoad.Add("sn") > $Null
    $Searcher.PropertiesToLoad.Add("distinguishedName") > $Null
    
    $Searcher.SearchRoot = "LDAP://" + $Domain.distinguishedName
    $SearchResults = $Searcher.FindAll()
    $dnsSuffix = $CurrentDomain.Name
    
    foreach ($Result in $SearchResults){
     	$firstName = $Result.Properties.Item("givenName")
     	$lastName = $Result.Properties.Item("sn")
     	$newLogonName = "$firstName.$lastName"
    	[System.DirectoryServices.DirectoryEntry]$userObject = $Result.GetDirectoryEntry()
    	$oldLogonName = $userObject.sAMAccountName
        Write-Host "Changing $oldLogonName to $newLogonName..."
        try {
            if (!($previewMode)) {
        	   $userObject.sAMAccountName = $newLogonName            
               $userObject.CommitChanges() 
            }
            Write-Host "$oldLogonName successfully changed to $newLogonName."
        }
        catch{
            Write-Host "Error changing $oldLogonName to $newLogonName."
        }
    }  
    #-----------------------------------------------------------------------------------------

    • Proposed as answer by OwenINX Tuesday, August 7, 2012 1:18 AM
    Wednesday, June 20, 2012 8:47 PM
  • If by logon name you mean the "pre-Windows 2000 logon" name, this is the value of the sAMAccountName attribute and Anthony's script should help. However, remember that sAMAccountName must be unique in the domain. Also, what if givenName (first name) or sn (last name) have no value? You might want to skip those cases. If instead you want to change the "name" of the user object (the value of the cn attribute, also called the Common Name), then you must rename the object in AD. In this case, the value of the cn attribute need only be unique in the parent OU/container (where the object resides in AD). Also, sAMAccountName for users  is limited to 20 characters, for cn 64 characters.


    Richard Mueller - MVP Directory Services

    Wednesday, June 20, 2012 8:48 PM
    Moderator
  • That's a really good point on Richard's part...I am NOT checking for null values for first and last name, which normally I would do. If the OP responds, I will modify the script to perform that type of check. 

    EDIT: Also to Richard's point I am not changing the LDAP common name (cn) or userPrincipalName for that matter.

    Wednesday, June 20, 2012 9:08 PM
  • I have exported all CN's for existing OU's to a CSC file.
    Here is the header line that was created:
    Name,Description,Type,

    I have 10 OU's

    I want to change the existing "User Logon Name" to be their first name.last name

    I have installed PowerShell Ver 2 on a Windows 2003 server

    Thursday, June 21, 2012 12:57 PM
  • A couple of points:

    • Your csv file does not uniquely identify the users in the general case. The cn attribute is only unique in the parent OU. All AD cmdlets (and all other methods of retrieve user information) require either the distinguishedName, the sAMAccountName (the "pre-Windows 2000 logon" name), the objectSID, or the objectGUID. In your case, if all users are in a specified (hard coded) OU, then the cn can be used to find the users, but a search or query will be required. That is, unless we can be sure that the cn value is identical to the sAMAccountName.
    • We need to be clear what attribute is being changed. I believe you want to change the sAMAccountName (and not the cn attribute).
    • The script should skip any users that do not have both a first name (givenName) and last name (sn).
    • Assuming the sAMAccountName is to be updated, you should either skip any users where <givenName>.<sn> exceeds 20 characters, or you should decide how to truncate the value.
    • You need to either trap the possible error if the sAMAccountName is not unique in the domain, or check first if the proposed value is used elsewhere.
    • It would also make sense to only update the user if the sAMAccountName is not already in the desired form.

    If you used some method to export the users into your csv file, I suggest you repeat this process to document the sAMAccountName's of the users. I see no reason to include the description.

    Also, what do you mean by "type"? What attribute of user objects does this correspond to? Perhaps it is not needed for this project.

    Also, if all users in the 10 OU's are to updated, it would be simpler to just enumerate the users in the specified OU's.


    Richard Mueller - MVP Directory Services

    Thursday, June 21, 2012 2:16 PM
    Moderator
  • Richard's bullet points serve well as a criteria list. I'll modify my script to meet those.

    Richard, another thing I was thinking about in regard to the first draft of my script above: After I instantiate the DirectoryEntry object (with this line: [System.DirectoryServices.DirectoryEntry]$userObject = $Result.GetDirectoryEntry() ), I assume I should call the .Dispose method on $userObject? I remember someone from MS at one point telling me that any ADSI object required one to call .Dispose lest that loop would chew up memory. I assume DirectoryEntry would have the same issue?

    Thursday, June 21, 2012 3:46 PM
  • I am not expert in System.DirectoryServices.DirectoryEntry, but I don't recall ever seeing the Dispose method used. Most PowerShell scripts use the [ADSI] type accelerator, which is a shortcut (wrapper) for System.DirectoryServices. There is also the [ADSISearcher] accelerator.


    Richard Mueller - MVP Directory Services

    Thursday, June 21, 2012 4:51 PM
    Moderator
  • Looking back at my notes I actually got an out of memory error when I first started using PowerShell when reading/modifying users in loops as shown above. I need to play around with a few things but it seems that both the DirectoryEntry and DirectorySearcher objects have Dispose methods:

    http://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.dispose.aspx

    Friday, June 22, 2012 2:18 PM
  • Here is my suggestion of a PowerShell V1 script to rename all users (sAMAccountName):


    # Filter on all users that have givenName and sn assigned.
    $searcher=[adsisearcher]'(&(objectCategory=person)(objectClass=user)(givenName=*)(sn=*))'
    [void]$searcher.PropertiesToLoad.Add('distinguishedName')
    [void]$searcher.PropertiesToLoad.Add('sAMAccountName')
    [void]$searcher.PropertiesToLoad.Add('givenName')
    [void]$searcher.PropertiesToLoad.Add('sn')
    $searcher.PageSize = 200
    $Results = $searcher.FindAll()
    ForEach ($Result In $Results)
    {
        # Retrieve values.
        $DN = $Result.Properties.Item("distinguishedName")
        $NTName = $Result.Properties.Item("sAMAccountName")
        $First = $Result.Properties.Item("givenName")
        $Last = $Result.Properties.Item("sn")
        # Construct desired "pre-Windows 2000 logon" name.
        $NewName = "$First.Last"
        # Make sure new name 20 characters or less.
        If ($NewName.Length -gt 20)
        {
            # I don't know how to trim $First in PowerShell to make $NewName 20 characters.
        }
        # Check if name should be updated (case insensitive).
        If ($NTName -ine $NewName)
        {
            # Trap possible error.
            Trap
            {
                "Unable to rename user $DN to $NewName"
                "Error description: $_"
                Continue
            }
            # Bind to user object in AD.
            $User = [ADSI]"LDAP://$DN"
            # Assign new name.
            $User.sAMAccountName = $NewName
            # Save the change to AD.
            $User.SetInfo()
        }
    }

    -----

    Test first, by commenting out the SetInfo() statement and echo $DN and $NewName for all  users. Note that the script could trim $First to limit $First.$Last to 20 characters, but I could not find a way to do this. Also, this script does nothing to prevent duplicate sAMAccountName values, but the possible error will echo to the screen.


    Richard Mueller - MVP Directory Services


    Friday, June 22, 2012 4:41 PM
    Moderator
  • There must be a better way to trim the new name to 20 characters, but this worked for me:


    If ($NewName.Length -gt 20)
    {
        # Name longer than 20 characters
        $c = 19 - $Last.Length
        $NewFirst = ""
        For ($k = 0;$k -lt $c;$k = $k + 1)
        {
            $NewFirst = $NewFirst + $First[$k]
        }
        $NewName = "$NewFirst.$Last"
    }

    -----



    Richard Mueller - MVP Directory Services

    Friday, June 22, 2012 5:11 PM
    Moderator
  • Richard, what would be the best way in PowerShell V2 to determine that the first and last names are not null? I'm seeing some pretty strange results when I test against those conditions depending on the script, even if the property value return types are the same. I can't seem to do a "-eq $null" check of the resulting property collection, so I grabbed an "IsNullOrEmpty" method I found which works here (the user's first name is populated while the last name is not):

    Clear-Host
    
    function IsNullOrEmpty($inputString){
    	[bool]$returnValue = $false
    	if (!($inputString)){		
    		$returnValue = $true
    	}
    	Return $returnValue
    }
    
    $dn = "CN=nameless,OU=thisOU,DC=mydomain,DC=local"
    
    $Result = [System.DirectoryServices.DirectoryEntry]"LDAP://$dn"
    
    $firstName = $Result.Properties.Item("givenName")
    $lastName = $Result.Properties.Item("sn")
    
    IsNullOrEmpty $firstName.Value
    IsNullOrEmpty $lastName.Value

    The result to the console is:

    False

    True

    The next block of code is a modification of the script I posted above simply to determine the best method to check for empty or null values:

    Clear-Host
    
    function IsNullOrEmpty($inputString){
    	[bool]$returnValue = $false
    	if (!($inputString)){		
    		$returnValue = $true
    	}
    	Return $returnValue
    }
    
    
    $CurrentDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
    $Domain = [System.DirectoryServices.DirectoryEntry]"LDAP://$CurrentDomain"
    $Searcher = New-Object System.DirectoryServices.DirectorySearcher
    $Searcher.PageSize = 1000
    $Searcher.SearchScope = "Subtree"
    
    $Searcher.Filter = "(&(objectCategory=person)(objectClass=user)(sAMAccountName=nameless)(!(objectClass=computer)))"
    $Searcher.PropertiesToLoad.Add("givenName") > $Null
    $Searcher.PropertiesToLoad.Add("sn") > $Null
    $Searcher.PropertiesToLoad.Add("sAMAccountName") > $Null
    
    $Searcher.SearchRoot = "LDAP://" + $Domain.distinguishedName
    $SearchResults = $Searcher.FindAll()
    $dnsSuffix = $CurrentDomain.Name
    
    foreach ($Result in $SearchResults){
     	$firstName = $Result.Properties.Item("givenName")
     	$lastName = $Result.Properties.Item("sn")
     	
    	IsNullOrEmpty $firstName.Value
    	IsNullOrEmpty $lastName.Value
     }  
    

    ..and that block of code shows this for console output:

    True

    True

    Do you have any idea why the second block of code doesn't show the same results as the first? If not what's the best way to check for empty/null values on AD attributes?

    Friday, June 22, 2012 5:27 PM
  • The best method by far is to add clauses to the filter, so the resultset retrieved from AD only includes users with values assigned to both givenName and sn. For example:


    $Searcher.Filter = "(&(objectCategory=person)(objectClass=user)(givenName=*)(sn=*))"

    -----

    Note you don't need the clause to exclude computers, because they do not have objectCategory person. Also, all users must have value assigned to sAMAccountName, so it doesn't matter what the value is. It is not possible to filter sAMAccountName on any combination of givenName and sn.


    Richard Mueller - MVP Directory Services

    • Proposed as answer by Bigteddy Sunday, June 24, 2012 6:21 AM
    • Marked as answer by IamMred Friday, June 29, 2012 7:22 PM
    Friday, June 22, 2012 5:39 PM
    Moderator
  • I'd prefer to do it in the filter anyhow. Much thanks.

    Friday, June 22, 2012 5:51 PM
  • This is an easier way:


    $PreWindows2000Name
    ="LongNameWayOver20Characters"


    If  ($PreWindows2000Name.Length -gt 20)

    { 
          #If name is longer than 20 characters, cut it down to 20 characters

          $PreWindows2000Name=$PreWindows2000Name.Substring(0, 20)

    }
    Tuesday, December 18, 2012 5:42 PM
  • Hi,

    I can see that you can specify the OU and Domain for which to pick up the Users from, however I want to test first with only a few users from the same OU (but not all of them) and therefore was thinking of CSV Import.

    Can you advise the Import-CSV command for this script please??

    I have already tested on one user in an OU using the DistinguishedName and the script worked perfectly thanks!

    Thanks Very Much

    Holly Wilkinson

    Tuesday, August 12, 2014 1:36 PM
  • Question is very old and thread is closed.  You are asking a completely new and different question.  Please start your own thread and provide your script and any errors.

    Thank you.


    ¯\_(ツ)_/¯

    Tuesday, August 12, 2014 2:11 PM