Hey, Scripting Guy! How Can I Edit Terminal Server Profiles for Users in Active Directory?

Hey, Scripting Guy! How Can I Edit Terminal Server Profiles for Users in Active Directory?

  • Comments 5
  • Likes

Hey, Scripting Guy! Question

Hey, Scripting Guy! I need to modify the terminal server profile for a bunch of users in Active Directory, and I don't get it. I can see the properties on Active Directory Users and Computers, but when I look in ADSI Edit, I cannot find the property names. I know they must be there, but I am at a loss to figure it out. Can you help?

- TD

SpacerHey, Scripting Guy! Answer

Hi TD,

Of course we can help. What you are asking is actually quite common. You have run into the difference between "Active Directory" and "Active Directory Users and Computers." You, of course, know the difference: Active Directory is the directory service that stores stuff about users, groups, computers, all kinds of security things, and even information used by applications such as Microsoft Exchange Server. In fact, since Active Directory is at its heart a database. It can be used for all kinds of things.

Active Directory Users and Computers is simply an Active Directory application. It is used primarily for configuring things related to users, computers, and groups. It is first and foremost a network administrator's tool. Because it is a network administrator's tool, the Terminal Service added a tab to Active Directory Users and Computers to give administrators an easy way to configure Terminal Service settings for users. Honestly and truly, they did this to help out-not to create tons of confusion or cause millions of network administrators around the world to waste countless hours searching for the missing Terminal Service attributes in Active Directory. What are we talking about? In Figure 1, we have the Terminal Services Profile tab:

Figure 1 The Terminal Services Profile tab

The Terminal Services Profile tab

 

Even though this is great for configuring a few terminal server users, it does not scale well beyond about three users. So we need to be able to script the settings. This should be easy using ADSI scripting. All we need to do is look in ADSI Edit and try to find the terminal server profile attribute names. So we open ADSI Edit, and we connect to our test user:

Figure 2 Connecting to a test user via ADSI Edit

Connecting to a test user via ADSI Edit

 

As you can see, you cannot see any Terminal Server attributes. They are missing in action as TD mentioned in his e-mail to us. So what can you do about this? Well, you can use WMI to configure these settings, and I have a whole chapter in the Microsoft Press Windows PowerShell Scripting Guide book. But it sounds as if TD wants to use ADSI scripting, so what can we do?

It turns out that the Terminal Server team felt kind of bad about the confusion surrounding the missing Terminal Server attributes, so they created a special interface to the Terminal Server attributes that allow you to write a script that looks like it is an ADSI script so that you can both query and set the missing attributes. How is this possible? Well, you have to put on ruby slippers, close your eyes, and click your heels three times saying, “There are no home folder attributes.”

Or you can use the IADsTSUserEx interface that was created by the Terminal Server team. Here is the QueryAndModifyTerminalServerProperties.ps1 script:

function SetTSProperties()
{
 $ou = [adsi]"LDAP://ou=mytestou,dc=nwtraders,dc=com"
 $user = $ou.psbase.get_children().find($userDN)
 $user.psbase.invokeSet("allowLogon",1)
 $user.psbase.invokeSet("TerminalServicesHomeDirectory",$hDirValue)
 $user.psbase.invokeSet("TerminalServicesProfilePath",$ppValue)
 $user.psbase.invokeSet("TerminalServicesHomeDrive",$hdValue)
 $user.setinfo() 
} #end SetTSProperties

function QueryTSProperties()
{
 $ou = [adsi]"LDAP://ou=mytestou,dc=nwtraders,dc=com"
 $user = $ou.psbase.get_children().find($userDN)
 foreach($property in $aryTSProperties)
 {
  "$($Property) value: $($user.psbase.invokeget($Property))"
 } #end foreach
} #end QueryTSProperties

$userDN = "CN=My User"
$hDirValue = "\\Hamburg\TSUsers\Home\TestUser"
$hdValue = "t:"
$ppValue = "\\Hamburg\TSUsers\Profiles\TestUser"
$aryTSProperties="allowLogon","TerminalServicesHomeDirectory",
  "TerminalServicesHomeDrive","TerminalServicesProfilePath"
SetTSProperties
queryTSProperties

Let's begin our exploration of the script by looking at the SetTSProperties function. One thing to keep in mind with Windows PowerShell is that functions need to be at the top of the script, as opposed to the common method in VBScript where we put subroutines and functions at the bottom of script (to keep them out of the way). Technically, we simply need to create a function before using it. Windows PowerShell scripts are parsed from top to bottom, so functions end up at the top of the scripts. It is also a good practice to arrange your functions in the order in which you are planning on calling them. This will make your scripts easier to read.

