Jul 112017

I have been using Full Desktop’s inside of XenApp forever. Lately I have been working on a project where I will be using only published apps. We are a CSP and a managed service provider who uses LabTech (Now ConnectWise Automate) to control all of our systems. LabTech uses a great remote access product called ScreenConnect to connect to the systems. All of this works flawlessly inside of a full desktop. However, when I published LabTech as a seamless app (LTClient.exe), everything seems to work fine except for ScreenConnect. I got a great Citrix engineer on the line who actually used all of the collected data I uploaded and troubleshot the issue. ConnectWise is actually a “ClickOnce” application which leverages dfsvc.exe to install and launch ScreenConnect. You can read this super exciting article on ClickOnce applications here.

Technically Citrix, nor Microsoft support any of these ClickOnce applications. Kudos on the Citrix engineer for continuing to work the issue with me, even though this is true. Luckily I already built a 2012R2 RemoteApp environment and was able to get this working to show Citrix this was not an application issue, but a Citrix seamless app issue. During troubleshooting he pointed me to this interesting article on ClickOnce and XenApp 6.5 here. I’m on 7.6 LTSR CU3, but still a good article on how this stuff works.

Anyway, after looking at the procmon information in the ticket, he quickly found that in the working scenario dfsvc.exe was calling ScreenConnect.WindowsClient.exe, where the seamless app was not. His “solution” was to simply run dfsvc.exe before calling LTClient.exe. Not really a “fix” but hell, it worked! So, I created a simple powershell script.

start-process "C:\Windows\Microsoft.NET\Framework\v4.0.30319\dfsvc.exe"
start-process "C:\Program Files (x86)\LabTech Client\LTClient.exe"

Lastly, I added dfsvc.exe to LogoffCheckSysModules per this CTX article.






Sep 302016


XenApp 7.6

700+ Delivered (published) Applications

60+ Windows servers (2008 R2 and 2012 R2)



Recently I had a request to replicate 100+ applications from PROD to QA, using QA server configured with identical applications and identical application locations/paths. Obviously all paths to EXE files need to be the same in order for this to work 100% (unless I missed a memo and XA can now support publishing of identical applications from various paths.  As far as I know, this was not yet available in 7.6).  If QA server has some of the applications in different paths, not all is lost. You can still use this process and script to migrate large number of applications between delivery groups and then modify paths later in Studio.

While I could add few more lines to my PoSH script to actually replicate each application at a time, amount of time it took me to create this script and ability to duplicate applications in Studio seemed unnecessary.

My goal was to replicate, or proper Citrix term would be duplicate all applications and then assign them to another delivery group. Seems simple enough for Citrix and PoSH guru. But for those who are just getting their feet wet could use following process to speed up their delivery time to less than 5 mins and go look for the end of the Internet, while telling client it took you hours 😉



1 – I will be duplicating all requested applications using ol’ Citix Studio.

2 – I will run script below to change Delivery Group and application folder, as visible by the user (you mileage might vary, depending on your requirements)

This script/process is no rocket science, but might help someone to quickly replicate applications and migrate them to another delivery group, instead of publishing them over again.  Modify script below according to your environment before running it.  (WARNING: It is fairly simple script, so review and try to understand exactly what this script is doing, before executing it.)  Also, I am no expert when it comes to creating powershell scripts, but just another Citrix admin.  So, pardon if you can make it better.  Please do improve and share!  I am all for helping fellow Citrix admins anyway I can.  Even if it’s buying a pint!


Step 1

citrixirc1Create alternative application folder in Studio.  For our scenario I am going to create folder named “QA” inside already created “Europe” folder.

Right-click on all applications that you need to replicate in QA (you can select multiple applications at once).

Click Duplicate Application 

Now select all duplicates and drag them over to QA folder.  In my scenario I will be dragging these to Europe\QA.

Step 2

Below script will prompt for the admin folder name where all the duplicates reside (that’s the new folder you just created.  In my example it’s called Europe\QA).  I repeat- do not select your production applications folder, as script will move all your production apps to new delivery group.  Use newly created QA folder where you moved all duplicate applications to in step 1 above.

It is assumed that new delivery group is already created.

Another item to note; there is an optional line (in yellow) to change client-side folder location of newly created applications.  This is to help users identify whether they are running PROD or QA applications. It also looks cleaner in Storefront or WI.  You can add more commands into Foreach loop.  Things like modifying users who have access, or changing actual name of the application and etc.  My goal was to keep all the same and just deliver from QA server.


asnp Citrix*

$adminfolder = (Get-BrokerApplication -MaxRecordCount 10000).AdminFolderName | sort | select -unique | Out-GridView -Title "Select Admin Folder Name" -OutputMode Single
$applist = Get-Brokerapplication -AdminFolderName $adminfolder
$originalDG = (Get-BrokerDesktopGroup -MaxRecordCount 10000).Name | sort | Out-GridView -Title "Select Original Delivery Group Name" -OutputMode Single
$newDG = (Get-BrokerDesktopGroup -MaxRecordCount 10000).Name | sort | Out-GridView -Title "Select New Delivery Group Name" -OutputMode Single

Write-Host "Migrating all applications in $adminfolder`nFrom $originalDG Delivery Group to $newDG Delivery Group" -ForegroundColor Green

