HOWTO Dynamically Determine Application Server Addresses

Purpose

This article describes some example mechanisms that may be used to dynamically determine what "Application Server" should host a particular application.  This can be useful for, for example, providing remote access to users to their individual desktop machines (assuming that the desktop machines are running a network service to allow remote access, such as Remote Assistance in Windows XP Professional or Windows Vista.)  

Background

SGD was designed to allow the publishing of applications on shared servers to groups of users.  So, for example, hundreds of users may have access to a word processing program installed on 2 or 3 shared servers. 
 
The administration interface reflects this, where "Applications" are linked to "Applications Servers" and the combined "package" is published to groups of users.  The definition of "Applications" and "Applications Servers" are, for the purposes of this discussion, anyway, "static", or known in advance. 

Lately, we've begun delivering the Sun Virtual Desktop Infrastructure Software, where the mapping is now a single user to a single virtual machine (VM) instance, and significantly, the VM itself is *not* known in advance; it's determined at launch-time. The VM can be temporarily assigned to a user, or permanently assigned. In the case of a temporary assignment, the physical VM assigned to a particular user may be different each time they login.  

 The way this works in the VDI environment is that there's a single application object that represents the "Desktop" application, which has as its "Application Server" the address of a server running an RDP Broker Service. When a user launches this application, SGD connects to the RDP Broker service, passing it the user credentials. The RDP Broker looks up the VM the user is assigned to, requests it be started up, and then redirects the RDP connection to the destination.

  So, from an SGD administrative perspective, there's a single application ("My Desktop"), and a single application server (the server(s)running the RDP Broker service.) The "My Desktop" application his is then published to the target user population using DSI, etc.  This provides an extremely limited amount of SGD administration; the responsibility to determining what application server (desktop OS instance) the user should be connected to is delegated to the broker. 

But what about environments where they're still running the traditional physical desktop, with hundreds or thousands of Windows XP desktops?  Our VDI broker works only with 'desktop' instances running under the control of our broker, in concert with some virtualization platform, such as VirtualBox, VMWare vSphere, or Microsoft's Hyper-V; the broker software has no visibility to physical machines not under control of a virtualization provider.  So, how can SGD be used to provide secure access to these desktops, and grant such access only to the "owners' of these desktops?

You could create one application object, (a "Windows Desktop" application), and one "Application Server" object for each desktop you want to service, and then publish these Application/Application Server pairs to the appropriate desktop owner; but this wouldn't be very scalable at all. In terms of administration, if these relationships were done manually, this could be a very burdensome task. Ideally, scripts would be developed to make the initial assignments easier.  But, especially in environments with lots of desktops, this level of administrative involvement will be too high for many.  

Administrative burden aside, creating too many objects in the SGD ENS creates its own problems, slowing down the administration tools, and increasing replication loads, which can be  problematic in large arrays or geographically-split arrays.

We can make this a lot easier by using a single application definition with a custom expect script that dynamically determines which application server (or "Windows XP desktop", in this case), the application should start on.   Of course, every situation is going to be different, and so the mechanisms for determining what the target desktop hostname will be different.  The purpose of this article is to provide an example of such a dynamic mechanism that can be used to dynamically determine the target machine.  This example shows one mechanism that could be used, and is to illustrate the concepts discussed, and can be altered to fit your unique requirements.  Of course, it must be noted, this is not directly supported. 

Using A Directory Service to Determine Application Server

This shows how you can use an external directory service to map users to their assigned desktops.  This is only one way of doing it - the important point to note is that by altering the TTA_HOSTNAME variable in the expect environment, you can assign / alter the destination host.  For a complete list of login script environment variables, click here.

There doesn't appear to be a simple mechanism in the Active Directory schema to associate users and their desktops.  So, at least in this approach, there's some manual administration required.  In this approach,  an otherwise unused attribute in the database ("Office" or "physicalDeliveryOfficeName") is used to store and retrieve desktop DNS addresses.

1.  Create a shell script to implement the AD lookup

Create a script like the following in /opt/tarantella/var/serverresources/expect - I named mine getdesktop.sh - but you can call it anything you like, and store it anyplace you like.  Just alter the expect script in step 2 to point to the right place.

