Monday, August 11, 2014

Powershell Find and Replace the Remote Desktop Services Profile

A customer of mine has configured all roaming user profiles on a Remote Desktop Services environment through the user account in Active Directory instead of utilising Group Policy setting "Set roaming profile path for all users logging onto this computer", the Microsoft preferred method as it ensures each profile folder matches the username and prevents inconsistencies which may encore as a result of an administrator incorrectly naming a folder.  It is recommended all Remote Desktop Services environments utilise Group Policy as a means of setting roaming profile and not the Active Directory user accounts.

I am in the process of implementing a new file server into the customers environment and updating the Remote Desktop Services profiles to point to the new file server.  My customer has the remote desktop services profile specified on each user account and there are inconsistencies as to how the profile is named, it does not always match username!  As a result we are not able to simply move to Group Policy roaming profile mapping moving forward.

I need a way of performing a find and replace to update the Remote Desktop Services User Profile path to match the new file server.  I achieved this by writing a PowerShell script to perform this task which I would like to share with you - here is a copy of my code:


$erroractionpreference = “stop"

 

$pDirValueOld = "\\oldserver\rdsprofiles"

$pDirValueNew = "\\newserver\rdsprofiles"

 

$profilepath = $null

 

$searcher = New-Object adsisearcher

$searcher.Filter = "(&(objectCategory=person)(objectClass=user))"

$searcher.SearchRoot = "LDAP://OU=Users,OU=Avantgarde Technologies,OU=Companies,OU=Active Directory,DC=at,DC=local"

$searcher.PageSize = 1000

$results = $searcher.FindAll()

 

 

foreach($result in $results)

{

$ADuser = [adsi]$result.Path

        foreach($user in $ADuser)

        {

        echo $user.distinguishedName

        $profilepath = $null

        $profilepath = $user.psbase.InvokeGet(“TerminalServicesProfilePath") -replace [regex]::Escape($pDirValueOld),($pDirValueNew)

        $user.psbase.InvokeSet(“TerminalServicesProfilePath",$profilepath)

        $user.setinfo()

        }

}  

To utilise this code you want to modify the following values:

$pDirValueOld = "\\oldfileserver\share"

$pDirValueNew = \\newfileserver\share

$searcher.SearchRoot = "LDAP://OU=Users,OU=Avantgarde Technologies,OU=Companies,OU=Active Directory,DC=at,DC=local
  • pDirValueOld is the value you want to search for to be replaced.
  • pDirValueNew is the value you wish to set the profile to.
  • $searcher.SearchRoot is the LDAP path in Active Directory you wish to run this query recursively against.
Now I have a bunch of users in the Avantgarde Technologies --> Users OU which need to be updated.  As you see I have my Remote Desktop Services profile populated below:

 
I put the code into my PowerShell ISE development environment (as an alternative from saving it as a .ps1 script).  Then I made sure the following fields were populated correctly.
 
 
Run the code and all users recursively will have the Remote Desktop Services profile updated to point to the new path through a find and replace!
 
We can see the profile was successfully updated.
 
 
You can also modify my above code to perform a fine and replace on other items in the user account!

Hope you have found this post helpful!

9 comments:

  1. Thanks! I had exactly the same issue with my new employer and your script worked like a charm!!! Much appreciated!!!

    Chris, Netherlands

    ReplyDelete
  2. Is it possible to use a blank value to remove the profile path from all users?

    ReplyDelete
  3. Of course. Simply add the replace value with "" meaning nothing.

    ReplyDelete
  4. Well, as I am not a PS expert, could you please help me with the message I get when I run your script in several OUs? This happens when the OU contains many users, but it happened sometimes to see it also in OUs which contain just 6-7 users. Here is what I get:

    Exception calling "InvokeGet" with "1" argument(s): "The directory property cannot be found in the cache.
    "
    At line:64 char:9
    + $profilepath = $user.psbase.InvokeGet(“TerminalServicesProfilePath") -re ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : DotNetMethodTargetInvocation

    Thank you very much in advance!

    ReplyDelete
  5. Also, I was expecting that the output would show only the users who still have the old settings. But if I run it ,for example, next day, I am still getting the same results, even though if the accounts have the new settings.

    ReplyDelete
  6. OK, to help you a bit, I found that this only happens on user objects where the 'User Profile' is blank AND where it has never been filled in before. But how can I get just a notification message instead of an error message which stops the procedure?

    ReplyDelete
  7. I experience the same error in my environment. You can switch the top line $erroractionpreference = "stop" to $erroractionpreference = "inquire" and the script will stop at each error at least, but I haven't been able to get this to work in my case where I just want to simply set the previously blank value to a new path.

    ReplyDelete
  8. #Script Update to remove any values
    $erroractionpreference = “continue"


    $searcher = New-Object adsisearcher
    $searcher.Filter = "(&(objectCategory=person)(objectClass=user))"
    $searcher.SearchRoot = "LDAP://OU=RHSC,DC=RHSC,DC=local"
    $searcher.PageSize = 1000
    $results = $searcher.FindAll()

    foreach($result in $results)

    {

    $ADuser = [adsi]$result.Path
    foreach($user in $ADuser)
    {
    echo $user.distinguishedName
    $profilepath = $null
    $profilepath = $user.psbase.InvokeGet(“TerminalServicesProfilePath") -replace [regex]::Escape($user.psbase.InvokeGet(“TerminalServicesProfilePath")),($null)
    $user.psbase.InvokeSet(“TerminalServicesProfilePath",$profilepath)
    $user.setinfo()
    }
    }

    ReplyDelete
    Replies
    1. #Script Update to remove any values, quick update when I found how to remove the RemoteDesktop Services Profile Home Folder
      $erroractionpreference = “continue"


      $searcher = New-Object adsisearcher
      $searcher.Filter = "(&(objectCategory=person)(objectClass=user))"
      $searcher.SearchRoot = "LDAP://OU=RHSC,DC=RHSC,DC=local"
      $searcher.PageSize = 1000
      $results = $searcher.FindAll()

      foreach($result in $results)

      {

      $ADuser = [adsi]$result.Path
      foreach($user in $ADuser)
      {
      echo $user.distinguishedName
      echo $user.psbase.InvokeGet(“TerminalServicesHomeDirectory")
      echo $user.psbase.InvokeGet(“TerminalServicesProfilePath")
      echo $user.psbase.InvokeGet(“TerminalServicesHomeDrive")
      $profilepath = $null
      $profilepath = $user.psbase.InvokeGet(“TerminalServicesProfilePath") -replace [regex]::Escape($user.psbase.InvokeGet(“TerminalServicesProfilePath")),($null)
      $profilepath2 = $user.psbase.InvokeGet(“TerminalServicesHomeDirectory") -replace [regex]::Escape($user.psbase.InvokeGet(“TerminalServicesHomeDirectory")),($null)
      $user.psbase.InvokeSet(“TerminalServicesProfilePath",$profilepath)
      $user.psbase.InvokeSet(“TerminalServicesHomeDirectory",$profilepath2)
      $user.setinfo()
      }
      }

      Delete