Back to the code…the SetTSProperties function begins by using the [adsi] type accelerator to connect to the myTestOU organizational unit. Next we use the DirectoryEntry object that we stored in the $ou variable to find the user. To do this, we use the Get_Children method from the base DirectoryEntry object, and we use the Find method to search for the user we wish to connect to. In reality we do not need to do this for this particular script, but this is a good way to find users in Active Directory. The object that is returned is another DirectoryEntry object. These two lines of code are seen here:

$ou = [adsi]"LDAP://ou=mytestou,dc=nwtraders,dc=com"
$user = $ou.psbase.get_children().find($userDN)

Now we want to write values to the various Terminal Server properties. To do this, we need to use the InvokeSet method from the base DirectoryEntry object. The InvokeSet method takes the name of the attribute and the value we wish to assign to the attribute. The last thing we need to do is to call the SetInfo method. This is the same as any other ADSI script. This section of the code is seen here:

$user.psbase.invokeSet("allowLogon",1)
$user.psbase.invokeSet("TerminalServicesHomeDirectory",$hDirValue)
$user.psbase.invokeSet("TerminalServicesProfilePath",$ppValue)
$user.psbase.invokeSet("TerminalServicesHomeDrive",$hdValue)
$user.setinfo()

Once we have finished updating our user, we can look in Active Directory Users and Computers to see if it was updated properly. As seen in Figure 3, it worked just fine for us:

Figure 3 The user was updated properly

The user was updated properly

 

Well, TD, this actually answers your question about setting the Terminal server attributes. But at the Script Center, we like to give you more than you actually pay for. Think of it as a scripting baker's dozen. So here is the answer to the question you did not ask, namely “How can I query for the Terminal Service Profile settings?” The QueryTSProperties function is very similar to the SetTSProperties function. It begins the same way: We connect to the organizational unit that houses the users we want to query. Next we use the Get_Children method from the base DirectoryEntry object and use the Find method to locate our user. When we have the connection to the user object, we use the forEach statement to walk through an array of property names. With each property name, we use the invokeGet method from the base DirectoryEntry object, and we feed it the property name we are interested in. This section of the code is shown here:

function QueryTSProperties()
{
 $ou = [adsi]"LDAP://ou=mytestou,dc=nwtraders,dc=com"
 $user = $ou.psbase.get_children().find($userDN)
 foreach($property in $aryTSProperties)
 {
  "$($Property) value: $($user.psbase.invokeget($Property))"
 } #end foreach
} #end QueryTSProperties

When we run the QueryTSProperties function, what does the output look like? Well, it is not particularly impressive, but it is functional (no pun intended):

Figure 4 The QueryTSProperties output

The QueryTSProperties output

 

The entry point to the script assigns values to the variables, and calls each of the TS functions in turn. There is really nothing unusual about this section of the code. It could easily be populated via a text file to enable you to modify more than a single user. Some of the values you might prefer to leave hard-coded in the script such as the four attribute names stored in the $aryTSProperties variable. This code is shown here:

$userDN = "CN=My User"
$hDirValue = "\\Hamburg\TSUsers\Home\TestUser"
$hdValue = "t:"
$ppValue = "\\Hamburg\TSUsers\Profiles\TestUser"

$aryTSProperties="allowLogon","TerminalServicesHomeDirectory",
  "TerminalServicesHomeDrive","TerminalServicesProfilePath"

SetTSProperties
queryTSProperties

TD, I hope this helps to put an end to your Terminal Server attribute problem. If you have any more questions, shoot us e-mail at scripter@microsoft.com (in English, if possible).

Ed Wilson and Craig Liebendorfer, Scripting Guys

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • I know this is an oldie but goodie, but I'm visiting this issue and invokeget doesn't recogize any of the Properties.  When I change

    $arTSProperties to "IsAccountLocked","PasswordMinimumLength"

    it works.

  • Dshadix,

    The variable he mentions is "$aryTSProperties", with a "y". Not "$arTSProperties"

  • is there a possibility to change the ts profile path directly for all users?

  • I'm new with Windows 2012.

    Unfortunately, I keep getting the message: unkown name (Exception from HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME).

    Does anyone know how the problem can be solved?

    Thanks in advance.

  • How would I adapt this script to apply these changes to everyone in an OU?