Make sure it's readable/executable by ttasys:ttaserv 

Edit your settings as necessary - (and note the unpleasant requirement for storing a bind password in a file.)  Not good.  Make sure this isn't a privileged account, or enable anonymous binds on your AD server.  For an LDAP server, this shouldn't be an issue, as anonymous binds are generally permitted.  Use the appropriate search filter, either the LDAP or AD search filter.

You can also download the script here.

#!/bin/sh
# note that "HOST" is a static domain controller; using nslookup to determine a local domain controller
# would be an obvious improvement to this script.

HOST=dc.example.com
BASEDN="cn=Users, dc=example, dc=com"
BINDDN="cn=SGD ServiceAccount, cn=Users, dc=example, dc=com"
BINDPW="secret"
USERNAME=$1

if [ "$#" != "1" ]
then
        echo "Usage: $@: Username"
        exit 1
fi
# If using LDAP as a login authority, this is the default search filter used by SGD for processing logins
# For performance reasons, trimming this filter down is a good idea; be sure the filter below matches what SGD uses.

FILTER="(|(cn=$USERNAME)(uid=$USERNAME) (mail=$USERNAME) (userPrincipalName=$USERNAME)(sAMAccountName=$USERNAME))"

# If using AD as a login authority, the search used by SGD is userPrincipalName only,
# as follows - note that UPN is of the format user@domain

# FILTER="userPrincipalName=$USERNAME"

RESULTS=`/usr/bin/ldapsearch  -1 -h $HOST -b "${BASEDN}" -D "${BINDDN}" -w "${BI
NDPW}" "${FILTER}" physicalDeliveryOfficeName | grep physicalDeliveryOfficeName`

if [ "$?" != "0" ]
then
        exit 1
fi

VMNAME=`echo $RESULTS | cut -d: -f2`

# this code just ensures the hostname is resolvable
# can be altered or omitted as desired

host $VMNAME >/dev/null 2>&1

if [ "$?" != "0" ]
then
        exit 2
fi
echo $VMNAME
exit 0
2.  Create expect Script

In the /opt/tarantella/var/serverresources/expect directory, create a new expect script, e.g. "getdesktop.exp", and copy the following contents into it.   Note that this is a slightly altered version of the expect provided with the VDI software, itself an altered version of the standard 'wcpwts.exp.   You can also download the script here.

## Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
# getdesktop.exp: expect script to retrieve hostname from AD and
# start a Windows desktop session
#

global argv env timers

set timeout 60
#set progresstimeout 5
set GETDESKTOP "/opt/tarantella/var/serverresources/expect/getdesktop.sh"
progress "Starting Desktop Connector ..."

# Update the progress dialog
#clienttimer $progresstimeout $message 4

if { ![info exists argv] } {
    set haveAuth 0
} elseif { [llength argv] > 0 } {
    set haveAuth 1
} else {
    set haveAuth 0
}

if { $haveAuth } {
    set uname [lindex $argv 0]
    set pword [lindex $argv 1]
} else {
    set uname ""
    set pword ""
}

if {[string length $uname] > 0} {

    progress "Requesting Desktop for $uname...\n"
    if [catch {exec $GETDESKTOP $uname} result ] {
        send_error "Couldn't find Desktop for $uname: $result\n"
        exit -1
    } else {
        set DESKTOP_IP $result
        progress "Found Virtual Desktop for $uname: $DESKTOP_IP...\n"
    }

    if {[string length $DESKTOP_IP] > 0} {
        set env(TTA_HOSTNAME) $DESKTOP_IP
    } else {
        send_error "Error: Unable to retrieve a Desktop for $uname.\n"
        exit -1
    }
} else {
    send_error "Error: Unable to retrieve an assigned Desktop - No username found for currently logged in user.\n"
    exit -1
}

puts "$DESKTOP_IP"
sleep 3

# Stop SGD prompting
set promptForAuth 0

