Dec 202017

In my environment I have nearly 100 persistent MCS VDAs. Our developers, contractors, and even us IT people need the ability to change things on our VDI desktops (install/uninstall/set registry/create local IIS sites/etc.) and have them persist reboots. Luckily, the only software I have to maintain on these desktops is the VDA itself. That still means when Citrix releases a new version that I want to move to, I have to upgrade almost 100 individual desktops. The last time I had to do this I wanted to rip my hair out, and decided to write a script to automate the task. It has saved me TONS of time, so I want to share it in hopes that it can save someone else some time (and hair).

I wrote the script specifically for my MCS environment, which runs on XenServer, but with a little tweaking it can be modified for any environment.

How it works (the short version):

  1. Gets the computer(s) you set – can be manual input or a delivery controller query
  2. On each computer it will create 2 scripts, and 2 scheduled tasks
    1. The first script loads auto logon info into the registry
    2. The second script handles the vda removal and install
  3. One scheduled task runs once to run the first script and reboot
  4. The second scheduled task runs the second script at logon (first script sets up a user – in my case the local administrator – to logon automatically)

Most things are explained in the script, and this time I’ve also created a youtube video to go through it all/show it in action.



Oct 242017

I moved from UPM to FSLogix earlier this year, and decided to write my own powershell script to convert the UPM profiles to .vhd.  FSLogix has its own conversion process (which I didn’t find a whole lot of info on), but I decided to create my own.

What this script does:

  1. Gets a list of all UPM profile directories in the root path (that you supply) and displays them to you to select which one(s) you would like to convert via out-gridview
  2. For each profile you select:
    1. Gets the username from the profile path – you will have to edit this part for your environment… explanation in the script
    2. Use the username to get the SID (FSLogix profiles use the username and sid to name the profile folder)
    3. Creates the FSLogix profile folder (if it doesn’t exist)
    4. Sets the user as the owner of that folder, and gives them full control
    5. Creates the .vhd (my default is 30GB dynamic – edit on line 70 if you wish to change it) – if it doesn’t exist (if it does skip 7, 9-10)
    6. Attaches the .vhd
    7. Creates a partition and formats it ntfs
    8. Assigns the letter T to that drive (edit on line 73 if you wish to change that)
    9. Creates the T:\Profile directory
    10. Grants System/Administrators and the user full control of the profile directory
    11. Copies the profile from the UPM path to the T:\Profile directory with /E /Purge – if you are re-running this script on a particular profile it will overwrite everything fresh
    12. Creates T:\Profile\AppData\Local\FSLogix if it doesnt exist
    13. Creates T:\Profile\AppData\Local\FSLogix\ProfileData.reg if it doesn’t exist (this feeds the profilelist key at logon)

Here is the script!

Apr 032017

Original Post

