Dec 222020

I was recently tasked with moving some PVS target/vDisk to a new PVS environment. There are many ways to achieve this. However, this is how I migrate devices from one PVS Farm to another. I am sure there are better ways to automate this as my blog is just a simple way to get it done. Migrating PVS targets to a new PVS Server. Another way I have done it in the past was add new PVS servers into an existing Farm, then go through the actions to make them the main servers, and of course decom the old ones. This is a very simple task, and most people know this. But if not, they will help.

Open your PVS Console, and select the PVS vDisk, then click export.

Select the lowest version to grab the vDisk Chain

You will see a Filename.xml get created. This is a manifest file that will instruct the new destination PVS server to inject the version in.

For me I like to create Stores for all my different vDisk.

Give it a name

Check all the PVS Server that will contain the vDisk

Create the folder in the given location that you are storing the vDisk and Validate the paths.

I leave the default write cache location in the area it selects, as we do use the old legacy ways of the Write cache to server.

Now I just copy the Connects form the original Server to the destination

At this point I open of the PVS console and go to vDisk pool

Right click and Import

Select the store you create earlier, and Select a PVS server that you copied it to, and click search

Or you can import it from the Store, so you don’t have to select anything and take the change of getting something wrong.

Click Add and close

Click ok, after vDisk was added

Now you can see the Vdisk

For me I use a dedicated VMware device for updating my master images and such. I am going to move the VMware device over to the new PVS server and update the subnet and MAC as well to reflect a new subnet we just added.


Change the Port group and note the MAC

Now at this point I manually create the Device in the new PVS Server.

Add the new MAC, Update the vDIsks area to the vDisk you imported in.

Change the Type to Maintenance

Now because I use the ISO BDM method, I need to update it to the new BDM to the new PVS Boot path.

If you have more than one new PVS server, make sure you copy the vDisk files to the second server as well, or when it boots up based on the default load balancing, it will hit the second server and tell you no vDisk found.

Check and make sure the replication status is green on both servers

On the device, you will need to reset the Machine account from PVS, to recreate the Domain trust password

Now, boot the Maintenance Machine up and it should stream to the new location. Found the vDISK

Typically for moving all the targets I would do something like this, then I would edit the MAC addresses so they would align with the new subnet that I change from. 

How to Migrate PVS Target Devices | | Apps, Desktops, and Virtualization (

For this situation, I can now create all new devices that will have the same vDisk. I will create a new Machine Catalog/Deliver Group. You can also use the same one if needed. I use FSlogix, so the profile data will be intact for the user, the user will not even notice anything has changed as far as the devices name or its moved to a different PVS server.

Oct 232020

This quick write-up isn’t anything advanced. Most of you out there know this. But some may still question it. We know there is a lot of documentation around PVS and Martin Zugec is updating a lot of this within Citrix Tech Zone . This is more of a confirmation that running a defrag on the VHDX on a merged vDisk is still needed on today’s PVS server vDisk. I reached out to some guys on slack, as I knew I would get a solid answer fast. As you can see it is still needed. Some rebuild images, and some just do what I do. Which is a manual way. Which can be automated as well. Some people today may think, is this needed when I have SSD/NVMe. Yes, it is, because we are talking about all the version changes, and it’s the actual VHDX itself. Think of how you shrink FSLogix VHDX/VHD. To me, it’s the same concept. The type of storage doesn’t matter because it not at the OS layer. It’s about keeping the VHDX clean, optimized, and reducing and write cache bloats that will occurs of the fragmentation on the VHDX as a whole.

my advice is to read this blog, as it will shed a lot of light on what I am talking about and much more.

“Defragment the vDisk before deploying the image and after major changes. Defragmenting the vDisk resulted in write cache savings of up to 30% or more during testing. This will impact any of you who use versioning as defragmenting a versioned vDisk is not recommended. Defragmenting a versioned vDisk will create excessively large versioned disks (.AVHD files). Run defragmentation after merging the vDisk versions.Note: Defragment the vDisk by mounting the .VHD on the PVS server and running a manual defragmentation on it. This allows for a more robust defragmentation as the OS is not loaded. An additional 15% reduction in the write cache size was seen with this approach over standard defragmentation.

Here is a good script to defrag and shrink the vhdx

  1. Put the devices into maintenance mode in studio (No devices can be streaming of the vDisk to defrag the vDisk) 
  1. Make sure the connections say “0”
  1. You could copy the merged vDisk of and import it to do all the work if needed, Then you would need to mess with the active streaming devices. Then after your complete, just update all the targets. 
  2. But for this, I didn’t do it this way. But it’s an option.
  3. Make sure you have no locks either. As per my example, XD7CALLTST has none.
  4. Now merge the selected vDisk, 
  5. Select merged base -Last base + all updates from that base
  6. Then select the test mode
  1. Wait for the % to complete 100%
  1. Once it’s merged, it will look like this
  1. Now click on the vDisk and click mount
  1. The icon will look like this on the vDisk
  1. Now open up file explorer, and find the drive.
  1. Analyze it to see if it’s needed.
  1. Now defrag it.
  1. Once Completed, go back and select dismount it
  1. The Icon will go back to the original Look
  1. At this point I boot up my Maintenance device to make sure things are good
  1. Add you see its number 3.
  1. Once the machine boots up, you will see it in use in the “show vDisk Usage”
  1. Machine is up, log in and make sure it’s good.
  1. Now shut it down, and promote to PROD
  2. You will see the “show vDisk Usage” got to no devices
  1. Promote to Prod. Select Immediate or you can schedule it.
  1. It will now look like this
  1. Now copy the new Vdisk # 3 file to your other PVS servers to get this all green
  1. Copy from the Server you did the work on. In my case VS1PVS03


  1. Once the copy is completed, the replication will show green
  1. PVS Target check
  1. At this point I booted it up and it was golden.

This can be automated

Extra information

Total Breakdown of why it’s needed

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.


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.

Oct 192015

Sacha Thomet who was one of my competitors for the “Geekovation” contest at Synergy (he won!) wrote a PVS documentation script (  He tweeted a revision, and after looking at the code I decided to try my hand at it.  I have to give credit to Remko Weijnen ( for the code to change the mcli text output into arrays, and to Martin Pugh for the code to make a pretty web report (website and info in the comments of the function).

The script requires the mcli pssnapin (
Here is the script 
Edit line 131 to set the path to the html output file.
Add “invoke-expression $htm” at the end if you want it to auto launch the html report.

Feel free to edit the script – the html output is kinda dirty but it works.

pvs report

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
    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:" -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.