# If there is a username ensure we have a domain as well
if { [string length $uname ] > 0 } {
    if { [info exists env(TTA_Domain)] } {
        set initialDomain $env(TTA_Domain)
    } else {
        set initialDomain ""
    }
}

tarantella -nosocket

################################################################
#Set our Environment Variables
################################################################
set vars [split $env(TTA_ENVIRONMENT) ";" ]
foreach i $vars {
    set idx [string first "=" $i]
    set name [string range $i 0 [expr $idx - 1] ]
    set value [string range $i [expr $idx + 1] [string length $i] ]

    if {$name == "ALTDISPLAY"} { set ALTDISPLAY $value }
    if {$name == "DISPLAY"} { set DISPLAY $value }
}
################################################################
#Check everything we need to run is there
################################################################
if { ![info exists env(TTA_AppletWidth)] || ![info exists env(TTA_AppletHeight)] } {
    send_error "No Resolution Parameters Specified\n"
    exit $exitCode(ErrCommandExecutionFailed)
}
if { ![info exists env(TTA_CONNECTIONSERVICE)] } {
    send_error "No Transport Parameter Specified\n"
    exit $exitCode(ErrCommandExecutionFailed)
}
if { ![info exists env(TTA_Domain)] } {
    scan $env(TTA_HOSTNAME) "%\[A-z\]" DOMAIN
} else {
    set DOMAIN $env(TTA_Domain)
}

if { [info exists env(TTA_ApplicationPlacement)] } {
    if { $env(TTA_ApplicationPlacement) == "MultipleWindows" } {
       send_error "Cannot run with the client window manager\n"
       exit $exitCode(ErrCommandExecutionFailed)
    }
}

################################################################
#Build the command line. You may want to substitute
#DISPLAY for ALTDISPLAY
################################################################
set ProtocolCmd "ttatsc"

if {[string length "$uname"] > 0} {
    set USERCMD "-user"
    #is the user in the form of domain\user
    set idx [string first "\\" $uname]
    if { $idx != -1 } {
        set DOMAIN [string range $uname 0 [expr $idx - 1] ]
        set uname [string range $uname [expr $idx + 1] [ string length $uname ]]
    }
} else {
    set USERCMD {}
}

if {[string length "$pword"] > 0} {
    set PASSWORDCMD "-password"
} else {
    set PASSWORDCMD {}
}

if {[string length "$DOMAIN"] > 0} {
    set DOMAINCMD "-domain"
} else {
    set DOMAINCMD {}
}

# Do we have an initial command to run or is it a full desktop session?
set APPCMD {}
set APP {}
if { [info exists env(TTA_FilePath)] } {
    if { $env(TTA_FilePath) != "" } {
        # Is this just a placeholder to start a new session?
        if { $env(TTA_FilePath) != "-" } {
             set APPCMD "-application"
             set APP "$env(TTA_FilePath)"
             if { [info exists env(TTA_Arguments)] } {
                  if { $env(TTA_Arguments) != "" } {
                       append APP " $env(TTA_Arguments)"
                  }
             }
        }
    }
}
set AUDIO "-noaudio"
set AUDIOQUALITY ""
if { [info exists env(TTA_AudioQuality)] } {
    if { $env(TTA_AudioQuality) != "" } {
    set AUDIO "-audioquality"
    set AUDIOQUALITY "$env(TTA_AudioQuality)"
    }
}

set TTATSC_ARGUMENTS {}
if { [info exists env(TTA_ProtocolArguments)] } {
    set TTATSC_ARGUMENTS "$env(TTA_ProtocolArguments)"
}

set SWM {}
if { [info exists env(TTA_ApplicationPlacement)] } {
    if { $env(TTA_ApplicationPlacement) == "seamlesswindows" } {
        set SWM "-swm"
    }
}

set NAMECMD ""
set TTATSC_NAME ""