The first script I wrote was from before Citrix added real powershell functionality into PVS.  This new script leverages the new powershell module.  I am not exactly sure when they first added this into PVS (if you know please comment below), but this was developed using PVS 7.12.  Pay attention to comments marked with multiple hash tags (####) – these are places you will need to edit the script to fit your enviroment.

Here is the new script

Mar 312017

In my environment I have about 300 provisioned desktops, and when I update the vDisk sometimes it can take a while (days) before all of the VMs get the latest version.  Especially, in the case of a roll back I need a good way to get all of the VMs which are on the wrong version (and are waiting for a session) to boot to the correct version.

I think powershell version 3, but since I did it with v4 I’ll say 4+
PVS powershell module installed (learn more here)
Powershell remoting enabled on your delivery controller (also need to supply the name in the script)

What the script does:

  1. Gets a list of the Collections in your PVS environment, and outputs it to out-gridview so you can select the collection you want to focus on
    1. If you hit cancel the script ends
  2. After your select the collection and hit ok it will get all of the devices in that collection which are powered on, and cannot access a maintenance/private disk image
  3. For each of the devices in that list it checks to see if they have the latest vDisk version, or if they have the correct OverRide version.
    1. If they do then it tells you the device is on the correct version, and moves on to the next device
    2. If they do not then it will pass the device name to the check function, and after moves on to the next device
      1. invokes powershell on the delivery controller to see if the VM is in use (powershell remoting required)
        1. If it is, it tells you it is in use and does nothing
        2. If it is not it will reboot the vm, and tell you

Here is the script!  Remember to test it before using it in a production environment.


Feb 092017

I recently ran into an issue after upgrading my XenDesktop environment to 7.12 from 7.11.  Prior to that update I was able to create MCS desktops in my XenServer pool with no issues.  After upgrading to 7.12 the newly created VMs would not register with the delivery controller, and according to XenServer they did not have the tools installed.  I found that the network card didn’t even appear in Network Connections, and in device manager it showed up as a Realtek adapter instead of the XenServer PV device it should have.

After further investigation I found that the new VMs were created missing some things in XenServer if compared to VMs which were working (xe vm-list name-label=<name of vm> params=all).  I posted on to see if anyone had run into this issue before, and Carl Stalhood pointed me to this post.

Apparently, there is a private hotfix that will correct the problem, but it requries you do delete/re-create the machine catalog (which shows as the answer to the problem on the thread).  Reading through the post Paul Browne gives a much better/easier solution:
“Also in the short term to get your VM`s working correctly.

Shutdown the VM
putty or console into the XenServer

xe vm-list

(find UUID of the Vm you wish to fix)

xe vm-param-set has-vendor-device=true uuid=<UUID of the VM>

It should now work as expected.”

I wrote this PowerShell script to perform the operation, that Paul gives above, automatically across multiple VMs.  Requires Powershell v3, scripts enabled (set-executionpolicy to change it), and XenCenter to be installed on the machine the script is run from.  Read the comments of the script to see an explanation of what the script is doing.

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.

Jul 252016

As in my previous post we are querying the Citrix monitordata tables in SQL instead of doing web queries against ODATA because it is super fast.

The script needs to run as a user who has at least read rights to the database.  Just like the original it will query the monitordata tables to gather the information, but I think I have improved on the queries a bit.  Also, just like the original it gets a lot more information than what is actually needed, so the queries can be used again for other information (logon duration reporting, accessing outside vs inside report, client version reports, etc.).  Feel free to edit and use this script as you see fit.

Here is the updated script!  Pay attention to anything marked with multiple hashes (####).  It is referencing something you need to change for your environment (ie: sql server name), or items of note.

Here is an example of the updated report.


Jul 212016


My boss wanted a report to show the usage of our XenDesktop environment (desktop sessions only), so I wrote a script that queries the delivery controller once per minute to get sessions.  It then writes that information to an .xml file, and then I have another script that reads the .xml file to report on usage.  Well, that is a bit complicated, and all that data already exists in the ODATA in director.  I have demonstrated how to query that data in a previous script I wrote to get logon times, but gathering each web table and then combining can take a long time.

That ODATA information is also in the monitordata tables of your Citrix database (which is split off into a separate database from the main database after 7.8 I think), and querying SQL is A LOT faster!

The script I wrote needs to be run as a user who has at least read rights to the database.  It will query the monitordata tables and combine the information into an array which can output the information we want.  For example if you query monitordata.session it will give you all the sessions, but the only indication of which user launched that session is the UserID field… which is just a number.  You have to join monitordata.session and monitordata.[user] in order to figure out who it actually was in that session.  The query I wrote actually gets a lot more information than is needed for this script, so I can use it as a basis to write more scripts.  Feel free to use this as a base for your own scripts… if you do note that all the date/time values in the database are UTC time.

Here is the script!  Be sure to pay attention to anything marked with ####

Here is an example report email.


Jun 212016

This will be short and sweet (hopefully sweet).  If you have a session recording server setup you may notice that the entries/files don’t go away on their own.  Here is how to clean them up:

Just create a scheduled task to run the code below once per day (as system – elevated):

C:\Program Files\Citrix\SessionRecording\Server\Bin\icldb.exe remove /RETENTION:7 /DELETEFILES /F /S /L

remove = remove from the database
/RETENTION = days of files to keep (above code will keep 7 days)
/DELETEFILES = delete files as well
/F = Force
/S = Suppress copyright
/L = Log to event log

Me being the powershell lover that I am created a script (one liner below), saved the .ps1, and created a scheduled task to run powershell calling my script.
StartProcess -FilePath ‘C:\Program Files\Citrix\SessionRecording\Server\Bin\icldb.exe’ -ArgumentList “remove /RETENTION:7 /DELETEFILES /F /S /L”

May 062016

If you have tried to use Windows 10 in XenDesktop with Citrix Profile Management you have probably run into two major issues.

The Issues
The first issue is the start menu… which is now a database located at %localappdata%\TileDataLayer\Database.  At logoff when profile manager tries to copy it off it can’t due to services locking the files.  This results in the user logging on and their start menu not working.

The second issue revolves around SMB2/3.  SMB1 would close files as soon as it was done with them, but 2/3 leave them open for a little longer in case they are requested again.  This means when a user logs off and their Pooled Random desktop shuts down file locks can remain in the profile store if the shutdown process happens too fast (which it does 99% of the time).  Basically, if a user was to logoff and then try to log back on in a short period of time their logon would be greatly delayed due to the “ghost” file locks.

The Workarounds
Start Menu – This one is a bit tricky.  When a user logs off we need to stop the Tile Data model server and State Repository Service (in that order) so that profile manager can copy the start menu database off to the user store.  Here is the rub… a normal user cannot stop these services, so you can’t use a logoff script!  Here is what you do… logon as a local administrator and…

  1. Create a powershell script on the root of C:\ – name it logoff.ps1
    1. Open powershell_ise.exe as administrator and write these 2 lines in the white space at the top (if no white space hit the new button to create a new script).
      stop-service tiledatamodelsvc -force
      stop-service staterepository -force
    2. Save it as C:\logoff.ps1 (or put it where ever you want – just remember where it is)
  2. Right click the start menu – hit run – type in taskschd.msc and hit ok
  3. Right click the Task Scheduler Library node and select Create Basic Task…
  4. Name it whatever you want – I named mine logoff – hit next
  5. Select “When a specific event is logged” on the next screen and hit next
  6. Under Log: start typing “Sec”  the Security log should show up
  7. Under Source type in “Microsoft Windows security auditing” (no quotes)
  8. Event ID will be 4647 – hit next
  9. leave Start a program selected – hit next
  10. in the program/script blank C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
  11. in the add arguments blank “-executionpolicy unrestricted -file c:\logoff.ps1” (no quotes) – if you put the script somewhere else make sure you have the path correct.
  12. click next, and on the next screen check the box “Open the Properties dialog…” and hit Finish
  13. Click Change User or Group, type in “system” (no quotes) and hit ok
  14. Check “Run with highest privileges”, and hit ok

Now when a user initiates a logoff the system will stop the Tile Data model server and State Repository Service.  This will allow profile manager to copy off the start menu database.

Ghost File locks
This one is pretty easy – we just delay the shutdown with a shutdown script.  This allows the file locks to be released at shutdown.  Here is what you do… (you should still be logged on as a local administrator)

  1. Run powershell_ise.exe as administrator and type these 2 lines in the white space at the top… again if no white space hit the new button.
    stop-service brokeragent -force
    start-sleep -s 30
  2. Save it as C:\shutdown.ps1 (or where ever you want)
  3. Right click the start menu – hit run – type in gpedit.msc
  4. Under “Computer Configuration\Windows Settings\Scripts” double click on Shutdown
  5. Click the PowerShell Scripts tab
  6. Click Add – browse to the script you just created – hit ok
  7. Hit Ok again on the shutdown properties box, and close the local group policy editor

At shutdown this script will kill the brokeragent service (just in case delaying the shutdown would allow the desktop to appear “available” again), and delay the shutdown by 30 seconds.  This allows all file locks in the profile manager store to be released.

Bonus – UPM policy settings for Windows 10 (These are mine, so you may need to tweak for your environment – of course redirect all you can)

Exclusion list (registry) 
Software\Microsoft\Internet Explorer\Recovery

Exclusion list – directories
AppData\Local\Microsoft\Windows Live
AppData\Local\Microsoft\Windows Live Contacts
AppData\Local\Microsoft\Terminal Server Client
AppData\Local\Windows Live
AppData\Local\Google\Chrome\User Data\Default\Cache
AppData\Local\Microsoft\Windows\Temporary Internet Files
AppData\Roaming\Citrix\PNAgent\Icon Cache
AppData\Roaming\Macromedia\Flash Player\\support\flashplayer\sys
AppData\Roaming\Macromedia\Flash Player\#SharedObjects
AppData\Local\Microsoft\Internet Explorer\Recovery
AppData\Local\Microsoft\Windows Mail
AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Dropbox
AppData\Local\Downloaded Installations
AppData\Local\Cisco\Unified Communications\Jabber\CSF\Voicemail
AppData\Local\Cisco\Unified Communications\Jabber\Voicemail
AppData\Local\ATT Connect
AppData\Local\Cisco\Unified Communications\Jabber\Crash Dump
AppData\Local\Cisco\Unified Communications\Jabber\CSF\Logs
AppData\Roaming\Microsoft\Internet Explorer\UserData
AppData\Local\Microsoft\Windows\Application Shortcuts
AppData\Local\Microsoft\Group Policy
AppData\Local\Microsoft\Media Player

Exclusion list – files

Files to synchronize

Directories to synchronize

Folders to mirror

Process Internet Cookie files on logoff  – Enabled

Process logons of local administrators – Enabled

Profile streaming – Enabled

Path to user store – \\server\share\%username%.%userdomain%\!CTX_PROFILEVER!!CTX_OSBITNESS!

Enable Profile management – Enabled

One more bonus!!
Based mostly on this Citrix blog – Windows 10 Optimization for XenDesktop – I wrote a powershell script to automatically optimize your Windows 10 gold PVS image… available HERE.