foreach ($app in $applist.ApplicationName){
                Write-host "Migrating $app"
                Get-BrokerApplication -ApplicationName $app | Add-BrokerApplication -DesktopGroup $newDG
                Get-BrokerApplication -ApplicationName $app | Remove-BrokerApplication -DesktopGroup $originalDG
                Get-BrokerApplication -ApplicationName $app | Set-BrokerApplication -ClientFolder "Europe\QA" #optional to show all applications inside QA folder and not in the same folder with production apps


BTW, using similar add-brokerapplication command you can publish, or rather deliver same application from multiple delivery groups.  Just comment out remove-brokerapplication command and it will now launch from servers in prod and qa, or any other DG of your choice.  Comes really handy when you have multiple DGs that host different applications, but some of the applications are identical.  You can spread the load across multiple DGs.  Think of it as a worker groups concept in XA 6.x with server groups.   I had such requirement that was easily achievable in XA 6.x, but not so much in XA 7.x.  I paid for someone’s case of beer when they told me that I can use above mentioned command to deliver same application from multiple DG’s, as it’s not clearly documented by Citrix. There is a surprise…

That’s all folks. My first ever citrixirc blog.  Whoo-hoo!

Over and out.

Apr 122016

Thank you Microsoft for changing fundamental things about your operating system, with little or no regard to those of us running in an RDS/XenApp type environment. Check out this technet article. In this article it states how changes have been made.

“In  Pre-Win 8, apps could set the default handler for a file type/protocol by manipulating the registry, this means you could easily have a script or a group policy manipulating the registry. For example  for Mailto protocol you just needed to change the “default” value under HKEY_CLASSES_ROOT\mailto\shell\open\command”

More importantly, you were able to use Group Policy Preferences (GPP) to set these values inside a GPO. You could also Item Level Target (ITL) them by using the GPP. This means you could easily have users run Acrobat Pro for .pdfs on SecurityGroupA and Adobe Reader for .pdfs on SecurityGroupB. However, the technet article goes on to say that in post Windows 8,

“the registry changes are verified by a hash (unique per user and app) “

A little more digging tells us that the new hashing mechanism is also on a per-machine basis. This means that a hash would be different for each user, per app, per XenApp server. Very inconvenient and annoying. This also means that we can not use the built in GPP functions in Active Directory to set these file type associations. Also very inconvenient and annoying.

James Rankin did a great blogpost on this subject as well. You can read that here. He did a great job overviewing this issue and provided a solution with using AppSense. This blog will show you how to do this with good old batch scripting and group policy. To be honest, I’m quite annoyed that I had to put together this “hack” to get around something that worked PERFECTLY FINE in 2008R2 with GPPs. If anyone has a more elegant solution, I’d love to see it. I’m not the best scripter in the world, but I’m very pragmatic. “It works”

The first thing we want to do is create a logoff script to delete HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.pdf. However, because of that pesky Deny in the UserChoice key, we are unable to simply do this. So, I have made a simple “regini” command to overwrite the permissions on that key so that we can delete. In my environment, I have created FTA_PDF.txt in the NETLOGON directory. Inside this file is simply a registry value and some codes, which allow SYSTEM, Administrators, Interactive User, etc, FULL CONTROL of the key.

Next, I create a FTA_Delete.bat file in NETLOGON. This runs the “regini” command to change the permissions, then a “reg delete” command to delete the key.

Then we need to create the script for the logon process. I’ve busted out good old “ifmember” for this one. It’s a simple executable that will check AD group membership. My script is pretty simple. It checks to see if a user is a member of the Acrobat group. If so, run the “reg add” to add the association to Acrobat. If not, it falls back to the default .pdf reader in this environment. In this case, it’s Adobe Reader. Keep in mind that you can add multiple programs and associations using this method. You can add Foxit here if you would like.

So, the sad fact of the matter is when I tried to set this as an actual “Logon Script” the functionality didn’t work. I had to set this in a User GPO: Administrative Templates\System\Logon\Run these programs at user logon. I’m also the type of person that hates to see a CMD window flash up on the screen right after I login. So, I wrote ANOTHER script called FTA_Starter.bat to call this script to run in a minimized window.

This is the script I added to the GPO.

So, I fought with this for a long time and it wasn’t working. I had to re-read James’s blog and found this little blub at the bottom.

“Update – I built a third XenApp server, just to be sure, and this time the solution wouldn’t work until I removed the Registry key HKLM\Software\Classes\.pdf.”

This DID NOT WORK until the HKLM key was deleted from the servers. Do not forget this step.

I hope this helps you work through this issue in less time that it took me to do it.

Nov 052015

As a Citrix CSP, having a good set of scripts to deploy a base environment is critical. Setting up 50 environments by hand would take far more time than with good scripting. Now that I’ve finally had some time to sit down and not be working on 6.5 stuff, I have been able to write scripts to install and configure XenDesktop 7.6 with Storefront 3 using PowerShell (and some batch). Full disclosure! I am NOT good at PowerShell, or scripting in general. I’m sure there are a million better/easier ways to do these things, but what I have works, dammit. The point of this blog is to show you the commands, and what they do. I will attach my final scripts where you can tune and tweak as needed.

As always, credit where credit is due. I took some code from Aaron Parker from here. I also took some code from Eric from here. Lastly, I’d like to give a BIG THANKS to Esther Barhel (@virtuEs_IT) for helping me out with the non-documented Storefront 3 PowerShell commands.

If you have read my blog, most of my environments are built for the SMB/SMG space. That being said in this blog we will be setting up a single instance configuration. There will be a single server with both the XDC and Storefront roles on it. You could easily take this code to add additional XDCs or Storefront servers to your environment. I will be using code snippets to explain what they are for during the blog.

The first thing we want to do is install XenDesktop. As this is a small environment, we are going to use SQL Express on the XDC. Personally, I do not like to use SQL instances, so in my configuration I am going to install SQL with the default instance instead of the SQLEXPRESS instance. For this, I have created a directory with the XD7.6 disk extracted and shared on the network. I then created a SQL directory and extracted SQL2014 Express in there. I shared this directory out as XA76. (Long live XenApp!)

Installing SQL and XenDesktop

This first “Script” simply does a net use to the share, installs SQL, then installs XenDesktop with only the Controller and Studio roles. Note that you do not want to install Storefront yet. This script will reboot the server. As a scripting/PowerShell noobie, I learned a lot during this. For example. In PowerShell, the use of –% tells PowerShell to stop parsing code after those characters. This helped me a lot because PowerShell was trying to parse the \’s and /’s and failing miserably.

net use --% x: \\\XA76
cd '.\SQL'
write-host "Installing SQL"
 .\SETUP.EXE --% /QS /ACTION=install /FEATURES=SQL,Tools /INSTANCENAME=MSSQLSERVER  /IAcceptSQLServerLicenseTerms=true
cd ..
cd '.\x64\XenDesktop Setup'
write-host "Installing XenDesktop"
.\XenDesktopServerSetup.exe --% /components CONTROLLER,DESKTOPSTUDIO /NOSQL /quiet /configure_firewall
shutdown -f -r -t 2

XenDesktop Configuration

After the server is done rebooting, we can start with the configuration of the XenDesktop Site. First thing we want to do is setup the databases. I’m going to setup the default databases to the local system we just installed SQL on, using the NetBIOS Name of the domain as the site name. In this example $NBN is NetBIOS Name.

New-XDDatabase -AllDefaultDatabases -DatabaseServer $env:COMPUTERNAME -SiteName $NBN

The next thing we want to do is setup the Site. In my configuration, I use the NetBIOS name and append it to the DBs. In this example $LDB would be CitrixConfigLoggingTEST where TEST is the NetBIOS name. The same is done with the Monitor Database and Site Database.

New-XDSite -DatabaseServer $env:COMPUTERNAME -LoggingDatabaseName $LDB -MonitorDatabaseName $MDB -SiteDatabaseName $SDB -SiteName $NBN

Next, we want to setup licensing. This is pretty self-explanatory. This sets the license server and the port. IT then sets the product and edition. I generally use a generic DNS name for the licensing server, “ctxlicesnse” in this example, that points to the license server IP address. Then this sets up the product code and product edition. My example shows a setup for XenDesktop Advanced edition. You can get these variables with the following commands.

PS C:\> Get-ConfigProduct

Code                    Name
----                    ----
XDT                     XenDesktop
MPS                     XenApp
PS C:\> Get-ConfigProductEdition -ProductCode XDT

Here is my code to add licensing.

Set-XDLicensing -LicenseServerAddress ctxlicense -LicenseServerPort 27000
Set-ConfigSite -LicensingModel Concurrent -ProductCode XDT -ProductEdition ADV

Next, we setup a Machine catalog. This assumes that we already have at least one VDA setup, already pointed to this Delivery Controller. This is a simple persistent desktop Machine Catalog. This is setup for XenApp type Full Desktops (MultiSession), and we add the first XD box to the Machine Catalog.

New-BrokerCatalog -SessionSupport MultiSession -ProvisioningType Manual -AllocationType Random -Name 2012R2 -Description 2012R2 -PersistUserChanges OnLocal -MachinesArePhysical $true
New-BrokerMachine -MachineName TEST-RDU-XD-01 -CatalogUid 1

This next bit of code sets up the Desktop Group. This code snip was taken from Aaron Parker’s blog here and edited for my using. I encourage you to read his blog page to understand what this stuff does. It is much more involved than my code, and does a great job setting up the delivery group. I will try to break down the piece for all of us though. Let’s walk through the variables first.

$assignedGroup = "$NBN`\$NBN`_CTX_Desktop"
$desktopGroupName = "Some Desktop Group"
$desktopGroupPublishedName = "Some Desktop Group"
$desktopGroupDesc = "Some Desktop Group"
$colorDepth = 'TwentyFourBit'
$deliveryType = 'DesktopsandApps'
$desktopKind = 'Shared'
$sessionSupport = "MultiSession"
$functionalLevel = 'L7_6'
$timeZone = 'EST Eastern Standard Time'
$offPeakBuffer = 10
$peakBuffer = 10
$machineCatalogName = "2012R2"

You can set a TON of stuff in here, and it can get complicated when you are doing MCS/PVS and stuff. In this example, we are setting up a XenApp Full Desktop to the Machine Catalog we created above (2012R2). $deliverytime can be DesktopsOnly, DesktopsandApps, or AppsOnly. $deliverykind can be Shared or Private. $sessionSupport can be MultiSession or SingleSession. This is a new environment, with all 7.6, so we set the $functionalLevel to L7_6. This can be set to 5, 7, or 7.6. There are so many other commands in here that Aaron has detailed; I won’t go into them here. The command I have used for this example is below.

New-BrokerDesktopGroup -ErrorAction SilentlyContinue -AdminAddress $XDC -Name $desktopGroupName -DesktopKind $desktopKind -DeliveryType $deliveryType -Description $desktopGroupPublishedName -PublishedName $desktopGroupPublishedName -MinimumFunctionalLevel $functionalLevel -ColorDepth $colorDepth -SessionSupport $sessionSupport -InMaintenanceMode $False -IsRemotePC $False -SecureIcaRequired $False -Scope @()

The next thing we need to do is to add machines to the desktop group.

$machineCatalog = Get-BrokerCatalog -AdminAddress $XDC -Name $machineCatalogName
Add-BrokerMachinesToDesktopGroup -AdminAddress $XDC -Catalog $machineCatalog -Count $machineCatalog.UnassignedCount -DesktopGroup $desktopGroup

Then we want to add users to the desktop group

$brokerUsers = New-BrokerUser -AdminAddress $XDC -Name $assignedGroup
New-BrokerEntitlementPolicyRule -AdminAddress $XDC -Name ($desktopGroupName + "_" + $Num.ToString()) -IncludedUsers $brokerUsers -DesktopGroupUid $desktopGroup.Uid -Enabled $True -IncludedUserFilterEnabled $False

Lastly, we want to allow access through Access Gateway

New-BrokerAccessPolicyRule -AdminAddress $XDC -Name $accessPolicyRule -IncludedUsers @($brokerUsers.Name) -AllowedConnections 'ViaAG' -AllowedProtocols @('HDX','RDP') -AllowRestart $True -DesktopGroupUid $desktopGroup.Uid -Enabled $True -IncludedSmartAccessFilterEnabled $True -IncludedSmartAccessTags @() -IncludedUserFilterEnabled $True

Here is the full script from start to finish.

add-pssnapin c*
$nbn = $env:USERDOMAIN
$assignedGroup = "$NBN`\$NBN`_CTX_Desktop"
$LDB = "CitrixConfigLogging" + $nbn
$MDB = "CitrixMonitor" + $nbn
$SDB = "Citrix" + $nbn

# Desktop Group properties
$desktopGroupName = "Some Desktop Group"
$desktopGroupPublishedName = "Some Desktop Group"
$desktopGroupDesc = "Some Desktop Group"
$colorDepth = 'TwentyFourBit'
$deliveryType = 'DesktopsandApps'
$desktopKind = 'Shared'
$sessionSupport = "MultiSession"
$functionalLevel = 'L7_6'
$timeZone = 'EST Eastern Standard Time'
$offPeakBuffer = 10
$peakBuffer = 10
$machineCatalogName = "2012R2"

write-host "Creating Citrix Databases"
New-XDDatabase -AllDefaultDatabases -DatabaseServer $env:COMPUTERNAME -SiteName $NBN

write-host "Setting up Site"
New-XDSite -DatabaseServer $env:COMPUTERNAME -LoggingDatabaseName $LDB -MonitorDatabaseName $MDB -SiteDatabaseName $SDB -SiteName $NBN

write-host "Setting up licensing"
Set-XDLicensing -LicenseServerAddress ctxlicense -LicenseServerPort 27000
Set-ConfigSite -LicensingModel Concurrent -ProductCode XDT -ProductEdition ADV

write-host "Setting up Machine Catalog"
New-BrokerCatalog -SessionSupport MultiSession -ProvisioningType Manual -AllocationType Random -Name 2012R2 -Description 2012R2 -PersistUserChanges OnLocal -MachinesArePhysical $true
New-BrokerMachine -MachineName TEST-RDU-XD-01 -CatalogUid 1

$VerbosePreference = "Continue"

write-host "Creating Desktop Group"

If (!(Get-BrokerDesktopGroup -Name $desktopGroupName -ErrorAction SilentlyContinue)) {
Write-Verbose "Creating new Desktop Group: $desktopGroupName"
$desktopGroup = New-BrokerDesktopGroup -ErrorAction SilentlyContinue -AdminAddress $XDC -Name $desktopGroupName -DesktopKind $desktopKind -DeliveryType $deliveryType -Description $desktopGroupPublishedName -PublishedName $desktopGroupPublishedName -MinimumFunctionalLevel $functionalLevel -ColorDepth $colorDepth -SessionSupport $sessionSupport -InMaintenanceMode $False -IsRemotePC $False -SecureIcaRequired $False -Scope @()
If ($desktopGroup) {

Write-Verbose "Getting details for the Machine Catalog: $machineCatalogName"
$machineCatalog = Get-BrokerCatalog -AdminAddress $XDC -Name $machineCatalogName
Write-Verbose "Adding $machineCatalog.UnassignedCount machines to the Desktop Group: $desktopGroupName"
$machinesCount = Add-BrokerMachinesToDesktopGroup -AdminAddress $XDC -Catalog $machineCatalog -Count $machineCatalog.UnassignedCount -DesktopGroup $desktopGroup

Write-Verbose "Creating user/group object in the broker for $assignedGroup"
If (!(Get-BrokerUser -AdminAddress $XDC -Name $assignedGroup -ErrorAction SilentlyContinue)) {
$brokerUsers = New-BrokerUser -AdminAddress $XDC -Name $assignedGroup
} Else {
$brokerUsers = Get-BrokerUser -AdminAddress $XDC -Name $assignedGroup

$Num = 1
Do {
$Test = Test-BrokerEntitlementPolicyRuleNameAvailable -AdminAddress $XDC -Name @($desktopGroupName + "_" + $Num.ToString()) -ErrorAction SilentlyContinue
If ($Test.Available -eq $False) { $Num = $Num + 1 }
} While ($Test.Available -eq $False)
Write-Verbose "Assigning $brokerUsers.Name to Desktop Catalog: $machineCatalogName"
$EntPolicyRule = New-BrokerEntitlementPolicyRule -AdminAddress $XDC -Name ($desktopGroupName + "_" + $Num.ToString()) -IncludedUsers $brokerUsers -DesktopGroupUid $desktopGroup.Uid -Enabled $True -IncludedUserFilterEnabled $False

# Check whether access rules exist and then create rules for direct access and via Access Gateway
$accessPolicyRule = $desktopGroupName + "_Direct"
If (Test-BrokerAccessPolicyRuleNameAvailable -AdminAddress $XDC -Name @($accessPolicyRule) -ErrorAction SilentlyContinue) {
Write-Verbose "Allowing direct access rule to the Desktop Catalog: $machineCatalogName"
New-BrokerAccessPolicyRule -AdminAddress $XDC -Name $accessPolicyRule -IncludedUsers @($brokerUsers.Name) -AllowedConnections 'NotViaAG' -AllowedProtocols @('HDX','RDP') -AllowRestart $True -DesktopGroupUid $desktopGroup.Uid -Enabled $True -IncludedSmartAccessFilterEnabled $True -IncludedUserFilterEnabled $True
} Else {
Write-Error "Failed to add direct access rule $accessPolicyRule. It already exists."
$accessPolicyRule = $desktopGroupName + "_AG"
If (Test-BrokerAccessPolicyRuleNameAvailable -AdminAddress $XDC -Name @($accessPolicyRule) -ErrorAction SilentlyContinue) {
Write-Verbose "Allowing access via Access Gateway rule to the Desktop Catalog: $machineCatalogName"
New-BrokerAccessPolicyRule -AdminAddress $XDC -Name $accessPolicyRule -IncludedUsers @($brokerUsers.Name) -AllowedConnections 'ViaAG' -AllowedProtocols @('HDX','RDP') -AllowRestart $True -DesktopGroupUid $desktopGroup.Uid -Enabled $True -IncludedSmartAccessFilterEnabled $True -IncludedSmartAccessTags @() -IncludedUserFilterEnabled $True
} Else {
Write-Error "Failed to add Access Gateway rule $accessPolicyRule. It already exists."

} #End If DesktopGroup

Installing Storefront

This portion of the scripting is going to do a bunch of things. It will install the pre-requisites for Storefront, including IIS. It installs Storefront. It imports a certificate and binds it to the default website. The sets up the initial Storefront base URL then finishes the configuration. The first thing I did was to copy the 3.x version of CitrixStoreFront-x64 into my share to the x64\StoreFront directory and overwrite the default one. Luckily, this works so we can use XenDesktopServerSetup.exe again to install it.

The first thing we are going to do is install the pre-requisites and install Storefront. Again, I am just going to do a net-use to my share and run everything.

net use --% x: \\\XA76
Import-Module ServerManager
write-host "Installing Storefront Prereqs"
Add-WindowsFeature AS-Net-Framework,Web-Net-Ext45,Web-AppInit,Web-ASP-Net45,Web-ISAPI-Ext,Web-ISAPI-Filter,Web-Default-Doc,Web-HTTP-Errors,Web-Static-Content,Web-HTTP-Redirect,Web-HTTP-Logging,Web-Filtering,Web-Windows-Auth,Web-Client-Auth
cd '.\x64\XenDesktop Setup'
write-host "Installing Storefront"
.\XenDesktopServerSetup.exe --% /components STOREFRONT /NOSQL /quiet

Next, we want to import the certificate and bind it to the default web site. First, we ask for the cert password.

$myPW = read-host -Prompt "Enter Cert Password here"

We then want to import the certificate and assign it to the default website. Copy the .pfx file to the root of C:\. You will need the thumbprint of the certificate to put in the XXXXXXXXXXXXXXXXXX location of the script.

$certpw = ConvertTo-SecureString -String $myPW -Force -AsPlainText
Import-PfxCertificate -filepath "C:\cert.pfx" Cert:\LocalMachine\My -Password $certpw
New-WebBinding -Name "Default Web Site" -IPAddress "*" -Port 443 -Protocol https
cd IIS:
cd .\SSLBindings

Storefront Configuration

This gets us all set to start configuring Storefront. We will first need to import the Storefront PowerShell modules.

. "C:\Program Files\Citrix\Receiver StoreFront\Scripts\ImportModules.ps1"

Let us take a look at some of the variables we will be using here.

$nbn = $env:USERDOMAIN
$GatewayAddress = "https://site.domain.com"
$Farmname = "XenApp76 Farm"
$Port = "80"
$TransportType = "HTTP"
$sslRelayPort = "443"
$LoadBalance = $false
$FarmType = "XenDesktop"
$fqdn = "$env:computername.$env:userdnsdomain"
$baseurl = "https://" + $fqdn
$SFPath = "/Citrix/" + $nbn.toLower()
$SFPathWeb = "$SFPath`Web"
$SFPathDA = "$SFPath`DesktopAppliance"
$GatewayName = "TEST-RDU-NS-01"
$staservers = "http://" + $fqdn + "/scripts/ctxsta.dll"
$snipIP = ""

Again, keep in mind this is a small environment, so we will be using a single server for the XDC/Storefront roles. My $baseurl variable will resolve to https://server.domain.local. I set the store name to $nbn (NetBIOS Name), in this example it is TEST. Then using some simple PowerShell I set $SFPath, $SFPathWeb, and $SFPathDA to /Citrix/test, /Citrix/testWeb, and /Citrix/testDesktopAppliance respectively. You can set these variables as appropriate for your environment. The first command we want to run will do the initial configuration of Storefront.

Set-DSInitialConfiguration -hostBaseUrl $baseurl -farmName $Farmname -port $Port -transportType $TransportType -sslRelayPort $sslRelayPort -servers $fqdn -loadBalance $LoadBalance -farmType $FarmType -StoreFriendlyName TEST -StoreVirtualPath $SFPath -WebReceiverVirtualPath $SFPathWeb -DesktopApplianceVirtualPath $SFPathDA

The next thing I do here is setup the beacons. I set this up now because if you setup the gateway first, it sets the $baseurl as an external beacon. In my configuration, I do NOT want $baseurl to be an external beacon. At the time of writing this blog, Citrix has not written the full documentation for these PowerShell modules. I have already put in an RFE to get these up on Citrix’s site. That being said, I was not able to figure out HOW to remove an external beacon. The gateway module detects if you have any external beacons configured. If it detects none are configured, it automatically makes $baseurl and www.citrix.com the two external beacons. Setting up the external beacons is as simple as these commands.

$beaconID = ([guid]::NewGuid()).ToString()
Add-DSGlobalExternalBeacon -BeaconID $beaconID -BeaconAddress http://www.google.com
$beaconID = ([guid]::NewGuid()).ToString()
Add-DSGlobalExternalBeacon -BeaconID $beaconID -BeaconAddress http://www.citrix.com

Next, we are going to add a NetScaler gateway to the configuration. Reference the variables above. Not too much complicated in this command. This box is also the XDC, so the STA setup simply points to http://server.domain.local/scripts/ctxsta.dll.

$GatewayID = ([guid]::NewGuid()).ToString()
Add-DSGlobalV10Gateway -Id $GatewayID -Name $GatewayName -Address $GatewayAddress -Logon Domain -IPAddress $snipIP -SessionReliability $false -SecureTicketAuthorityUrls $staservers -IsDefault $true

The previous command creates the NetScaler gateway. We then have to enable NetScaler authentication and link this gateway to the store.

$gateway = Get-DSGlobalGateway -GatewayId $GatewayID
Set-DSStoreGateways -SiteId 1 -VirtualPath $SFPath -Gateways $gateway
Set-DSStoreRemoteAccess -SiteId 1 -VirtualPath $SFPath -RemoteAccessType "StoresOnly"
Add-DSAuthenticationProtocolsDeployed -SiteId 1 -VirtualPath /Citrix/Authentication -Protocols CitrixAGBasic
Set-DSWebReceiverAuthenticationMethods -SiteId 1 -VirtualPath $SFPathWeb -AuthenticationMethods ExplicitForms,CitrixAGBasic

This next command was from the Eric’s blog referenced above. This disables the check publisher’s certificate revocation to speed up console start-up

set-ItemProperty -path "REGISTRY::\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\WinTrust\Trust Providers\Software Publishing\" -name State -value 146944

Lastly we as we are using $fqdn as $baseurl, we will want to turn the loopback to OnUsingHTTP because the certificate is not going to match. You can look at more details on this command here.

Set-DSLoopback -SiteId 1 -VirtualPath $SFPathWeb -Loopback OnUsingHttp

There we have it. Storefront configuration is DONE, dude! All we need to do is setup an internal DNS cname to point site.domain.com to server.domain.local and we have single URL for internal/external access to your XenDesktop 7.6 environment.

Full Script is below.

# Certificate Password
$myPW = read-host -Prompt "Enter Cert Password here"

# StoreFront Parameters
$nbn = $env:USERDOMAIN
$GatewayAddress = "https://site.domain.com"
$Farmname = "XenApp76 Farm"
$Port = "80"
$TransportType = "HTTP"
$sslRelayPort = "443"
$LoadBalance = $false
$FarmType = "XenDesktop"
$fqdn = "$env:computername.$env:userdnsdomain"
$baseurl = "https://" + $fqdn
$SFPath = "/Citrix/" + $nbn.toLower()
$SFPathWeb = "$SFPath`Web"
$SFPathDA = "$SFPath`DesktopAppliance"
$GatewayName = "TEST-RDU-NS-01"
$staservers = "http://" + $fqdn + "/scripts/ctxsta.dll"
$snipIP = ""

#write-host "Mapping Drive"
net use --% x: \\\XA76
Import-Module ServerManager

write-host "Installing Storefront Prereqs"
Add-WindowsFeature AS-Net-Framework,Web-Net-Ext45,Web-AppInit,Web-ASP-Net45,Web-ISAPI-Ext,Web-ISAPI-Filter,Web-Default-Doc,Web-HTTP-Errors,Web-Static-Content,Web-HTTP-Redirect,Web-HTTP-Logging,Web-Filtering,Web-Windows-Auth,Web-Client-Auth
cd '.\x64\XenDesktop Setup'

write-host "Installing Storefront"
.\XenDesktopServerSetup.exe --% /components STOREFRONT /NOSQL /quiet

#write-host "Copy certificate to C:\ before moving on"

write-host "Installing Certificate"
$certpw = ConvertTo-SecureString -String $myPW -Force -AsPlainText
Import-PfxCertificate -filepath "C:\wildcard.vc3advantage.com-NEW.pfx" Cert:\LocalMachine\My -Password $certpw
New-WebBinding -Name "Default Web Site" -IPAddress "*" -Port 443 -Protocol https
cd IIS:
cd .\SSLBindings
Get-Item Cert:\LocalMachine\My\8CE850C9DCD7C26DF8E8FD4C44BF7D9E586E8AD1 | new-item!443

# Import Storefront module
write-host "Installing Storefront Modules"
. "C:\Program Files\Citrix\Receiver StoreFront\Scripts\ImportModules.ps1"

# Setup Initial Configuration
write-host "Initial Storefront Configuration"
Set-DSInitialConfiguration -hostBaseUrl $baseurl -farmName $Farmname -port $Port -transportType $TransportType -sslRelayPort $sslRelayPort -servers $fqdn -loadBalance $LoadBalance -farmType $FarmType -StoreFriendlyName TEST -StoreVirtualPath $SFPath -WebReceiverVirtualPath $SFPathWeb -DesktopApplianceVirtualPath $SFPathDA

write-host "Configuring Beacons"
$beaconID = ([guid]::NewGuid()).ToString()
Add-DSGlobalExternalBeacon -BeaconID $beaconID -BeaconAddress http://www.google.com
$beaconID = ([guid]::NewGuid()).ToString()
Add-DSGlobalExternalBeacon -BeaconID $beaconID -BeaconAddress http://www.citrix.com

$GatewayID = ([guid]::NewGuid()).ToString()
Add-DSGlobalV10Gateway -Id $GatewayID -Name $GatewayName -Address $GatewayAddress -Logon Domain -IPAddress $snipIP -SessionReliability $false -SecureTicketAuthorityUrls $staservers -IsDefault $true
$gateway = Get-DSGlobalGateway -GatewayId $GatewayID
Set-DSStoreGateways -SiteId 1 -VirtualPath "/Citrix/test" -Gateways $gateway
Set-DSStoreRemoteAccess -SiteId 1 -VirtualPath /Citrix/test -RemoteAccessType "StoresOnly"
Add-DSAuthenticationProtocolsDeployed -SiteId 1 -VirtualPath /Citrix/Authentication -Protocols CitrixAGBasic

write-host "Disable check publisher's cert revocation"
set-ItemProperty -path "REGISTRY::\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\WinTrust\Trust Providers\Software Publishing\" -name State -value 146944

write-host "Setting Loopback to OnUsingHttp"
Set-DSLoopback -SiteId 1 -VirtualPath $SFPathWeb -Loopback OnUsingHttp
Mar 202015

EDIT:  People have been requesting a tool to deploy SMS2 secret keys en mass, and the developer hasn’t implemented it yet.  Until he does I wrote a powershell script that will remotely connect to the sql database and inject the information needed for each user you select (http://pastebin.com/NBJHJPsX).  I have it setup for TOTP keys… which I think is what most people will use.
EDIT2:  I created a new script that does basically the same thing as the script posted above, but you can direct it against a specific AD group (http://pastebin.com/L9D8Jwaf).  Also, if you haven’t yet – upgrade your netscalers to version 11 – much easier to control the portal themes.

Get SMS2

Go to http://www.wrightccs.com/ and register for your free copy – an email will be sent to you with a download link and your xml based license.

Prepare your environment

You will need SQL/SQLExpress if you don’t already have it (will assume you do). You also need .NET 4 on the RADIUS server (will assume you have that too).

1. On the server you wish to use for RADIUS authentication open server management and click Add Roles and Features

2. Install the Network Policy and Access Services role and add any features that go along with that role – accept all the defaults.

3. Open the Network Policy Server Console

a. Expand Policies and select Network Policies

b. Right click Connections to other access servers and select properties

c. Change it from Deny access to Grant access and hit ok

d. Expand RADIUS Clients and Servers

e. Right click RADIUS Clients and select New

f. Create a connection for the local computer (so you can test connections).
Friendly name – whatever you want to name it
Address – the IP address of the RADIUS server you are creating
Shared secret – type something in that you will remember (will need it later)
Hit OK

g. Do the same thing for your Netscaler(s) using the NSIP(s) – again remember your shared secret – if you have more than one Netscaler use the same shared secret.
Should look something like this when you are done

4. Install SMS2

a. Next

b. For my purposes I select Custom (I don’t want SMS based authentication – just token)

i. Services I set CloudSMS to not install

ii. Under Clients I set all to install but the Citrix Web Interface Customization and SMS2…

c. Configure AuthEngine – enter the license text from the email you received and hit Check License (should pop up when it expires) – click ok and then Next

d. Leave the account as Local System and hit Next

e. On the next screen change the AuthEngine Address to (will reply on all IP addresses of the server)
Type in your domain controller name/address and fill in user account credentials of a user with access to AD
optionally you can change the BaseDN, but I’ll leave it as the root of my test domain
test your config and hit Next if successful

f. Enter your SQL server information
If the SQL instance is on the RADIUS server itself (as it is in my case) check the box to “Use named pipes (local)”
Click Test Connection – I get an error about how it could not use the database… it wasn’t there yet. I hit test connection again and it is successful.

g. Enter your email information – uncheck SSL and Use Auth if you don’t need them (straight smtp for me) – Finish

h. Configure OATHCalc – Next – Finish

i. Configure AdminGUI/Clients – Set the AuthEngine Address to the IP of the RADIUS server, and hit Finish

j. Next – install – Finish

Configure SMS2 for Token

1. Browse to C:\Program Files\WrightCCS2\Settings (assuming you installed the 64bit version… if not the Settings directory will be in x86)

2. Open Configuration.xml in notepad and change these settings (by default they are True, which will mess things up)

3. Find the <AuthProviders> line

a. Under CloudSMS – disable it (we didn’t install it anyways)

b. Under OATHCalc set it as default

c. Under PINTAN – disable

d. Under Email – disable

4. Save the .xml file and restart the WrightAuthEngine service (if they are not started – start them)

Setup all users for token (this could potentially take a long time)

1. Launch the SMS2 Admin Console

2. Select the user on the right hand side to select, and hit Configuration Menu at the top.

3. Go to the Auth Options tab (don’t need the others)

4. Click TOTP (time-based) and click Generate Shared Secret – record the shared secret if you want

5. Click Save configuration and you will see a popup – click OK and then you will see a QR code – copy it to the clipboard and send it to the user (also keep a record of it if you want)

6. Click Close

7. Do that again and again until you have a token for every user who needs to connect to XenApp/XenDesktop through the gateway

At this point users would download Google Authenticator or Microsoft Authenticator (probably others) to their smartphone and add the account using that QR code. Let’s assume everyone has done that.


1. Download NTRadPing (https://thwack.solarwinds.com/thread/14486) – google it if that link doesn’t work… you will find it

2. From your RADIUS server unzip it and run it (remember we created a client connection for the local computer earlier)
Type the IP of your radius server (port is 1812 if it isn’t there by default)
Leave the reply/retries set to default
Type in your secure string that you associated with the local computer RADIUS client
Type in the domain\username of a user you have configured to use one of the authenticator apps
Type in the password followed immediately with whatever code is showing in your authenticator app. If the password is “P@ssword!” then the password would be P@ssword!456123 (where 456123) is the number generated.
Click Send – If you see Reply-Message=Message accepted then you are good to go. If not then something is wrong.

Configure Netscaler

GUI 10.5

1. Logon your netscaler and browse to Netscaler Gateway\Policies\Authentication\RADIUS

2. Click the Servers tab and click Add
Give it a name
Select Server IP and punch in the IP of the RADIUS server
Port will be 1812
Type in the secret key you used to create the Netscaler RADIUS clients on the RADIUS server
Click Details and set Accounting* to OFF
Click Create

3. Click the Policies tab and click Add
Name the policy
Select the Server you just created (if it isn’t pre-selected)
Type in “ns_true” into the Expression field and hit Create

4. Bind the policy to your Netscaler Gateway virtual server(s) (NetScaler Gateway\Virtual Servers)
Select the virtual server and hit edit
Click the + on Authentication
Choose RADIUS and Secondary from the drop downs and hit Continue
Click to select the policy
Tick the policy you just created and hit ok
Click Bind
Click done and save

At this point you should be ready to test logging onto the gateway page

Testing the gateway

1. Hit your gateway address you will probably notice it has changed and looks something like this:
Password1 is your password
Password2 is your token pin

2. Logon using your credentials and the token generated by Google Authenticator (or whatever app you are using).

a. If it works then you are good to go and can move onto customizing the web interface

b. If it does not work unbind the policy and test to figure out where things are going wrong

i. Could be the wrong IP entered in for the Netscaler on the RADIUS server or wrong security string

Fixing the gateway appearance

1. Download Notepad++ and install (http://notepad-plus-plus.org/download/v6.7.5.html)

2. Download Tunnelier (http://www.bitvise.com/ssh-client-download)

3. Install and run Tunnelier (Bitvise SSH Client)

4. Connect to your netscaler using the password method

5. A command window and a SFTP window will open – select the SFTP window and on the right hand side browse to: /var/netscaler/gui/vpn

6. Select login.js and click the download button at the bottom (will download to your local desktop by default unless you change it on the left side… should be ok).

7. On the right side go into the resources folder and download en.xml

8. Make a backup copy of the files just in case

9. Open login.js using Notepad++
Find this line: if ( pwc == 2 ) { document.write(‘&nbsp;1’); }
and change it to:
if ( pwc == 2 ) { document.write(‘&nbsp;’); }
Just remove the 1 basically
Find the line that starts with: document.write(‘<TR><TD align=right style=”padding-top:
and change right to left

10. Save login.js

11. Open en.xml in Notepad++
Find this line: <String id=”Password2″>Password 2:</String>
Change it to: <String id=”Password2″>Token:</String>
You can name it whatever you want… I’m just using Token:

12. In Tunnelier upload the files to their respective directories overwriting them

13. Refresh your browser and your changes should be reflected.

The only problem now is that this change will not survive a reboot. In older versions of netscaler you could use a rewrite policy to rewrite the page and that would persist. In 10.1+ you have to use a custom theme.


Set a custom theme so the gateway appearance persists a reboot

NOTE: Linux is case sensitive… type things exactly as I have them.

1. Using Tunnelier switch to your terminal window
cp /nsconfig/ns.conf /nsconfig/ns.conf.save
mkdir /var/ns_gui_custom
cd /netscaler
tar -cvzf /var/ns_gui_custom/customtheme.tar.gz ns_gui/*

What we did there was make a backup of ns.conf (in case something goes awry – reverse the “cp” command to restore it), created a folder, and zipped the contents of /netscaler/ns_gui to /var/ns_gui_custom/customtheme.tar.gz ß that is the file and location that netscaler knows to use for a custom theme.

2. Open your netscaler in your browser, logon and navigate to NetScaler Gateway\Global Settings

3. Click the Change Global Settings link on the right side

4. Click the Client Experience tab and scroll to the bottom

5. Switch the UI Theme to Custom and hit OK

6. TEST the gateway page (I use a chrome incognito window when I make a change as it doesn’t use the cached website)

7. If the test is successful save your netscaler configuration

a. If you have a HA pair I am pretty sure you have to mirror all the steps on the secondary except for setting the UI Theme to Custom. On your secondary:

i. Copy the files to the correct locations on the secondary netscaler

ii. Run the commands from the terminal window

iii. Force a sync from the gui (System\High Availability à Actions)

Mar 162015

UPDATE:  I found trying to run this script through the Netscaler Gateway failed due to differences in the web pages.  I re-wrote the script so it will work internal directly to StoreFront, and externally with Netscaler Gateway.  The main caveat is that the wficalib.dll doesn’t allow you to logoff the session when going through the gateway.  I simply set it to look for any wfica32 processes prior to launching the application/desktop, compare that to the processes after launch, and kill the new process (disconnecting the session).  The script identifies the webpage as a gateway if */vpn/* is in the path.  I also set it to logoff of the Storefront/Gateway page when it ends the script.  If you were to run it back to back without logging off of the page it wouldn’t find what it is looking for initially and fail (because you’d probably already be logged on).  I may re-write this in the future with more functions to make it a bit shorter/cleaner, but as is it should work.
NOTE: I tested this with Netscaler 10.5… it may not work with previous versions as is, but if you read the script you should be able to figure out what needs to be changed.

I need to give credit to this Citrix blog post for getting me started.  This script will launch an application or desktop as a user you specify from the StoreFront web page, then send you an email to let you know if it was successful or not.

Variables you should edit:

In the send-results function
$smtpserver (line 19)
$msg.From (line 28)
$msg.to.Add (line 29)

In the main script (be sure to read the comments)
$username (line 84)
$passstring (line 86)
$resource (line 92)
$mask (line 94)
$wait (line 96)
$internetexplorer.visible (line 98)
$internetexplorer.navigate2 (line 99)

Powershell (x86)! – otherwise you cannot tie into the x86 dll for Receiver
(If you are going to the Netscaler Gateway it doesn’t matter which version as we won’t tie into the dll in that case)

Add the following to the registry if you are pointing to the internal StoreFront url – if you are pointing to the Netscaler Gateway it won’t matter
Windows Registry Editor Version 5.00


If you are running on an x86 machine change the registry path to exclude Wow6432Node, and change the path of the .dll on line 156 of the script to the correct path of the wficalib.dll.

Here is the script!

Contact me in in the channel David62277

Oct 312014

I have seen where PVS targets (mainly Desktop OS) will fail to activate via KMS after booting, and/or not get the proper group policy settings.  I think this is because PVS hasn’t released the network when Windows is trying to activate/update gpo (or something along those lines).  On top of this in my environment I have PvD and Random desktops booting off of the same vdisk image.  To fix this I created the script below, and setup a scheduled task to run at startup (using SYSTEM account).

Note: Using this script you can do a lot more than just slmgr /ato and gpupdate /force commands.  For instance if you have an antivirus service that you just want to start if the vdisk is in standard mode… you could just add a “start-service” command (of course you’d want that service to be set to manual).  Feel free to edit however it suits your environment.

Steps to implement:

  1. Start a maintenance version of your vdisk
  2. Logon to that desktop/server
  3. Open powershell_ise, or notepad
    1. Copy the script below and paste it
    2. Edit line 2 to be the FQDN of your domain
      Example: yourdomain.com
    3. Save it (remember where you saved it) – I just save mine to the root of C:\ to keep it simple
  4. Open Task Scheduler
    1. Right click on “Task Scheduler Library” and select “Create a Basic Task”
    2. Name your task and optionally add a description and click Next
    3. On the Task Trigger screen select “When the computer starts” and click Next
    4. On the Action screen click Next (Start a program should be selected by default)
    5. On the Start a Program Screen
      1. Type the path, or browse to powershell.exe (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe)
      2. in the Add arguments section:
        1. -executionpolicy unrestricted -file <path to the .ps1 file you just saved>
          Example: -executionpolicy unrestricted -file c:\startup.ps1
    6. Click Next
    7. Check “Open the Properties dialog for this task when I click Finish” and click Finish
    8. On the properties page click Change User or Group
      1. In the Select User or Group box type in “system” (no quotes) in the box and hit OK
      2. You should now see “NT AUTHORITY\SYSTEM” as the user account to run as
    9. Check the Run with highest privileges box, and click OK
  5. Perform any cleanup operations you typically do, run PvD inventory (if you use PvD), and shutdown the machine
  6. Place your vdisk into Test mode, and test away
  7. When satisfied set the vdisk to production

function startup {
while ((Test-Connection "fqdn of your domain ie: contoso.com" -count 1) -eq $null) {
Start-Sleep -Milliseconds 500
& cscript.exe c:\windows\system32\slmgr.vbs /ato
& gpupdate.exe /force
$p = gc c:\personality.ini
$r = (Get-ItemProperty registry::'HKLM\SOFTWARE\Citrix\personal vDisk\config').vhdmountpoint
if (($r -eq $null) -and (($p -like "*diskmode=p*") -or ($p -like "*writecachetype=0*"))) {

Explanation of the script:

When executed it will get the content of c:\personality.ini and the value of REG_SZ vhdmountpoint.  If personality.ini contains diskmode=p or writecachetype=0 and vhdmountpoint value is blank/non-existent it will stop the script (this indicates the vdisk is in private or maintenance mode).

PvD – value of vhdmountpoint will not be blank, so even if for whatever reason the .ini file shows the disk in private/maintenance it will go on and run the function
Shared Random – value of vhdmountpoint will be blank, but the .ini should show diskmode=s and writecachetype=something other than 0 (depends on the mode), so it will also run the function.

If the break condition is not met (indicating the disk is in shared mode) then it will run the startup function.  This function tries to ping the fqdn of your domain 1 time.  If it gets a reply it will run the activation command, and gpupdate.  If it does not, it will wait half a second and try again… over and over until it gets a reply from the fqdn of your domain.


Sep 182014

I recently ran into an issue where Citrix Profile Manager was not catching all the files from a user installed Office Add-in. I found the path to the files and they were in %localappdata%\Apps\2.0. Even if I specifically added a policy to sync that folder it still did not get every file needed for the Add-in to work. After banging my head against the wall trying to get profile manager to handle it I decided to write a logoff script to “backup” that folder to the user’s home share at logoff, and a logon script to “restore” it at logon. That worked, but it delayed logon/logoff as those files were copied to/from the user profile.

On top of that issue, I also had the need to redirect Chrome and Firefox cache directories to user home shares as they were soon to be installed on our VDI image. Of course, you can do both without the script you are about to see (GPO in the case of Chrome, and an .ini file with Firefox). I just figured kill three birds with one stone.

The answer ended up being very simple. Junction points! For those of you who may not know a junction point (aka reparse point or symbolic link) is basically a shortcut that Windows treats as a folder. A normal shortcut to “\\server\share\path” on your desktop would show that path in the address bar if you clicked on it. A symbolic link would show C:\Users\<username>\Desktop\<name of the link>. This allows you to basically “redirect” a specific folder. For those of you who have a “crappy app” that points its data to %userprofile%\AppData\<path> instead of %AppData% and prevents you from redirecting AppData this may help you as well.

Now all I need is a logon script (powershell) to create these junction points.

Requirements for this script:
1. Users must have the “Create symbolic links” right (set in GPO)

2. Users must have a Home Folder (if they don’t you could simply rewrite the script to create a folder somewhere using $env:username in the path).

Detailed explanation of what this script does:
1. Sets the location to C:\ so when it calls cmd it won’t complain about the path (assuming the script is going to run from a network share)

2. Imports a .csv file with the information
This .csv file has headers “localpath”, “homepath”, and “LorR”
localpath = the folder within local or roaming appdata. If the path is down inside somewhere make sure you include the whole path. In my case I want to get %localappdata%\Apps\2.0, so the localpath would be “Apps\2.0”
homepath = the path in the users home directory where you want the files located. Same as localpath you don’t include the entire share path… just the path you want it to create inside the share.
LorR = local or roam – so the script knows where to put the junction points.

3. For each line in the .csv file calls a function that will:
a. Decide if the path is in local or roaming appdata (LorR)
b. Create the folder structure on the user’s home folder
c. Checks if the local folder already exists. If so, checks to see if it is already a junction (which it shouldn’t be). If the path is *2.0 (path to the Office Add-ins) it moves those files to the home folder, and creates the junction. If it is anything else it deletes the folder (would be user installed chrome/firefox cache directories), and creates the junction.

After running this at logon my user now shows this in %appdata%

If I open Mozilla it shows to be local, but it is actually pointing to my home folder


The only “issue” I have seen thus far is Chrome will warn that the cache is on a network share the first time it is launched.

Below is the powershell script. Feel free to edit it to fit your needs, and make sure you test thoroughly before implementing into any production environment.


Jun 032014

I am in the process of building out a new XenApp environment for a customer, and was thinking… “It would be so nice to automate the ‘Configure and run discovery’ settings”. So how do you accomplish this? I know my service desk would appreciate it!

The answer is a custom “MMC”. Below are the steps to accomplish this.


Part 1: Create the custom MMC

  • Open up a 32bit MMC console (universally will work better and create less stress)
    • On 32 bit OS run “mmc”
    • On 64 bit OS run “mmc /32”

  •  Click File > Add/Remove Snap-in…

  •  Select the console you are going to push out to your users and add it to the “Selected snap-ins”
    • (In my case it is going to be AppCenter)

  •  Right Click on “XenApp”
    • Select “Configure and run discovery”

  •  Select the “Skip this screen in the future” box
    • Click Next

  •  Click on Add
    • For the server, I am going to pick my two XenApp Controllers
      • Depending on your setup, and where you are publishing this, you will need to pick the correct server(s)… (setting up a load balanced VIP on your NetScaler….  hmmm….)

  •  Click Next

  •  Check the box next to “Close this wizard when discovery is successful”

  •  Change the options of your new custom MMC console
    • Click on File > Options…

  •  In my case, I want to restrict access to areas of the tree
    • I am going to select “User mode – limited access, single window”

Below is an explanation of each option:

  • Author mode
    • Enables full customization of the snap-in console, including the ability to add or remove snap-ins, create new windows, create Favorites and taskpads, and access all the options of the Customize View and Options dialog boxes. Users creating a custom console file for themselves or others typically use this mode. The resulting snap-in console is usually saved in one of the user modes in this table.
  • User mode – full access
    • The same as author mode, except that users cannot add or remove snap-ins, change snap-in console options, create Favorites, or create taskpads.
  • User mode—limited access, multiple window
    • Provides access only to those parts of the tree that were visible when the console file was saved. Users can create new windows, but cannot close any existing windows.
  • User mode – limited access, single window
    • Provides access only to those parts of the tree that were visible when the console file was saved. Users cannot create new windows.


  •  Now we want to save our custom MMC
    • Click File > Save As…

  •  Save it where ever you would like. I am going to save mine to c:\custom mmc\AppCenter.mmc on each of my terminal servers.



Part 2: Publish the custom MMC

This section could be done a dozen different ways. I will show you how to publish out the mmc we just created as it being accessed directly from each server.

  • Select “Skip this screen in the future”
    • Click Next

  •  Enter in a Display name for your application
    • In my case I am using “Citrix AppCenter”

  •  Use the defaults
    • Application
      • Accessed from a server
        • Installed application

  •  Location to mmc.exe and the location of the custom mmc
    • Command Line:
      • c:\windows\system32\mmc.exe “c:\windows\system32\AppCenter.mmc”
    • Working directory:
      • c:\windows\system32
    • Click Next

  •  Click Add
    • Select the Servers or Worker Group that contains the servers you would like to publish out the mmc too.
      • Click Next

  •  Click Add
    • Add the users that need access to the mmc
      • Click Next

  •  Go to where your Citrix management console is installed (where the console is installed), and right click > properties
    • Then click on “Change Icon…”

  •  Copy out the location of the .ico

  • Go back to your application you are publishing and click “Chang icon…”

  • Click Browse
    • Input the location of the .ico file you just copied
      • Click OK

  •  Click Next

  •  Click Finish

You now have a management console that your admins will not have to configure for discovery!

May 302014

This will go over how to setup a single Access Gateway Vserver connecting to Multiple Domains using a single Pair of web interface servers.  This might sound confusing at first but in reality its pretty strait forward.  This article assumes that you already have some basic knowledge of how to setup netscaler and xenapp with WI in a single domain.

Environment  (LB is not required)

  1. Pair of Netscalers v10.1 in LB config.
  2. Pair of Web Interface 5.4 Servers with LB Vserver.
  3. Two AD Forests/Domains.
  4. A few STA servers (Doesn’t matter what domain they are on)
  5. A couple XA farms in Different domains (We are using XA6.5 and XA6)

Step 1

Configure the AD Polices on the NS.  There are many articles out there on how to do this.  The Top two in this list are my first domain and the 3rd one is the second domain.


Multidomain NS Step1

Step 2

Build your Vservers for WI and XML for each Domain.  It helps to have the xml servers for each farm on different ports to save IP address’s.  In this case I have three different farms specified.  The one on port 8888 and 8080 are on the same domain and the xml server setup on port 80 is the second domain.   I had to create a separate IP for the second domain as my WI vserver is also using port 80.  Again if you need help on how to build these their is plenty of articles out there on how to do it.


Multidomain Step2

Step 3.

Configure the AG Vserver to hit multple domains.  The NS will step through these in order of priority until it finds a matching username/password match.  If you have the same username/password combination on both domains it will always grab the one that has the lowest priority.  In this case the Top two Policies hit the first domain and the 3rd one hits the second domain.


MultiDomain Step3

Under the Published Applications Tab for the AG Vserver you need to configure some STA’s.  In my case I used the first domain/farm servers for STA’s.  I would make sure that all the STA’s belong to the same domain/farm.  You do not need to have a STA for each domain here.


MultiDomain STAs

Step 4.

On the WebInterface Servers configure a Xenapp site for Each Domain making sure to point to the XML Vserver’s created in Step2.  Make Sure you have each Site pointing back to the respective Vserver XML LB IP/port and configure it to point to your AG Vserver.   In this case the top Site is pointing to the Second Domain and the Second Site is pointing to the First domain.  For this it really doesn’t matter what domain


Multidomain WIConfig


For the Sta Config on the WI Servers I am using STA servers on the First domain even though the users are coming into the second domain.  All sites/domains should be setup with the same STA servers and they should match what your AG Vserver has configured for STA servers in Step 2.

MultiDomain WI_STA

Step 5.

Configure the AAA Policies and Profiles to hit the AG Vserver.  For this to work the AAA group name must match the AD group that the user is a member of.  In this Case the Second one down ASP_Access is my first Domain, and the one A_Access is the Second Domain.

MultiDomain Step4_1

Inside each AAA Group you have your Session policies that point to the specific URL/Domain.  Each Domain/AAA group should point back to a different Session policy

MultiDomain AAA_Config

Next we need to make sure the Session profile is pointed back to our WI Server site for that domain.  Make sure you have the corresponding  domain specified and the override global check boxes checked.  You will have to create one of these for each domain so that users from that domain hit the appropriate site.

MultiDomain AAA__ses_Profile

Step 6. Profit!!!

Once again if you have users that have the same username and password in multiple domains they will always get the lower priority domain.  If you have any questions feel free to jump on the channel and ask Splatone.