<# .SYNOPSIS Test StoreFront or NetScaler Gateway (integrated with StoreFront) and XenApp by launching one or more Published Applications. .DESCRIPTION This script launches an a Published Applications through StoreFront or NetScaler Gateway (integrated with StoreFront). It attempts to closely resemble what an actual user would do by: -Opening Internet Explorer. -Navigating directly to the Receiver for Web site or NetScaler Gateway portal. -Entering Username and Password. -Logging in. -Clicking on the Application(s). -Logging off the StoreFront site. You can use thie Script to verify your most important Applications load. You can also use this Script to verify that each of your servers is repsonding correctly. To do this, you will need to Publish an Application from each server. For example, you could publish Task Manager (taskmgr.exe), from each server, and name it something like "Server 1 Task Manager", "Server 2 Task Manager", etc, etc. Requirements: -Citrix Receiver installed in the default location. -Must be launched from an Elevated Administrator console of PowerShell x86. The 64 Bit Powershell will not work since the script is using Citrix Reciever, which is 32 bit. -No other sessions should be connected/running before running the script. -SiteURL should be part of the Intranet Zone or Trusted Site Zone in order to be able to download AND launch the ICA file. This can be done through a GPO. -StoreFront 2.0 or higher or NetScaler Gateway, version 9.3 or higher. -Changes in web.config under C:\inetpub\wwwroot\Citrix\Web\: autoLaunchDesktop to false, pluginAssistant to false. -Currently works for desktops or already subscribed apps only. -You can auto subscribe users to apps by setting "KEYWORDS:Auto" in the published app's description or make sure the apps have been subscribed to manually. -This script does not support AppController at this time. -In order for the script to interact with the ICO, it requires the following to be set in the registry: -Windows 64 Bit: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Citrix\ICA Client\CCM -Windows 32 Bit: HKEY_LOCAL_MACHINE\SOFTWARE\Citrix\ICA Client\CCM -AllowLiveMonitoring REG_DWORD 1 -AllowSimulationAPI REG_DWORD 1 .PARAMETER SiteURL The complete URL of the StoreFront Receiver for Web site or NetScaler Gateway portal. .PARAMETER UserName The name of the user which is used to log on. Acceptable forms are down-level logon name or user principal name. .PARAMETER Password The password of the user which is used to log on. .PARAMETER smtpServer The SMTP Server to send email alerts to .PARAMETER emailFrom Email Address that the alerts will come from .PARAMETER emailTo Email Address to send the alerts to .PARAMETER SleepBeforeLogoff The time in seconds to sleep after clicking the resource and before logging off. Default is 5. .PARAMETER NumberOfRetries The number of retries when retrieving an element. Default is 30. .PARAMETER LogFilePath Directory path to where the log file will be saved. Default is SystemDrive\Temp. .PARAMETER LogFileName File name for the log file. By default, the script creates a log file named "SFLauncher.log". .PARAMETER EmailAlertsLagTime This sets the amount of time, in seconds, an alert email will be sent if the same error is encountered, default is 3600 seconds. For example, if Application 1 fails, and the script is set to run every 30 minutes, you can set this to something higher than 1800 so not to be notified every 30 minutes. .PARAMETER TwoFactorAuth The token or password used for two-factor authentication. This is used in the NetScaler Gateway portal. .PARAMETER ResourceName The display name(s) of the Published Application(s) to be launched. Just add the applications at the end of the command line. See the example for more info. .PARAMETER TimeToWaitForApp The amount of time, in seconds, to wait for the Application to launch, default is 30 seconds .PARAMETER TimeToWaitForLogoff The amount of time, in seconds, to wait before issuing the Logoff Command, default is 30 seconds .EXAMPLE SFLauncher.ps1 -SiteURL "http://storefront.domain.com" -UserName "domain1\User1" -Password "P4ssw0rd" -smtpServer "smtp.domain.com -emailFrom "XenAppMon@domain.com" -emailTo "email@domain.com" -LogFilePath C:\Logifles -LogFileName SFLauncher.log "Application 1" "Application 2" .LINK UserName format used in StoreFront. http://msdn.microsoft.com/en-us/library/windows/desktop/aa380525(v=vs.85).aspx#down_level_logon_name .LINK Change to autoLaunchDesktop. http://support.citrix.com/proddocs/topic/dws-storefront-20/dws-configure-wr-view.html .LINK Change to logoffAction. http://support.citrix.com/proddocs/topic/dws-storefront-20/dws-configure-wr-workspace.html .LINK Automating the launch of HDX sessions through StoreFront (and NetScaler Gateway integrated with StoreFront) http://blogs.citrix.com/2014/06/12/scripting-update-automating-the-launch-of-hdx-sessions-through-storefront-and-netscaler-gateway-integrated-with-storefront .NOTES Created using SFLauncher.ps1 originally written by Citrix Systems, Inc. #> Param ( [Parameter(Mandatory=$true,Position=0)] [string]$SiteURL, [Parameter(Mandatory=$true,Position=1)] [string]$UserName, [Parameter(Mandatory=$true,Position=2)] [string]$Password, [Parameter(Mandatory=$true,Position=3)] [string]$smtpServer, [Parameter(Mandatory=$true,Position=4)] [string]$emailFrom, [Parameter(Mandatory=$true,Position=5)] [string]$emailTo, [Parameter(ValueFromRemainingArguments=$true)] $ResourceName, [Parameter(Mandatory=$false)] [int]$SleepBeforeLogoff = 5, [Parameter(Mandatory=$false)] [int]$NumberOfRetries = 10, [Parameter(Mandatory=$false)] [string]$LogFilePath = "$($env:SystemDrive)\Temp\", [Parameter(Mandatory=$false)] [string]$LogFileName = "SFLauncher3.log", [Parameter(Mandatory=$false)] [string]$TwoFactorAuth, [Parameter(Mandatory=$false)] $EmailAlertsLagTime = "3600", [Parameter(Mandatory=$false)] $TimeToWaitForApp = "30", [Parameter(Mandatory=$false)] $TimeToWaitForLogoff = "30" ) #region Variables Set-Variable -Name NGLoginButtonId -Value "Log_On" -Option Constant -Scope Script Set-Variable -Name NGLoginButtonId2 -Value "Logon" -Option Constant -Scope Script Set-Variable -Name NGUserNameTextBoxName -Value "Enter user name" -Option Constant -Scope Script Set-Variable -Name NGPasswordTextBoxName -Value "passwd" -Option Constant -Scope Script Set-Variable -Name NGTwoFactorTextBoxName -Value "passwd1" -Option Constant -Scope Script Set-Variable -Name SFLoginButtonId -Value "loginBtn" -Option Constant -Scope Script Set-Variable -Name SFUsernameTextBoxId -Value "username" -Option Constant -Scope Script Set-Variable -Name SFPasswordTextBoxId -Value "password" -Option Constant -Scope Script Set-Variable -Name SFLogOffLinkId -Value "userdetails-logoff" -Option Constant -Scope Script Set-Variable -Name SFLogOffLinkIdv3 -Value "menuLogOffBtn" -Option Constant -Scope Script Set-Variable -Name NSErrorLicense -Value "Error: Login exceeds maximum allowed users." -Option Constant -Scope Script Set-Variable -Name AlertBoxId -Value "alertbox" -Option Constant -Scope Script Set-Variable -Name AlertBoxIdv3 -Value "genericMessageBoxPopup" -Option Constant -Scope Script Set-Variable -Name MsgBoxButton -Value "button defaultDialogButton" -Option Constant -Scope Script Set-Variable -Name x64DLLPath -Value "C:\Program Files (x86)\Citrix\ICA Client" -Option Constant -Scope Script Set-Variable -Name x86DLLPath -Value "C:\Program Files\Citrix\ICA Client" -Option Constant -Scope Script Set-Variable -Name SFMyFavorites -Value "myAppsBtn" -Option Constant -Scope Script # Setting up log files [string]$PreviuosLogFile = $($LogFilePath.TrimEnd('\') + "\SFLauncher_PreviuosRun.log") [string]$AlertsEmailed = $($LogFilePath.TrimEnd('\') + "\SFLauncher_AlertsEmailed.log") [string]$CurrentAlerts = $($LogFilePath.TrimEnd('\') + "\SFLauncher_AlertsCurrent.log") [string]$AlertEmail = $($LogFilePath.TrimEnd('\') + "\SFLauncher_AlertsEmailTimeStamp.log") [string]$ErrorStyle = "style=""background-color: #000000; color: #FF3300;""" [string]$Script:LogFile=$($LogFilePath.TrimEnd('\') + "\$LogFileName") # Reads the current directory path from the location of this file [string]$Script:currentDir = Split-Path $MyInvocation.MyCommand.Path #endregion Variables #region Functions function ScriptInfo { "=========================Script Parameters=========================" | LogMe -displaynormal "StoreFront / Netscaler Gateway URL: $SiteURL" | LogMe -displaynormal "User Name: $UserName" | LogMe -displaynormal "SMTP Server: $smtpServer" | LogMe -displaynormal "Email From Address: $emailFrom" | LogMe -displaynormal "Email To Address: $emailTo" | LogMe -displaynormal "Log File: $Script:LogFile" | LogMe -displaynormal "Number of Resources found in command line: " + $ResourceName.count | LogMe -displaynormal foreach($Resource in $ResourceName) {"Resource: `"$Resource`"" | LogMe -displaynormal} "Number of Seconds before sending repeat Email: $EmailAlertsLagTime" | LogMe -displaynormal "Number of Seconds to wait for the App to Launch: $TimeToWaitForApp" | LogMe -displaynormal "Number of Seconds to wait to send Logoff Command: $TimeToWaitForLogoff" | LogMe -displaynormal "Number of Seconds to wait before StoreFront Logoff: $SleepBeforeLogoff" | LogMe -displaynormal "===================================================================" | LogMe -displaynormal } function CheckforDLL { if ((test-path -Path $x64DLLPath) -eq "TRUE") { Set-Variable -Name DLLPath -Value "$x64DLLPath\WfIcaLib.dll" -Option Constant -Scope Script "Found DLL in $x64DLLPath" | logme -displaygreen } elseif ((test-path -Path $x86DLLPath) -eq "TRUE") { Set-Variable -Name DLLPath -Value "$x86DLLPath\WfIcaLib.dll" -Option Constant -Scope Script "Found DLL in $x86DLLPath\WfIcaLib.dll" | logme -displaygreen } else { "Cannot locate DLL." | logme -error throw "WfIcaLib.dll cannot be found, is the Citrix Client installed in it's default location?" } } function Create-ProgressBar($Activity,$Status){ Write-Progress -Activity $Activity -status $Status } function Complete-ProgressBar($Activity,$Status){ Write-Progress -Activity $Activity -status $Status -Completed } function Get-ICAClientVersion{ # ============================ # # Get Client Version Function # # ============================ # $ErrorActionPreference = "SilentlyContinue" $ica = New-Object -ComObject 'Citrix.ICAClient' if($ica) { return $ica.ClientVersion } else { return 0 } } function CheckforReceiverWindow { # This function will only work when running the script interactively. It will not work as a Scehduled Task or in a Minimized Remote Session. # I am not even calling it, just left it for reference. $receiverproc = get-process | where-object {$_.name -eq "receiver"} if ($receiverproc) { try { $pinvokes = @' [DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr FindWindow(IntPtr sClassName, String sAppName); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetForegroundWindow(IntPtr hWnd); '@ Add-Type -AssemblyName System.Windows.Forms Add-Type -MemberDefinition $pinvokes -Name NativeMethods -Namespace MyUtils $WindowCR = [MyUtils.NativeMethods]::FindWindow([IntPtr]::Zero, "Citrix Receiver") if ($WindowCR -ne 0) { "Found a Window Titled `"Citrix Receiver`"" | logme -error "Setting the Citrix Receiver window to the foreground" | logme -displaynormal $null = [MyUtils.NativeMethods]::SetForegroundWindow($WindowCR) "Sending the Enter command to the Citrix Receiver window" | logme -displaynormal $null = [System.Windows.Forms.SendKeys]::SendWait("{ENTER}") "A Citrix Receiver dialog window was detected while trying to launch `"$App`", which means Citrix Receiver threw an error." | logme -error "This can be caused by a number of things, such as; a network disruption, a licensing issue, a bad STA or a firewall preventing the connection." | logme -error "You may need to manually launch `"$App`" and try to reproduce." | logme -error $Script:WindowCRDetected = $true } else { $Script:WindowCRDetected = $false } } catch {} } } function CheckforServerWindow { # This function will only work when running the script interactively. It will not work as a Scehduled Task or in a Minimized Remote Session. # I am not even calling it, just left it for reference. $receiverproc = get-process | where-object {$_.name -eq "receiver"} if ($receiverproc) { try { $pinvokes = @' [DllImport("user32.dll", CharSet=CharSet.Unicode)] public static extern IntPtr FindWindow(IntPtr sClassName, String sAppName); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetForegroundWindow(IntPtr hWnd); '@ Add-Type -AssemblyName System.Windows.Forms Add-Type -MemberDefinition $pinvokes -Name NativeMethods -Namespace MyUtils $WindowWL1 = [MyUtils.NativeMethods]::FindWindow([IntPtr]::Zero, "Windows Logon - \\Remote") $WindowWL2 = [MyUtils.NativeMethods]::FindWindow([IntPtr]::Zero, "Windows sign-in - \\Remote") $WindowCCL = [MyUtils.NativeMethods]::FindWindow([IntPtr]::Zero, "Citrix Client Logon Message - \\Remote") if ($WindowWL1 -ne 0){ $Script:WindowsWLTitle = "Windows Logon - \\Remote" $null = [MyUtils.NativeMethods]::SetForegroundWindow($WindowWL1) $null = [System.Windows.Forms.SendKeys]::SendWait("{ENTER}") $Script:WindowWL1Detected = $true } else {$Script:WindowWL1Detected = $false} if ($WindowWL2 -ne 0){ $Script:WindowsWLTitle = "Windows sign-in - \\Remote" $null = [MyUtils.NativeMethods]::SetForegroundWindow($WindowWL2) $null = [System.Windows.Forms.SendKeys]::SendWait("{ENTER}") $Script:WindowWL2Detected = $true } else {$Script:WindowWL2Detected = $false} if ($WindowCCL -ne 0){ $null = [MyUtils.NativeMethods]::SetForegroundWindow($WindowCCL) $null = [System.Windows.Forms.SendKeys]::SendWait("{ENTER}") $Script:WindowCCLDetected = $true } else {$Script:WindowCCLDetected = $false} } catch {} } } function CheckforAlertBox { $alertbox = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $AlertboxId) if ($alertbox.GetType() -ne [DBNull]) { $alertboxlink=$internetExplorer.Document.getElementsByTagName("a") | where-object {$_.className -eq "button defaultDialogButton"} if ($alertboxlink -ne $null) { $alertext = $alertbox.innerText.Trim() $alertext=$alertext.Trim([char]0x0020,[char]0x004F,[char]0x004B) $alertext=$alertext.trimend() "Storefront Alert Box Detected" | LogMe -error if ($alertext -eq "Cannot start app `"$App`".") { $alertext | LogMe -error "The Server hosting `"$App`" is either not accepting Logons, is down, or there is a load issue. Check the Event Log, `"Citrix Delivery Services`", on the Storefront server for more information." | LogMe -error } else { $alertext | LogMe -error } $Script:alertboxdetected = $true "Clicking on the Alert Box button" | LogMe -displaynormal $alertboxlink.Click() "Refreshing the page to get rid of the Alert Box" | LogMe -displaynormal $script:internetExplorer.refresh() } else {$Script:alertboxdetected = $false} } } function CheckforAlertBoxv3 { $alertboxv3 = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $AlertboxIdv3) if ($alertboxv3.GetType() -ne [DBNull]) { #$alertboxlink=$internetExplorer.Document.getElementsByTagName("a") | where-object {$_.className -eq "dialog button default" } $alertboxlink=[System.__ComObject].InvokeMember(“getElementsByTagName”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $internetExplorer.Document, "a") | where-object {$_.className -eq "dialog button default" } if ($alertboxlink -ne $null) { $alertext = $alertboxv3.innerText.Trim() $alertext=$alertext.Trim([char]0x0020,[char]0x004F,[char]0x004B) $alertext=$alertext.trimend() "Storefront v3 Alert Box Detected" | LogMe -error $Script:alertboxdetected = $true if ($alertext -eq "Cannot start app `"$App`".") { $alertext | LogMe -error "The Server hosting `"$App`" is either not accepting Logons, is down, or there is a load issue. Check the Event Log, `"Citrix Delivery Services`", on the Storefront server for more information." | LogMe -error } else { $alertext | LogMe -warning } "Clicking on the Alert Box button" | LogMe -displaynormal $alertboxlink.Click() "Refreshing the page to get rid of the Alert Box" | LogMe -displaynormal $script:internetExplorer.refresh() } else {$Script:alertboxdetected = $false} } } function GetElapsedTime([datetime]$starttime) { # ====================== # # Time Elapsed Function # # ====================== # $runtime = $(get-date) - $starttime $retStr = [string]::format("{0} sec(s)", $runtime.TotalSeconds) $retStr } function CreateRegEntry ($RegPath,$RegName,$PropType,$Val){ # ===================== # # Create Registry Item # # ===================== # try {New-ItemProperty -Path $RegPath -Name $RegName -PropertyType $PropType -Value $Val -ErrorAction Stop} catch { $Script:RegError += $_.Exception.Message $_.Exception.Message | LogMe -error } } function ModifyRegEntry ($RegPath,$RegName,$Val){ # ===================== # # Modify Registry Item # # ===================== # try {Set-ItemProperty -Path $RegPath -Name $RegName -Value $Val -ErrorAction Stop} catch { $Script:RegError += $_.Exception.Message $_.Exception.Message | LogMe -error } } function CreateRegKey ($RegPath){ # ===================== # # Create Registry Item # # ===================== # try {New-Item -Path $RegPath -ErrorAction Stop} catch { $Script:RegError += $_.Exception.Message $_.Exception.Message | LogMe -error } } function CheckRegistry { # 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 $Script:Activity="Checking Registry for Required Settings" $Path = "HKLM:SOFTWARE\Citrix\ICA Client\CCM" $TestRegPath = Get-ItemProperty -Path $Path -ErrorAction SilentlyContinue if (!$TestRegPath) {CreateRegKey $Path} $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 ($Script:RegError -ne $Null) { "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 "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Citrix\ICA Client\CCM, AllowLiveMonitoring, DWORD, 1" | LogMe -error "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Citrix\ICA Client\CCM, AllowLiveMonitoring, DWORD, 1" | LogMe -error } } function Send-Email { #============================================================================================= # Email Section #============================================================================================= $Script:Activity="Sending Email, if needed" If (($Script:NewAlerts -eq $True) -or ($Script:SendEmail -eq $True)) { $script:ErrorMessage += "
Test last ran on: {0}
Computer Script was ran from: {3}
Script Path: {4}
URL Tested: {5}
" -f (Get-Date -displayhint date),$env:userdomain,$env:username,$env:COMPUTERNAME,$Script:currentDir,$SiteURL #if (Test-Path $AlertsEmailed) { clear-content $AlertsEmailed } #foreach ($line in $script:ErrorMessage) { $Line | Add-Content $AlertsEmailed } # Setting MailFlag for the validation and error handling $MailFlag = $True If(!$emailFrom) { $MailFlag = $False; Write-Warning "From Email is NULL" } If(!$emailTo) { $MailFlag = $False; Write-Warning "To Email is NULL" } If(!$smtpServer) { $MailFlag = $False; Write-Warning "SMTP Server is NULL" } # $MailFlag = $True # Send email only if From, To and SMTP adress are not null if($MailFlag -match $False) { "Email could not send as the email parameters (FROM/TO/SMTP) failed" | LogMe -error } else { # Send email only if there is either an Error or Warning Detected $msg = new-object System.Net.Mail.MailMessage $msg.From=$emailFrom $msg.to.Add($emailTo) if($emailCC) { $msg.cc.add($emailCC) } $msg.Subject="StoreFront Error Detected" $msg.IsBodyHtml=$true $msg.Body=$Script:ErrorMessage $msg.Attachments.Add($Script:LogFile) $smtp = new-object System.Net.Mail.SmtpClient $smtp.host=$smtpServer Try { $smtp.Send($msg) "Email Sent" | LogMe -displaygreen } Catch { $emaillog="Error Sending Email. See Error Messages Below:" $emaillog+="Error Message: " + $_.Exception.Message $emaillog+="Error Item: " + $_.Exception.ItemName } Start-Sleep 3 $msg.Dispose() if (Test-Path $AlertEmail) { clear-content $AlertEmail } $(Get-Date -format g) | Add-Content $AlertEmail } } elseif ($script:ErrorMessage -ne $Null -and $Script:LagTime -eq $True) { "Not sending an email since there were no NEW Errors detected" | LogMe -displaynormal } else { "Not sending an email since there were no Errors detected" | LogMe -displaynormal } } function CheckForErrors { $Script:Activity="Checking for Errors" if ($script:ErrorMessage -ne $Null) { #if (Test-Path $CurrentAlerts) { clear-content $CurrentAlerts } #foreach ($line in $script:ErrorMessage) {$Line | Add-Content $CurrentAlerts} if (Test-Path $AlertEmail) {$AlertFileTime = gc $AlertEmail -TotalCount 1 if ($AlertFileTime){ $AlertTimeSpan = New-TimeSpan $AlertFileTime $(Get-Date -format g) $AlertTimeDifference = $AlertTimeSpan.TotalSeconds if (!$AlertTimeDifference) { $Script:SendEmail = $True } if ($AlertTimeDifference -ge $EmailAlertsLagTime) { $Script:SendEmail = $True "Email Alerts lag Time is $EmailAlertsLagTime seconds" | logme -displaynormal "Alerts last sent on $AlertFileTime or $AlertTimeDifference seconds ago" | logme -displaynormal } else { $Script:SendEmail = $False $Script:LagTime = $True "Not sending an Email" "Email Alerts lag Time is $EmailAlertsLagTime seconds" | logme -displaynormal "Alerts last sent on $AlertFileTime or $AlertTimeDifference seconds ago" | logme -displaynormal } } } else { $Script:SendEmail = $True } } #else { if (Test-Path $CurrentAlerts) { clear-content $CurrentAlerts } } } function KillProcess { # 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 $ctxprocess = @() $ctxproclist = @("wfcrun32","concentr","redirector","wfica32","receiver","cdviewer","cpviewer","ctxcfrui","ssonsvr","webhelper","selfserviceplugin","cleanup","ConfigurationWizard") foreach ($proc in $ctxproclist) { Get-Process $proc -ErrorAction silentlycontinue | select id | % { $ctxprocess += $_.id } } if ($ctxprocess -ge 1) { "Citrix Receiver processes were detected. Killing the processes so the script will work, if they exist they will prevent the ICO from working." | logme -displaynormal foreach ($id in $ctxprocess) {kill -id $id -force -ErrorAction silentlycontinue -ErrorVariable +err} } if ($err.count -gt 0) { "Could not kill Citrix Reciever processes" | logme -error throw "Could not kill Citrix Reciever processes" } } function Create-PreviuosRunLog { # Creating Log file for previuos ran jobs # File is cleared when it reaches 1MB if (test-path $PreviuosLogFile) { $size = Get-ChildItem $PreviuosLogFile if ($size.length -ge 1048576) {clear-content $PreviuosLogFile} } $LogPattern = @("[SUCCESS]","[ERROR]","[WARNING]","[SCRIPT_START]","[SCRIPT_END]") if (Test-Path $Script:LogFile) {Get-Content $Script:LogFile | add-Content $PreviuosLogFile} } Function LogMe() { #===================================================================== # # Sends the results into a logfile as well as in the powershell window # #===================================================================== # Param( [parameter(Mandatory = $true, ValueFromPipeline = $true)] $logEntry, [switch]$displaygreen, [switch]$error, [switch]$warning, [switch]$displaynormal, [switch]$displayscriptstartend, [switch]$justprogressbar ) $Status = $logEntry if($error) { $script:ErrorMessage += "

