Skip to content

Instantly share code, notes, and snippets.

@alainassaf
Last active October 4, 2021 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alainassaf/f516924a5866d11044ccb66639e5cd20 to your computer and use it in GitHub Desktop.
Save alainassaf/f516924a5866d11044ccb66639e5cd20 to your computer and use it in GitHub Desktop.
<#
.SYNOPSIS
Searches Citrix sessions that are running a certain published application and stops it if a session is idle for a certain time.
.DESCRIPTION
Searches Citrix sessions that are running a certain published application and stops it if a session is idle for a certain time. Logs are generated in the same directory as the script runs.
.PARAMETER deliverycontroller
Mandatory string parameter. Which Citrix Delivery Controller (farm) to query for session.
.PARAMETER pubApp
Mandatory string parameter. Which published application to stop.
.PARAMETER idletime
Mandatory integer parameter. Number of minutes a session has to be idle before stopping the process.
.EXAMPLE
PS> stop-idleprocess.ps1 -deliverycontroller SOMEDDC -pubApp 'notepad' -idletime 0 -Verbose
Checks for sessions using SOMEDDC running notepad that are idle for zero minutes. Returns additional script feedback.
.INPUTS
None
.OUTPUTS
None
.NOTES
NAME: stop-idleProcess.ps1
VERSION: 1.00
CHANGE LOG - Version - When - What - Who
1.00 - 08/08/2021 - Initial script - Alain Assaf
AUTHOR: Alain Assaf
LASTEDIT: August 08, 2021
.LINK
http: //www.linkedin.com/in/alainassaf/
#>
[cmdletbinding()]
param(
[Parameter(Mandatory)]
[ValidateScript( { Test-NetConnection -ComputerName $_ -Port 80 -InformationLevel Quiet } )]
[Alias("DDC")]
[string]$deliverycontroller,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$pubApp,
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[int]$idletime,
[Parameter()]
[Switch]$testscript
)
#Region Functions
Function Get-MySnapin {
<#
.SYNOPSIS
Checks for a PowerShell Snapin(s) and imports it if available, otherwise it will display a warning and exit.
.DESCRIPTION
Checks for a PowerShell Snapin(s) and imports it if available, otherwise it will display a warning and exit.
.PARAMETER snapins
Required parameter. List of PSSnapins separated by commas.
.INPUTS
None
.OUTPUTS
None
.EXAMPLE
PS> get-MySnapin PSSNAPIN
Checks system for installed PSSNAPIN and imports it if available.
.LINK
http://www.linkedin.com/in/alainassaf/
http://wagthereal.com
https://github.com/alainassaf/get-mysnapin
.NOTES
NAME : Get-MySnapin
VERSION : 1.00
CHANGE LOG - Version - When - What - Who
1.00 - 02/13/2017 - Initial script - Alain Assaf
LAST UPDATED: 02/13/2017
AUTHOR : Alain Assaf
#>
[CmdletBinding()]
Param([string]$snapins)
$ErrorActionPreference = 'silentlycontinue'
foreach ($snap in $snapins.Split(",")) {
if (-not(Get-PSSnapin -Name $snap)) {
if (Get-PSSnapin -Registered | Where-Object { $_.name -like $snap }) {
Add-PSSnapin -Name $snap
$true
} else {
Write-Warning "$snap PowerShell Cmdlet not available."
Write-Warning "Please run this script from a system with the $snap PowerShell Cmdlet installed."
exit 1
}
}
}
}
### Written by Sacha Thomet sachathomet.ch
function LogMe() {
Param(
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $logEntry,
[switch]$display,
[switch]$logerror,
[switch]$warning,
[switch]$progress
)
if ($logerror) {
$logEntry = "[ERROR] $logEntry" ; Write-Host "$logEntry" -ForegroundColor Red
} elseif ($warning) {
Write-Warning "$logEntry" ; $logEntry = "[WARNING] $logEntry"
} elseif ($progress) {
Write-Host "$logEntry" -ForegroundColor Green
} elseif ($display) {
Write-Host "$logEntry"
}
#$logEntry = ((Get-Date -uformat "%D %T") + " - " + $logEntry)
$logEntry | Out-File $Global:stopProcessLogFile -Append
}
#endregion
#Region Constants and parameters
$datetime = Get-Date -Format "MM-dd-yyyy_HH-mm"
$Domain = (Get-ChildItem env:USERDNSDOMAIN).value
$ScriptRunner = (Get-ChildItem env:username).value
$compname = (Get-ChildItem env:COMPUTERNAME).value
$scriptName = $MyInvocation.MyCommand.Name
$scriptpath = $MyInvocation.MyCommand.Path
$currentDir = Split-Path $MyInvocation.MyCommand.Path
$PSSnapins = ("Citrix.Broker.Admin.V2")
$logFileTime = Get-Date -Format "MM_dd_yyyy_HH_mm_ss"
$Global:stopProcessLogFile = Join-Path $currentDir ("stopIdleProcess_$logFileTime.log")
$maxIntValue = [int]::maxvalue
#endregion
#region log init
#Cleanup Logs by size size
$logsToDelete = Get-ChildItem *.log | Sort-Object lastwritetime
if ($logsToDelete.Count -gt 5) {
$logsToDelete | Where-Object { $_.length -lt 2000 } | Remove-Item -Force
}
#Cleanup Logs by number
$logsToDelete = Get-ChildItem *.log | Sort-Object lastwritetime
if ($logsToDelete.Count -gt 5) {
$logsToDelete | Select-Object -First ((Get-ChildItem *.log | Sort-Object lastwritetime).count - 5) | Remove-Item -Force
}
#endregion
Get-MySnapin $PSSnapins
#Confirm Published Application is correct
try {
$xaPublishedApp = Get-BrokerApplication -AdminAddress $deliverycontroller -PublishedName $pubApp -MaxRecordCount $maxIntValue
"[$pubapp] found using Delivery Controller: [$deliverycontroller]" | LogMe -display
} catch {
"Unable to find published application [$pubapp]." | LogMe -logerror
$Error[1] | Logme -logerror
exit 1
}
$xaProcessUsers = Get-BrokerSession -AdminAddress $deliverycontroller -AppState Active -MaxRecordCount $maxIntValue -ApplicationInUse $xaPublishedApp.Name | Where-Object { $_.IdleDuration -ne $null -and $_.BrokeringUserName -ne $null -and $_.ApplicationsInUse -match $pubApp } | Where-Object { $_.idleduration.Minutes -ge $idletime }
if ($null -eq $xaProcessUsers) {
"No session idle time exceeds [$idletime] minutes for published application [$pubapp]." | LogMe -display
"Script terminating with no action taken." | LogMe -display
Write-Verbose "SCRIPT NAME: $scriptName"
Write-Verbose "SCRIPT PATH: $scriptPath"
Write-Verbose "SCRIPT RUNTIME: $datetime"
Write-Verbose "SCRIPT USER: $ScriptRunner"
Write-Verbose "SCRIPT SYSTEM: $compname.$domain"
Exit 0
} else {
#$xaProcessCount = ($xaProcessUsers | Select-Object brokeringusername, applicationsinuse | Measure-Object).Count
$xaProcessCountToStop = ($xaProcessUsers | Measure-Object).Count
$xaAppCmdline = ($xaPublishedApp.CommandLineExecutable).Split('\')
$xaAppProcessTmp = $xaAppCmdline[($xaAppCmdline.Split('\').length - 1)]
$xaAppProcess = ($xaAppProcessTmp -Split ".exe")[0]
"Published Application: '$pubApp' uses executable '$xaAppProcess'" | LogMe -display
"Total processes in sessions idle for [$idletime] that script will ATTEMPT to stop: $xaProcessCountToStop" | LogMe -display
}
foreach ($session in $xaProcessUsers) {
$processInstance = Get-CimInstance -ClassName Win32_process -ComputerName $session.DNSName -Verbose:$false | Where-Object { $_.name -match $xaAppProcess }
if ($processInstance) {
try {
foreach ($proc in $processInstance) {
Invoke-CimMethod -InputObject $proc -MethodName Terminate -Verbose:$false
$tmpUser = $session.UserFullName.ToString()
$tmpServer = $session.DNSName.ToString()
$tmpApp = $xaPublishedApp.BrowserName.ToString()
$tmpStartTime = $proc.CreationDate.ToString()
$tmpKilledTime = (Get-Date).ToString()
"USER: $tmpUser SERVER: $tmpServer PUBLISHED_APP: $tmpApp PROCESS: $xaAppProcess PROCESS STARTED: $tmpStartTime TIMEKILLED: $tmpKilledTime" | logme -progress
$tmpXAProcessCountKill += $tmpUser
}
} catch {
$tmpUser = $session.UserFullName.ToString()
$tmpServer = $session.DNSName.ToString()
$tmpKilledTime = (Get-Date).ToString()
"Failed to terminate $xaAppProcess for $tmpUser on $tmpServer at $tmpKilledTime" | LogMe -logerror
$Error[1] | LogMe -logerror
}
}
}
#Script info
Write-Verbose "SCRIPT NAME: $scriptName"
Write-Verbose "SCRIPT PATH: $scriptPath"
Write-Verbose "SCRIPT RUNTIME: $datetime"
Write-Verbose "SCRIPT USER: $ScriptRunner"
Write-Verbose "SCRIPT SYSTEM: $compname.$domain"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment