Configuring CredSSP for Deploying XenDesktop via DSC

During my presentation at E2EVC in Berlin, we released new open source Powershell Desired State Configuration (DSC) resources for Citrix XenDesktop 7. Unfortunately for everyone there, I never actually finished the presentation.

The number of questions and level of interaction in the session was awesome. Even with the attempted sabotage by @drtritsch when he tried to blow the camera up, we recovered and soldiered on. Ultimately – having not completed the presentation and missing a few key implementation details – I thought it would be prudent to at least document how you can use the new resources! You know me – always a true professional :).

Credential Delegation

If you are using the new Citrix XenDesktop DSC resources in conjunction with WMF 4.0 then you will need to configure CredSSP on any machines that you will be installing the Citrix XenDesktop delivery controller role on. These machines will need to be configured to delegate credentials to all other delivery controllers and the Microsoft SQL server machine hosting the XenDesktop databases.

The reason for this is that the underlying Citrix XenDesktop PowerShell cmdlets have to run under Active Directory domain credentials. Unfortunately for us the DSC Local Configuration Manager (LCM) runs under the LOCALSYSTEM context and Citrix do not provide us with a –Credential parameter [sigh].

This means that we have to resort to using Powershell remoting to connect to local machine with alternate credentials! Unfortunately, this “loopback” mechanism means that if we subsequently attempt to connect to another XenDesktop delivery controller or even the Microsoft SQL server, we are subjected to the “double-hop” restriction.

You can see the “loopback” implementation details in the code. The majority of the custom resources invoke a script block on the local machine when credentials are supplied like so:

if ($Credential) { AddInvokeScriptBlockCredentials -Hashtable $invokeCommandParams -Credential $Credential; }
else { $invokeCommandParams['ScriptBlock'] = [System.Management.Automation.ScriptBlock]::Create($scriptBlock.ToString().Replace('$using:','$')); }
Write-Verbose ($localizedData.InvokingScriptBlockWithParams -f [System.String]::Join("','", @($Name, $Enabled, $Ensure)));
return Invoke-Command @invokeCommandParams;

Here, if the -Credential parameter is supplied in the configuration document then we add splatted parameters to invoke the script block on the local computer via remoting and specifying the credentials. If the –Credential parameter is not supplied then we just execute the script block as-is (after stripping out all the $using: statements).

Example Configuration

You can see an example of the CredSSP implementation in the example files used in the E2E presentation. Here, all node names in the $ConfigurationData that have a role of ‘Controller’ are put into an array (both NetBIOS and FQDN). Finally, the Microsoft SQL server’s name is added to ensure we can delegate credentials to create the databases.

## Need delegated access to all Controllers (NetBIOS and FQDN) and the database server
$credSSPDelegatedComputers = $ConfigurationData.AllNodes | Where Role -eq 'Controller' | ForEach {
    Write-Output $_.NodeName
    if ($_.NodeName.Contains('.')) { ## Output NetBIOS name as well
        Write-Output ('{0}' -f $_.NodeName.Split('.')[0]);
    }
    else { ## Output FQDN as well
        Write-Output ('{0}.{1}' -f $_.NodeName, $ConfigurationData.NonNodeData.XenDesktop.Site.DomainName);
    }
};
$credSSPDelegatedComputers += $ConfigurationData.NonNodeData.XenDesktop.Site.DatabaseServer;

When enumerating the configuration for each delivery controller, for the first delivery controller we create the site and for all other delivery controllers we join the (now existing) site.

node ($AllNodes | Where Role -eq 'Controller' | Select -First 1).NodeName {
    XD7LabSite FirstSiteController {
        Credential = $Credential;
        DatabaseServer = $ConfigurationData.NonNodeData.XenDesktop.Site.DatabaseServer;
        DelegatedComputers = $credSSPDelegatedComputers;
        LicenseServer = ($ConfigurationData.AllNodes | Where Role -eq 'Licensing' | Select -First 1).NodeName;
        SiteAdministrators = $ConfigurationData.NonNodeData.XenDesktop.Site.Administrators;
        SiteName = $ConfigurationData.NonNodeData.XenDesktop.Site.Name;
        XenDesktopMediaPath = $Node.MediaPath;
    }
    ...
}
...
node ($AllNodes | Where Role -eq 'Controller' | Select -Skip 1 | ForEach { $_.NodeName } ) {
    XD7LabController AdditionalSiteController {
        Credential = $Credential;
        DelegatedComputers = $credSSPDelegatedComputers;
        ExistingControllerAddress = ($ConfigurationData.AllNodes | Where Role -eq 'Controller' | Select -First 1).NodeName;
        SiteName = $ConfigurationData.NonNodeData.XenDesktop.Site.Name;
        XenDesktopMediaPath = $Node.MediaPath;
    }
}

