I got few questions regarding my Citrix XenApp monitoring script written in Powershell. I’ve decided to share with you it’s second better and improved version which has additional conditions on checking if really Citrix ICA connections was launched properly and your Citrix application is available for interaction with user. What is also improved is the overall time needed for the script to run. Right now all logons happen simultaneously thanks to which the script finishes below 2 minutes ( I tested that for about 20 servers). So you have now possibility to schedule a task that will be running more frequent and you will be able to better check health of your Citrix XenApp environment.
Before I share my script please check some prerequisites that are required by a script to execute properly:
1. You must have Citrix Receiver for Windows installed.
2. This script will only work in the x86 version of Powershell. You run it manually like this: %SystemRoot%\syswow64\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -file “path to the script”
3. You would schedule it like this:
Program/Script: %SystemRoot%\syswow64\WindowsPowerShell\v1.0\powershell.exe
Arguments: -ExecutionPolicy Bypass -file “path to the script”
4. You have to create a service account to be used by the scheduled task
5. To allow task to interact with the desktop you have to configure task to run only when the user is logged in
6. You have to set autologon in your Windows desktop or server according to: https://support.microsoft.com/en-us/kb/310584
7. You have to download PSTerminalServices Powershell module from: http://blogs.microsoft.co.il/scriptfanatic/2010/02/22/psterminalservices-powershell-module-for-terminal-services/
8. After that you have to place it under %SystemRoot%\syswow64\WindowsPowerShell\v1.0\Modules
9. In order for the script to interact with the ICO, it requires the following to be set in the registry:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Citrix\ICA Client\CCM
AllowLiveMonitoring REG_DWORD 1
AllowSimulationAPI REG_DWORD 1
And of course I would like to underline that original script was written by Stan Czerno and is available under below link:
Let me know if you would have any questions or comments.
!!! There is a problem with Powershell code in WordPress. Please check in Windows Powershell ISE editor if everything is OK!!!
# This script launches a specified Published Application for one or more XenApp Servers. # You can use to this as a Test to make sure the server is actually launching Published Applications # It will also log the time is takes to logon. # It can be run by Scheduled Task to check availability of the Citrix XenApp servers. # You must have Citrix Receiver for Windows installed. # This script will only work in the x86 version of Powershell # You run it manually like this: %SystemRoot%\syswow64\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -file "path to script" # You would schedule it like this: # Program/Script: %SystemRoot%\syswow64\WindowsPowerShell\v1.0\powershell.exe # Arguments: -ExecutionPolicy Bypass -file C:\XenAppMonitoringScript\Check-XAAppLaunchReport.ps1 # You have to create a service account to be used by the scheduled task # To allow task to interact with the desktop you have to configure task to run only when the user is logged in # You have to set autologon in your Windows desktop or server according to: https://support.microsoft.com/en-us/kb/310584 #You have to download PSTerminalServices Powershell module from: http://blogs.microsoft.co.il/scriptfanatic/2010/02/22/psterminalservices-powershell-module-for-terminal-services/ # After that you have to place it under %SystemRoot%\syswow64\WindowsPowerShell\v1.0\Modules # In order for the script to interact with the ICO, it requires the following to be set in the registry: # HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Citrix\ICA Client\CCM # AllowLiveMonitoring REG_DWORD 1 # AllowSimulationAPI REG_DWORD 1 # Parts of the script comes from: http://www.czerno.com/blog/post/2014/06/11/powershell-script-to-launch-a-published-application-from-one-or-more-xenapp-servers # Created by: Paweł Serwan # Date: 25.03.2015 # Version: 2.1 ## ============================================================ ## ## User Variables ## ## ============================================================ ## #Import Citrix modules from Citrix SDK #Add-PSSnapin Citrix* # Publish Application Name to launch $pubapp = "YOUR APP NAME" # List of Servers to test, one or more can be listed # If more than one, use this format: "Server1","Server2","Server3" # If all servers in the farm should be tested: # $XAServers = Get-XAServer | Select ServerName $XAServers = Import-Csv "PATH TO THE CSV FILE WITH LIST OF SERVERS" #CSV file should look like: #ServerName #Server1 #Server2 # User name $user = "USERNAME" # User password $passwd = "PASSWORD" # Domain Name $domain = "YOUR DOMAIN" # E-mail settings # If $emailFrom, $emailTo or $smtpServer are blank, no email will be sent # Define multiple email addresses like "email@domain.com,email2@domain.com" $emailFrom = "MAIL_FROM@MYDOMAIN.COM" $emailTo = "MAIL_TO@MYDOMAIN.COM" # If not using a CC address, leave the value blank #$emailCC = "mailCC@mydomain.com" $smtpServer = "SMTP.MYDOMAIN.COM" # The variable below sets how often to send an email if no "new" alerts are detected. # For example, Server 1 is having issues, an email is sent. If the test is scheduled to run again in 15 minutes # you may want to set the lag time to something greater than 15 minutes so you are not constantly sent emails # about the same issue. # If an existing alert still exists, it will not be sent again until this lag time is reached. # Be sure to align this with how often you schedule the task. # The variable is set in seconds, 7200 seconds (2 hours) is the default. #$EmailAlertsLagTime = "7200" # This next variable defines if the script will type "EXIT + press ENTER". # This is optional, I left it becuase it was cool. If using a Command Prompt as a Test App, # you can let the script actually type EXIT then press ENTER. Pretty cool if watching it interactively. # If you are not using a Command Prompt as a test you probably will want to mark this as $False and # set the $logoffmethod as $True. # If you choose this option, keep the Published Application as Command Prompt, typing in Notepad or Word # will hang the logoff asking you to save the file. $typeexit = $False # Just sends the Logoff command. # If using the $typeexit method above, leave this $False $logoffmethod = $False ## ============================================================ ## ## END User Variables ## ## ============================================================ ## # ==================== # # Setup Log Files # # ==================== # # Reads the current directory path from the location of this file function Get-ScriptDirectory { $Invocation = (Get-Variable MyInvocation -Scope 1).Value; if($Invocation.PSScriptRoot) { $Invocation.PSScriptRoot; } Elseif($Invocation.MyCommand.Path) { Split-Path $Invocation.MyCommand.Path } else { $Invocation.InvocationName.Substring(0,$Invocation.InvocationName.LastIndexOf("\")); } } $currentDir = Get-ScriptDirectory $logfile = Join-Path $currentDir ("LaunchPublishedApp.log") $PreviuosLogFile = Join-Path $currentDir ("LaunchPublishedApp_previuosrun.log") $AlertsEmailed = Join-Path $currentDir ("LaunchPublishedApp_AlertsEmailed.log") $CurrentAlerts = Join-Path $currentDir ("LaunchPublishedApp_AlertsCurrent.log") $AlertEmail = Join-Path $currentDir ("LaunchPublishedApp_AlertsEmailTimeStamp.log") rm $logfile -force -EA SilentlyContinue # ==================== # # Email Style # # ==================== # $ErrorStyle = "style=""background-color: #000000; color: #FF3300;""" #===================================================================== # # Sends the results into a logfile as well as in the powershell window # #===================================================================== # Function LogMe() { Param( [parameter(Mandatory = $true, ValueFromPipeline = $true)] $logEntry, [switch]$displaygreen, [switch]$error, [switch]$warning, [switch]$displaynormal, [switch]$displayscriptstart, [switch]$displayscriptend ) if($error) { Write-Host "$logEntry" -Foregroundcolor Red; $logEntry = "[ERROR] $logEntry" } elseif($warning) { Write-Host "$logEntry" -Foregroundcolor Yellow; $logEntry = "[WARNING] $logEntry"} elseif ($displaynormal) { Write-Host "$logEntry" -Foregroundcolor White; $logEntry = "[INFO] $logEntry" } elseif($displaygreen) { Write-Host "$logEntry" -Foregroundcolor Green; $logEntry = "[SUCCESS] $logEntry" } elseif($displayscriptstart) { Write-Host "$logEntry" -Foregroundcolor Green; $logEntry = "[SCRIPT_START] $logEntry" } elseif($displayscriptend) { Write-Host "$logEntry" -Foregroundcolor Green; $logEntry = "[SCRIPT_END] $logEntry" } else { Write-Host "$logEntry"; $logEntry = "$logEntry" } $logEntry | Out-File $logFile -Append } # ============================ # # Get Client Version Function # # ============================ # function Get-ICAClientVersion{ $ErrorActionPreference = "SilentlyContinue" $ica_client = New-Object -ComObject 'Citrix.ICAClient' if($ica_client) { return $ica_client.ClientVersion } else { return 0 } } # ====================== # # Time Elapsed Function # # ====================== # function GetElapsedTime([datetime]$starttime) { $runtime = $(get-date) - $starttime $retStr = [string]::format("{0} sec(s)", $runtime.TotalSeconds) $retStr } # ===================== # # Create Registry Item # # ===================== # function CreateRegEntry ($RegPath,$RegName,$PropType,$Val) { try {New-ItemProperty -Path $RegPath -Name $RegName -PropertyType $PropType -Value $Val -ErrorAction Stop} catch {$Script:RegError += $_.Exception.Message $_.Exception.Message | LogMe -error } } # ===================== # # Modify Registry Item # # ===================== # function ModifyRegEntry ($RegPath,$RegName,$Val) { try {Set-ItemProperty -Path $RegPath -Name $RegName -Value $Val -ErrorAction Stop} catch {$Script:RegError += $_.Exception.Message $_.Exception.Message | LogMe -error } } ################################################################################ # ============================================================================ # # Start of Script # # ============================================================================ # ################################################################################ # === Script Start Time $startTime = Get-Date cls "Script Started at $startTime" | LogMe -displayscriptstart # Making sure Registry Entries exist # If not, try to create or modify # If they cannot be created or modified, logging an error # and skipping ICA test $Path = "HKLM:SOFTWARE\Wow6432Node\Citrix\ICA Client\CCM" $AllowLiveMonitoringName = "AllowLiveMonitoring" $AllowSimulationAPIName = "AllowSimulationAPI" $PropertyType = "DWORD" $Value = "1" $AllowLiveMonitoring = Get-ItemProperty -Path $Path -Name $AllowLiveMonitoringName -ErrorAction SilentlyContinue $AllowSimulationAPI = Get-ItemProperty -Path $Path -Name $AllowSimulationAPIName -ErrorAction SilentlyContinue if (!$AllowLiveMonitoring) { "AllowLiveMonitoring does not exist, creating it" | LogMe -warning CreateRegEntry $Path $AllowLiveMonitoringName $PropertyType $Value } elseif ($AllowLiveMonitoring.AllowLiveMonitoring -ne "1") { "AllowLiveMonitoring Value does not equal 1, setting to 1" | LogMe -warning ModifyRegEntry $Path $AllowLiveMonitoringName $Value } if (!$AllowSimulationAPI) { "AllowSimulationAPI does not exist, creating it" | LogMe -warning CreateRegEntry $Path $AllowSimulationAPIName $PropertyType $Value } elseif ($AllowSimulationAPI.AllowSimulationAPI -ne "1") { "AllowSimulationAPI value exists but does not equal 1, setting to 1" | LogMe -warning ModifyRegEntry $Path $AllowSimulationAPIName $Value } if ($RegError -ne $Null) { " " | LogMe "Errors were encountered when trying to add or modify registry entries required for this script to work" | LogMe -error "You will either need to run this script once as an Adminitrator or create the following registry entries manually:" | LogMe -error " " | LogMe "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Citrix\ICA Client\CCM, AllowLiveMonitoring, DWORD, 1" | LogMe -displaynormal "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Citrix\ICA Client\CCM, AllowSimulationAPI, DWORD, 1" | LogMe -displaynormal $ICAErrors += @" <p $ErrorStyle>Errors were encountered when trying to add or modify registry entries required for this script to work. You will either need to run this script once as an Adminitrator or create the following registry entries manually:<br> HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Citrix\ICA Client\CCM, AllowLiveMonitoring, DWORD, 1 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Citrix\ICA Client\CCM, AllowSimulationAPI, DWORD, 1 /p> "@ } if ($RegError -eq $Null) { # Getting Client Version $ClientVersion = Get-ICAClientVersion Add-Type -AssemblyName System.Windows.Forms # Loading the LIB File try {Add-Type -Path "C:\Program Files (x86)\Citrix\ICA Client\WfIcaLib.dll"} catch {$AddTypeError = $_.Exception.Message $_.Exception.Message | LogMe -error } If ($AddTypeError -eq $Null) { #define new array of objects to store ICO connection information $connections = @() #define new array of objects to store Process ID information of new ICA connections $pids = @() for($i=0;$i -lt $XAServers.count;$i++) { $XAserver = $XAServers[$i].ServerName # Buidling the ICO and starting the ICA Connection to each server $ICA = New-Object WFICALib.ICAClientClass "Testing Published Application, $pubapp, on Server $XAServer" $ICA.Address = "$XAserver" $ICA.Username = "$user" $ICA.SetProp("Password","$passwd") $ICA.Domain = "$domain" $ICA.InitialProgram = "$pubapp" $ICA.Launch = $true $ICA.OutputMode = [WFICALib.OutputMode]::OutputModeNormal $ICA.DesiredHRes = 200 $ICA.DesiredVRes = 400 $ICA.DesiredColor = [WFICALib.ICAColorDepth]::Color16bit $ICA.TWIMode = $true "Launching ICA Session using Citrix Client version $ClientVersion" $LaunchTime = Get-Date "Connect Started at $script:LaunchTime" #Launch Citrix app $ICA.Connect() sleep 2 $connections += $ICA #Get PID of launched WFICA32 process $sessionID= get-Process -name wfica32|sort starttime -Descending | select -first 1 $pids += $sessionID } #wait for ICA connections to launch sleep 40 for($j=0;$j -lt $connections.count;$j++) { if ($connections[$j].IsConnected() -eq $true -and $connections[$j].Address -eq $XAServers[$j].ServerName) { if ($pids[$j].MainWindowTitle -eq "TITLE OF THE WINDOW OF YOUR TESTED APPLICATION") { "Test ICA Session found running on $($XAServers[$j].ServerName); logging off" | LogMe -displaygreen $logonelapsed = GetElapsedTime $LaunchTime "Time taken to logon is: $logonelapsed" | LogMe -displaygreen $connections[$j].Logoff() } elseif ($pids[$j].MainWindowTitle -eq "Windows Logon" -or $pids[$j].MainWindowTitle -eq "Notepad" ) { "Connection failed to server " + $($XAServers[$j].ServerName) | LogMe -Error $ICAErrors += "<p $ErrorStyle>Error Connection to server <strong>$($XAServers[$j].ServerName).</strong></p>" $ICAErrors += "<p $ErrorStyle>Please check for a hanged service (e.g. CpSvc) on server <strong>$($XAServers[$j].ServerName).</strong></p>" $connections[$j].Logoff() } else { "Connection failed to server " + $($XAServers[$j].ServerName) | LogMe -Error $ICAErrors += "<p $ErrorStyle>Error Connection to server <strong>$($XAServers[$j].ServerName).</strong></p>" $ICAErrors += "<p $ErrorStyle>Problem with logon process to server <strong>$($XAServers[$j].ServerName) - please check.</strong></p>" $connections[$j].Logoff() } } elseif ($connections[$j].IsConnected() -eq $false -and $connections[$j].Address -eq $XAServers[$j].ServerName) { "Connection failed to server " + $($XAServers[$j].ServerName) | LogMe -Error $ICAErrors += "<p $ErrorStyle>Error Connection to server <strong>$($XAServers[$j].ServerName)</strong></p>" "Clicking OK in the Citrix Receiver dialog window" | LogMe -displaynormal Add-Type @" using System; using System.Runtime.InteropServices; public class SFW { [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetForegroundWindow(IntPtr hWnd); } "@ "Finding a window named Citrix Receiver" | LogMe -displaynormal $hwnd = (get-process Receiver).MainWindowHandle # just one Receiver window must be opened! if ($hwnd -ne 0) { "Set Foreground Window to Citrix Receiver" | LogMe -displaynormal [SFW]::SetForegroundWindow($hwnd) "Sending the Enter command to Window" | LogMe -displaynormal [System.Windows.Forms.SendKeys]::SendWait("{ENTER}") } else { "There was no Window titled Citrix Receiver, the error is most likely due to the script" | LogMe -warning } } else { $ICAErrors += "<p $ErrorStyle>Error launching application <strong>$pubapp</strong> on server <strong>$XAServer</strong></p>" "Did not detect a Successful Logon or a Connect Failure to server $XAServers[$j].ServerName" | LogMe -Error "Assuming Logon Failure, Server is not available or the Application is not available" | LogMe -Error "Check the Published Application, Server Logons are Enabled, User Account and/or Script User Variables" | LogMe -Error } } } else { $ICAErrors += @" <p $ErrorStyle>Errors were encountered when trying to load WfIcaLib.dll.<br> $AddTypeError</p> "@ } } # Get the Current Time for Email Alert Time Stamp $CurrTime = Get-Date -format g # Checking to see if there were any Errors Detected # If there were, update the CurrentAlerts Log File for later comparison if ($ICAErrors -ne $Null) { if (Test-Path $CurrentAlerts) { clear-content $CurrentAlerts } foreach ($line in $ICAErrors) {$Line | Add-Content $CurrentAlerts} # Get Content from Alert Log File, we want the first line, which should be the previuos time stamp. if (Test-Path $AlertEmail) { $AlertFileTime = gc $AlertEmail -TotalCount 1 } # Check to see if the Time Span for sending alerts has passed if ($AlertFileTime) {$AlertTimeSpan = New-TimeSpan $AlertFileTime $(Get-Date -format g) $AlertTimeDifference = $AlertTimeSpan.TotalSeconds if (!$AlertTimeDifference) { $SendEmail = $True } if ($AlertTimeDifference -ge $EmailAlertsLagTime) { $SendEmail = $True } else { $SendEmail = $False } } if (!$AlertFileTime) { $SendEmail = $True } # Checking Alert Log Contents from last run for comparison if (Test-Path $AlertsEmailed) { $AlertLogContents = gc $AlertsEmailed } # Checking Current Errors and Warnings found this duration for comparison if (Test-Path $CurrentAlerts) { $CurrentAlertsContents = gc $CurrentAlerts } # If the Alerts Email Log is empty but the Current Log is not, there are new alerts. If (!$AlertLogContents -and $CurrentAlertsContents) { $NewAlerts = $True $SendEmail = $True " " | LogMe "New Alerts Detected" | LogMe -warning } # If the Alert Email Log and the Current Alert Log matches then there are most likely no new errors If ($AlertLogContents -and $CurrentAlertsContents) { $AlertsDiff = compare-object $CurrentAlertsContents $AlertLogContents | Measure If ($AlertsDiff.count -ne 0) { $NewAlerts = $True " " | LogMe "New Alerts Detected" | LogMe -warning } else { $NewAlerts = $False " " | LogMe "No New Alerts Detected" | LogMe -displaynormal } } } else {if (Test-Path $CurrentAlerts) {clear-content $CurrentAlerts} } #============================================================================================= # Email Section #============================================================================================= If (($NewAlerts -eq $True) -or ($SendEmail -eq $True)) { if (Test-Path $AlertsEmailed) { clear-content $AlertsEmailed } foreach ($line in $ICAErrors) { $Line | Add-Content $AlertsEmailed } # Setting MailFlag for the validation and error handling $MailFlag = $false If(!$emailFrom) { $MailFlag = $True; Write-Warning "From Email is NULL" | LogMe -error } If(!$emailTo) { $MailFlag = $True; Write-Warning "To Email is NULL" | LogMe -error } If(!$smtpServer) { $MailFlag = $True; Write-Warning "SMTP Server is NULL" | LogMe -error } # $MailFlag = $True # Send email only if From, To and SMTP adress are not null if($MailFlag -match $True) { "Email could not send as the email parameters (FROM/TO/SMTP) failed" | LogMe -error} # Send email only if there is either an Error or Warning Detected $BODY = $ICAErrors $msg = new-object System.Net.Mail.MailMessage $msg.From=$emailFrom $msg.to.Add($emailTo) if($emailCC) { $msg.cc.add($emailCC) } $msg.Subject=$emailSubject $msg.IsBodyHtml=$true $msg.Body=$BODY #$msg.Attachments.Add($logfile) $smtp = new-object System.Net.Mail.SmtpClient $smtp.host=$smtpServer Try { $smtp.Send($msg) " " | LogMe "Email Sent" | LogMe -displaygreen } Catch { "" | LogMe "Error Sending Email. See Error Messages Below:" | LogMe -error "Error Message: " + $_.Exception.Message | LogMe -error "Error Item: " + $_.Exception.ItemName | LogMe -error } Start-Sleep 2 $msg.Dispose() if (Test-Path $AlertEmail) { clear-content $AlertEmail } $CurrTime | Add-Content $AlertEmail } else { " " | LogMe "Not sending an email since there were no new Errors detected" | LogMe -displaynormal } $scriptelapsed = GetElapsedTime $startTime " " | LogMe "Total Elapsed Script Time: $scriptelapsed" | LogMe -displaygreen " " | LogMe "Script Ended at $(get-date)" | LogMe -displayscriptend " " | LogMe # Killing left over processes from Receiver so the script will work the next time it is executed # This is really only needed when running as a scheduled task # If these processes are allowed to remain the Connection fails after the second try $KillApps = $True if ($KillApps -eq $True){ try {kill -name wfcrun32 -Force -ErrorAction SilentlyContinue} catch {$_.Exception.Message | LogMe -warning} try {kill -name concentr -Force -ErrorAction SilentlyContinue} catch {$_.Exception.Message | LogMe -warning} try {kill -name redirector -Force -ErrorAction SilentlyContinue} catch {$_.Exception.Message | LogMe -warning} try {kill -name wfica32 -Force -ErrorAction SilentlyContinue} catch {$_.Exception.Message | LogMe -warning} try {kill -name receiver -Force -ErrorAction SilentlyContinue} catch {$_.Exception.Message | LogMe -warning} try {kill -name ssonsvr -Force -ErrorAction SilentlyContinue} catch {$_.Exception.Message | LogMe -warning} } # Creating Log file for previuos ran jobs # File is cleared when it reaches 1MB $size = Get-ChildItem $PreviuosLogFile if ($size.length -ge 1048576) {clear-content $PreviuosLogFile} $LogPattern = @("[SUCCESS]","[ERROR]","[WARNING]","[SCRIPT_START]","[SCRIPT_END]") if (Test-Path $LogFile) {Get-Content $LogFile | Select-String -Pattern $LogPattern -SimpleMatch | add-Content $PreviuosLogFile} EXIT #============================================================================================== # END OF SCRIPT #==============================================================================================
8 August 2015 at 4:08 AM
Hi Paul
Thanks for taking your valuable time to share your time and script. Really appreciate it. I would be interested to see how the output looks like . Could you post that as well please if you get some time. Thanks again . Suresh
LikeLike
8 August 2015 at 9:28 AM
Hi Suresh,
I will try to post how output data looks. Thanks for kind words.
Regards,
Paweł
LikeLike
13 June 2017 at 9:11 AM
Can you please provide the lines to suppress windows logon screen.. where we need to manually click on OK
LikeLike
14 June 2017 at 9:40 AM
hi Praveen,
You shouldn’t have to click manually ok when the test is running. Can you please provide when exactly that is required?
LikeLike
19 July 2017 at 1:20 PM
Hi,
I’m receiving 2 errors:
1. Get-Process : Cannot find a process with the name “wfica32”. Verify the process name and call the cmdlet again.
2. Get-Process : Cannot find a process with the name “Receiver”. Verify the process name and call the cmdlet again.
Please help.
Thank you,
Andrei
LikeLike
19 July 2017 at 2:33 PM
hi Andrei,
Does any Citrix session starts properly? If so – can you see and wfica.exe process under Task Manager?
LikeLike
12 July 2018 at 6:52 PM
Hi Pawelserman, yes sessions starts properly and I saw wfica.exe under Task Manager. But I still having this errors.
can you help?
Thank You.
Edioson.
LikeLike
24 May 2018 at 10:42 AM
################################################################################################
## XenApp Server HealthCheck based on the WorkerGroup Name report
## Author: Rajat (email id)
## April 2015
## v3.2 — Included User Profile Service check in the script.
## v3.3 — Included Licensing service and usage report
## — Updated new header and footer with Wright logo
## — Added new color code INFO-Yellow, DISPLAY-Ash
##
## The script checks the health of XenApp 6.x servers based on the WorkerGroup membership (with powershell SDKs installed) and e-mails the report.
## This script checks the following:
## – Ping response
## – Logon enabled?
## – Assigned correct Load Evaluator?
## – Active and Disconnected sessions
## – RDP port response
## – ICA port response
## – Citrix IMA, Citrix Print Manager and Spooler services status
## – WMI response (to check for WMI corruption)
## – Uptime, CPU/RAM Usage
## – Farm Session Info – Total active & disconnected sessions
##
## You are free to use this script in your environment but please e-mail me for any improvements.
################################################################################################
if ((Get-PSSnapin „Citrix.*” -EA silentlycontinue) -eq $null) {
try { Add-PSSnapin Citrix.* -ErrorAction Stop }
catch { write-error „Error loading XenApp Powershell snapin”; Return }
}
# Change the below variables to suit your environment
#==============================================================================================
# Servers belongs to this worker group only evaluated
$WorkerGroupName = „Weekly Reboot”
# Default load evaluator assigned to servers.
$defaultLE = „Default”
# License Server Name for Citrix Farm to check the license usage.
$LicenseServer = „clicense.us.wmgi.com”
# License Type to be defined [Eg: @(„MPS_PLT_CCU”, „MPS_ENT_CCU”)].
# The license file can be in Enterprise or Platinum versions.
$LicenseTypes = @(„MPS_ENT_CCU”,”MPS_PLT_CCU”)
# Only change this if you have changed the default port to custom port
$RDPPort = „3389”
$ICAPort = „1494”
$maxUpTimeDays = „7”
$sessionReliabilityPort = „2598”
$Amberram = „70”
$Redram = „95”
# E-mail report details
$emailFrom = „WMT.Monitoring@wright.com”
#$emailTo = „rajat.gupta@wright.com,Jesudass.Chinapan@wright.com”
#$emailTo = „WMTInfrasupport@wmt.com,WMTITISCitrix@wmt.com,dan.gabrielson@wmt.com,Micheal.Cooley@wmt.com,Lakshmanan.Veluchamy@wmt.com,WMTITISL3@wmt.com”
$emailTo = „WrightInfrasupport@wright.com,WMTITISCitrix@wmt.com”
$smtpServer = „exch-f01.us.wmgi.com”
$emailSubject = „Wright Health Check Report 6.5 [Citrix] ”
# Identifies the current directory path and creates logs there.
$currentDir = Split-Path $MyInvocation.MyCommand.Path
$logfile = Join-Path $currentDir („wmt.log”)
$resultsHTM = Join-Path $currentDir („wsm_Health_Check_Results.htm”)
# Images are used as the company logo for better presentation
$ImgHdr = Join-Path $currentDir („header1.jpg”)
$ImgFtr = Join-Path $currentDir („footer1.jpg”)
$Image1path = Join-Path $currentDir („wmt.gif”)
$Image2path = Join-Path $currentDir („cts.gif”)
$Signaturepath = Join-Path $currentDir („cts_sign.gif”)
# Table Header creates based on the number of header names defined
$headerNames = „FolderPath”, „Ping”, „Logons”, „Active” , „Disconnected”, „LoadEvaluator”, „RDPPort”, „GroupPolicy” , „ICAPort”, „IMA”, „CGPPort” , „UserProfile” , „CitrixPrint”, „WMI”, „Spooler” , „Uptime” , „RAMUsage” , „DiskFree”, „ServerLoad”
$headerWidths = „6”, „4”, „4”, „4”, „4” , „4” , „4”, „4”, „4”, „4”, „4”, „4” , „4” , „4”, „4” ,”4″ , „4” , „4” , „4”
#==============================================================================================
# Functions used in the Main program
#==============================================================================================
#==============================================================================================
# This function will check the license usage report on the license server and will report the current usage
#==============================================================================================
Function CheckLicense()
{
if(!$LicenseServer) { „No License Server Name defined” | LogMe -Error; $LicenseResult = ” Error; Check Detailed Logs „; return $LicenseResult }
if(!$LicenseTypes) { „No License Type defined” | LogMe -Error; $LicenseResult = ” Error; Check Detailed Logs „; return $LicenseResult }
# Declare the variables to capture and calculate the License Usage
[int]$TotalLicense = 0; [int]$InUseLicense = 0; [int]$PercentageLS = 0; $LicenseResult = ” ”
# Error Handling if the server is not accessible for the license details or WMI issues
Try
{ if(Get-Service -Display „Citrix Licensing” -ComputerName $LicenseServer -ErrorAction Stop) { „Citrix Licensing service is available.” | LogMe }
else { „Citrix Licensing’ service is NOT available.” | LogMe -Error; Return „Error; Check Logs” }
Try { if($licensePool = gwmi -class „Citrix_GT_License_Pool” -Namespace „ROOT\CitrixLicensing” -comp $LicenseServer -ErrorAction Stop)
{ „License Server WMI Class file found” | LogMe -Display
$LicensePool | ForEach-Object{
foreach ($Ltype in $LicenseTypes)
{ if ($_.PLD -match $Ltype) { $TotalLicense = $TotalLicense + $_.count; $InUseLicense = $InUseLicense + $_.InUseCount } } }
„The total number of licenses available: $TotalLicense ” | LogMe
„The number of licenses are in use: $InUseLicense ” | LogMe
if(!(($InUseLicense -eq 0) -or ($TotalLicense -eq 0 ))) { $PercentageLS = (($InUseLicense / $TotalLicense ) * 100); $PercentageLS = „{0:N2}” -f $PercentageLS }
if($PercentageLS -gt 90) { „The License usage is $PercentageLS % ” | LogMe -Error }
elseif($PercentageLS -gt 80) { „The License usage is $PercentageLS % ” | LogMe -Warning }
else { „The License usage is $PercentageLS % ” | LogMe }
$LicenseResult = „$InUseLicense [ $PercentageLS % ]”; return $LicenseResult
}
} Catch {
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
„License Server WMI Class file failed. An Error Occured while capturing the License information” | LogMe -Error
„You may need to uninstall your License Server and reinstall.” | LogMe -Error
„There are known issues with doing an in place upgrade of the license service.” | LogMe -Error
$LicenseResult = ” Error; Check Detailed Logs „; return $LicenseResult }
} Catch { „Error returned while checking the Licensing Service. Server may be down or some permission issue” | LogMe -error; return „Error; Check Detailed Logs” }
}
#================================================================================================
# The function will check the service using its display name. If the service could not find, then return a value as N/A
Function CheckService() { Param($ServiceName)
#if (!(Get-Service -Name $ServiceName -ComputerName $server -ErrorAction SilentlyContinue) ) { „$ServiceName is not available…” | LogMe; $ServiceResult = „N/A” }
if (!(Get-Service -Display $ServiceName -ComputerName $server -ErrorAction SilentlyContinue) ) { „$ServiceName is not available…” | LogMe; $ServiceResult = „N/A” }
else {
if ((Get-Service -Display $ServiceName -ComputerName $server).Status -Match „Running”) { „$ServiceName is running…” | LogMe; $ServiceResult = „Success” }
else { „$ServiceName is not running” | LogMe -display -error; $ServiceResult = „Error” }
}
return $ServiceResult
}
#==============================================================================================
# The function will log the data / result into a logfile. The data is send to function as parameter or a piped command output
Function LogMe() {
Param( [parameter(Mandatory = $true, ValueFromPipeline = $true)] $logEntry,
[switch]$display,
[switch]$error,
[switch]$warning,
[switch]$progress)
if ($error) { $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 $logFile -Append
}
#==============================================================================================
# The function will check the ping response to the server and reports failure
Function Ping ([string]$hostname, [int]$timeout = 200) {
$ping = new-object System.Net.NetworkInformation.Ping #creates a ping object
try { $result = $ping.send($hostname, $timeout).Status.ToString() }
catch { $result = „Failure” }
return $result
}
#==============================================================================================
# The function check the response from the port like a telnet query. If ICA port, then it check for the ICA response too
Function Check-Port() { param ([string]$hostname, [string]$port)
try { $socket = new-object System.Net.Sockets.TcpClient($hostname, $Port) } #creates a socket connection to see if the port is open
catch {
$socket = $null
„Socket connection on $port failed” | LogMe -display -error
return $false }
if($socket -ne $null) { „Socket Connection on $port Successful” | LogMe
if ($port -eq „1494”) {
$stream = $socket.GetStream() #gets the output of the response
$buffer = new-object System.Byte[] 1024
$encoding = new-object System.Text.AsciiEncoding
Start-Sleep -Milliseconds 500 #records data for half a second
while($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
$response = $encoding.GetString($buffer, 0, $read)
#Write-Host „Response: ” + $response
if($response -like ‚*ICA*’){
„ICA protocol responded” | LogMe
return $true }
}
„ICA did not response correctly” | LogMe -display -error
return $false
} else { return $true }
} else { „Socket connection on $port failed” | LogMe -display -error; return $false }
}
#===========================================================================================
# HTML Report Formating Part
#===========================================================================================
Function writeHtmlHeader { param($title, $fileName, $TotalServers)
$date = ( Get-Date -format R)
$head = @”
$title
$title $date
Total Number of Wright Citrix Servers: $TotalServers
Citrix License Usage: $LicenseReport
Total Active Sessions: $TotalActiveSessions
Total Disconnected: $TotalDisconnectedSessions
„@
$head | Out-File $fileName
}
# ==============================================================================================
Function writeTableHeader { param($fileName)
$tableHeader = @”
ServerName
„@
$i = 0
while ($i -lt $headerNames.count) {
$headerName = $headerNames[$i]
$headerWidth = $headerWidths[$i]
$tableHeader += „$headerName”
$i++ }
$tableHeader += „”
$tableHeader | Out-File $fileName -append
}
# ==============================================================================================
Function writeData { param($data, $fileName)
$data.Keys | sort | foreach {
$tableEntry += „”
$computerName = $_
$tableEntry += („$computerName”)
#$data.$_.Keys | foreach {
$headerNames | foreach {
#”$computerName : $_” | LogMe -display
try {
if ($data.$computerName.$_[0] -eq „SUCCESS”) { $bgcolor = „#387C44”; $fontColor = „#FFFFFF” }
elseif ($data.$computerName.$_[0] -eq „WARNING”) { $bgcolor = „#FF7700”; $fontColor = „#FFFFFF” }
elseif ($data.$computerName.$_[0] -eq „INFO”) { $bgcolor = „#ffff99”; $fontColor = „#000000” }
elseif ($data.$computerName.$_[0] -eq „ERROR”) { $bgcolor = „#FF0000”; $fontColor = „#FFFFFF” }
else { $bgcolor = „#CCCCCC”; $fontColor = „#003399” }
$testResult = $data.$computerName.$_[1]
}
catch {
$bgcolor = „#CCCCCC”; $fontColor = „#003399”
#$bgcolor = „#AEB404”; $fontColor = „#4000FF”
$testResult = „N/A”
}
$tableEntry += („$testResult”)
}
$tableEntry += „”
}
$tableEntry | Out-File $fileName -append
}
# ==============================================================================================
Function writeHtmlFooter
{
param($fileName)
@”
NOTE: The above results are generated from an automation task created by Wright Citrix Team. Any issues reported in the above report will be actioned by Wright Citrix Team
Author: Wright Citrix Team Email: rajat.gupta@wright.com ; WMTITISCitrix@wmt.com
„@ | Out-File $FileName -append
}
# END OF THE FUNCTIONS
#=================================================================================================
# ==============================================================================================
# ======= MAIN SCRIPT =======
# ==============================================================================================
rm $logfile -force -EA SilentlyContinue
$date1 = Get-Date -format R
„Checking server health…… ($date1)” | LogMe -display -progress
$TotalServers = 0
$allResults = @{}
# Calling the license checking function here
„Checking Citrix License usage on $LicenseServer” | LogMe -Display
$LicenseReport = CheckLicense
# Get session list once to use throughout the script
$sessions = Get-XASession
$WorkerGroupServers = Get-XAWorkerGroup $WorkerGroupName
$servers = $WorkerGroupServers.ServerNames | sort-object $_.ServerNames
#$servers = „C1″,”C10″,”C11″,”C12″,”C13″,”C14″,”C15″,”C16″,”C17″,”C18″,”C19″,”C2″,”C20″,”C23″,”C24″,”C25″,”C26″,”C27″,”C28″,”C29″,”C3″,”C30″,”C4″,”C5″,”C6″,”C7″,”C8″,”QTSVPW8CTX01″,”QTSVPW8CTX02″,”QTSVTW8CTX01″,”QTSVTW8CTX02”
#$servers= „C1″,”C10″,”C6″,”QTSVPW8CTX01”
foreach($server in $servers){
$tests = @{}
„———————————————————” | LogMe
$server | LogMe -display -progress
$TotalServers = $TotalServers + 1
# Check Folder Path
$folder= (Get-XAServer -ServerName $server | % {$_.FolderPath})
if ($folder -ne $null)
{
$tests.FolderPath = „DISPLAY”, $folder.trim(„Servers/”)
„$folder” | LogMe
}
else
{
$tests.FolderPath = „ERROR”, $folder
}
# Ping server
$result = Ping $server 100
if ($result -ne „SUCCESS”)
{
$tests.Ping = „ERROR”, $result
„Ping Results : $result. Skipping rest of the tests” | LogMe -display -error
}
else
{
$tests.Ping = „SUCCESS”, $result
„Ping Results : $result.” | LogMe
# Check server logons
if((Get-XAServer -ServerName $server).LogOnsEnabled -eq $false)
{
„Logons are disabled on this server” | LogMe -display -error
$tests.Logons = „ERROR”, „Disabled”
}
else
{
„Logons are enabled on this server” | LogMe
$tests.Logons = „SUCCESS”,”Enabled”
}
# Test Session Reliability port
if (Check-Port $server $sessionReliabilityPort)
{ $tests.CGPPort = „SUCCESS”, „Success” }
else { $tests.CGPPort = „ERROR”, „No response” }
# Check Load Evaluator
if ((Get-XALoadEvaluator -ServerName $server).LoadEvaluatorName -ne $defaultLE)
{
#”Non-default Load Evaluator assigned” | LogMe -display -warning
$LE = (Get-XALoadEvaluator -ServerName $server)
$tests.LoadEvaluator = „SUCCESS”, $LE
„$LE Load Evaluator assigned” | LogMe -display
}
else
{
„Default Load Evaluator assigned” | LogMe
$tests.LoadEvaluator = „DISPLAY”, $defaultLE
}
# Report on active server sessions
#$activeServerSessions = [array]($sessions | ? {$_.State -eq „Active” -and $_.Protocol -eq „Ica” -and $_.ServerName -match $server})
$activeServerSessions = [array]($sessions | ? {$_.State -eq “Active” -and $_.Protocol -eq “Ica” -and $_.ServerName -eq $server})
if ($activeServerSessions)
{
$totalActiveServerSessions = $activeServerSessions.count
}
else
{
$totalActiveServerSessions = 0
}
$tests.Active = „SUCCESS”, $totalActiveServerSessions
„Number of active sessions on the server: $totalActiveServerSessions” | LogMe
# Report on disconnected sessions
$discServerSessions = [array]($sessions | ? {$_.State -eq „Disconnected” -and $_.Protocol -eq „Ica” -and $_.ServerName -match $server})
if ($discServerSessions)
{
$totalDiscServerSessions = $discServerSessions.count
}
else
{
$totalDiscServerSessions = 0
}
$tests.Disconnected = „SUCCESS”, $totalDiscServerSessions
„Number of disconnected sessions on the server: $totalDiscServerSessions” | LogMe
# Creates a warnig if both active and disconnected sessions are zero
if (($totalActiveServerSessions -eq 0) -and ($totalDiscServerSessions -eq 0))
{
$tests.Active = „INFO”, $totalActiveServerSessions
$tests.Disconnected = „INFO”, $totalDiscServerSessions
}
# Test ICA connectivity
if (Check-Port $server $ICAPort)
{
$tests.ICAPort = „SUCCESS”, „Success”
}
else
{
$tests.ICAPort = „ERROR”,”No response”
}
# Test RDP connectivity
if (Check-Port $server $RDPPort)
{
$tests.RDPPort = „DISPLAY”, „Success”
}
else
{
$tests.RDPPort = „SUCCESS”, „Disabled”
}
# Check the IMA service
$ServiceOP = CheckService („Citrix Independent Management Architecture”)
if ($ServiceOP -eq „Error”)
{
$tests.IMA = „ERROR”, $ServiceOP
}
else
{
$tests.IMA = „SUCCESS”, $ServiceOP
}
# Checks the Print Spooler service
$ServiceOP = CheckService („Print Spooler”)
if ($ServiceOP -eq „Error”)
{
$tests.Spooler = „ERROR”, $ServiceOP
}
else
{
$tests.Spooler = „SUCCESS”, $ServiceOP
}
# Checks the Citrix Print Manager service
$ServiceOP = CheckService („Citrix Print Manager Service”)
if ($ServiceOP -eq „Error”)
{
$tests.CitrixPrint = „ERROR”, $ServiceOP
}
else
{
$tests.CitrixPrint = „SUCCESS”, $ServiceOP
}
# Checks the Group policy Client service
$ServiceOP = CheckService („Group Policy Client”)
if ($ServiceOP -eq „Error”)
{
$tests.GroupPolicy = „ERROR”, $ServiceOP
}
else
{
$tests.GroupPolicy = „SUCCESS”, $ServiceOP
}
# Checks the User profile service
$ServiceOP = CheckService („User Profile Service”)
if ($ServiceOP -eq „Error”)
{
$tests.UserProfile = „ERROR”, $ServiceOP
}
else
{
$tests.UserProfile = „SUCCESS”, $ServiceOP
}
# Test WMI
try { $wmi=Get-WmiObject -class Win32_OperatingSystem -computername $server }
catch { $wmi = $null }
if ($wmi -ne $null)
{
$tests.WMI = „SUCCESS”, „Success”
$LBTime=$wmi.ConvertToDateTime($wmi.Lastbootuptime)
[TimeSpan]$uptime=New-TimeSpan $LBTime $(get-date)
if ($uptime.days -gt $maxUpTimeDays){
„Server reboot warning, last reboot: {0:D}” -f $LBTime | LogMe -display -warning
$tests.Uptime = „WARNING”, $uptime.days
} else {
$tests.Uptime = „SUCCESS”, $uptime.days
}
}
else
{
$tests.WMI = „ERROR”,”Error”
„WMI connection failed – check WMI for corruption” | LogMe -display -error
}
# If the IMA service is running, check the server load
if ($tests.IMA[0] -eq „Success”) {
$CurrentServerLoad = Get-XAServerLoad -ServerName $server
#$CurrentServerLoad.GetType().Name|LogMe -display -warning
if( [int] $CurrentServerLoad.load -lt 7500) {
„Serverload is low” | LogMe
$tests.Serverload = „SUCCESS”, ($CurrentServerload.load)
}
elseif([int] $CurrentServerLoad.load -lt 9000) {
„Serverload is Medium” | LogMe -display -warning
$tests.Serverload = „WARNING”, ($CurrentServerload.load)
}
else {
„Serverload is High” | LogMe -display -error
$tests.Serverload = „ERROR”, ($CurrentServerload.load)
}
$CurrentServerLoad = 0
}
#RAM Usage
#$OS = (Get-WmiObject Win32_OperatingSystem -computername $server).caption
try { $RAM = Get-WmiObject -Class Win32_OperatingSystem -computername $server | Select-Object Name, TotalVisibleMemorySize, FreePhysicalMemory }
catch { $RAM = $null }
if ($RAM -ne $null )
{
$tests.RAM = „SUCCESS”, „Success”
$TotalRAM = $RAM.TotalVisibleMemorySize/1MB
$FreeRAM = $RAM.FreePhysicalMemory/1MB
$UsedRAM = $TotalRAM – $FreeRAM
$RAMPercentused = ($usedRAM / $TotalRAM) * 100
$TotalRAM = [Math]::Round($TotalRAM, 2)
$FreeRAM = [Math]::Round($FreeRAM, 2)
$UsedRAM = [Math]::Round($UsedRAM, 2)
$RAMPercentused = [Math]::Round($RAMPercentUsed, 2)
$RAMPUsed = „$RAMPercentused%”
„RAM Usage: $RAMPUsed” | LogMe
if ($RAMPercentused -gt $Redram){
$tests.ramusage = „ERROR”, $RAMPUsed
} elseif (($RAMPercentused -gt $Amberram) -AND ($RAMPercentused -lt $Redram)) {
$tests.ramusage = „WARNING”, $RAMPUsed
} else {
$tests.ramusage = „SUCCESS”, $RAMPUsed
}
}
else
{
$tests.RAM = „ERROR”,”Error”
„WMI connection failed – check WMI for corruption” | LogMe -display -error
}
}
#Hard Disk
try { $HardDisk = Get-WmiObject Win32_LogicalDisk -ComputerName $server -Filter „DeviceID=’C:’” | Select-Object Size,FreeSpace }
catch { $HardDisk = $null }
if ($HardDisk -ne $null)
{
$DiskTotalSize = $HardDisk.Size
$DiskFreeSpace = $HardDisk.FreeSpace
$PercentageDS = (($DiskFreeSpace / $DiskTotalSize ) * 100); $PercentageDS = „{0:N2}” -f $PercentageDS
$DSP =”$PercentageDS%”
if( $PercentageDS -gt 15)
{ „Disk Free is normal: $DSP” | LogMe
$tests.DiskFree = „SUCCESS”, $DSP
}
elseif( $PercentageDS -lt 15)
{ „Disk Free is Low: $DSP” | LogMe
$tests.DiskFree = „WARNING”, $DSP
}
elseif( $PercentageDS -lt 5)
{ „Disk Free is normal: $DSP” | LogMe
$tests.DiskFree = „ERROR”, $DSP
}
elseif( $PercentageDS -eq 0)
{ „Disk Free test failed” | LogMe -error;
$tests.DiskFree = „ERROR”, „Err”
} }
else
{ $tests.DiskFree = „ERROR”, ($PercentageDS)
„Disk Free is Critical” | LogMe -display -error
}
$PercentageDS = 0
$allResults.$server = $tests
}
# Get farm session info
$ActiveSessions = [array]($sessions | ? {$_.State -eq „Active” -and $_.Protocol -eq „Ica”})
$DisconnectedSessions = [array]($sessions | ? {$_.State -eq „Disconnected” -and $_.Protocol -eq „Ica”})
if ($ActiveSessions) { $TotalActiveSessions = $ActiveSessions.count }
else { $TotalActiveSessions = 0 }
if ($DisconnectedSessions) { $TotalDisconnectedSessions = $DisconnectedSessions.count }
else { $TotalDisconnectedSessions = 0 }
„Total Active Sessions: $TotalActiveSessions” | LogMe -display
„Total Disconnected Sessions: $TotalDisconnectedSessions” | LogMe -display
# Write all results to an html file
writeHtmlHeader $emailSubject $resultsHTM $TotalServers
writeTableHeader $resultsHTM
$allResults | % { writeData $allResults $resultsHTM }
writeHtmlFooter $resultsHTM
#=============================================================================================
# Emailing Part
#=============================================================================================
$msg = new-object System.Net.Mail.MailMessage
$msg.From=$emailFrom
$msg.to.Add($emailTo)
$msg.Subject=$emailSubject
$msg.IsBodyHtml=$true
# Images are found by the ‚COntentID-CID’ in the body of the message. Both logos to be paced in the same directory where we save the script
$att = new-object System.Net.Mail.Attachment($ImgHdr)
$att.ContentDisposition.Inline = $True
$att.ContentDisposition.DispositionType = „Inline”
$att.ContentType.MediaType = „image/jpeg”
$att.ContentId = „header”
$att1 = new-object System.Net.Mail.Attachment($ImgFtr)
$att1.ContentDisposition.Inline = $True
$att1.ContentDisposition.DispositionType = „Inline”
$att1.ContentType.MediaType = „image/jpeg”
$att1.ContentId = „Signature”
$msg.Body=(gc $resultsHTM) | Out-String
$msg.Attachments.Add($logfile)
$msg.Attachments.Add($att)
$msg.Attachments.Add($att1)
$smtp = new-object System.Net.Mail.SmtpClient
$smtp.host=$smtpServer
$smtp.Send($msg)
Start-Sleep 5
$att.Dispose()
$att1.Dispose()
$msg.Dispose()
Write-host „Email Sent”
#======================= END OF MAIN PROGRAM ++++++++++++++++++++++++++++++++++++++++++++++
###############################################################################################
LikeLike
24 May 2018 at 12:25 PM
———- Forwarded message ———
From: Pradeep B
Date: Wed, 23 May 2018, 6:22 pm
Subject: Healthcheck 6.5
To: Joseph A
################################################################################################
## XenApp Server HealthCheck based on the WorkerGroup Name report
## Author: Rajat (email id)
## April 2015
## v3.2 — Included User Profile Service check in the script.
## v3.3 — Included Licensing service and usage report
## — Updated new header and footer with Wright logo
## — Added new color code INFO-Yellow, DISPLAY-Ash
##
## The script checks the health of XenApp 6.x servers based on the WorkerGroup membership (with powershell SDKs installed) and e-mails the report.
## This script checks the following:
## – Ping response
## – Logon enabled?
## – Assigned correct Load Evaluator?
## – Active and Disconnected sessions
## – RDP port response
## – ICA port response
## – Citrix IMA, Citrix Print Manager and Spooler services status
## – WMI response (to check for WMI corruption)
## – Uptime, CPU/RAM Usage
## – Farm Session Info – Total active & disconnected sessions
##
## You are free to use this script in your environment but please e-mail me for any improvements.
################################################################################################
if ((Get-PSSnapin „Citrix.*” -EA silentlycontinue) -eq $null) {
try { Add-PSSnapin Citrix.* -ErrorAction Stop }
catch { write-error „Error loading XenApp Powershell snapin”; Return }
}
# Change the below variables to suit your environment
#==============================================================================================
# Servers belongs to this worker group only evaluated
$WorkerGroupName = „Weekly Reboot”
# Default load evaluator assigned to servers.
$defaultLE = „Default”
# License Server Name for Citrix Farm to check the license usage.
$LicenseServer = „clicense.us.wmgi.com”
# License Type to be defined [Eg: @(„MPS_PLT_CCU”, „MPS_ENT_CCU”)].
# The license file can be in Enterprise or Platinum versions.
$LicenseTypes = @(„MPS_ENT_CCU”,”MPS_PLT_CCU”)
# Only change this if you have changed the default port to custom port
$RDPPort = „3389”
$ICAPort = „1494”
$maxUpTimeDays = „7”
$sessionReliabilityPort = „2598”
$Amberram = „70”
$Redram = „95”
# E-mail report details
$emailFrom = „WMT.Monitoring@wright.com”
#$emailTo = „rajat.gupta@wright.com,Jesudass.Chinapan@wright.com”
#$emailTo = „WMTInfrasupport@wmt.com,WMTITISCitrix@wmt.com,dan.gabrielson@wmt.com,Micheal.Cooley@wmt.com,Lakshmanan.Veluchamy@wmt.com,WMTITISL3@wmt.com”
$emailTo = „WrightInfrasupport@wright.com,WMTITISCitrix@wmt.com”
$smtpServer = „exch-f01.us.wmgi.com”
$emailSubject = „Wright Health Check Report 6.5 [Citrix] ”
# Identifies the current directory path and creates logs there.
$currentDir = Split-Path $MyInvocation.MyCommand.Path
$logfile = Join-Path $currentDir („wmt.log”)
$resultsHTM = Join-Path $currentDir („wsm_Health_Check_Results.htm”)
# Images are used as the company logo for better presentation
$ImgHdr = Join-Path $currentDir („header1.jpg”)
$ImgFtr = Join-Path $currentDir („footer1.jpg”)
$Image1path = Join-Path $currentDir („wmt.gif”)
$Image2path = Join-Path $currentDir („cts.gif”)
$Signaturepath = Join-Path $currentDir („cts_sign.gif”)
# Table Header creates based on the number of header names defined
$headerNames = „FolderPath”, „Ping”, „Logons”, „Active” , „Disconnected”, „LoadEvaluator”, „RDPPort”, „GroupPolicy” , „ICAPort”, „IMA”, „CGPPort” , „UserProfile” , „CitrixPrint”, „WMI”, „Spooler” , „Uptime” , „RAMUsage” , „DiskFree”, „ServerLoad”
$headerWidths = „6”, „4”, „4”, „4”, „4” , „4” , „4”, „4”, „4”, „4”, „4”, „4” , „4” , „4”, „4” ,”4″ , „4” , „4” , „4”
#==============================================================================================
# Functions used in the Main program
#==============================================================================================
#==============================================================================================
# This function will check the license usage report on the license server and will report the current usage
#==============================================================================================
Function CheckLicense()
{
if(!$LicenseServer) { „No License Server Name defined” | LogMe -Error; $LicenseResult = ” Error; Check Detailed Logs „; return $LicenseResult }
if(!$LicenseTypes) { „No License Type defined” | LogMe -Error; $LicenseResult = ” Error; Check Detailed Logs „; return $LicenseResult }
# Declare the variables to capture and calculate the License Usage
[int]$TotalLicense = 0; [int]$InUseLicense = 0; [int]$PercentageLS = 0; $LicenseResult = ” ”
# Error Handling if the server is not accessible for the license details or WMI issues
Try
{ if(Get-Service -Display „Citrix Licensing” -ComputerName $LicenseServer -ErrorAction Stop) { „Citrix Licensing service is available.” | LogMe }
else { „Citrix Licensing’ service is NOT available.” | LogMe -Error; Return „Error; Check Logs” }
Try { if($licensePool = gwmi -class „Citrix_GT_License_Pool” -Namespace „ROOT\CitrixLicensing” -comp $LicenseServer -ErrorAction Stop)
{ „License Server WMI Class file found” | LogMe -Display
$LicensePool | ForEach-Object{
foreach ($Ltype in $LicenseTypes)
{ if ($_.PLD -match $Ltype) { $TotalLicense = $TotalLicense + $_.count; $InUseLicense = $InUseLicense + $_.InUseCount } } }
„The total number of licenses available: $TotalLicense ” | LogMe
„The number of licenses are in use: $InUseLicense ” | LogMe
if(!(($InUseLicense -eq 0) -or ($TotalLicense -eq 0 ))) { $PercentageLS = (($InUseLicense / $TotalLicense ) * 100); $PercentageLS = „{0:N2}” -f $PercentageLS }
if($PercentageLS -gt 90) { „The License usage is $PercentageLS % ” | LogMe -Error }
elseif($PercentageLS -gt 80) { „The License usage is $PercentageLS % ” | LogMe -Warning }
else { „The License usage is $PercentageLS % ” | LogMe }
$LicenseResult = „$InUseLicense [ $PercentageLS % ]”; return $LicenseResult
}
} Catch {
$ErrorMessage = $_.Exception.Message
$FailedItem = $_.Exception.ItemName
„License Server WMI Class file failed. An Error Occured while capturing the License information” | LogMe -Error
„You may need to uninstall your License Server and reinstall.” | LogMe -Error
„There are known issues with doing an in place upgrade of the license service.” | LogMe -Error
$LicenseResult = ” Error; Check Detailed Logs „; return $LicenseResult }
} Catch { „Error returned while checking the Licensing Service. Server may be down or some permission issue” | LogMe -error; return „Error; Check Detailed Logs” }
}
#================================================================================================
# The function will check the service using its display name. If the service could not find, then return a value as N/A
Function CheckService() { Param($ServiceName)
#if (!(Get-Service -Name $ServiceName -ComputerName $server -ErrorAction SilentlyContinue) ) { „$ServiceName is not available…” | LogMe; $ServiceResult = „N/A” }
if (!(Get-Service -Display $ServiceName -ComputerName $server -ErrorAction SilentlyContinue) ) { „$ServiceName is not available…” | LogMe; $ServiceResult = „N/A” }
else {
if ((Get-Service -Display $ServiceName -ComputerName $server).Status -Match „Running”) { „$ServiceName is running…” | LogMe; $ServiceResult = „Success” }
else { „$ServiceName is not running” | LogMe -display -error; $ServiceResult = „Error” }
}
return $ServiceResult
}
#==============================================================================================
# The function will log the data / result into a logfile. The data is send to function as parameter or a piped command output
Function LogMe() {
Param( [parameter(Mandatory = $true, ValueFromPipeline = $true)] $logEntry,
[switch]$display,
[switch]$error,
[switch]$warning,
[switch]$progress)
if ($error) { $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 $logFile -Append
}
#==============================================================================================
# The function will check the ping response to the server and reports failure
Function Ping ([string]$hostname, [int]$timeout = 200) {
$ping = new-object System.Net.NetworkInformation.Ping #creates a ping object
try { $result = $ping.send($hostname, $timeout).Status.ToString() }
catch { $result = „Failure” }
return $result
}
#==============================================================================================
# The function check the response from the port like a telnet query. If ICA port, then it check for the ICA response too
Function Check-Port() { param ([string]$hostname, [string]$port)
try { $socket = new-object System.Net.Sockets.TcpClient($hostname, $Port) } #creates a socket connection to see if the port is open
catch {
$socket = $null
„Socket connection on $port failed” | LogMe -display -error
return $false }
if($socket -ne $null) { „Socket Connection on $port Successful” | LogMe
if ($port -eq „1494”) {
$stream = $socket.GetStream() #gets the output of the response
$buffer = new-object System.Byte[] 1024
$encoding = new-object System.Text.AsciiEncoding
Start-Sleep -Milliseconds 500 #records data for half a second
while($stream.DataAvailable) {
$read = $stream.Read($buffer, 0, 1024)
$response = $encoding.GetString($buffer, 0, $read)
#Write-Host „Response: ” + $response
if($response -like ‚*ICA*’){
„ICA protocol responded” | LogMe
return $true }
}
„ICA did not response correctly” | LogMe -display -error
return $false
} else { return $true }
} else { „Socket connection on $port failed” | LogMe -display -error; return $false }
}
#===========================================================================================
# HTML Report Formating Part
#===========================================================================================
Function writeHtmlHeader { param($title, $fileName, $TotalServers)
$date = ( Get-Date -format R)
$head = @”
$title
$title $date
Total Number of Wright Citrix Servers: $TotalServers
Citrix License Usage: $LicenseReport
Total Active Sessions: $TotalActiveSessions
Total Disconnected: $TotalDisconnectedSessions
„@
$head | Out-File $fileName
}
# ==============================================================================================
Function writeTableHeader { param($fileName)
$tableHeader = @”
ServerName
„@
$i = 0
while ($i -lt $headerNames.count) {
$headerName = $headerNames[$i]
$headerWidth = $headerWidths[$i]
$tableHeader += „$headerName”
$i++ }
$tableHeader += „”
$tableHeader | Out-File $fileName -append
}
# ==============================================================================================
Function writeData { param($data, $fileName)
$data.Keys | sort | foreach {
$tableEntry += „”
$computerName = $_
$tableEntry += („$computerName”)
#$data.$_.Keys | foreach {
$headerNames | foreach {
#”$computerName : $_” | LogMe -display
try {
if ($data.$computerName.$_[0] -eq „SUCCESS”) { $bgcolor = „#387C44”; $fontColor = „#FFFFFF” }
elseif ($data.$computerName.$_[0] -eq „WARNING”) { $bgcolor = „#FF7700”; $fontColor = „#FFFFFF” }
elseif ($data.$computerName.$_[0] -eq „INFO”) { $bgcolor = „#ffff99”; $fontColor = „#000000” }
elseif ($data.$computerName.$_[0] -eq „ERROR”) { $bgcolor = „#FF0000”; $fontColor = „#FFFFFF” }
else { $bgcolor = „#CCCCCC”; $fontColor = „#003399” }
$testResult = $data.$computerName.$_[1]
}
catch {
$bgcolor = „#CCCCCC”; $fontColor = „#003399”
#$bgcolor = „#AEB404”; $fontColor = „#4000FF”
$testResult = „N/A”
}
$tableEntry += („$testResult”)
}
$tableEntry += „”
}
$tableEntry | Out-File $fileName -append
}
# ==============================================================================================
Function writeHtmlFooter
{
param($fileName)
@”
NOTE: The above results are generated from an automation task created by Wright Citrix Team. Any issues reported in the above report will be actioned by Wright Citrix Team
Author: Wright Citrix Team Email: rajat.gupta@wright.com ; WMTITISCitrix@wmt.com
„@ | Out-File $FileName -append
}
# END OF THE FUNCTIONS
#=================================================================================================
# ==============================================================================================
# ======= MAIN SCRIPT =======
# ==============================================================================================
rm $logfile -force -EA SilentlyContinue
$date1 = Get-Date -format R
„Checking server health…… ($date1)” | LogMe -display -progress
$TotalServers = 0
$allResults = @{}
# Calling the license checking function here
„Checking Citrix License usage on $LicenseServer” | LogMe -Display
$LicenseReport = CheckLicense
# Get session list once to use throughout the script
$sessions = Get-XASession
$WorkerGroupServers = Get-XAWorkerGroup $WorkerGroupName
$servers = $WorkerGroupServers.ServerNames | sort-object $_.ServerNames
#$servers = „C1″,”C10″,”C11″,”C12″,”C13″,”C14″,”C15″,”C16″,”C17″,”C18″,”C19″,”C2″,”C20″,”C23″,”C24″,”C25″,”C26″,”C27″,”C28″,”C29″,”C3″,”C30″,”C4″,”C5″,”C6″,”C7″,”C8″,”QTSVPW8CTX01″,”QTSVPW8CTX02″,”QTSVTW8CTX01″,”QTSVTW8CTX02”
#$servers= „C1″,”C10″,”C6″,”QTSVPW8CTX01”
foreach($server in $servers){
$tests = @{}
„———————————————————” | LogMe
$server | LogMe -display -progress
$TotalServers = $TotalServers + 1
# Check Folder Path
$folder= (Get-XAServer -ServerName $server | % {$_.FolderPath})
if ($folder -ne $null)
{
$tests.FolderPath = „DISPLAY”, $folder.trim(„Servers/”)
„$folder” | LogMe
}
else
{
$tests.FolderPath = „ERROR”, $folder
}
# Ping server
$result = Ping $server 100
if ($result -ne „SUCCESS”)
{
$tests.Ping = „ERROR”, $result
„Ping Results : $result. Skipping rest of the tests” | LogMe -display -error
}
else
{
$tests.Ping = „SUCCESS”, $result
„Ping Results : $result.” | LogMe
# Check server logons
if((Get-XAServer -ServerName $server).LogOnsEnabled -eq $false)
{
„Logons are disabled on this server” | LogMe -display -error
$tests.Logons = „ERROR”, „Disabled”
}
else
{
„Logons are enabled on this server” | LogMe
$tests.Logons = „SUCCESS”,”Enabled”
}
# Test Session Reliability port
if (Check-Port $server $sessionReliabilityPort)
{ $tests.CGPPort = „SUCCESS”, „Success” }
else { $tests.CGPPort = „ERROR”, „No response” }
# Check Load Evaluator
if ((Get-XALoadEvaluator -ServerName $server).LoadEvaluatorName -ne $defaultLE)
{
#”Non-default Load Evaluator assigned” | LogMe -display -warning
$LE = (Get-XALoadEvaluator -ServerName $server)
$tests.LoadEvaluator = „SUCCESS”, $LE
„$LE Load Evaluator assigned” | LogMe -display
}
else
{
„Default Load Evaluator assigned” | LogMe
$tests.LoadEvaluator = „DISPLAY”, $defaultLE
}
# Report on active server sessions
#$activeServerSessions = [array]($sessions | ? {$_.State -eq „Active” -and $_.Protocol -eq „Ica” -and $_.ServerName -match $server})
$activeServerSessions = [array]($sessions | ? {$_.State -eq “Active” -and $_.Protocol -eq “Ica” -and $_.ServerName -eq $server})
if ($activeServerSessions)
{
$totalActiveServerSessions = $activeServerSessions.count
}
else
{
$totalActiveServerSessions = 0
}
$tests.Active = „SUCCESS”, $totalActiveServerSessions
„Number of active sessions on the server: $totalActiveServerSessions” | LogMe
# Report on disconnected sessions
$discServerSessions = [array]($sessions | ? {$_.State -eq „Disconnected” -and $_.Protocol -eq „Ica” -and $_.ServerName -match $server})
if ($discServerSessions)
{
$totalDiscServerSessions = $discServerSessions.count
}
else
{
$totalDiscServerSessions = 0
}
$tests.Disconnected = „SUCCESS”, $totalDiscServerSessions
„Number of disconnected sessions on the server: $totalDiscServerSessions” | LogMe
# Creates a warnig if both active and disconnected sessions are zero
if (($totalActiveServerSessions -eq 0) -and ($totalDiscServerSessions -eq 0))
{
$tests.Active = „INFO”, $totalActiveServerSessions
$tests.Disconnected = „INFO”, $totalDiscServerSessions
}
# Test ICA connectivity
if (Check-Port $server $ICAPort)
{
$tests.ICAPort = „SUCCESS”, „Success”
}
else
{
$tests.ICAPort = „ERROR”,”No response”
}
# Test RDP connectivity
if (Check-Port $server $RDPPort)
{
$tests.RDPPort = „DISPLAY”, „Success”
}
else
{
$tests.RDPPort = „SUCCESS”, „Disabled”
}
# Check the IMA service
$ServiceOP = CheckService („Citrix Independent Management Architecture”)
if ($ServiceOP -eq „Error”)
{
$tests.IMA = „ERROR”, $ServiceOP
}
else
{
$tests.IMA = „SUCCESS”, $ServiceOP
}
# Checks the Print Spooler service
$ServiceOP = CheckService („Print Spooler”)
if ($ServiceOP -eq „Error”)
{
$tests.Spooler = „ERROR”, $ServiceOP
}
else
{
$tests.Spooler = „SUCCESS”, $ServiceOP
}
# Checks the Citrix Print Manager service
$ServiceOP = CheckService („Citrix Print Manager Service”)
if ($ServiceOP -eq „Error”)
{
$tests.CitrixPrint = „ERROR”, $ServiceOP
}
else
{
$tests.CitrixPrint = „SUCCESS”, $ServiceOP
}
# Checks the Group policy Client service
$ServiceOP = CheckService („Group Policy Client”)
if ($ServiceOP -eq „Error”)
{
$tests.GroupPolicy = „ERROR”, $ServiceOP
}
else
{
$tests.GroupPolicy = „SUCCESS”, $ServiceOP
}
# Checks the User profile service
$ServiceOP = CheckService („User Profile Service”)
if ($ServiceOP -eq „Error”)
{
$tests.UserProfile = „ERROR”, $ServiceOP
}
else
{
$tests.UserProfile = „SUCCESS”, $ServiceOP
}
# Test WMI
try { $wmi=Get-WmiObject -class Win32_OperatingSystem -computername $server }
catch { $wmi = $null }
if ($wmi -ne $null)
{
$tests.WMI = „SUCCESS”, „Success”
$LBTime=$wmi.ConvertToDateTime($wmi.Lastbootuptime)
[TimeSpan]$uptime=New-TimeSpan $LBTime $(get-date)
if ($uptime.days -gt $maxUpTimeDays){
„Server reboot warning, last reboot: {0:D}” -f $LBTime | LogMe -display -warning
$tests.Uptime = „WARNING”, $uptime.days
} else {
$tests.Uptime = „SUCCESS”, $uptime.days
}
}
else
{
$tests.WMI = „ERROR”,”Error”
„WMI connection failed – check WMI for corruption” | LogMe -display -error
}
# If the IMA service is running, check the server load
if ($tests.IMA[0] -eq „Success”) {
$CurrentServerLoad = Get-XAServerLoad -ServerName $server
#$CurrentServerLoad.GetType().Name|LogMe -display -warning
if( [int] $CurrentServerLoad.load -lt 7500) {
„Serverload is low” | LogMe
$tests.Serverload = „SUCCESS”, ($CurrentServerload.load)
}
elseif([int] $CurrentServerLoad.load -lt 9000) {
„Serverload is Medium” | LogMe -display -warning
$tests.Serverload = „WARNING”, ($CurrentServerload.load)
}
else {
„Serverload is High” | LogMe -display -error
$tests.Serverload = „ERROR”, ($CurrentServerload.load)
}
$CurrentServerLoad = 0
}
#RAM Usage
#$OS = (Get-WmiObject Win32_OperatingSystem -computername $server).caption
try { $RAM = Get-WmiObject -Class Win32_OperatingSystem -computername $server | Select-Object Name, TotalVisibleMemorySize, FreePhysicalMemory }
catch { $RAM = $null }
if ($RAM -ne $null )
{
$tests.RAM = „SUCCESS”, „Success”
$TotalRAM = $RAM.TotalVisibleMemorySize/1MB
$FreeRAM = $RAM.FreePhysicalMemory/1MB
$UsedRAM = $TotalRAM – $FreeRAM
$RAMPercentused = ($usedRAM / $TotalRAM) * 100
$TotalRAM = [Math]::Round($TotalRAM, 2)
$FreeRAM = [Math]::Round($FreeRAM, 2)
$UsedRAM = [Math]::Round($UsedRAM, 2)
$RAMPercentused = [Math]::Round($RAMPercentUsed, 2)
$RAMPUsed = „$RAMPercentused%”
„RAM Usage: $RAMPUsed” | LogMe
if ($RAMPercentused -gt $Redram){
$tests.ramusage = „ERROR”, $RAMPUsed
} elseif (($RAMPercentused -gt $Amberram) -AND ($RAMPercentused -lt $Redram)) {
$tests.ramusage = „WARNING”, $RAMPUsed
} else {
$tests.ramusage = „SUCCESS”, $RAMPUsed
}
}
else
{
$tests.RAM = „ERROR”,”Error”
„WMI connection failed – check WMI for corruption” | LogMe -display -error
}
}
#Hard Disk
try { $HardDisk = Get-WmiObject Win32_LogicalDisk -ComputerName $server -Filter „DeviceID=’C:’” | Select-Object Size,FreeSpace }
catch { $HardDisk = $null }
if ($HardDisk -ne $null)
{
$DiskTotalSize = $HardDisk.Size
$DiskFreeSpace = $HardDisk.FreeSpace
$PercentageDS = (($DiskFreeSpace / $DiskTotalSize ) * 100); $PercentageDS = „{0:N2}” -f $PercentageDS
$DSP =”$PercentageDS%”
if( $PercentageDS -gt 15)
{ „Disk Free is normal: $DSP” | LogMe
$tests.DiskFree = „SUCCESS”, $DSP
}
elseif( $PercentageDS -lt 15)
{ „Disk Free is Low: $DSP” | LogMe
$tests.DiskFree = „WARNING”, $DSP
}
elseif( $PercentageDS -lt 5)
{ „Disk Free is normal: $DSP” | LogMe
$tests.DiskFree = „ERROR”, $DSP
}
elseif( $PercentageDS -eq 0)
{ „Disk Free test failed” | LogMe -error;
$tests.DiskFree = „ERROR”, „Err”
} }
else
{ $tests.DiskFree = „ERROR”, ($PercentageDS)
„Disk Free is Critical” | LogMe -display -error
}
$PercentageDS = 0
$allResults.$server = $tests
}
# Get farm session info
$ActiveSessions = [array]($sessions | ? {$_.State -eq „Active” -and $_.Protocol -eq „Ica”})
$DisconnectedSessions = [array]($sessions | ? {$_.State -eq „Disconnected” -and $_.Protocol -eq „Ica”})
if ($ActiveSessions) { $TotalActiveSessions = $ActiveSessions.count }
else { $TotalActiveSessions = 0 }
if ($DisconnectedSessions) { $TotalDisconnectedSessions = $DisconnectedSessions.count }
else { $TotalDisconnectedSessions = 0 }
„Total Active Sessions: $TotalActiveSessions” | LogMe -display
„Total Disconnected Sessions: $TotalDisconnectedSessions” | LogMe -display
# Write all results to an html file
writeHtmlHeader $emailSubject $resultsHTM $TotalServers
writeTableHeader $resultsHTM
$allResults | % { writeData $allResults $resultsHTM }
writeHtmlFooter $resultsHTM
#=============================================================================================
# Emailing Part
#=============================================================================================
$msg = new-object System.Net.Mail.MailMessage
$msg.From=$emailFrom
$msg.to.Add($emailTo)
$msg.Subject=$emailSubject
$msg.IsBodyHtml=$true
# Images are found by the ‚COntentID-CID’ in the body of the message. Both logos to be paced in the same directory where we save the script
$att = new-object System.Net.Mail.Attachment($ImgHdr)
$att.ContentDisposition.Inline = $True
$att.ContentDisposition.DispositionType = „Inline”
$att.ContentType.MediaType = „image/jpeg”
$att.ContentId = „header”
$att1 = new-object System.Net.Mail.Attachment($ImgFtr)
$att1.ContentDisposition.Inline = $True
$att1.ContentDisposition.DispositionType = „Inline”
$att1.ContentType.MediaType = „image/jpeg”
$att1.ContentId = „Signature”
$msg.Body=(gc $resultsHTM) | Out-String
$msg.Attachments.Add($logfile)
$msg.Attachments.Add($att)
$msg.Attachments.Add($att1)
$smtp = new-object System.Net.Mail.SmtpClient
$smtp.host=$smtpServer
$smtp.Send($msg)
Start-Sleep 5
$att.Dispose()
$att1.Dispose()
$msg.Dispose()
Write-host „Email Sent”
#======================= END OF MAIN PROGRAM ++++++++++++++++++++++++++++++++++++++++++++++
###############################################################################################
LikeLike
24 May 2018 at 12:25 PM
Thanks
LikeLike