################################################################
# Pre-version 3.42 client name settings
################################################################
#set NAMECMD "-name"
#set TTATSC_NAME "tarantella user"
#if { [info exists env(TTA_DisplayName)] } {
#    set TTATSC_NAME "$env(TTA_DisplayName)"
#} else {
#    if { [info exists env(TTA_UserName)] } {
#        set NAME $env(TTA_UserName)
#        if [ expr [string first ".../_anon/" $NAME ] != 0 ] {
#            set idx [string last "/" $NAME]
#            set TTATSC_NAME [string range $NAME [expr $idx + 1] [string length $NAME ] ]
#        } else {
#            set TTATSC_NAME "anonymous"
#        }
#    }
#}

################################################################
#Execute the command
################################################################
set env(TTATSC) "$TTATSC_ARGUMENTS"
set TTATSC1 "$USERCMD $uname\n$PASSWORDCMD $pword\n\n"

if [catch {exec -- echo $TTATSC1 | $ProtocolCmd $AUDIO $AUDIOQUALITY $SWM "-stdin" "-display" $DISPLAY $DOMAINCMD $DOMAIN $APPCMD $APP $NAMECMD "$TTATSC_NAME" $env(TTA_HOSTNAME) } res ] {
    set err [lindex $errorCode [expr [llength $errorCode] - 1]]
    send_error "\n"
   if {$err == 8} {
       send_error "Failed to connect to host $env(TTA_HOSTNAME)\n"
       exit $exitCode(ErrApplicationServerConnectionFailed)
   } elseif {$err == 2 } {
       send_error "Cannot run with a window manager\n"
       exit $exitCode(ErrWindowsManagerExists)
   } elseif {$err == 3 } {
       send_error "Invalid desktop size\n"
       exit $exitCode(ErrInvalidDesktopSize)
   } elseif {$err == 4} {
       send_error "The temporary Terminal Services license has expired\n"
       exit $exitCode(ErrTscLicenseError)
   } elseif {$err == 5} {
       send_error "No Terminal Services licenses are available\n"
       exit $exitCode(ErrTscLicenseError)
   } elseif {$err == 6} {
       send_error "The Terminal Services license is invalid\n"
       exit $exitCode(ErrTscLicenseError)
   } elseif {$err == 7} {
       send_error "Unknown Terminal Services license error\n"
       exit $exitCode(ErrTscLicenseError)
   } else {
       send_error "$res (error = $err)\n"
       send_error "$exitMessage(ErrCommandExecutionFailed)\n"
       exit $exitCode(ErrCommandExecutionFailed)
   }
}

3.  Create Application 

Create a Windows application object with the appropriate settings, and set the "Login Script" to be the name you used in step 2 above, e.g. "getdesktop.exp".  Be sure to leave the "Application Server" assignments null.   After testing, this is a good candidate for the "My Desktop" application. 

4.   Assign Application to User(s)

You can selectively assign the application to individual users, or can just assign to the "LDAP User Profile", meaning every use that authenticates via LDAP (or AD) will get this application on the desktop.   If users don't have a valid hostname assigned to them in AD, they won't be able to alter the destination or otherwise access any other hosts; the connection will fail. 

5.  Assign Desktop Addresses to Users


Assign desktop DNS addresses to individual users using the appropriate management tool, such as the Active Directory Users and Computers snap-ip.  Alter the "Office/physicalDeliveryOfficeName" attribute (or whichever attribute you choose to use) appropriately.

Now, when users login, and execute the application you've defined in step 3, if they have a valid DNS name assigned to them, SGD will connect them to that name via the RDP protocol.

One final note: if you want client drive mapping, you'll still want to install the SGD Enhancement module on the desktop operating systems.  The other features provided by the Enhancement module for Windows servers (seamless windows, load-balancing) do not apply.   Other features within RDP, such as audio and Smartcard redirection, should function normally.  

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.

Sign up or Log in to add a comment or watch this page.


The individuals who post here are part of the extended Sun Microsystems community and they might not be employed or in any way formally affiliated with Sun Microsystems. The opinions expressed here are their own, are not necessarily reviewed in advance by anyone but the individual authors, and neither Sun nor any other party necessarily agrees with them.

Copyright 1994-2009 Sun Microsystems, Inc.
Powered by Atlassian Confluence
Sun Guidelines on Public Discourse Privacy Policy Terms of Use Trademarks Site Map Employment Investor Relations Contact