Jul 292019

Update: Added cli functionality.

This post is an updated version of my original profile compacting script ( http://www.citrixirc.com/?p=829). I wrote it using Powershell Studio and converted it into an executable. This version does not require the Hyper-V powershell module as it uses defrag and diskpart to perform the shrink function.


  2. .Net 4.5

Instructions/How it works:

    2. Gets the current size again before processing
    4. Finds the first available drive letter it can assign (between D and Z)
    5. Waits 3 seconds before assigning the drive letter (there can be a timing issue if you don’t wait a couple of seconds between mount and drive letter assignment)
    6. Defrags the mounted volume
    7. Detaches and re-attaches as read only
    8. Uses diskpart to compact the vhd(x)
    9. Gets the size of the file post processing
    10. Updates the results pane
  6. To run via CLI simply run from command prompt and add the path to the root profile folder. (ie: ShrinkFSL.exe \\servername\share\profiles)

You can keep track of the .vhd(x) attaching/detaching via diskmgmt.msc if you want. If for some reason the program hangs up you can click the stop button. It will detect if there is a vhd(x) attached, and detach it. If there is more than one file attached it will not attempt to detach any file. It will instead open diskmgmt.msc for you, so you can perform the cleanup manually (why I recommend not running it on a machine with .vhd(x) files already attached).


Here is a link to the tool

Oct 022018

According to this article, they say ” SAML with Microsoft Azure is only supported if you are using AD FS”. We are not using ADFS in our environment. We are simply using Azure AD Connect to do Password Synchronization into Azure AD from our on-premises Active Directory Domain Services. I figured out a way to make this work without using ADFS.

Log into your Azure instance, click on “Azure Active Directory” and select “Enterprise Applications”. Click “New Application” and select “Non-gallery application”

Call it something and hit “Add”

While this is configuring, log into your ConnectWise Manage server and go to the URL (https://{site}/v4_6_release/auth/{companyId}/metadata) This will download a metadata file. Save it somewhere.

Back in the Azure portal, your Enterprise Application should now be up. Click on “Users and Groups” and add a group that you would like and hit “Select”, then “Assign”. I am going to select a group with all of our Active Directory users her. (Remember: Our environment is setup using Azure AD Connect with password sync)

Next, click on “Single sign-on” and select “SAML”

I’m using the “New Experience” here. You can switch to and from it with the following button at the top.

Click edit on “Basic SAML Configuration”. Then click “Upload metadata file” at the top and upload the metadata file you downloaded above. It will add the top two lines. I have added the “Sign on URL” manually by just adding the base URL. After you are done with all of this, click “Save”

Next, download the Base64 cert (Under “SAML Signing Certificate”) and save it somewhere.

Under #4, copy both the “Login URL” and the “Azure AD Identifier” into notepad somewhere.

Next, select the “old experience” using the button at the top.  Set “User Identifier” to “user.employeeid” and click “Save” at the top.

You can switch back to the “New Experience” now. You should see your change here:

Log into Manage and go to “System” and “Setup Tables” then “SSO Configuration”. Click “+” to add a new one.

Enter a description and put in “SSO Type” of “SAML” (You may want to set this to inactive while you are screwing with it). Select the location in the top right.

Enter “Login URL” in the “Login URL” field

Enter “Azure AD Identifier” in the “Identity Provider ID” Field

Upload the Base64 certificate from above.

Click “Save”

When you are ready to test it, uncheck the “Inactive” button, and save the configuration.   The login will look like this now:

One last tidbit.  If somehow you DO lock yourself out of your environment, you can change your SSO configuration directly in the database. Just find dbo.SSO_Configuration, and set your “Inactive_Flag” to True.  Not that I did that or anything.  🙂 🙂

Sep 272017

Tick tock, tick tock. June 30th, 2018 is fast approaching and will be here before we know it. If you are anything like me, you still have plenty of old 2008R2 XenApp 6.5 farms lying around. I’m sure you have seen all the articles like this, this, this, and this. These are great resources on how to migrate your XenApp 6.5 farm information into a 7.x site collection. However, everything I have read is missing a critical piece of information that I needed in my environment. How do I get my existing session hosts migrated into this 7.x site collection? I have seen this Citrix article that states the basic premise, however most things I have read/heard state that you should always install a clean VDA and reinstall your applications.  For my environment, this just is not feasible.  I have hundreds of applications across dozens of customers and Active Directory forests. Many of these applications were difficult to install on XenApp in the first place. Some of them required software vendor coordination to install. There is the issue of license key transfer, etc. etc. Too many issues arise for this to work in any sane amount of hours. For my needs, I needed to figure out a consistent way to move my workers from 6.5 to 7.x. I needed to upgrade my hosts, plain and simple. If you have ever tried to uninstall XenApp 6.5, it does not do a very good job, sadly.  It leaves a lot of remnants that the 7.x installation detects and then fails to install the VDA.  A LOT.

I developed a process that does the following:

  • Uninstalls XenApp 6.5 (For real)
  • Upgrades 2008R2 to 2012R2
  • Installs the VDA

I will be sharing with you the uninstallation of XenApp 6.5.  I spend countless hours (less than my estimate of fresh install, exponentially, of course) on this process figuring out what pieces 7.x detects and going back to the uninstallation to add the removal of that piece to the script.  A lot of the things I found needed to be uninstalled in a specific order, or other pieces would fail.

The first part of this script uninstalls all 7 Rollup Packs, in reverse order.

start-process -ea 0 -Filepath "msiexec" -Argumentlist "/uninstall {D23001A2-7FF8-EAFD-7E32-58B3A003F5B5} /package {1471A89F-8CAB-4C46-89AB-942432D1DD3D} /passive REBOOT=ReallySuppress" -wait
start-process -ea 0 -Filepath "msiexec" -Argumentlist "/uninstall {6534B232-8426-2242-316E-D9B1F5A46E1A} /package {1471A89F-8CAB-4C46-89AB-942432D1DD3D} /passive REBOOT=ReallySuppress" -wait
start-process -ea 0 -Filepath "msiexec" -Argumentlist "/uninstall {ED7485F0-8579-F605-3326-9D058656F2B0} /package {1471A89F-8CAB-4C46-89AB-942432D1DD3D} /passive REBOOT=ReallySuppress" -wait
start-process -ea 0 -Filepath "msiexec" -Argumentlist "/uninstall {D511345D-32F8-8940-8B55-398DBDE50F66} /package {1471A89F-8CAB-4C46-89AB-942432D1DD3D} /passive REBOOT=ReallySuppress" -wait
start-process -ea 0 -Filepath "msiexec" -Argumentlist "/uninstall {38D5B4B1-08DD-E8BA-3D9C-AEE979D52A7C} /package {1471A89F-8CAB-4C46-89AB-942432D1DD3D} /passive REBOOT=ReallySuppress" -wait
start-process -ea 0 -Filepath "msiexec" -Argumentlist "/uninstall {B1CF9796-DC5D-2498-CA8D-E03BF20DDD70} /package {1471A89F-8CAB-4C46-89AB-942432D1DD3D} /passive REBOOT=ReallySuppress" -wait
start-process -ea 0 -Filepath "msiexec" -Argumentlist "/uninstall {B4A6E274-BC1D-D17F-17AE-B7BB94FE8493} /package {1471A89F-8CAB-4C46-89AB-942432D1DD3D} /passive REBOOT=ReallySuppress" -wait
start-process -ea 0 -Filepath "msiexec" -Argumentlist "/uninstall {343BE097-0B21-F62C-9D0A-886C9D142DBF} /package {1471A89F-8CAB-4C46-89AB-942432D1DD3D} /passive REBOOT=ReallySuppress" -wait

The next part of the script does the uninstallation of on XenApp 6.5.

start-process -ea 0 -Filepath "msiexec" -Argumentlist "/x {1471A89F-8CAB-4C46-89AB-942432D1DD3D} /L*v c:\output.log CTX_MF_FORCE_SUBSYSTEM_UNINSTALL=Yes /passive REBOOT=ReallySuppress" -wait

The next part of the script does uninstallation of all of the crap that is left after this uninstall.

start-process -ea 0 -Filepath "msiexec" -Argumentlist "/x {68376322-B36A-47CE-A637-37943D56476A} /passive REBOOT=ReallySuppress" -wait
start-process -ea 0 -Filepath "msiexec" -Argumentlist "/x {C4567AFA-6577-46C6-9153-457509317506} /passive REBOOT=ReallySuppress" -wait
blah blah blah A ton more uninstallation crap here

During testing I ran through this uninstallation at least 50 times. I took a snapshot of the XenApp 6.5 system, tested the uninstall, reverted to the snapshot and tested again. The insane thing is that I would get different results, and different failures, randomly throughout my testing. What is the definition of insanity? “Doing the same thing over and over again expecting different results” Well, I guess I’m officially insane. Due to this, I added 2 more XenApp 6.5 servers to my testing in order to see what other failures this process may uncover. This was a smart idea, because I found many more things that needed to be scripted in an attempt to catch them all. So many orphaned services, registry keys and files left, randomly after each uninstall. Frustrating! Most were only found until after the 2012R2 upgrade and trying to install the VDA and digging into the logs for specific failures. VERY frustrating! I was tempted to hit the bottle many times at work during this process.

This next part was annoying and odd, and may not be necessary in your environment. I had a bitch of a time getting some of the C++ redistributables uninstalled. These are a critical component of XenApp 6.5 AND 7.x. If these are not removed cleanly, the VDA installation process fails miserably. I was not able to uninstall mine as they kept pointing to the original installation directory that did not exist anymore. I ended up downloading the installation files to a directory on the C: and changing the registry to point the installation to that location. Sigh.

This portion uses the PowerShell module Expand-ZIPFile to extract the installation files to the C:. I have attached everything at the end of the article. You can use whatever method you would like to extract the files. Please note the .reg file sets the install (uninstall) directory to C:\.

Expand-ZIPFile –File "C:\uninstallme.zip" –Destination "C:\"
REG IMPORT C:\fixme.reg

After these files are in place, I am able to successfully uninstall these C++ components.

start-process -ea 0 -Filepath "msiexec" -Argumentlist "/x {1D8E6291-B0D5-35EC-8441-6616F567A0F7} /passive REBOOT=ReallySuppress" -wait
start-process -ea 0 -Filepath "msiexec" -Argumentlist "/x {F0C3E5D1-1ADE-321E-8167-68EF0DE699A5} /passive REBOOT=ReallySuppress" -wait
start-process -ea 0 -Filepath "msiexec" -Argumentlist "/x {743C9F75-F327-4D1C-9016-6C573930ADC1} /passive REBOOT=ReallySuppress" -wait

After this portion. Reboot. Finally. This process takes a good half hour, at least, depending on your hardware.

Lastly, there is a cleanup script that removes all the orphaned services, registry keys, and files that I found to be left during multiple uninstall attempts. It also removed the Remote Desktop Services role.

$Ctxmemop = Get-WmiObject -Class Win32_Service -Filter "Name='Citrix 64-bit Virtual Memory Optimization'"
$CtxAudioSvc = Get-WmiObject -Class Win32_Service -Filter "Name='CtxAudioSvc'"
Blah blah remove more crap here
Remove-Item "C:\Program Files (x86)\Citrix" -recurse -force
Remove-Item "C:\Program Files (x86)\Common Files\Citrix" -recurse -force
Blah blah delete more crap here
Import-Module ServerManager
Remove-WindowsFeature Remote-Desktop-Services

Reboot. This part of the script doesn’t take long at all. This should now give you a clean slate (tabula rasa) in which you can upgrade and install the VDA.

The rest of the process is pretty self-explanatory. You do an in-place upgrade of 2008R2 to 2012R2. Then install the VDA. There is a lot more to it, and I can post a write-up if comments demand it.

I have attached the scripts/files to github. Thanks to braynyac (Tim Riegler) for posting them for me.

I hope this has been helpful to some of you. This was very time consuming and I hope I have saved some of you a ton of time who are in the same situation as we are in our XenApp 6.5 environment.

Have fun!

Link to all github with all files.

Aug 252017

(See the new tool Here)

I recently switched from Citrix Profile Management to FSLogix! For those of you who do not know how it works… it mounts a virtual hard drive at the C:\Users\%UserName% folder for each user who connects. A huge advantage to this is that it is not copying the profile in at logon (or out at logoff), which greatly reduces logon times. Depending on how you have things setup you can literally (not figuratively) expect logon times of around 15 seconds reported in director no matter how your users decide to bloat their profiles.

There is one “problem” with dynamic vhd/vhdx profiles… they don’t shrink on their own. For instance, if you were to copy a 4GB file into a dynamic vhd file you would obviously see it grow by about that much. If you delete that 4GB file the vhd stays the same size! This isn’t really that big of a deal to me, but it might be to my storage administrator.

I wrote a script to compact the vhd profiles not in use.

Windows 10 – Powershell v5
Hyper-V module (Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All) – reboot required
Powershell run as Administrator

What this script does:

  1. Checks to make sure the above requirements are met
  2. Loads functions and variables (you will have to edit some of them for your environment – see the script for more info)
  3. Gets a list of all vhd/vhdx files in the root of your profile share
  4. Runs a for each loop
    1. Tests to see if the vhd file is currently locked (in use)
      1. If yes it will display a message on your screen saying it is locked and that it is being skipped (in the report the Success column will be marked with locked)
      2. If no then it will move on
    2. Tries to mount the vhd in read only mode
      1. If it fails it will send you an alert email and end the script as something is not right
      2. If it mounts then the script moves on
    3. Tries to optimize the vhd file (compact)
      1. Gets the original size in MB, size after compact in MB, computes the total reduction in MB, and notes failure/success
    4. Tries to dismount the vhd
  5. Once finished it will email you a report like the one below (names, paths, and sids are masked obviously)

Here is the script

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.

Feb 242016


Here is the script





Aug 062014

Post credit goes to David62277, a community contributor from #Citrix IRC on Freenode In order to update certain software on a PVS image (hosting platform tools, pvs target device software, etc) it is required to “reverse image” your vdisk back to a normal VM, install your update, and image back to vdisk. This is because the machine is actually booted to network (it is getting its C: drive from a network share essentially), and if you disrupt that connection while the vdisk is in private mode it can (most likely will) ruin your vdisk image. The process of reverse imaging, and re-imaging your vdisk can be very time consuming. There is a better way… update your vDisk without having to reverse image by directly booting to a .VHD from Windows PE.

You will need the Microsoft Windows AIK (I’m using Windows 7 version)


  1. Download and install the Microsoft Windows AIK and install it on your workstation
    1. This is what we will use to create a bootable .iso image
  2. Once installed create your .iso
    1. Start => All Programs => Microsoft Windows AIK => Deployment Tools Command Prompt1-BootVHDwithPE
    2. Run “copype.cmd amd64 c:\winpe” (note: you can use x86 or ia64 depending on your architecture… don’t think it matters though. Also, you can specify a different directory).2-BootVHDwithPE.png
    3. Now we want to add boot support
      1. Imagex /apply c:\winpe\winpe.wim 1 c:\winpe\mount
      2. Copy c:\winpe\iso\bootmgr c:\winpe\mount
      3. Mkdir c:\winpe\mount\boot3-BootVHDwithPE.png
    4. Run the following commands:
      1. Bcdedit /createstore c:\winpe\mount\boot\BCD
      2. Bcdedit /store c:\winpe\mount\boot\BCD -create {bootmgr} /d “Boot Manager”
      3. Bcdedit /store c:\winpe\mount\boot\BCD -set {bootmgr} device boot
      4. Bcdedit /store c:\winpe\mount\boot\BCD -create /d “WINPE” -application osloader4-BootVHDwithPE
    5. Run the following commands using the guid you received from the last command
      1. Bcdedit /store c:\winpe\mount\boot\BCD -set <GUID> osdevice boot
      2. Bcdedit /store c:\winpe\mount\boot\BCD -set <GUID> device boot
      3. Bcdedit /store c:\winpe\mount\boot\BCD -set <GUID> path \windows\system32\winload.exe
      4. Bcdedit /store c:\winpe\mount\boot\BCD -set <GUID> systemroot \windows
      5. Bcdedit /store c:\winpe\mount\boot\BCD -set <GUID> winpe yes
      6. Bcdedit /store c:\winpe\mount\boot\BCD -set <GUID> detecthal yes
      7. Bcdedit /store c:\winpe\mount\boot\BCD -displayorder <GUID> -addlast5-BootVHDwithPE
    6. One last command
      1. Oscdimg -n -m -o -bc:\winpe\etfsboot.com c:\winpe\mount c:\winpe\bootdisk.iso6-BootVHDwithPE
    7. Done with the “hard” part
  3. Copy the .iso file to your ISO storage repository (xenserver, vmware, whatever)
  4. Create a new disk in your hypervisor that is about 2x as large as the size of your vdisk (note: use the size it shows in the PVS console x 2) – this gives you room to play.
  5. Attach that disk to your PVS server
    1. Create a partition, format, etc.
  6. Merge your vdisk to a new merged base (set it as test)7-BootVHDwithPE
  7. Copy the merged .vhd file to the new drive you just attached to the PVS server
  8. Detach the new drive from the pvs server, and attach it to the VM you normally use to update your image (should be the only drive attached), or a VM that matches the virtual hardware of your target devices.
    1. If the vm is a target device in PVS set it to boot from hard disk not vdisk
  9. If using Xenserver set the VM to boot from CD, if using vmware edit settings of the vm to boot into bios so you can change the boot order so CD is first.
  10. Boot the VM and you should see this screen8-BootVHDwithPE
  11. Run the following commands
    1. Diskpart
    2. List disk
      1. Should see disk 0 and only9-BootVHDwithPE
    3. Select disk 0
    4. Select part 1
    5. Active
      1. This makes partition 1 on disk 0 bootable10-BootVHDwithPE
    6. exit
    7. Now I want to rename the .vhd file on disk 0 (C:\), so I can use it over and over again (NOTE: this is CASE SENSITIVE)
    8. C:
    9. Dir
      1. Just so I can see the file name
    10. Ren win8.8.vhd temp.vhd (or whatever you want to name it. NOTE: this is CASE SENSITIVE)11-BootVHDwithPE
    11. X:
    12. Diskpart
    13. Select vdisk file=c:\temp.vhd
    14. Attach vdisk
    15. List vol
  12. Now I can see my vhd is mounted as volume D (yours may be different, but make note of it)
  13. A couple more commands
    1. Exit (to exit diskpart)
    2. Bcdboot d:\windows /s c:
      1. Use whatever volume letter you have for your vdisk13-BootVHDwithPE
    3. Exit
      1. System should now reboot and boot into your vhd (just remember not to hit any key, or just eject the cd image from the vm)
  14. Make the changes that normally would require you to reverse image, and shut down.
  15. Detach the drive from the editing VM and re-attach it to your PVS server.
  16. Copy the .vhd back to the store
  17. Detach the drive (keep it around for next time)
  18. Rename the original vhd (win8.8.vhd for me) to .old, and rename the temp.vhd to whatever the original name was. (NOTE: this is CASE SENSITIVE)
  19. Boot your test devices and make sure everything is working
  20. Once satisfied, shutdown the test devices, delete the .old file, and promote the vdisk to production
  21. Switch the editing VM back to booting from network for when you have your normal updates

From here on out these kinds of edits should be easy.

  1. Attach the virtual disk to pvs
  2. Remove the old temp.vhd file (assuming you didn’t before)
  3. Copy the vhd you wish to edit to it
  4. Rename it to temp.vhd (or whatever you named it to begin with)
  5. Detach the drive from PVS
  6. Change your editing vm target device in PVS to boot to hard drive
  7. Attach the drive to your editing vm
  8. Boot it
  9. Follow steps 14-20 above
Apr 042014

Often when troubleshooting an issue a need arises to collect logs from multiple machines.  This is easily achieved when the number of machines is small, or known – a few servers or desktops here or there – but when it comes to analyzing logs for a large pooled-random group of desktops or several hundred computers in your organization, grabbing the files can be a pain.  Using the magic of Powershell, you can easily collect logs from as many computers as you desire in a matter of minutes.

Add-PSSnapin Citrix*
$DeliveryController = "citrixdc.contoso.local"
$vmCollection = Get-BrokerDesktop -PowerState On -AdminAddress $DeliveryController -MaxRecordCount 9999
# you can replace or repeat this with any collection you want (i.e. get-adcomputer).

foreach($hostedDesktop in $vmCollection)
$vm = $_.DNSName
if(Test-Connection -Cn $vm -BufferSize 16 -Count 1 -ea 0 -quiet)
Write-Host "$vm is online, collecting logs"
$path="\\$vm\d$\" #Use your log location. In this case, logs are co-located logs on the write cache D: drive.
dir -Path $path -recurse -include "*.evtx","*.log" -Exclude "*vdiskcache","pagefile.sys" | %{ # Critera shown for example. If you include all, be sure you exclude vdiskcache and pagefile.sys
$destPath="$destbasePath\$vm" # The full destination path
if(!(test-path $destPath)) { mkdir $destPath } #make new folders for each VM you collect from
copy $_.pspath $destPath #Make the magic happen