The list of computers to permitted to delegate credentials to is then passed to the composite DSC resource with the -DelegatedComputers parameter. Within the CitrixXenDesktop7Lab composite resources you can see the CredSSP implementation where the CredSSP client is actually configured:

Import-DscResource -ModuleName xCredSSP, CitrixXenDesktop7;

xCredSSP CredSSPServer {
    Role = 'Server';
}
    
xCredSSP CredSSPClient {
    Role = 'Client';
    DelegateComputers = $DelegatedComputers;
}

Easy when you know how, eh?! Just remember that how you configure CredSSP is up to you and you don’t have to use the xCredSSP DSC resource; you could use Group Policy if you wanted to. Just remember that if you’re running WMF 4.0 then CredSSP has to be configured somehow!

WMF 5.0

If you’re lucky enough to be able to use WMF 5.0 then all this CredSSP configuration becomes a moot point. In the latest WMF 5.0 preview the Powershell team added a default -PsDscRunAsCredential parameter to all resources that handles the impersonation for us. Yay! \o/

This means that if you use the CitrixXenDesktop7 DSC resources on WMF 5.0 machines you should not specify the –Credential parameter in your configuration, but leverage the built-in -PsDscRunAsCredential parameter instead. This also means that the xCredSSP resources is no longer required and can be removed from your configuration or any composite DSC resources.

Remember, the CitrixXenDeskop7 and CitrixXenDesktop7Lab resources are open-source projects. We would love to see the community contribute, add resources and round out the implementation. Join us and get involved!

Active Setup – Stubpath Command Lines

I spend a lot time working with mandatory profiles and RES Workspace Manager, especially when using Citrix XenApp or Remote Desktop Services. One of the key elements to creating a slick mandatory profile is to ensure the Active Setup keys are added to the mandatory profile or you will forever see the annoying “Personaliz(s)ing Settings” message. We have covered how to do this in a previous post here by using our great free tool the Virtual Engine Profile Update Utility (PuU).

image

While you can merge these Active Setup Keys to stop the message box appearing; this isn’t actually where the story ends. Behind some Active Setup Components there is a command line (Stubpath) that needs to run once per user i.e. for new users logging on for the first time (for a great explanation of Active Setup, check out Helge Klein’s write up here). The drawback of just merging these keys will be that the command line (Stubpath) will not run for any user. This could have undesirable results as mentioned in the RES Blog post here and Andrew Morgan’s Blog post here.

So the purpose of this blog is really for informational purposes above anything else and to detail the most common Active Setup components containing Stubpaths, by OS. Should you need this information, it’s here for reference. For example, if you disable the ActiveSetup option within RES Workspace Manager or merge the ActiveSetup keys using the Profile Update Utility (PuU), you may have to reinstate a particular action if it causes issues (like Andy’s issue). The command line (Stubpath) is highlighted in yellow and can be used to remedy the situation if necessary:

UPDATE : Windows 8 Consumer Preview (Subject to Change) – Yes ActiveSetup is still here!

{2C7339CF-2B09-4501-B3F3-F3508C9228ED}
Themes Setup
%SystemRoot%\system32\regsvr32.exe /s /n /i:/UserInstall %SystemRoot%\system32\themeui.dll

{44BBA840-CC51-11CF-AAFA-00AA00B6015C}
Microsoft Windows (MailNews)
"%ProgramFiles%\Windows Mail\WinMail.exe" OCInstallUserConfigOE

{6BF52A52-394A-11d3-B153-00C04F79FAA6}
Microsoft Windows Media Player
%SystemRoot%\system32\unregmp2.exe /FirstLogon

{89820200-ECBD-11cf-8B85-00AA005B4340}
Windows Desktop Update
regsvr32.exe /s /n /i:U %SystemRoot%\System32\shell32.dll

{89820200-ECBD-11cf-8B85-00AA005B4383}
Web Platform Customizations
C:\Windows\System32\ie4uinit.exe -BaseSettings

{89B4C1CD-B018-4511-B0A1-5476DBF70820}
DOTNETFRAMEWORKS
C:\Windows\System32\Rundll32.exe C:\Windows\System32\mscories.dll,Install

>{22d6f312-b0f6-11d0-94ab-0080c74c7e95}
Microsoft Windows Media Player
%SystemRoot%\system32\unregmp2.exe /ShowWMP

>{26923b43-4d38-484f-9b9e-de460746276c}
Internet Explorer
C:\Windows\System32\ie4uinit.exe -UserIconConfig

>{60B49E34-C7CC-11D0-8953-00A0C90347FF}
Browser Customizations
"C:\Windows\System32\rundll32.exe" "C:\Windows\System32\iedkcs32.dll",BrandIEActiveSetup SIGNUP

>{ABB824FE-FBBE-464D-9AAA-FAFED848BF41}
IE History
C:\Windows\System32\ie4uinit.exe -UpgradeOldHistoryEntries

Windows XP

{2C7339CF-2B09-4501-B3F3-F3508C9228ED}
Themes Setup
%SystemRoot%\system32\regsvr32.exe /s /n /i:/UserInstall %SystemRoot%\system32\themeui.dll

{44BBA842-CC51-11CF-AAFA-00AA00B6015B}
NetMeeting 3.01
rundll32.exe advpack.dll,LaunchINFSection C:\WINDOWS\INF\msnetmtg.inf,NetMtg.Install.PerUser.NT

{5945c046-1e7d-11d1-bc44-00c04fd912be}
Windows Messenger 4.7
rundll32.exe advpack.dll,LaunchINFSection C:\WINDOWS\INF\msmsgs.inf,BLC.QuietInstall.PerUser

{6BF52A52-394A-11d3-B153-00C04F79FAA6}
Microsoft Windows Media Player
rundll32.exe advpack.dll,LaunchINFSection C:\WINDOWS\INF\wmp11.inf,PerUserStub

{7790769C-0471-11d2-AF11-00C04FA35D02}
Address Book 6
"%ProgramFiles%\Outlook Express\setup50.exe" /APP:WAB /CALLER:WINNT /user /install

{89820200-ECBD-11cf-8B85-00AA005B4340}
Windows Desktop Update
regsvr32.exe /s /n /i:U shell32.dll

{89820200-ECBD-11cf-8B85-00AA005B4383}
Internet Explorer
C:\Windows\System32\ie4uinit.exe -BaseSettings

{89B4C1CD-B018-4511-B0A1-5476DBF70820}
DOTNETFRAMEWORKS
C:\Windows\system32\Rundll32.exe C:\Windows\system32\mscories.dll,Install

<{12d0ed0d-0ee0-4f90-8827-78cefb8f4988}
Internet Explorer Version Update
C:\WINDOWS\system32\ieudinit.exe

>{22d6f312-b0f6-11d0-94ab-0080c74c7e95}
Microsoft Windows Media Player
C:\WINDOWS\inf\unregmp2.exe /ShowWMP

>{26923b43-4d38-484f-9b9e-de460746276c}
Internet Explorer
C:\WINDOWS\system32\ie4uinit.exe -UserIconConfig

>{60B49E34-C7CC-11D0-8953-00A0C90347FF}
Browser Customizations
"C:\Windows\System32\rundll32.exe" "C:\Windows\System32\iedkcs32.dll",BrandIEActiveSetup SIGNUP

>{60B49E34-C7CC-11D0-8953-00A0C90347FF}MICROS
Browser Customizations
RunDLL32 IEDKCS32.DLL,BrandIE4 SIGNUP

>{881dd1c5-3dcf-431b-b061-f3f88e8be88a}
Outlook Express
%systemroot%\system32\shmgrate.exe OCInstallUserConfigOE

Windows 7 32bit

{2C7339CF-2B09-4501-B3F3-F3508C9228ED}
Themes Setup
%SystemRoot%\system32\regsvr32.exe /s /n /i:/UserInstall %SystemRoot%\system32\themeui.dll

