Feb 212018

I plagiarized David Ott’s script for migration of Citrix Profile Manager (UPM) profiles to FSLogix and created it for Local Profiles.

This requires using frx.exe, which means that FSLogix needs to be installed on the server that contains the profiles. The script will create the folders in the USERNAME_SID format, and set all proper permissions.

Use this script. Edit it. Run it (as administrator) from the Citrix server. It will pop up this screen to select what profiles to migrate.

#### EDIT ME
$newprofilepath = "\\domain.com\share\path"

#### Don't edit me
$ENV:PATH=”$ENV:PATH;C:\Program Files\fslogix\apps\”
$oldprofiles = gci c:\users | ?{$_.psiscontainer -eq $true} | select -Expand fullname | sort | out-gridview -OutputMode Multiple -title "Select profile(s) to convert"

# foreach old profile
foreach ($old in $oldprofiles) {

$sam = ($old | split-path -leaf)
$sid = (New-Object System.Security.Principal.NTAccount($sam)).translate([System.Security.Principal.SecurityIdentifier]).Value

# set the nfolder path to \\newprofilepath\username_sid
$nfolder = join-path $newprofilepath ($sam+"_"+$sid)
# if $nfolder doesn't exist - create it with permissions
if (!(test-path $nfolder)) {New-Item -Path $nfolder -ItemType directory | Out-Null}
& icacls $nfolder /setowner "$env:userdomain\$sam" /T /C
& icacls $nfolder /grant $env:userdomain\$sam`:`(OI`)`(CI`)F /T

# sets vhd to \\nfolderpath\profile_username.vhd
$vhd = Join-Path $nfolder ("Profile_"+$sam+".vhd")

frx.exe copy-profile -filename $vhd -sid $sid

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!

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 discussions.citrix.com 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.

Dec 052016


Using Citrix Profile Management with profile streaming enabled should keep your logon times pretty low.  Recently, they updated the version of our Antivirus (Trend Micro) to the latest, and suddenly I started seeing desktops fail with “session prepare failure” errors.  After much investigation it had something to do with the behavior monitoring feature of the AV software hanging Citrix Profile Management long enough to cause the desktop to freeze/unregister.  Turning off the profile streaming option fixed that issue.

Since then I have noticed logon times have skyrocketed.  I used to see an average logon time of about 30 seconds, and after disabling profile streaming I see average times well over 1 minute (some individuals as high as 5 minutes).

In order to try to get our logon times back down to a reasonable time I wrote a script to investigate the profile store.  I found a few folders which contain a ton of files – files that have to be copied in at each logon slowing things down.

The Fix

I wrote a blog post a while back about using a Powershell Script to redirect folders via symbolic links.  You can find that here http://www.citrixirc.com/?p=315.   We can use that same method to redirect “bad” folders, but using Citrix Workspace Environment Management instead (no need to learn how to script!).  NOTE: Users must have the “Create symbolic links” right!


Of course, you are going to need WEM setup in your environment.  Carl Stalhood has a great step by step on how to set it up here http://www.carlstalhood.com/workspace-environment-manager/.

A couple of caveats I would add to his blog post:
If you are installing on a PVS image before shutting down your maintenance/private mode vdisk to re-seal, kill the Norskale Agent Host Service.  For whatever reason if you don’t do this it can cause your vms in standard mode to take an obscenely long time to shutdown.
If you have a PVS environment and you have redirected the WEM cache to the persistent drive use a startup task to refresh the cache, force restart the Norskale Agent Host Service, and start netlogon after.  If the cache doesn’t already exist WEM doesn’t seem to check with the WEM server.  You have to create the cache, and then restart the service so that it reads it, and force restarting the Norskale Agent Host Service will stop netlogon (dependent on it).  Chicken or the egg thing…

Assuming you have WEM setup and running:

Create a Folders and Files action that creates the redirected folder structure in the user home directory or where ever you want to put it.  In the \\server\share\VDIPaths (or whatever you name it) have the folder structure laid out for all the folders you wish to redirect (empty folders) – this is your folder “template” directory:
ie: \\server\share\VDIPaths\Recent

The source path is the folder you just created and the target is where you want to put it.  Make sure overwrite target if existing and run once are unchecked

Under Options – Copy Directory Content (creates the folder structure in the user’s %homeshare% folder or where ever you put it), and make sure the Execution order is “0” – happens first.

Next we need to perform 3 tasks for each folder you want to redirect to the user %homeshare%\VDIPaths folder (or where ever you decide to put it)

We will first move the existing data in the profile to the redirect location, second delete the existing data in the profile, and finally create the symbolic link.


The first thing we do is create the move operation.  Name it whatever you want.  The source is the local profile path to the user, and the target is where we want to move the files

Under the Options Tab select Move Directory Content and set the execution order to 1.  We want the “VDIPaths\Recent” folder to have been created already by the create task


Set the source path to the directory you want to delete

Under the Options Tab select Delete Files / Folders and set the Execution order to 2 as we want the files to be moved to the new location prior to deleting.

Create the Symbolic Link

The Source will be the %homeshare% location, and the target will be the local profile path location.

Under the Options tab set the action to Create Directory Symbolic Link and set the execution order to 3.

Assign tasks

Assuming you have already setup configured users you can now assign the 4 tasks above to your test user or group.  Going forward you will only need to create the latter 3 tasks, and just make sure you update the “template” directory accordingly.

Assign the first task (create VDI_Paths in my case) setting always true
Assign the second task (Move Recent in my case) setting always true
Assign the third task (Delete Recent in my case) setting always true
Assign the 4th task (Recent_Symbolic_Link in my case) setting always true

0 – Create VDI_Paths – copies the folder structure to the user’s %homeshare% directory
1 – Move Recent – moves the contents of %appdata%\Microsoft\Windows\Recent to %homeshare%\VDIPaths\Recent
2 – Delete Recent – deletes the %appdata%\Microsoft\Windows\Recent folder
3 – Recent_Symbolic_Link – Creates a symbolic link at %appdata%\Microsoft\Windows\Recent pointing to %homeshare%\VDIPaths\Recent

You end up with something that looks like this:

If you click into the Recent symbolic link you will see it still looks like it is on the local C: drive as far as the path goes, but it is “redirected” to the path you specified.  If it does not work check the Norskale Vuem Agent.log file in the user profile, and look for the error.  If it is a permissions issue, then you probably have to allow the user to create symbolic links via gpo and/or local policy (I put it both places just to be sure it takes).

I have done this for the following paths thus far with success

%appdata%\Microsoft\Windows\Recent – all the recent places/docs a user has opened
%appdata%\Microsoft\Signatures – Outlook signatures
%localappdata%\Apps\2.0 – This one is for clickonce apps – I also had to make another task to create the “Apps” folder in the local profile in case it doesn’t exist (only one that is different from the rest)
%localappdata%\Microsoft\Internet Explorer\DOMStore – some IE cache that gets quite large
%appdata%\Microsoft\Office\Recent – all recent Office docs
%localappdata%\Google – Chrome cache
%appdata%\Mozilla – Firefox cache
%localappdata%\Mozilla – Firefox cache
%localappdata%\Apps\Evernote – Evernote (unfortunately some of my users need this)
%localappdata%\WebEx – WebEx Cache

Once you are certain all of the existing data for each of your users has been moved to their %homeshare% directory (or where ever you put it) you can exclude those folders from UPM, and remove the Move/Delete tasks.  It won’t hurt to leave them though…

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”