Here’s a quick list of Powershell commands I find myself using frequently. I’ll keep this updated.

Get a list of all brokered connections

Get-BrokerConnectionLog | Sort BrokeringTime

I always sort by BrokeringTime because the order appears to be odd otherwise because the log is updated with the initial brokering time as well as the EndTime.

Add an application to multiple delivery groups

Add-BrokerApplication -Name "AppNameHere" -DesktopGroup "NewDesktopGroupHere"

Adding an application to multiple desktop groups is 100% supported, it’s just not exposed in the GUI as of XenApp 7.5. Otherwise, if you try to add an application that already exists, you’ll get “Microsoft Word_1”. Also note when you do this, you can also set a priority to different desktop group- by default, the priority is added as zero – take a look:

PS C:\Windows\system32> Get-BrokerApplication -Name "Notepad"
ApplicationType : HostedOnDesktop
AssociatedDesktopGroupPriorities : {0, 0}
AssociatedDesktopGroupUids : {1, 2}
AssociatedUserFullNames : {Domain Users, Jake Rutski}
AssociatedUserNames : {lab\Domain Users, lab\jrutski}
AssociatedUserUPNs : {, [email protected]}
BrowserName : Notepad
ClientFolder : AppCategory
CommandLineArguments :

To set a different priority, use the ‘-priority’ argument:

Add-BrokerApplication -name "notepad" -DesktopGroup "Test Delivery Group" -Priority 5

View the SQL DB info for the XenDesktop site

Server=TESTSQL1;Initial Catalog=CitrixTestSite;Integrated Security=True

Monitoring Scripts

This script was used with a monitoring solution to return the count of unregistered machines, not in maintenance mode, powered on, and with a user assigned to the desktop:

(Get-BrokerDesktop -MaxRecordCount 2000 -AdminAddress yourddc.goes.here | Where {($_.RegistrationState -eq "Unregistered") -and ($_.AssociatedUserNames) -and !($_.InMaintenanceMode) -and ($_.PowerState -eq "On")}).Count
This script returns the count of session disconnections for the last 5 minutes – note that the polling interval for this script would be every 5 minutes. If you wanted to poll every 1 minute for example, change to AddMinutes(-1):
(Get-BrokerSession -SessionState Disconnected -AdminAddress yourddc.goes.here | Where {$_.SessionStateChangeTime -gt ((Get-Date).AddMinutes(-5))}).Count

Static Machine Addition

This script will get the ID of a VM, add it to the machine catalog with the CatalogUid of 3 and HypervisorConnectionUid of 2 (change these to match your needs); then assign a user and add to a catalog. Note a few variables used: $NewCluster and $NewVMName are specific to the path to the VM object; $NewVMName is the computer name of the machine.
$NewVMObj = Get-ADComputer -Server your.domain.controller -Filter {Name -eq $NewVMName}
Set-HypAdminConnection  -AdminAddress "yourddc.goes.here"
$XDVMId = (Get-Item "XDHyp:\Connections\Your Cluster\Your.datacenter\$($NewCluster).cluster\$($NewVMName).vm").Id
# Add machine to machine catalog; add user to machine
New-BrokerMachine -AdminAddress "yourddc.goes.here" -CatalogUid 3 -HostedMachineId $XDVMId -HypervisorConnectionUid 2 -MachineName $NewVMObj.SID
Add-BrokerUser  -AdminAddress "yourddc.goes.here" -Machine (Get-BrokerMachine -SID $NewVMObj.SID).Uid -Name "YOURDOMAIN\$($NewUser)"
# Add machine to desktop group;
Get-BrokerMachine -SID $NewVMObj.SID | Add-BrokerMachine  -AdminAddress "yourddc.goes.here" -DesktopGroup "Your Desktop Group Here"

XenDesktop Machines that have not been logged in to in the past XX days

This is for statically assigned VMs or PvD XD VMs, but it outputs nice human readable user names – requires AD PowerShell Snapin.
Get-BrokerDesktop -MaxRecordCount 2000 | Where {((Get-Date) - $_.LastConnectionTime).Days -gt 60} | sort LastConnectionTime | Select DNSName,LastConnectionTime,@{Name="UserName";Expression={$UserN = $_.AssociatedUserNames.Replace("YOURDOMAINHERE\",""); $UserObj = Get-ADUser -Filter {Name -eq $UserN}; "$($UserObj.GivenName) $($UserObj.Surname)"}}

One-liner to get a listing of all users that have not logged into their XD desktop in 60 days or more – this is for static desktops or XD PvD.


  • XenDesktop PowerShell SDK (studio commandlets)
  • AD Powershell commandlets
asnp Citrix*
Get-BrokerDesktop -MaxRecordCount 2000 | Where {((Get-Date) - $_.LastConnectionTime).Days -gt 60} | sort LastConnectionTime | Select DNSName,LastConnectionTime,@{Name="UserName";Expression={$UserN = $_.AssociatedUserFullNames.Replace("DOMAINHERE\",""); $UserObj = Get-ADUser -Filter {Name -eq $UserN}; "$($UserObj.GivenName) $($UserObj.Surname)"}}


  • Use MaxRecordCount for large environments
  • Replace “DOMAINHERE\” with your domain

Updating MDT OS Images with WSUS

# WIM offline updater
# Inspired by: https://technet.microsoft.com/en-us/magazine/hh825626.aspx
# Author: Jake Rutski
# @JRutski blogs.serioustek.net
# Use at your own risk; I am not responsible for any issues caused by this script

# Variables
$imagePath = "\\storage\Software\MDT\Operating Systems\Windows Server 2012 R2\sources\install.wim"
$wsusPath = "D:\WSUS\WsusContent"
$mountPath = "C:\Mount"
$imageIndex = 4

# Check for root
# http://blogs.technet.com/b/heyscriptingguy/archive/2011/05/11/check-for-admin-credentials-in-a-powershell-script.aspx
If(!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
Write-Host "`nThis script is not running as administrator - this is required to use DISM`n"
# Mount the image
Mount-WindowsImage -ImagePath $imagePath -Path $mountPath -Index $imageIndex

# Get the update files and install them offline
foreach($cabFile in (gci $wsusPath -Filter *.cab -Recurse))
Add-WindowsPackage -PackagePath $cabFile.FullName -Path C:\Mount

# Dismount and save the image
Dismount-WindowsImage -Path C:\Mount -Save

Cleaning up Static XenDesktop VMs (including PowerCLI)

These series of cmdlets will remove the desktop from the delivery group and machine catalog, remove the object from AD, and delete the VM from vCenter. Replace ‘Domain\ComputerName’ with the appropriate strings; if you are not running on the DDC or not already pointed at it, make sure to add the ‘adminaddress’ parameter. *Note: these commands remove and delete things, so BE CAREFUL!

$XDesktop = Get-BrokerMachine -MachineName Domain\ComputerName
$XDesktop | Set-BrokerMachineMaintenanceMode -MaintenanceMode $True
Stop-BrokerSession (Get-BrokerDesktop -DNSName machinename.domain.com).SessionUid
New-BrokerHostingPowerAction -MachineName Domain\ComputerName -Action shutdown
$XDesktop | Remove-BrokerMachine -DesktopGroup GroupUID -Force
$XDesktop | Remove-BrokerMachine -Force
Remove-ADComputer ComputerName
Get-VM VMName | Remove-VM -DeleteFromDisk

Creating a single text file from multiple files

This one-liner takes all text files in a directory and appends them all to a single new file.

Get-Content "C:\Test\Files\*.txt" -Force | Set-Content "C:\Test\Files\newFile.txt"

Copying all users in one AD group to another AD group

Simple one-liner to copy users from one group to another – requires AD Powershell plugins.

Get-ADGroupMember NameOfSourceGroup | % {Add-ADGroupMember NameOfDestinationGroup -Members $_}

Output all DFSN Targets from a common root

*Requires DFS tools

Get-DfsnFolder -Path "\\SomeDomain.com\files\*" | % {Get-DfsnFolderTarget -Path $_.Path | Select Path,NamespacePath,State,TargetPath,ReferralPriorityClass}

Basic IPMI Commands

Works on SuperMicro BMC IPMI on X9\X10 boards

$creds = Get-Credential
# To start the server
Get-PcsvDevice -TargetAddress your.ip.address.here -ManagementProtocol IPMI -Credential $creds | Start-PcsvDevice

# To stop the server
Get-PcsvDevice -TargetAddress your.ip.address.here -ManagementProtocol IPMI -Credential $creds | Stop-PcsvDevice

Uninstall from Windows Command Line

So this isn’t PowerShell, but it’s super useful.

wmic product get name
wmic product where name="..." call uninstall

Duplicate file Cleanup for Plex Camera Uploads

See the full post here.

Script to remove duplicate photos from the Plex Mobile Uploads directory

This script will enumerate all files in the Mobile Uploads directory, then find files that are the same size. A SHA1 hash is generated for each of these files to verify that they
are the same image prior to being deleted. Files that have incorrect date formats (1970-01-01) or are hyphenated take precedence to be deleted, unless all duplicate files are formatted
incorrectly, then the first file found to be duplicate will be deleted.

Script only looks for -1, -2 or -3 JPG files or -1 and -2 MP4 files
More file types can be added to the Get-ChileItem -Include section if needed

If the 'delete' parameter is used, the script will delete files once hashed duplicates have been found


$logFile = "$([Environment]::GetFolderPath("mydocuments"))\DupeDeleteLog.txt"

# Write to a log file in the current users MyDocs directory
Function Write-Log
    Add-Content $logFile -Value $logStr

# User select location to find duplicate files
Function Get-FolderName 
    Add-Type -AssemblyName System.Windows.Forms 
    $FolderBrowser = New-Object System.Windows.Forms.FolderBrowserDialog 

Write-Log "$(Get-Date)"
Write-Log "Waiting for input from user to select working directory..."
$mypath = Get-FolderName 

# Initial log file notes
If($delete){Write-Log "WARNING: File deletion will occur!"}
Write-Log "Checking for duplicates in $($mypath)"
Write-Log "Finding same size files and hashing them. This will take some time...."

$dupeHash = foreach ($i in (Get-ChildItem -path $mypath -Recurse -Include "*.jpg","*.jpeg","*.mp4" | ? {( ! $_.ISPScontainer)} | Group Length | ? {$_.Count -gt 1} | Select -Expand Group | Select FullName, Length)){Get-FileHash -Path $i.Fullname -Algorithm SHA1}
$dupeHashGrouped = $dupeHash | Group Hash | ? {$_.Count -gt 1}

# Set confirm, if $delete param is not set, no delete will occur
If(!($delete)){$delConfirm = "Y"}
    Write-Log "Waiting for input from user to confirm delete..."
    $delConfirm = Read-Host "WARNING: This script will now DELETE FILES. Press 'Y' to confirm you want to DELETE, or any other key to cancel"

If (($delConfirm -eq 'y') -or ($delConfirm -eq 'Y'))
    foreach ($dhGroup in $dupeHashGrouped)
        Write-Log "Current file hash: $($dhGroup.Group[0].Hash)"
        Write-Log "Matching files: $($dhGroup.Count)"
        $goodFile = $false
        $i = 1
        foreach($matchFile in $dhGroup.Group.Path)
            If($i -eq $dhGroup.Count -and (!($goodFile)))
                #Last file, no good found...keeping
                Write-Log "Last file in group. Keeping: $($matchFile)"
            ElseIf($matchFile -like "*1970*")
                Write-Log "File in group contains invalid date 1970: $($matchFile) - will be deleted."
                If($delete){Remove-Item -Path $matchFile -Confirm:$false}
            #modify if more duplicated hyphens are found
            ElseIf($matchFile -like "*-1.jpg" -or $matchFile -like "*-2.jpg" -or $matchFile -like "*-3.jpg" -or $matchFile -like "*-1.mp4" -or $matchFile -like "*-2.mp4")
                Write-Log "File in group is hyphenated: $($matchFile) - will be deleted."
                If($delete){Remove-Item -Path $matchFile -Confirm:$false}
                    #Already found valid file in group
                    Write-Log "Deleting this file: $($matchFile) already have good file."
                    If($delete){Remove-Item -Path $matchFile -Confirm:$false}
                    #Don't have good file in group; keep
                    Write-Log "Keeping: $($matchFile)"
                    $goodFile = $true
        Write-Log " "
    Write-Log "Deletion cancelled by user"

5 thoughts on “PowerShell”

  1. I wanted to run a question by you about the unregistered machines. We see the Orange Triangle on the unregistered VDI’s and I wrote a script similar to yours but check a couple extra options then reboots the machines.

    I’m just looking for validation that I am checking for the valid options. Do I need to use all of them?

    Below are the objects I query for to determine the status of the machines.

    FaultState -ne ‘None’ `
    -AND MachineInternalState -eq ‘Unregistered’ `
    -AND PowerState -ne ‘Off’ `
    -AND InMaintenanceMode -ne ‘True’ `
    -AND RegistrationState -ne ‘Registered’ `
    -AND SessionUserName -eq $null}|
    Select-Object HostedMachineName, DesktopGroupName

    • Rob, I’d say it depends on exactly what scenario you’re trying to prevent as to which parameters you need to check, but it sounds similar. I created that to integrate with PRTG and provide a count of unregistered machines…keep in mind this was before I deployed PVS, so all of the machines were statically assigned and the images would continually go nuts, thus causing unregistrations, etc.

  2. Thanx for this. These are helpful commands/scripts. I am new to powershell. We run XenDesktop 7.15. We publish applications to XenDesktops. I want to run Get-BrokerApplication to get a list of all the published applications, but I want the output to only show the PublishedName and the AssociatedUserName(s). How to I write the command/script to filter out all other details?


Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.