{44BBA840-CC51-11CF-AAFA-00AA00B6015C}
Microsoft Windows (MailNews)
"%ProgramFiles%\Windows Mail\WinMail.exe" OCInstallUserConfigOE

{6BF52A52-394A-11d3-B153-00C04F79FAA6}
Microsoft Windows Media Player
%SystemRoot%\system32\unregmp2.exe /FirstLogon /Shortcuts /RegBrowsers /ResetMUI

{89820200-ECBD-11cf-8B85-00AA005B4340}
Windows Desktop Update
regsvr32.exe /s /n /i:U shell32.dll

{89820200-ECBD-11cf-8B85-00AA005B4383}
Web Platform Customizations
C:\Windows\System32\ie4uinit.exe -BaseSettings

{89B4C1CD-B018-4511-B0A1-5476DBF70820}
DOTNETFRAMEWORKS
C:\Windows\system32\Rundll32.exe C:\Windows\system32\mscories.dll,Install

>{22d6f312-b0f6-11d0-94ab-0080c74c7e95}
Microsoft Windows Media Player
%SystemRoot%\system32\unregmp2.exe /ShowWMP

>{26923b43-4d38-484f-9b9e-de460746276c}
Internet Explorer
C:\Windows\System32\ie4uinit.exe -UserIconConfig

>{60B49E34-C7CC-11D0-8953-00A0C90347FF}
Browser Customizations
"C:\Windows\System32\rundll32.exe" "C:\Windows\System32\iedkcs32.dll",BrandIEActiveSetup SIGNUP

Windows 2008 R2 SP1 with Desktop Experience Installed

{2C7339CF-2B09-4501-B3F3-F3508C9228ED}
Themes Setup
%SystemRoot%\system32\regsvr32.exe /s /n /i:/UserInstall %SystemRoot%\system32\themeui.dll

{44BBA840-CC51-11CF-AAFA-00AA00B6015C}
Microsoft Windows (MailNews)
"%ProgramFiles%\Windows Mail\WinMail.exe" OCInstallUserConfigOE
"%ProgramFiles(x86)%\Windows Mail\WinMail.exe" OCInstallUserConfigOE

{6BF52A52-394A-11d3-B153-00C04F79FAA6}
Microsoft Windows Media Player
%SystemRoot%\system32\unregmp2.exe /FirstLogon /Shortcuts /RegBrowsers /ResetMUI

{89820200-ECBD-11cf-8B85-00AA005B4340}
Windows Desktop Update
regsvr32.exe /s /n /i:U shell32.dll

{89820200-ECBD-11cf-8B85-00AA005B4383}
Web Platform Customizations
C:\Windows\System32\ie4uinit.exe -BaseSettings
C:\Windows\SysWOW64\ie4uinit.exe -BaseSettings

{89B4C1CD-B018-4511-B0A1-5476DBF70820}
DOTNETFRAMEWORKS
C:\Windows\system32\Rundll32.exe C:\Windows\system32\mscories.dll,Install
C:\Windows\SysWOW64\Rundll32.exe C:\Windows\SysWOW64\mscories.dll,Install

{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}
Applying Enhanced Security Configuration (Admin)
"C:\Windows\System32\rundll32.exe" "C:\Windows\System32\iesetup.dll",IEHardenAdmin
"C:\Windows\SysWOW64\rundll32.exe" "C:\Windows\SysWOW64\iesetup.dll",IEHardenAdmin

{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}
Applying Enhanced Security Configuration (User)
"C:\Windows\System32\rundll32.exe" "C:\Windows\System32\iesetup.dll",IEHardenUser
"C:\Windows\SysWOW64\rundll32.exe" "C:\Windows\SysWOW64\iesetup.dll",IEHardenUser

>{22d6f312-b0f6-11d0-94ab-0080c74c7e95}
Microsoft Windows Media Player
%SystemRoot%\system32\unregmp2.exe /ShowWMP

>{26923b43-4d38-484f-9b9e-de460746276c}
Internet Explorer
C:\Windows\System32\ie4uinit.exe -UserIconConfig
C:\Windows\SysWOW64\ie4uinit.exe -UserIconConfig

