none
Bulk User Object Attribute Change - HELP!

    问题

  • Desired End Result:  Change user object displayName and CN/RDN

    Here is what I am doing to accomplish end result:

    1: Export user object displayName attribute via CSVDE to CSV file (No problems)

    2:Manipulate data in CSV file to reflect desired changes (No problems)

    3.Convert CSV file to LDF file via log parser (No problems)

    4.Import LDF file via LDIFDE (No problems)

    **Display Name changed for all user object at this point**

    5.Execute Admodify

    6.Select user objects to modify the CHANGE CN (RDN) option to the following 'displayName'

     -By doing this the RDN/CN/Name will be the same as the displayName attriburte

    **Errors out with the following information: (An invalid dn syntax has been specified HRESULT: 0x80072032)**

    Caveat:

    I can get step 6 to work IF the distinguishedname does not have a foward slash (/).  Currently most user objects are named:

    Doe, John Z CPT ABC/123

    If a name does not have a forward slash (/) step 6 will run without problems. It looks like when admodify finds the users it adds a backslash (\) in the DN next to the forward slash causing DN syntax errors.

    Please Help!

    Richard

    2012年6月16日 9:09

答案

  • Which ADModify are you using, ADModify.exe or ADModify.ps1? You may have found a bug. If so, there may be no workaround. Also, how do you select the users to be modified? If you use a command for this, what is it? Is the forward slash in the old or in the new RDN value (or both)?

    Some characters, such as the comma, must be escaped if they appear in RDN's. The AD escape character is the backslash (\). However, the forward slash (/) only needs to be escaped if ADSI is used. For example, if you use VBScript any for forward slash characters must be escaped with the backslash. VBScript uses ADSI to communicate with AD. If ADModify uses ADSI to retrieve the Distinguished Names (DN's) of the users, but does not use ADSI to modify the users (perhaps using the PowerShell AD modules, like Get-ADUser), that could account for what you experience.

    If the RDN of a user is "Doe, John Z CPT ABC/123", and ADSI is used to communicate with AD, then the DN used would be similar to "cn=Doe\, John Z CPT ABC\/123,ou=West,dc=MyDomain,dc=com".


    Richard Mueller - MVP Directory Services

    2012年6月16日 9:51
  • I developed a VBScript program to modify the RDN of all users in an OU to match the displayName. In the process, I learned something.

    When you retrieve the distinguishedName (DN) in VBScript (or any language or tool using ADSI), all characters will be properly escaped. However, the forward slash character will not. You must take the extra step to escape any forward slashes before using the DN to bind to the object. I assumed the same would be the case if you retrieved the ADsPath of the object. The ADsPath is the DN with the moniker "LDAP://" prepended. However, I was mistaken. No matter how I retrieve ADsPath, any forward slash characters are indeed escaped (as are all other characters that require it, such as the comma).

    When you modify the RDN, you cannot update the attribute (the cn attribute for user objects) directly. Instead, you must rename the object. In VBScript you use the MoveHere method of the parent OU object to rename. You pass the ADsPath of the object, plus the new RDN. When I started coding I expected that I would need to modify the ADsPath to escape any possible forward slash characters. However, I was too clever. Any forward slash characters are already escaped. If instead I had constructed the ADsPath by retrieving the DN and prepending "LDAP://", then I would have needed the extra step. Of course, if the new RDN (the value of the displayName attribute in your case) has any characters that must be escaped, then the code must escape them with the backslash escape character.

    In any case, here is the VBScript program I came up with:

    Option Explicit

    Dim objOU, objUser, strDisplay, strRDN

    ' Bind to the specified OU.
    Set objOU = GetObject("LDAP://ou=Sales,ou=West,dc=MyDomain,dc=com")

    ' Filter on objects with objectClass "user".
    objOU.Filter = Array("user")

    ' Enumerate all user objects.
    For Each objUser In objOU
        ' Skip computer objects (which have objectClass "user" but Class "computer").
        If (objUser.Class = "user") Then
            ' Retrieve RDN and displayName.
            strRDN = objUser.cn
            strDisplay = objUser.displayName
            ' Check if they match and displayName not blank.
            If ((LCase(strRDN) <> LCase(strDisplay)) And (strDisplay <> "")) Then
                ' The object must be renamed.
                ' Escape any commas in the new RDN.
                ' This assumes the value does not have any of the following characters:
                '  \ # + < > ; " = /
                strDisplay = Replace(strDisplay, ",", "\,")
                ' Trap possible errors (such as RDN not unique in the OU).
                On Error Resume Next
                objOU.MoveHere objUser.ADsPath, "cn=" & strDisplay
                If (Err.Number <> 0) Then
                    Wscript.Echo "Cannot rename " & objUser.distinguishedName _
                        & " to " & strDisplay
                    Wscript.Echo "Error Number: " & Err.Number
                    Wscript.Echo "Description: " & Err.Description
                End If
                On Error GoTo 0
            End If
        End If
    Next

    -----

    You must modify the statement that binds to the OU for your situation. As with most administrative scripts, this should be run at a command prompt using the cscript host program, in case there are any error messages. If the program is saved in a file named UpdateRDN.vbs, then at the command prompt of any computer joined to the domain, enter:

    cscript UpdateRDN.vbs

    This assumes you are in the folder where the file UpdateRDN.vbs is saved. Otherwise, you must specify the full path to the file. Also, you must be logged in as a user with permissions to modify the user objects.


    Richard Mueller - MVP Directory Services

    2012年6月16日 16:24
  • All the characters that need to be escaped in AD are documented in the Wiki article you linked. You can see this using a tool like ADSI Edit to view the distinguishedName attribute of objects. The escape character (the backslash) tells AD to accept whatever follows literally. For most of the characters I don't know why they must be escaped. Obviously, the comma separates components in DN values, to that must be escaped, and AD tends to strip off leading and trailing spaces, so that explains those characters. The "=" character would be confusing if it were not escaped. And, if the name has a backslash, then to prevent it from being interpreted as the escape character, it must be escaped. Thus "\\" resolves into "\". Also, any character, even normal ones, can be escaped. For example "\a" will simply resolve into "a". Finally, on distinguishedNames need to be escaped in AD. There is no such thing in any other attribute.

    You will notice that the forward slash character is not escaped when you view DN values in ADSI Edit. AD does not require that this character be escaped, but ADSI interprets the forward slash as a separator. For instance "LDAP://MyServer/ou=West,dc=MyDomain,dc=com".

    Most of the time we don't need to worry about which characters to escape (except the forward slash if we use ADSI), because when we retrieve DN's from AD the value will have all characters escaped that need it. However, in the code I posted, we are copying the value of another attribute, displayName, and using that to rename the object. We need to escape in this case. In the code I escaped all commas, but I cautioned that if you use any of the other characters, they would need to be escaped as well, using the Replace function. For completeness, you can use:

    strDisplay = Replace(strDisplay, ",", "\,")
    strDisplay = Replace(strDisplay, "\", "\\")
    strDisplay = Replace(strDisplay, "/", "\/")
    strDisplay = Replace(strDisplay, "#", "\#")
    strDisplay = Replace(strDisplay, "+", "\+")
    strDisplay = Replace(strDisplay, "<", "\<")
    strDisplay = Replace(strDisplay, ">", "\>")
    strDisplay = Replace(strDisplay, ";", "\;")
    strDisplay = Replace(strDisplay, """", "\""")
    strDisplay = Replace(strDisplay, "=", "\=")

    -----

    Even this doesn't cover leading and trailing spaces. Notice above that the (") character must be doubled in a quoted string, which is another form of escaping, in this case required by VBScript so it can properly parse quoted strings.

    Looking again at the code I posted, I neglected to escape the forward slash character. We know all of your names have both commas and forward slashes. When I tested I escaped both, but my code only operated on one test user (I didn't want to rename all users in an OU). I messed up when I copied into the code I posted.


    Richard Mueller - MVP Directory Services

    • 已标记为答案 Rich1221 2012年6月18日 6:57
    2012年6月17日 22:53

全部回复

  • Which ADModify are you using, ADModify.exe or ADModify.ps1? You may have found a bug. If so, there may be no workaround. Also, how do you select the users to be modified? If you use a command for this, what is it? Is the forward slash in the old or in the new RDN value (or both)?

    Some characters, such as the comma, must be escaped if they appear in RDN's. The AD escape character is the backslash (\). However, the forward slash (/) only needs to be escaped if ADSI is used. For example, if you use VBScript any for forward slash characters must be escaped with the backslash. VBScript uses ADSI to communicate with AD. If ADModify uses ADSI to retrieve the Distinguished Names (DN's) of the users, but does not use ADSI to modify the users (perhaps using the PowerShell AD modules, like Get-ADUser), that could account for what you experience.

    If the RDN of a user is "Doe, John Z CPT ABC/123", and ADSI is used to communicate with AD, then the DN used would be similar to "cn=Doe\, John Z CPT ABC\/123,ou=West,dc=MyDomain,dc=com".


    Richard Mueller - MVP Directory Services

    2012年6月16日 9:51
  • Richard....Thanks for the reply.

    I am using ADModify.exe.  I select users to be modified by:

    1)Choosing Domain List

    2)Choosing Domain Controller

    3)Browsing to the OU in the Domain Tree list

    4)Select AD to List

    All names are then added to the secondary window via DN.  Here is an example of a user that errors out and an user who is successfully modified.

    User Errors out:

    LDAP://CN=Doe\, John B SSgt USXXXX XXXX\/XXX,OU=xxxx,OU=xxxx,DC=xxxx,DC=XXX,DC=XXX

    This same user's DN attribute in AD is:

    CN=Doe\, John B SSgt USXXXX XXXX/XXX,OU=xxxx,OU=xxxx,DC=xxxx,DC=XXX,DC=XXX

    **Notice the backslash is not there***

    Here is a user that I can modify without error:

    CN=Doe\, Jane A MSgt USXXXX,OU=xxxx,OU=xxxx,DC=xxxx,DC=XXX,DC=XXX

    **This user does not have any (/) slashes in their name.

    I don't use any command line, strickly GUI. The foward (/) is in the old RDN.  Your example is exactly how ADModify displays the name.

    Richard

    2012年6月16日 10:11
  • I would call this a bug in ADModify.exe. The forward slash must be escaped if you use ADSI, but this character is not escaped in AD itself. The comma character is escaped in AD (you can see this using the Attribute Editor in ADUC to view the DN). No matter how your retrieve the DN, the (/) will not be escaped. If you plan to update the attribute using ADSI, then you must escape this character yourself, because it is required by ADSI (not AD). It looks like ADModify is adding the (\) character, but apparently does not use ADSI, so the DN is flawed.

    Strangely, the documentation for ADModify indicates that it uses the forward slash as an escape character. That's unfortunate, and may account for this bug. Also, I find no references to anyone else encountering this problem before.

    If you have no ability to correct the DN in ADModify, then there is no workaround. You probably need to use an alternate method to update your users. You could use VBScript or PowerShell.


    Richard Mueller - MVP Directory Services

    2012年6月16日 14:00
  • I developed a VBScript program to modify the RDN of all users in an OU to match the displayName. In the process, I learned something.

    When you retrieve the distinguishedName (DN) in VBScript (or any language or tool using ADSI), all characters will be properly escaped. However, the forward slash character will not. You must take the extra step to escape any forward slashes before using the DN to bind to the object. I assumed the same would be the case if you retrieved the ADsPath of the object. The ADsPath is the DN with the moniker "LDAP://" prepended. However, I was mistaken. No matter how I retrieve ADsPath, any forward slash characters are indeed escaped (as are all other characters that require it, such as the comma).

    When you modify the RDN, you cannot update the attribute (the cn attribute for user objects) directly. Instead, you must rename the object. In VBScript you use the MoveHere method of the parent OU object to rename. You pass the ADsPath of the object, plus the new RDN. When I started coding I expected that I would need to modify the ADsPath to escape any possible forward slash characters. However, I was too clever. Any forward slash characters are already escaped. If instead I had constructed the ADsPath by retrieving the DN and prepending "LDAP://", then I would have needed the extra step. Of course, if the new RDN (the value of the displayName attribute in your case) has any characters that must be escaped, then the code must escape them with the backslash escape character.

    In any case, here is the VBScript program I came up with:

    Option Explicit

    Dim objOU, objUser, strDisplay, strRDN

    ' Bind to the specified OU.
    Set objOU = GetObject("LDAP://ou=Sales,ou=West,dc=MyDomain,dc=com")

    ' Filter on objects with objectClass "user".
    objOU.Filter = Array("user")

    ' Enumerate all user objects.
    For Each objUser In objOU
        ' Skip computer objects (which have objectClass "user" but Class "computer").
        If (objUser.Class = "user") Then
            ' Retrieve RDN and displayName.
            strRDN = objUser.cn
            strDisplay = objUser.displayName
            ' Check if they match and displayName not blank.
            If ((LCase(strRDN) <> LCase(strDisplay)) And (strDisplay <> "")) Then
                ' The object must be renamed.
                ' Escape any commas in the new RDN.
                ' This assumes the value does not have any of the following characters:
                '  \ # + < > ; " = /
                strDisplay = Replace(strDisplay, ",", "\,")
                ' Trap possible errors (such as RDN not unique in the OU).
                On Error Resume Next
                objOU.MoveHere objUser.ADsPath, "cn=" & strDisplay
                If (Err.Number <> 0) Then
                    Wscript.Echo "Cannot rename " & objUser.distinguishedName _
                        & " to " & strDisplay
                    Wscript.Echo "Error Number: " & Err.Number
                    Wscript.Echo "Description: " & Err.Description
                End If
                On Error GoTo 0
            End If
        End If
    Next

    -----

    You must modify the statement that binds to the OU for your situation. As with most administrative scripts, this should be run at a command prompt using the cscript host program, in case there are any error messages. If the program is saved in a file named UpdateRDN.vbs, then at the command prompt of any computer joined to the domain, enter:

    cscript UpdateRDN.vbs

    This assumes you are in the folder where the file UpdateRDN.vbs is saved. Otherwise, you must specify the full path to the file. Also, you must be logged in as a user with permissions to modify the user objects.


    Richard Mueller - MVP Directory Services

    2012年6月16日 16:24
  • Richard,

    Thank you for confirming.  I assume not many companies use a (/) in their name field.  I am not  a scripting guy.  Do you know of any forums with templates that I could follow?

    Richard

    2012年6月16日 16:25
  • If you use PowerShell V1, the code would be very similar to the VBScript example I posted earlier, because it depends on ADSI. However, the Active Directory modules available in PowerShell V2 do not depend on ADSI. I decided to test to make sure they don't have the same bug as ADModify. They don't. I was able to use code similar to the following in PowerShell V2:

    # Retrieve all users in specified OU with displayName.
    $Users = Get-ADUser -SearchBase "ou=Sales,ou=West,dc=MyDomain,dc=com" -LDAPFilter "(displayName=*)" -Properties displayName
    ForEach ($User In $Users)
    {
        # Retrieve values
        $DN = $User.distinguishedName
        $RDN = $User.Name
        $Display = $User.displayName
        # Check if RDN and displayName already match (case insensitive).
        If ($RDN -ine $Display)
        {
            Rename-ADObject -Identity $DN -NewName $Display
        }
    }

    -----



    Richard Mueller - MVP Directory Services

    2012年6月16日 17:06
  • My test domain is one of the few that uses such characters. I have names with every possible keyboard character, plus some foreign characters, just to make sure they are possible and to catch any possible gotchas in scripts. I have never seen guidelines on naming conventions. It depends on the organization. Here is an old forum discussion with advice:

    http://social.technet.microsoft.com/Forums/en-CA/winserverDS/thread/eeb81bcc-0527-4de7-8459-4f23bc699863

    Most of the comments are about what is not allowed and the requirements for uniqueness. Note the links Paul provided.


    Richard Mueller - MVP Directory Services


    2012年6月16日 17:17
  • Richard,

    Great article!  And thank you so much for the script.  I will work with it on Monday.  Looks like you have put in so much work into my question.  You don't know how much I appreciate what you have done. 

    I do have one other question.

    Can you explain to me what the purpose of escaping characters?  I have read this article, but it only tells me what characters to escape.

    http://social.technet.microsoft.com/wiki/contents/articles/5312.active-directory-characters-to-escape-en-us.aspx

    Thank you!!

    Richard


    • 已编辑 Rich1221 2012年6月17日 21:19 OK
    2012年6月17日 21:16
  • Richard,

    Another questions regarding your last line:

    "Of course, if the new RDN (the value of the displayName attribute in your case) has any characters that must be escaped, then the code must escape them with the backslash escape character."

    The new RDN will have an escape character.  How do I escape that character.  The new display name will also have the (/).  All names currently have XXXXX/ZZZ at the end of each name.  The XXXXX will change for every user keeping /ZZZ the same.

    2012年6月17日 21:25
  • Richard,

    I just noticed that you wrote the article in which I posted.  Awesome!

    2012年6月17日 21:28
  • All the characters that need to be escaped in AD are documented in the Wiki article you linked. You can see this using a tool like ADSI Edit to view the distinguishedName attribute of objects. The escape character (the backslash) tells AD to accept whatever follows literally. For most of the characters I don't know why they must be escaped. Obviously, the comma separates components in DN values, to that must be escaped, and AD tends to strip off leading and trailing spaces, so that explains those characters. The "=" character would be confusing if it were not escaped. And, if the name has a backslash, then to prevent it from being interpreted as the escape character, it must be escaped. Thus "\\" resolves into "\". Also, any character, even normal ones, can be escaped. For example "\a" will simply resolve into "a". Finally, on distinguishedNames need to be escaped in AD. There is no such thing in any other attribute.

    You will notice that the forward slash character is not escaped when you view DN values in ADSI Edit. AD does not require that this character be escaped, but ADSI interprets the forward slash as a separator. For instance "LDAP://MyServer/ou=West,dc=MyDomain,dc=com".

    Most of the time we don't need to worry about which characters to escape (except the forward slash if we use ADSI), because when we retrieve DN's from AD the value will have all characters escaped that need it. However, in the code I posted, we are copying the value of another attribute, displayName, and using that to rename the object. We need to escape in this case. In the code I escaped all commas, but I cautioned that if you use any of the other characters, they would need to be escaped as well, using the Replace function. For completeness, you can use:

    strDisplay = Replace(strDisplay, ",", "\,")
    strDisplay = Replace(strDisplay, "\", "\\")
    strDisplay = Replace(strDisplay, "/", "\/")
    strDisplay = Replace(strDisplay, "#", "\#")
    strDisplay = Replace(strDisplay, "+", "\+")
    strDisplay = Replace(strDisplay, "<", "\<")
    strDisplay = Replace(strDisplay, ">", "\>")
    strDisplay = Replace(strDisplay, ";", "\;")
    strDisplay = Replace(strDisplay, """", "\""")
    strDisplay = Replace(strDisplay, "=", "\=")

    -----

    Even this doesn't cover leading and trailing spaces. Notice above that the (") character must be doubled in a quoted string, which is another form of escaping, in this case required by VBScript so it can properly parse quoted strings.

    Looking again at the code I posted, I neglected to escape the forward slash character. We know all of your names have both commas and forward slashes. When I tested I escaped both, but my code only operated on one test user (I didn't want to rename all users in an OU). I messed up when I copied into the code I posted.


    Richard Mueller - MVP Directory Services

    • 已标记为答案 Rich1221 2012年6月18日 6:57
    2012年6月17日 22:53
  • Richard,

    You are very well respected by me. (Probably doesn't matter to you).  I appreciate your detailed explaination, which now I clearly understand escape characters and how they interact via ADSI.  The VBS script works like a champ!!!! You are a lifesaver.

    Regards,

    Richard

    2012年6月18日 6:56