Last active
October 4, 2021 14:18
-
-
Save alainassaf/f516924a5866d11044ccb66639e5cd20 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.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