>{60B49E34-C7CC-11D0-8953-00A0C90347FF}
Browser Customizations
"C:\Windows\System32\rundll32.exe" "C:\Windows\System32\iedkcs32.dll",BrandIEActiveSetup SIGNUP
"C:\Windows\SysWOW64\rundll32.exe" "C:\Windows\SysWOW64\iedkcs32.dll",BrandIEActiveSetup SIGNUP

Should anyone wish to expand on what each Active Setup Component does please feel free to leave a comment I’ll update the blog accordingly; some are more obvious than others Winking smile.

Enjoy

Nathan

Updating your XenApp farm using RES Automation Manager

When publishing an application across multiple servers in a XenApp farm one of the key elements to a trouble free environment is having consistency across the farm.  RES Automation manager can help with getting this right.

So, you have your software package imported into Automation Manager and you deploy to each XenApp server in turn.  Here I will go through a method of updating that software and running maintenance automatically with no outage and (most importantly) an easy life for everyone.

First we should set out exactly what we need to do.  There are several stages to this process:

  1. Disable access to the XenApp server
  2. Ensure users are not logged on.
  3. Switching to install mode.
  4. Check version and run Installation.
  5. Switching to execute mode.
  6. Enable logon to the server.

These stages can easily be broken down into 3 Automation Manager Modules; let’s take a look at how to get them setup.

Module 1
Disable logons, wait for the server to be free from users and switch to install mode.
We have three tasks to run here:

Task 1 – Disable logons
Click Add to create the first task of the module.  Select Remote terminal server logins (Change) from the configuration folder and select :
Disable remote logins

Make sure you name the task appropriately and you’re done.

Task 2 – Wait for the server to be free from users
Best practice when installing software on a XenApp server suggests that you should not have any user sessions on that server.  Therefore we need to wait for users to leave.  Now I’m not all that interested in sitting in front of the Access Management Console hitting refresh every 5 minutes until everyone is off, so let’s get AM to do that for us!

We need to make use of the PowerShell capabilities for this, so click Add and select Windows PowerShell Script (Execute) from the advanced folder.  One of the recent improvements to AM is the ability to input a script directly into the task, this helps keep the whole process in a single place.  You can also easily save the script as a resource and point the task to it (good if you have a single script being reused in multiple tasks).

On the settings tab select “Use Windows PowerShell script from “Script” tab” to enable the Script tab, or point the task to the .ps1 file if you have already saved it as a resource.

By default, the PowerShell scripts that you run using this Task need to be digitally signed.  Select Override execution policy for this Task to temporarily lower the PowerShell execution policy to “Unrestricted” and use an unsigned PowerShell script.  After execution of the Task, the PowerShell security will be reverted to the previous security level.

Next you will need to set the timeout.  Since you want the script to run until all users are logged off you need to set this to the maximum allowed, this being 300 minutes.  It would be nice if there was the option to disable the timeout if required, but there isn’t at the moment so 5 hours will have to do for now.

Once you’re done here you’ll need to click the script tab and add the script.

The Script

The script checks the session count on the XenApp server every 5 minutes.  If any user accounts are disconnected, it will log them off.  The script will loop until there are zero sessions or the timeout is reached.  This is key to allowing a RES AM job that waits for a condition to occur before moving on, since the AM native conditions will only be evaluated once at the start of the job.

Although you will not be able to view the output of the script while it is running, it is kept in the job history to help with diagnosis.

Script File (Zip)

Task 3 – Switch to install mode
The final Task in this Module will switch the XenApp server to install mode.  Using the Command (Execute) task from the advanced folder we will use the following command:

Change user /install

Make sure you tick Use Windows command interpreter.

This command is not strictly required anymore, XenApp is intelligent enough to recognise an install process and switch to install mode automatically, but since AM uses the system account and since you won’t always want to just run an msi or an exe it’s better to set it and be sure.

At this point your XenApp server is ready to accept whatever installs and modifications you need to apply.  You could use this set of tasks and finish with an email notification telling you the server is ready to manually have other modules run against it.  However, we are looking for a fully automated process.

Module 2
Check version and run Installation, configuration etc.

Module 2 is the set of tasks you want to run against the box.   Here I am going to use an MSI that I built using a combination of VSS and WixBuild to demonstrate a fully automated software update process.

To start with, I save the MSI as a resource.  The resource type should be “stored in datastore”, this way AM assigns a GUID to the resource, and I will explain why you need this GUID later.

Next I need to add a new Module and create the task, in my case this was a Windows Installer package task from the Provisioning folder.  On the settings tab click the Browse button next to the Filename field and select the resource, configure any other settings (such as Properties or Transforms on the Parameters tab) and click OK.

Note:  With MSI Installs I would always recommend using the Log tab to set the required level of logging and click “Remember as Default”.  This way you will have the installer log files available in the job history should you ever need to diagnose an issue.

Once you have added all the required tasks (including any reboots needed) you are almost ready, just one final module to create.

Module 3
Switch to execute mode and enable logon to the server

This is the final module.  All you need now is for the XenApp server to be made available.  For this you will need a module with 2 tasks (as in Module 1) with the following:

Task 1 – Command (Execute), but this time we will run “change user /execute”

Task 2 – Remote terminal server logins (Change).  This time we are going to Enable remote logins.

Run Book

Run Books are used to create a chain of jobs; each job can be run on a different agent in the same run book.

Add a Run Book, then on the Jobs tab add Module 1 (Disable logons, wait for the server to be free from users and switch to install mode), select the XenApp server as the agent and Click OK.  Repeat this for Module 2 (Check version and run Installation) and 3 (Switch to execute mode and enable logon to the server).

Once these are added the jobs can be cloned and the Agent name changed so that you have Modules 1, 2 and 3 running on each XenApp server in turn or use Teams and split the job to schedule the upgrades/updates in batches.

Schedule and New versions

This Run book can then be scheduled to run at an appropriate time using Job scheduling.

Once the schedule is in place all you need to do to update the Citrix farm is open up the resource files that Module 2 points to and update them.  Since AM has assigned a GUID to the resources the new files will automatically be associated with the task.  Next time the Job runs each Citrix server will disable logins, wait for each user to log off (or log off disconnected sessions) run the new MSI to update the software and re-enable itself.

You meanwhile, can sit back and relax.

If you want to avoid running the Modules during every schedule (as there may not have been an update to the software) then you can use a combination of evaluators and conditions to ensure that the specified tasks/modules do or do not run as required. Make sure the first task is an Installed programs query (found in the System state folder), configure an evaluator that checks for the latest version number and sets a parameter to “True” or “False”.  Once this is in you then set a condition on each individual task to run dependant on the evaluator.  Using this method you can quickly build up a single Run book that runs all your regular Citrix maintenance.

Dan

Assigning static IP addresses when using XenApp and Provisioning Server

Ok so you’ve now decided what a really cool product Citrix Provisioning Server (PVS) is and have decided to use it to stream the OS to your XenApp servers. I’m assuming here you already have the PVS infrastructure in place and have created the image of your XenApp server onto a vDisk.

Now when using PVS inconjuction with a virtual desktop solution it’s well documented that you can use DHCP scope options 66 and 67 to locate the relevant Provisioning Sever and bootstrap file; which will allow PVS to stream the image; where the virtual desktops are allocated a dynamic IP address from DHCP.

Great we say now let’s use this technique for our XenApp servers…..mmm but we want our XenApp server to have static IP address; not one dynamically assigned from DHCP.

From what I’ve seen this isn’t very well documented but is a very real life example of how organisations do things.

One way of doing this that first springs to mind would be to create a bootable PVS ISO that contains the IP address of the XenApp server etc. I’m not a fan of this because you would need an ISO per XenApp server amongst other things.

The best, most controlled and visible method to do this would be to use reservations in DHCP based on a specified MAC address i.e. the one used in the PVS target device properties.

The basic steps to achieve this are:

  1. Create a DHCP scope limiting the range to the IP address of your XenApp servers i.e. 192.168.0.1 – 192.168.0.20 if you don’t already have one.
  2. Be sure to set the DHCP scope options required by PVS i.e. 66 and 67, if they haven’t already been set at the server level.
  3. Set the lease to be unlimited as they are reserved by the MAC address.
  4. Once you have created the scope then add the reservations entering the Reservation name (XenApp server name), MAC, IP and Description.

 You might think about duplicating the same scope on another DHCP server for fault tolerance remembering to add in the reservations and deactivating the scope otherwise you will get conflict errors. Only activate the scope should your other DHCP server fail; after this you are good to go.