$logEntry

" Write-Host "$logEntry" -Foregroundcolor Red; $logEntry = [DateTime]::Now.ToString("[MM/dd/yyy HH:mm:ss.fff]: ") + "[ERROR] $logEntry" } elseif($warning) { Write-Host "$logEntry" -Foregroundcolor Yellow; $logEntry = [DateTime]::Now.ToString("[MM/dd/yyy HH:mm:ss.fff]: ") + "[WARNING] $logEntry"} elseif ($displaynormal) { Write-Host "$logEntry" -Foregroundcolor White; $logEntry = [DateTime]::Now.ToString("[MM/dd/yyy HH:mm:ss.fff]: ") + "[INFO] $logEntry" } elseif($displaygreen) { Write-Host "$logEntry" -Foregroundcolor Green; $logEntry = [DateTime]::Now.ToString("[MM/dd/yyy HH:mm:ss.fff]: ") + "[SUCCESS] $logEntry" } elseif($displayscriptstartend) { Write-Host "$logEntry" -Foregroundcolor Magenta; $logEntry = "[SCRIPT_STARTEND] $logEntry" } elseif($justprogressbar) { $logEntry=$Null } else { Write-Host "$logEntry"; $logEntry = "$logEntry" } if ($logEntry -ne $null) {$logEntry | Out-File -FilePath $Script:LogFile -Append} Create-ProgressBar $Script:Activity $Status } function Wait-ForPageReady { "Waiting for Internet Explorer to return Page Ready" | LogMe -displaynormal $try = 1 do { $Script:Activity="Waiting for Page Ready" "Try #$try`: Waiting for Internet Explorer to return Page Ready" | LogMe -justprogressbar Start-Sleep 1 $try++ } until ($internetExplorer.ReadyState -eq 4 -or $try -gt 60) if ($internetExplorer.ReadyState -ne 4) { throw "Internet Explorer did not respond Page Ready within time allotted" } } function Open-InternetExplorer { Param ([Parameter(Mandatory=$true)] [string]$SiteURL) $Script:Activity="Opening Internet Explorer" "Creating Internet Explorer Component Object Model (COM)" | LogMe -displaynormal New-Variable -Name internetExplorer -Value (New-Object -ComObject "InternetExplorer.Application") -Scope Script "Setting Internet Explorer visible" | LogMe -displaynormal $internetExplorer.visible = $true "Navigating to '$SiteURL'" | Logme -displaynormal $internetExplorer.Navigate2($SiteURL) "Waiting until the page is ready" | Logme -displaynormal Wait-ForPageReady "Accessing Document Object Model (DOM)" | Logme -displaynormal New-Variable -Name document -Value $internetExplorer.Document -Scope Script } function Test-LoginForm { "Detecting NetScaler Gateway or StoreFront login form" | Logme -displaynormal $Script:Activity="Detecting NetScaler Gateway or StoreFront login form" Set-Variable -Name loginButton -Value $Null -Scope Script $try = 1 do { $NGloginButton1 = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $NGLoginButtonId) $NGloginButton2 = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $NGLoginButtonId2) $SFloginButton = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $SFLoginButtonId) if ($NGloginButton1 -ne $null -and $NGloginButton1.GetType() -ne [DBNull]) { "NETSCALER GATEWAY DETECTED" | Logme -displaygreen Start-Sleep -Seconds 1 Set-Variable -Name isNG -Value $true -Scope Script Set-Variable -Name loginButton -Value $NGloginButton1 -Scope Script break } elseif ($NGloginButton2 -ne $null -and $NGloginButton2.GetType() -ne [DBNull]) { "NETSCALER GATEWAY DETECTED" | Logme -displaygreen Start-Sleep -Seconds 1 Set-Variable -Name isNG -Value $true -Scope Script Set-Variable -Name loginButton -Value $NGloginButton2 -Scope Script break } elseif ($SFloginButton -ne $null -and $SFloginButton.GetType() -ne [DBNull]) { "STOREFRONT DETECTED" | Logme -displaygreen Start-Sleep -Seconds 1 Set-Variable -Name isNG -Value $false -Scope Script Set-Variable -Name loginButton -Value $SFloginButton -Scope Script break } else { "Try #$try`: Still detecting NetScaler Gateway or StoreFront login form..." | LogMe -justprogressbar Start-Sleep -Seconds 3 $try++ } } until ($try -gt $NumberOfRetries) if ($loginButton -eq $null -or $loginButton.GetType() -eq [DBNull]) { "Log on button not found" | Logme -error Set-Variable -Name loginAborted -Value $true -Scope Script break Start-Sleep -Seconds 1 } } function Submit-UserCredentials { $Script:Activity="Submitting User Credentials" if ($isNG) { "Getting UserName textbox" | Logme -displaynormal $userNameTextBox = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $NGUserNameTextBoxName) | where { $_.id -eq $NGUserNameTextBoxName } Start-Sleep -Seconds 1 "Getting Password textbox" | Logme -displaynormal $passwordTextBox = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $NGPasswordTextBoxName) | where { $_.id -eq $NGPasswordTextBoxName } Start-Sleep -Seconds 1 if ($TwoFactorAuth) { "Getting Two Factor Authentication textbox" | Logme -displaynormal $twoFactorTextBox = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $NGTwoFactorTextBoxName) | where { $_.name -eq $NGTwoFactorTextBoxName } if ($twoFactorTextBox -ne $null) { "Setting Two Factor Authentication" | Logme -displaynormal $twoFactorTextBox.value = $TwoFactorAuth Start-Sleep -Seconds 1 } else {"Two-factor authentication textbox not found" | Logme -error Start-Sleep -Seconds 1 } } } else { "Getting UserName textbox" | Logme -displaynormal $userNameTextBox = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $SFUsernameTextBoxId) Start-Sleep -Seconds 1 "Getting Password textbox" | Logme -displaynormal $passwordTextBox = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $SFPasswordTextBoxId) Start-Sleep -Seconds 1 } if ($userNameTextBox -ne $null -and $userNameTextBox.GetType() -ne [DBNull]) { "Setting UserName`: $UserName" | Logme -displaygreen $userNameTextBox.Value = $UserName Start-Sleep -Seconds 1 } else { "UserName textbox not found" | Logme -error if ($isNG) {"Make sure your Netscaler Gateway Virtual Server Portal Theme is set to either Default, X1, Greenbubble or your Custom Theme is created using one of these." | Logme -error} Set-Variable -Name loginAborted -Value $true -Scope Script break } if ($passwordTextBox -ne $null -and $passwordTextBox.GetType() -ne [DBNull]) { "Setting Password`: *********" | Logme -displaygreen $passwordTextBox.Value = $Password Start-Sleep -Seconds 1 } else { "Password textbox not found" | Logme -error if ($isNG) {"Make sure your Netscaler Gateway Virtual Server Portal Theme is set to either Default, X1, Greenbubble or your Custom Theme is created using one of these." | Logme -error} Set-Variable -Name loginAborted -Value $true -Scope Script break } if ($loginButton -ne $null -and $loginButton.GetType() -ne [DBNull]) { "Clicking Log On button" | Logme -displaynormal $loginButton.Click() Start-Sleep -Seconds 1 } else {"Login button not found" | Logme -error Set-Variable -Name loginAborted -Value $true -Scope Script break } } function Check-Resource { $Script:Activity="Getting Storefront resources page" "Getting Storefront resources page" | Logme -displaynormal $try = 1 do { $logoffLink = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $SFLogOffLinkId) $logoffLinkv3 = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $SFLogOffLinkIdv3) if ($logoffLink -ne $null -and $logoffLink.GetType() -ne [DBNull]) { "Successfully found Storefront resources page" | LogMe -displaygreen $Script:StorefrontVersion = "2" Start-Sleep -Seconds 1 break } elseif ($logoffLinkv3 -ne $null -and $logoffLinkv3.GetType() -ne [DBNull]) { "Successfully found Storefront v3 resources page" | LogMe -displaygreen $Script:StorefrontVersion = "3" Start-Sleep -Seconds 1 break } else { "Try #$try`: Still looking for Storefront resources page..." | LogMe -justprogressbar Start-Sleep -Seconds 1 $try++ } } until ($try -gt $NumberOfRetries) if (($logoffLink -eq $null -or $logoffLink.GetType() -eq [DBNull]) -and ($logoffLinkv3 -eq $null -or $logoffLinkv3.GetType() -eq [DBNull])) { $NSLicenseErr = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $NSErrorLicense) if ($NSLicenseErr -ne $null -and $NSLicenseErr.GetType() -ne [DBNull] ) { "Error: Login exceeds maximum allowed users." | Logme -error $Script:StoreFrontRP = $False } else { throw "StoreFront recources page not found, possible page loading issue or logon issue" } } } function WaitforDisconnected { "Waiting for any disconnected sessions to reconnect..." | logme -displaynormal $disconnectedtimeout = 30 $distry=0 do { "Try #$distry`: Still waiting for any disconnected sessions..." | LogMe -justprogressbar $distry++ start-sleep -seconds 1 } until ($distry -ge $disconnectedtimeout) LookforExistingSessions } function LookforExistingSessions { if ($ICO) { "ICO exists" | logme -displaynormal try{ "Removing the ICO." | logme -displaynormal $null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ICO) } catch {throw "Error trying to remove the existing ICO"} } $ICO = $null $EnumHandle = $null $NumSessions = $Null $sessionid = $Null $APPSERVER = $Null "Creating the ICA Client Object" | logme -displaynormal try {Add-Type -Path $DLLPath} catch {"Error loading WfIcaLib.dll" | logme -error} $ICO = New-Object WFICALib.ICAClientClass $Script:Activity="Looking for existing ICA Connections" "Looking for existing ICA Connections" | logme -displaynormal Start-Sleep -Seconds 1 $ICO.OutputMode = [WFICALib.OutputMode]::OutputModeNormal $EnumHandle = $ICO.EnumerateCCMSessions() $NumSessions = $ICO.GetEnumNameCount($EnumHandle ); if ($NumSessions -gt 0) { "Found $NumSessions session(s) via ICO" | Logme -displaygreen "The script will only work correctly when there are no existing connections. " | Logme -warning "If the ICO is able to interact with the current session it will be either logged off or disconnected." | Logme -warning "If the ICO is not working, existing Sessions will be disconnected when Receiver is killed at the end of the script and should work next time." | Logme -warning if($NumSessions -eq 1){ $sessionid = $ICO.GetEnumNameByIndex($EnumHandle, $index) $ICO.StartMonitoringCCMSession($sessionid,$true) $APPSERVER = $ICO.GetSessionString(0) if ([string]::IsNullOrEmpty($APPSERVER)){ "No APPPSERVER found, sending Disconnect command to SessionID $sessionid" | logme -displaynormal $ICO.Logoff() start-sleep -Seconds 10 $ICO.StopMonitoringCCMSession($sessionid); $ICO.OutputMode = [WFICALib.OutputMode]::OutputModeNormal $EnumHandle = $ICO.EnumerateCCMSessions() $NumSessions = $ICO.GetEnumNameCount($EnumHandle ); $try=0 if ($NumSessions -gt 0) { do { $EnumHandle = $ICO.EnumerateCCMSessions() $NumSessions = $ICO.GetEnumNameCount($EnumHandle ); $try++ start-sleep -Seconds 1 } until ($NumSessions -eq 0 -or $try -gt 30) if ($NumSessions -gt 0) {throw "A session still exists after attempting to disconnect. Disconnected sessions that cannot be enumerated can cause this."} } $null = $ICO.CloseEnumHandle($EnumHandle) } elseif ($APPSERVER) { "Active XenApp Server is: $APPSERVER" | Logme -displaygreen "Sending Logoff Command to SessionID $sessionid" | Logme -displaynormal $ICO.Logoff() Start-Sleep -Seconds 10 $ICO.StopMonitoringCCMSession($sessionid); $ICO.OutputMode = [WFICALib.OutputMode]::OutputModeNormal $EnumHandle = $ICO.EnumerateCCMSessions() $NumSessions = $ICO.GetEnumNameCount($EnumHandle ); if ($NumSessions -gt 0) { do { $EnumHandle = $ICO.EnumerateCCMSessions() $NumSessions = $ICO.GetEnumNameCount($EnumHandle ); $try++ start-sleep -Seconds 1 } until ($NumSessions -eq 0 -or $try -gt 30) if ($NumSessions -gt 0) {throw "A session still exists after attempting to logoff. Disconnected sessions that cannot be enumerated can cause this."} } $null = $ICO.CloseEnumHandle($EnumHandle) } } elseif ($NumSessions -gt 1) { "More than one Session was detected" | logme -displaynormal for( $index = 0; $index -lt $NumSessions ;$index++) { $sessionid = $ICO.GetEnumNameByIndex($EnumHandle, $index) $ICO.StartMonitoringCCMSession($sessionid,$true) $APPSERVER = $ICO.GetSessionString(0) if ([string]::IsNullOrEmpty($APPSERVER)){ $sessionid = $ICO.GetEnumNameByIndex($EnumHandle, $index) $ICO.StartMonitoringCCMSession($sessionid,$true) $ICO.Disconnect() start-sleep -Seconds 10 $ICO.StopMonitoringCCMSession($sessionid); } elseif ($APPSERVER){ "Active XenApp Server is: $APPSERVER" | Logme -displaynormal "Sending Logoff Command to SessionID $sessionid" | Logme -displaynormal $ICO.StartMonitoringCCMSession($sessionid,$true) $ICO.Logoff() $ICO.StopMonitoringCCMSession($sessionid); } } if ($NumSessions -gt 0) { do { $EnumHandle = $ICO.EnumerateCCMSessions() $NumSessions = $ICO.GetEnumNameCount($EnumHandle ); start-sleep -Seconds 1 $try++ } until ($NumSessions -eq 0 -or $try -gt 30) if ($NumSessions -gt 0) {throw "A session still exists after attempting to logoff. Disconnected sessions that cannot be enumerated can cause this."} } $null = $ICO.CloseEnumHandle($EnumHandle) } } else { "No exisitng connections found." | logme -displaynormal} } function LookforResource { $restry = 1 do { "Locating resource `"$App`"" | Logme -displaynormal $Script:resource = @([System.__ComObject].InvokeMember(“getElementsByTagName”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, "img")) | where { $_.alt -eq $App } if ($resource -ne $null) { "Successfully Located resource `"$App`"" | LogMe -displaygreen Start-Sleep -Seconds 1 break } else { "Try #$restry`: Still Locating resource `"$App`"..." | LogMe -justprogressbar Start-Sleep -Seconds 3 $restry++ } } until ($restry -gt $NumberOfRetries) } function Launch-App { foreach ($App in $ResourceName){ "========================================================" | logme -displaygreen "Testing `"$App`"" | logme -displaygreen "========================================================" | logme -displaygreen LookforResource if ($Script:resource -eq $null) { "The ResourceName specified, `"$App`", was not found" | LogMe -error "Either `"$App`" was mistyped on the command line or it is not visible on the Storefront main page" | LogMe -error break } LookforExistingSessions if ($ICO) { "ICO exists" | logme -displaynormal try{ "Removing the ICO." | logme -displaynormal $null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ICO) } catch {throw "Error removing the ICO"} } $ICO = $null $EnumHandle = $null $NumSessions = $Null $sessionid = $Null $APPSERVER = $Null "Creating the ICA Client Object" | logme -displaynormal try {Add-Type -Path $DLLPath} catch {throw "Error loading WfIcaLib.dll"} $ICO = New-Object WFICALib.ICAClientClass $Script:Activity="Launching `"$App`"" "Clicking resource `"$App`"" | Logme -displaynormal $Script:resource.Click() $LaunchTime = Get-Date "Connect Started at $LaunchTime" | Logme -displaygreen $index = $null "Looking for a Session, time out is $TimeToWaitForApp seconds." | Logme -displaynormal if ($Script:StorefrontVersion -eq "2") {"Checking for any Storefront v2.x Alert boxes." | Logme -displaynormal} if ($Script:StorefrontVersion -eq "3") {"Checking for any Storefront v3 Alert boxes." | Logme -displaynormal} Do { if ($Script:StorefrontVersion -eq "2") {CheckforAlertBox} if ($Script:StorefrontVersion -eq "3") {CheckforAlertBoxv3} $ICO.OutputMode = [WFICALib.OutputMode]::OutputModeNormal $EnumHandle = $ICO.EnumerateCCMSessions() $NumSessions = $ICO.GetEnumNameCount($EnumHandle ); "Try #$try`: Waiting to find a Session" | logme -justprogressbar Start-Sleep -Seconds 2 $try++ } until ($try -gt $TimeToWaitForApp -or $NumSessions -ge 1 -or $Script:alertboxdetected -eq $true) if ($Script:alertboxdetected -ne $true) { if([string]::IsNullOrEmpty($APPSERVER) -and $NumSessions -eq 0 ) { "There is no Server connection and there are no sessions." | logme -error "Unable to confirm that session launched for `"$App`"." | logme -error "If this occurs again, check the Storefront logs and/or manually try launching `"$App`"." | LogMe -error break } if([string]::IsNullOrEmpty($APPSERVER) -and $NumSessions -ge 1 ) { "Session found" | logme -displaygreen "Waiting for an Active Connection, time out is $TimeToWaitForApp seconds." | Logme -displaynormal $EnumHandle = $ICO.EnumerateCCMSessions() $sessionid = $ICO.GetEnumNameByIndex($EnumHandle, $index) $ICO.StartMonitoringCCMSession($sessionid,$true) $conntry = 0 Do { "Try #$apptry`: Waiting for a connection" | LogMe -justprogressbar if ($ICO.IsConnected() -eq $true) {"Session is connected" | logme -displaygreen} $conntry++ Start-Sleep -Seconds 2 } Until ($ICO.IsConnected() -eq $true -or $conntry -gt $TimeToWaitForApp -or $NumSessions -eq 0) if ($ICO.IsConnected() -eq $true) { "Session is Connected, looking for any active Windows." | logme -displaynormal $wintry = 0 $cdviewer = Get-Process "cdviewer" -ErrorAction silentlycontinue if ($cdviewer){"Found an active Citrix Desktop Viewer Process" | LogMe -displaygreen } else { do { "Try #$wintry`: Waiting to find an active Window" | LogMe -justprogressbar $TopLevelWindows = $ICO.Session.TopLevelWindows if ($TopLevelWindows.Count -gt 0){ [string]$TLW = $TopLevelWindows.Count "Number of Top Level Windows Detected: $TLW" | logme -displaygreen foreach ($window in $TopLevelWindows) { "Window ID: " + $window.WindowID | logme -displaynormal "Window Caption: " + $window.Caption | logme -displaynormal if ($window.Caption -eq "Windows sign-in" -or $window.Caption -eq "Windows Logon") { "Sending the ENTER key via the ICO Session" | logme -displaynormal $ICO.Session.Keyboard.SendKeyDown(0x0D) } } } $wintry++ start-sleep -Seconds 10 } until ($TopLevelWindows.Count -gt 0 -or $wintry -gt 30) } } if ($TopLevelWindows.Count -gt 0 -or $cdviewer -eq $true){ "Active Window found, looking for an active XenApp Server for `"$App`"" | logme -displaynormal $apptry = 0 Do { "Try #$apptry`: Waiting to find an active XenApp Server for `"$App`"" | LogMe -justprogressbar $EnumHandle = $ICO.EnumerateCCMSessions() $sessionid = $ICO.GetEnumNameByIndex($EnumHandle, $index) $ICO.StartMonitoringCCMSession($sessionid,$true) $APPSERVER = $ICO.GetSessionString(0) if ($APPSERVER) {"Found an active XenApp Server for `"$App`"" | LogMe -displaygreen} $apptry++ Start-Sleep -Seconds 1 } Until ($APPSERVER -or $apptry -gt $TimeToWaitForApp -or $NumSessions -eq 0) } if ([string]::IsNullOrEmpty($APPSERVER) -and $NumSessions -eq 0 -or $cdviewer -eq $false -and $apptry -ge $TimeToWaitForApp ) { "Unable to confirm that session launched for `"$App`"." | logme -error "If this occurs again, check the Storefront (or Server logs) and/or manually try launching `"$App`"." | LogMe -error } elseif ([string]::IsNullOrEmpty($APPSERVER) -and $NumSessions -ge 1 -and $cdviewer -eq $false -and $TopLevelWindows.Count -gt 0) { "Unable to confirm that a session launched for `"$App`", but an active session was found." | logme -error "`"$App`" may have taken longer than $TimeToWaitForApp seconds to load or there was an error message on the Server" | logme -error "If this occurs again you may need to try manually launching `"$App`"." | LogMe -error $sesswaittry = 0 Do { "Waiting for session to end" | LogMe -justprogressbar start-sleep 1 $EnumHandle = $ICO.EnumerateCCMSessions() $NumSessions = $ICO.GetEnumNameCount($EnumHandle ); $sesswaittry++ } until ($NumSessions -eq 0 -or $sesswaittry -eq 300) if ($NumSessions -gt 0) { "Possbile script error with the script and/or Citrix client" | logme -error throw "Possbile script error with the script and/or Citrix client" } } elseif ([string]::IsNullOrEmpty($APPSERVER) -and $NumSessions -ge 1 -and $TopLevelWindows.Count -lt 1 -and $cdviewer -eq $false) { "Unable to confirm that a session launched for `"$App`", but an active session was found." | logme -error "Cannot find a Top Level Window, this could just be an ICO issue on the client and may not be a Server issue." | logme -error "If this occurs again you may need to try manually launching `"$App`"." | LogMe -error $sesswaittry = 0 $ICO.Disconnect() Do { "Waiting for session to Logoff" | LogMe -justprogressbar start-sleep 1 $EnumHandle = $ICO.EnumerateCCMSessions() $NumSessions = $ICO.GetEnumNameCount($EnumHandle ); $sesswaittry++ } until ($NumSessions -eq 0 -or $sesswaittry -eq 300) if ($NumSessions -gt 0) { "Possbile error with the script and/or Citrix client" | logme -error throw "Possbile error with the script and/or Citrix client" } } elseif ($APPSERVER -or $cdviewer) { $logonelapsed = GetElapsedTime $LaunchTime "Time taken to launch App is: $logonelapsed" | Logme -displaygreen "Session ID: " + $sessionid | Logme -displaygreen if (!$cdviewer) {"Active XenApp Server is: " + $APPSERVER | Logme -displaygreen} "Sleeping for $TimeToWaitForLogoff seconds before sending ICA Logoff Command" | Logme -displaynormal Start-Sleep -Seconds $TimeToWaitForLogoff "Sending Logoff Command" | Logme -displaynormal $ICO.Logoff() "Waiting for session to Logoff, timeout is 120 seconds" | Logme -displaynormal $logofftry = 0 Do { "Waiting for session to Logoff" | LogMe -justprogressbar start-sleep 1 $EnumHandle = $ICO.EnumerateCCMSessions() $NumSessions = $ICO.GetEnumNameCount($EnumHandle ); $logofftry++ } until ($ICO.IsConnected() -ne $true -or $logofftry -eq 120) if ($ICO.IsConnected() -eq $true) { "Session is still connected, sending the Disconnect command" | logme -warning $ICO.Disconnect() } } } } $ICO.StopMonitoringCCMSession($sessionid) } } function Logoff-Storefront { $Script:Activity="Logoff StoreFront" #if ($Script:StoreFrontRP -ne $False){ "Sleeping $SleepBeforeLogoff seconds before logging off Storefront" | Logme -displaynormal Start-Sleep -Seconds $SleepBeforeLogoff "Getting log off link" | LogMe -displaynormal Start-Sleep -Seconds 3 $try = 1 do { $logoffLink = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $SFLogOffLinkId) $logoffLinkv3 = [System.__ComObject].InvokeMember(“getElementById”,[System.Reflection.BindingFlags]::InvokeMethod, $null, $document, $SFLogOffLinkIdv3) if ($logoffLink -ne $null -and $logoffLink.GetType() -ne [DBNull]) { "Found Storefront v2.x log off link" | LogMe -displaygreen $logofflinkv = "2" Start-Sleep -Seconds 1 break } elseif ($logoffLinkv3 -ne $null -and $logoffLinkv3.GetType() -ne [DBNull]) { "Found Storefront v3 log off link" | LogMe -displaygreen $logofflinkv = "3" Start-Sleep -Seconds 1 break } else { "Try #$try`: Still looking for log off link..." | LogMe -justprogressbar Start-Sleep -Seconds 3 $try++ } } until ($try -gt $NumberOfRetries) if (($logoffLink -eq $null -or $logoffLink.GetType() -eq [DBNull]) -and ($logoffLinkv3 -eq $null -or $logoffLinkv3.GetType() -eq [DBNull])) { "Log off link not found" | Logme -error Start-Sleep -Seconds 1 } else { "Clicking log off link" | Logme -displaynormal Start-Sleep -Seconds 1 if ($logofflinkv -eq "2") {$logoffLink.Click()} if ($logofflinkv -eq "3") {$logoffLinkv3.Click()} } #} } function CheckforElevation { $wid=[System.Security.Principal.WindowsIdentity]::GetCurrent() $prp=new-object System.Security.Principal.WindowsPrincipal($wid) $adm=[System.Security.Principal.WindowsBuiltInRole]::Administrator $IsAdmin=$prp.IsInRole($adm) if ($IsAdmin) { "Script is running with elevated privileges" | logme -displaygreen } else { throw "Script is NOT running with elevated privileges" } } #endregion Functions #region Try, Catch and Finally try { cls $Script:Activity = "Starting Script" "" "" "" "" "" "" "" "" "********** LAUNCHER SCRIPT START **********" | Logme -displayscriptstartend "" $startTime = Get-Date "Removing old log file if it exists" | logme -displaynormal if (test-path $($LogFilePath.TrimEnd('\') + "\$LogFileName")) {rm -path $($LogFilePath.TrimEnd('\') + "\$LogFileName") -force } ScriptInfo "Checking to make sure the script is elevated" | logme -displaynormal CheckforElevation "Checking for any Citrix Receiver Processes" | logme -displaynormal KillProcess "Checking for Citrix Cient DLL" | logMe -displaynormal CheckforDLL "Checking Citrix Client Verion" | Logme -displaynormal $ClientVersion = Get-ICAClientVersion if ($ClientVersion -lt "14.1") { throw "Citrix Client $ClientVersion is untested, please use Citrix Receiver 14.1 or higher" } else { "Citrix Client Version: " + $ClientVersion | Logme -displaygreen } CheckRegistry if ($Script:RegError -eq $Null) { Open-InternetExplorer -SiteURL $SiteURL Test-LoginForm Submit-UserCredentials Wait-ForPageReady Check-Resource if ($Script:StoreFrontRP -ne $False){ WaitforDisconnected Launch-App } } } catch { "Exception caught by script" | Logme -error $Error1 = $_.ToString() $Error1 | Logme -error $Error2 = $_.InvocationInfo.PositionMessage $Error2 | Logme -error throw $_ } finally { if ($internetExplorer -is [System.__ComObject]) { if ($internetExplorer | Get-Member 'Quit') { if ($loginAborted -ne $true) {Logoff-Storefront} "Quitting Internet Explorer" | Logme -displaynormal Start-Sleep -Seconds 3 $internetExplorer.Quit() } "Releasing Internet Explorer Component Object Model (COM)" | Logme -displaynormal Start-Sleep -Seconds 3 [System.Runtime.Interopservices.Marshal]::ReleaseComObject($internetExplorer) | Out-Null Remove-Variable -Name internetExplorer } KillProcess CheckForErrors Complete-ProgressBar $Activity="Script End" $Status="Exiting" #Create-PreviuosRunLog "" "********** LAUNCHER SCRIPT END **********" | Logme -displayscriptstartend "" Send-Email } #endregion Try, Catch and Finally