XDHealthCheck.psm1
#region Private Functions #region DirectorCodes.ps1 ########### Private Function ############### # source: DirectorCodes.ps1 # Module: XDHealthCheck ############################################ $script:RegistrationState = @{ 0 = 'Unknown' 1 = 'Registered' 2 = 'Unregistered' } $script:ConnectionState = @{ 0 = 'Unknown' 1 = 'Connected' 2 = 'Disconnected' 3 = 'Terminated' 4 = 'PreparingSession' 5 = 'Active' 6 = 'Reconnecting' 7 = 'NonBrokeredSession' 8 = 'Other' 9 = 'Pending' } $script:ConnectionFailureType = @{ 0 = 'None' 1 = 'ClientConnectionFailure' 2 = 'MachineFailure' 3 = 'NoCapacityAvailable' 4 = 'NoLicensesAvailable' 5 = 'Configuration' } $script:SessionFailureCode = @{ 0 = 'Unknown' 1 = 'None' 2 = 'SessionPreparation' 3 = 'RegistrationTimeout' 4 = 'ConnectionTimeout' 5 = 'Licensing' 6 = 'Ticketing' 7 = 'Other' 8 = 'GeneralFail' 9 = 'MaintenanceMode' 10 = 'ApplicationDisabled' 11 = 'LicenseFeatureRefused' 12 = 'NoDesktopAvailable' 13 = 'SessionLimitReached' 14 = 'DisallowedProtocol' 15 = 'ResourceUnavailable' 16 = 'ActiveSessionReconnectDisabled' 17 = 'NoSessionToReconnect' 18 = 'SpinUpFailed' 19 = 'Refused' 20 = 'ConfigurationSetFailure' 21 = 'MaxTotalInstancesExceeded' 22 = 'MaxPerUserInstancesExceeded' 23 = 'CommunicationError' 24 = 'MaxPerMachineInstancesExceeded' 25 = 'MaxPerEntitlementInstancesExceeded' 100 = 'NoMachineAvailable' 101 = 'MachineNotFunctional' } $global:MachineDeregistration = @{ 0 = 'AgentShutdown' 1 = 'AgentSuspended' 100 = 'IncompatibleVersion' 101 = 'AgentAddressResolutionFailed' 102 = 'AgentNotContactable' 103 = 'AgentWrongActiveDirectoryOU' 104 = 'EmptyRegistrationRequest' 105 = 'MissingRegistrationCapabilities' 106 = 'MissingAgentVersion' 107 = 'InconsistentRegistrationCapabilities' 108 = 'NotLicensedForFeature' 109 = 'UnsupportedCredentialSecurityversion' 110 = 'InvalidRegistrationRequest' 111 = 'SingleMultiSessionMismatch' 112 = 'FunctionalLevelTooLowForCatalog' 113 = 'FunctionalLevelTooLowForDesktopGroup' 200 = 'PowerOff' 203 = 'AgentRejectedSettingsUpdate' 206 = 'SessionPrepareFailure' 207 = 'ContactLost' 301 = 'BrokerRegistrationLimitReached' 208 = 'SettingsCreationFailure' 204 = 'SendSettingsFailure' 2 = 'AgentRequested' 201 = 'DesktopRestart' 202 = 'DesktopRemoved' 205 = 'SessionAuditFailure' 300 = 'UnknownError' 302 = 'RegistrationStateMismatch' } $script:MachineFailureType = @{ 4 = 'MaxCapacity' 2 = 'StuckOnBoot' 1 = 'FailedToStart' } $script:ConnectionState = @{ 0 = 'Unknown' 1 = 'Connected' 2 = 'Disconnected' 3 = 'Terminated' 4 = 'PreparingSession' 5 = 'Active' 6 = 'Reconnecting' 7 = 'NonBrokeredSession' 8 = 'Other' 9 = 'Pending' } #endregion #region Reports-Colors.ps1 ########### Private Function ############### # source: Reports-Colors.ps1 # Module: XDHealthCheck ############################################ if (Test-Path HKCU:\Software\XDHealth) { $global:XDHealth_Color1 = Get-ItemPropertyValue -Path HKCU:\Software\XDHealth -Name Color1 $global:XDHealth_Color2 = Get-ItemPropertyValue -Path HKCU:\Software\XDHealth -Name Color2 $global:XDHealth_LogoURL = Get-ItemPropertyValue -Path HKCU:\Software\XDHealth -Name LogoURL } else { New-Item -Path HKCU:\Software\XDHealth New-ItemProperty -Path HKCU:\Software\XDHealth -Name Color1 -Value '#2b1200' New-ItemProperty -Path HKCU:\Software\XDHealth -Name Color2 -Value '#f37000' New-ItemProperty -Path HKCU:\Software\XDHealth -Name LogoURL -Value 'https://gist.githubusercontent.com/smitpi/ecdaae80dd79ad585e571b1ba16ce272/raw/6d0645968c7ba4553e7ab762c55270ebcc054f04/default-monochrome.png' $global:XDHealth_Color1 = Get-ItemPropertyValue -Path HKCU:\Software\XDHealth -Name Color1 $global:XDHealth_Color2 = Get-ItemPropertyValue -Path HKCU:\Software\XDHealth -Name Color2 $global:XDHealth_LogoURL = Get-ItemPropertyValue -Path HKCU:\Software\XDHealth -Name LogoURL } #region Html Settings $global:TableSettings = @{ Style = 'cell-border' TextWhenNoData = 'No Data to display here' Buttons = 'searchBuilder', 'pdfHtml5', 'excelHtml5' FixedHeader = $true HideFooter = $true SearchHighlight = $true PagingStyle = 'full' PagingLength = 10 } $global:SectionSettings = @{ BackgroundColor = 'grey' CanCollapse = $true HeaderBackGroundColor = $XDHealth_Color1 HeaderTextAlignment = 'center' HeaderTextColor = $XDHealth_Color2 HeaderTextSize = '15' BorderRadius = '20px' } $global:TableSectionSettings = @{ BackgroundColor = 'white' CanCollapse = $true HeaderBackGroundColor = $XDHealth_Color2 HeaderTextAlignment = 'center' HeaderTextColor = $XDHealth_Color1 HeaderTextSize = '15' } #endregion #endregion #endregion #region Public Functions #region Get-CitrixConfigurationChange.ps1 ############################################ # source: Get-CitrixConfigurationChange.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Show the changes that was made to the farm .DESCRIPTION Show the changes that was made to the farm .PARAMETER AdminServer FQDN of the Citrix Data Collector .PARAMETER Indays Use this time frame for the report. .PARAMETER Export Export the result to a report file. (Excel, html or Screen) .PARAMETER ReportPath Where to save the report. .EXAMPLE Get-CitrixConfigurationChange -AdminServer $CTXDDC -Indays 7 #> Function Get-CitrixConfigurationChange { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Get-CitrixConfigurationChange')] PARAM( [Parameter(Mandatory = $true)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [string]$AdminServer, [Parameter(Mandatory = $true)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [int32]$Indays, [ValidateSet('Excel', 'HTML')] [string]$Export = 'Host', [ValidateScript( { if (Test-Path $_) { $true } else { New-Item -Path $_ -ItemType Directory -Force | Out-Null; $true } })] [System.IO.DirectoryInfo]$ReportPath = 'C:\Temp' ) if (-not(Get-PSSnapin -Registered | Where-Object {$_.name -like 'Citrix*'})) {Add-PSSnapin citrix* -ErrorAction SilentlyContinue} Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Starting] Config Changes Details" $startdate = (Get-Date).AddDays(-$Indays) $exportpath = (Get-Item (Get-Item Env:\TEMP).value).FullName + '\ctxreportlog.csv' if (Test-Path $exportpath) { Remove-Item $exportpath -Force -ErrorAction SilentlyContinue } Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Progress] Exporting Changes" Export-LogReportCsv -AdminAddress $AdminServer -OutputFile $exportpath -StartDateRange $startdate -EndDateRange (Get-Date) Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Progress] Importing Changes" $LogExportAll = Import-Csv -Path $exportpath -Delimiter ',' $LogExport = $LogExportAll | Where-Object { $_.'High Level Operation Text' -notlike '' } | Select-Object -Property High* $LogSum = $LogExportAll | Group-Object -Property 'High Level Operation Text' -NoElement Remove-Item $exportpath -Force -ErrorAction SilentlyContinue $CTXObject = New-Object PSObject -Property @{ DateCollected = (Get-Date -Format dd-MM-yyyy_HH:mm).ToString() AllDetails = $LogExportAll Filtered = $LogExport Summary = $LogSum } Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Ending] Config Changes Details" if ($Export -eq 'Excel') { $ExcelOptions = @{ Path = $(Join-Path -Path $ReportPath -ChildPath "\CitrixConfigurationChange-$(Get-Date -Format yyyy.MM.dd-HH.mm).xlsx") AutoSize = $True AutoFilter = $True TitleBold = $True TitleSize = '28' TitleFillPattern = 'LightTrellis' TableStyle = 'Light20' FreezeTopRow = $True FreezePane = '3' } $CTXObject.Filtered | Export-Excel -Title CitrixConfigurationChange -WorksheetName CitrixConfigurationChange @ExcelOptions } if ($Export -eq 'HTML') { $CTXObject.Filtered | Out-HtmlView -DisablePaging -Title 'Citrix Configuration Change' -HideFooter -SearchHighlight -FixedHeader -FilePath $(Join-Path -Path $ReportPath -ChildPath "\CitrixConfigurationChange-$(Get-Date -Format yyyy.MM.dd-HH.mm).html") } if ($Export -eq 'Host') { $CTXObject } } Export-ModuleMember -Function Get-CitrixConfigurationChange #endregion #region Get-CitrixConnectionFailures.ps1 ############################################ # source: Get-CitrixConnectionFailures.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Creates a report from monitoring data about machine and connection failures .DESCRIPTION Creates a report from monitoring data about machine and connection failures .PARAMETER MonitorData Use Get-CitrixMonitoringData to create OData, and use that variable in this parameter. .PARAMETER AdminServer FQDN of the Citrix Data Collector .PARAMETER SessionCount Will collect data for the last x amount of sessions. .PARAMETER Export Export the result to a report file. (Excel or html) .PARAMETER ReportPath Where to save the report. .EXAMPLE $monitor = Get-CitrixMonitoringData -AdminServer $AdminServer -SessionCount 50 Get-CitrixConnectionFailures -MonitorData $monitor #> Function Get-CitrixConnectionFailures { [Cmdletbinding(DefaultParameterSetName = 'Fetch odata', HelpURI = 'https://smitpi.github.io/XDHealthCheck/Get-CitrixConnectionFailures')] [OutputType([System.Object[]])] PARAM( [Parameter(Mandatory = $false, ParameterSetName = 'Got odata')] [PSTypeName('CTXMonitorData')]$MonitorData, [Parameter(Mandatory = $true, ParameterSetName = 'Fetch odata')] [string]$AdminServer, [Parameter(Mandatory = $true, ParameterSetName = 'Fetch odata')] [int32]$SessionCount, [Parameter(Mandatory = $false, ParameterSetName = 'Got odata')] [Parameter(Mandatory = $false, ParameterSetName = 'Fetch odata')] [ValidateSet('Excel', 'HTML')] [string]$Export = 'Host', [ValidateScript( { if (Test-Path $_) { $true } else { New-Item -Path $_ -ItemType Directory -Force | Out-Null; $true } })] [Parameter(Mandatory = $false, ParameterSetName = 'Got odata')] [Parameter(Mandatory = $false, ParameterSetName = 'Fetch odata')] [System.IO.DirectoryInfo]$ReportPath = 'C:\Temp' ) if (-not($MonitorData)) { try { $mon = Get-CitrixMonitoringData -AdminServer $AdminServer -SessionCount $SessionCount } catch {$mon = Get-CitrixMonitoringData -AdminServer $AdminServer -SessionCount $SessionCount -AllowUnencryptedAuthentication} } else {$Mon = $MonitorData} $ConnectionFailure = $mon.Connections.Where({$_.ConnectionFailureLog -notlike $null}) if ($ConnectionFailure.count -eq 0) {Write-Warning 'No connection Failures during this time frame'} else { [System.Collections.ArrayList]$ConnectionFails = @() foreach ($CFail in $ConnectionFailure.ConnectionFailureLog) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Connection Failures $($ConnectionFailure.ConnectionFailureLog.IndexOf($CFail)) of $($ConnectionFailure.ConnectionFailureLog.count)" try { $user = Invoke-RestMethod -Method Get -Uri "$($CFail.User.__deferred.uri)?`$format=json" -UseDefaultCredentials $device = Invoke-RestMethod -Method Get -Uri "$($CFail.Machine.__deferred.uri)?`$format=json" -UseDefaultCredentials } catch { $user = Invoke-RestMethod -Method Get -Uri "$($CFail.User.__deferred.uri)?`$format=json" -UseDefaultCredentials -AllowUnencryptedAuthentication $device = Invoke-RestMethod -Method Get -Uri "$($CFail.Machine.__deferred.uri)?`$format=json" -UseDefaultCredentials -AllowUnencryptedAuthentication } [void]$ConnectionFails.Add([pscustomobject]@{ UserName = $user.UserName Upn = $user.Upn Name = $device.Name IP = $device.IPAddress FailureDate = [datetime]$CFail.FailureDate FailureDetails = $SessionFailureCode[$CFail.ConnectionFailureEnumValue] }) } } if ($Export -eq 'Excel') { $ExcelOptions = @{ Path = $(Join-Path -Path $ReportPath -ChildPath "\CitrixConnectionFailures-$(Get-Date -Format yyyy.MM.dd-HH.mm).xlsx") AutoSize = $True AutoFilter = $True TitleBold = $True TitleSize = '28' TitleFillPattern = 'LightTrellis' TableStyle = 'Light20' FreezeTopRow = $True FreezePane = '3' } if ($ConnectionFails) {$ConnectionFails | Export-Excel -Title ConnectionFailures -WorksheetName ConnectionFailures @ExcelOptions} } if ($Export -eq 'HTML') { New-HTML -TitleText "CitrixConnectionFailures-$(Get-Date -Format yyyy.MM.dd-HH.mm)" -FilePath $(Join-Path -Path $ReportPath -ChildPath "\CitrixConnectionFailures-$(Get-Date -Format yyyy.MM.dd-HH.mm).html") { if ($ConnectionFails) { New-HTMLTab -Name 'Connection Failures' -TextTransform uppercase -IconSolid cloud-sun-rain -TextSize 16 -TextColor $color1 -IconSize 16 -IconColor $color2 -HtmlData {New-HTMLPanel -Content { New-HTMLTable -DataTable $($ConnectionFails) @TableSettings}}} } } if ($Export -eq 'Host') { [pscustomobject]@{ ConnectionFails = $ConnectionFails } } } #end Function Export-ModuleMember -Function Get-CitrixConnectionFailures #endregion #region Get-CitrixEnvTestResults.ps1 ############################################ # source: Get-CitrixEnvTestResults.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Perform and report on tests on catalogs, delivery groups, hypervisor and Infrastructure .DESCRIPTION Perform and report on tests on catalogs, delivery groups, hypervisor and Infrastructure .PARAMETER AdminServer FQDN of the Citrix Data Collector .PARAMETER Catalogs Report on Catalogs .PARAMETER DesktopGroups Report on Desktop Groups .PARAMETER Hypervisor Report on hypervisor .PARAMETER Infrastructure Report Infrastructure .PARAMETER Export Export the result to a report file. (Excel or html) .PARAMETER ReportPath Where to save the report. .EXAMPLE Get-CitrixEnvTestResults -AdminServer vulcan.internal.lab -Catalogs -DesktopGroups -Hypervisor -Infrastructure -Export HTML -ReportPath C:\temp -Verbose #> Function Get-CitrixEnvTestResults { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Get-CitrixEnvTestResults')] [OutputType([System.Object[]])] PARAM( [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [string]$AdminServer, [switch]$Catalogs = $false, [switch]$DesktopGroups = $false, [switch]$Hypervisor = $false, [switch]$Infrastructure = $false, [ValidateSet('Excel', 'HTML')] [string]$Export = 'Host', [ValidateScript( { if (Test-Path $_) { $true } else { New-Item -Path $_ -ItemType Directory -Force | Out-Null; $true } })] [System.IO.DirectoryInfo]$ReportPath = 'C:\Temp' ) if ($Catalogs) { try { [System.Collections.ArrayList]$catalogResults = @() foreach ($catalog in Get-BrokerCatalog -AdminAddress $AdminServer) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Catalog: $($catalog.Name)" $testResult = New-EnvTestDiscoveryTargetDefinition -AdminAddress $AdminServer -TargetIdType 'Catalog' -TestSuiteId 'Catalog' -TargetId $catalog.UUID | Start-EnvTestTask -AdminAddress $AdminServer -ExcludeNotRunTests $testResult.TestResults | ForEach-Object { [void]$catalogResults.Add([pscustomobject]@{ Name = $catalog.Name TestComponentStatus = $_.TestComponentStatus TestId = $_.TestId TestServiceTarget = $_.TestServiceTarget TestEndTime = $_.TestEndTime }) } } } catch {Write-Warning "Error: `nException:$($_.Exception.Message)"} } if ($DesktopGroups) { try { [System.Collections.ArrayList]$DesktopGroupResults = @() foreach ($DesktopGroup in Get-BrokerDesktopGroup -AdminAddress $AdminServer) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Catalog: $($DesktopGroup.Name)" $testResult = New-EnvTestDiscoveryTargetDefinition -AdminAddress $AdminServer -TargetIdType 'DesktopGroup' -TestSuiteId 'DesktopGroup' -TargetId $DesktopGroup.UUID | Start-EnvTestTask -AdminAddress $AdminServer -ExcludeNotRunTests $testResult.TestResults | ForEach-Object { [void]$DesktopGroupResults.Add([pscustomobject]@{ Name = $DesktopGroup.Name TestComponentStatus = $_.TestComponentStatus TestId = $_.TestId TestServiceTarget = $_.TestServiceTarget TestEndTime = $_.TestEndTime }) } } } catch {Write-Warning "Error: `nException:$($_.Exception.Message)"} } if ($Hypervisor) { try { [System.Collections.ArrayList]$HypervisorConnectionResults = @() foreach ($Hypervisor in Get-BrokerHypervisorConnection -AdminAddress $AdminServer) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Catalog: $($Hypervisor.Name)" $testResult = New-EnvTestDiscoveryTargetDefinition -AdminAddress $AdminServer -TargetIdType 'HypervisorConnection' -TestSuiteId 'HypervisorConnection' -TargetId $Hypervisor.Uid | Start-EnvTestTask -AdminAddress $AdminServer -ExcludeNotRunTests $testResult.TestResults | ForEach-Object { [void]$HypervisorConnectionResults.Add([pscustomobject]@{ Name = $Hypervisor.Name TestComponentStatus = $_.TestComponentStatus TestId = $_.TestId TestServiceTarget = $_.TestServiceTarget TestEndTime = $_.TestEndTime }) } } } catch {Write-Warning "Error: `nException:$($_.Exception.Message)"} } if ($Infrastructure) { try { [System.Collections.ArrayList]$InfrastructureResults = @() Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Catalog: Infrastructure" $Infra = New-EnvTestDiscoveryTargetDefinition -TestSuiteId Infrastructure | Start-EnvTestTask -AdminAddress $AdminServer -ExcludeNotRunTests $Infra.TestResults | ForEach-Object { [void]$InfrastructureResults.Add([pscustomobject]@{ TestComponentStatus = $_.TestComponentStatus TestId = $_.TestId TestServiceTarget = $_.TestServiceTarget TestEndTime = $_.TestEndTime }) } } catch {Write-Warning "Error: `nException:$($_.Exception.Message)"} } if ($Export -eq 'Excel') { $ExcelOptions = @{ Path = $(Join-Path -Path $ReportPath -ChildPath "\CitrixEnvTestResults-$(Get-Date -Format yyyy.MM.dd-HH.mm).xlsx") AutoSize = $true AutoFilter = $true TitleBold = $true TitleSize = '28' TitleFillPattern = 'LightTrellis' TableStyle = 'Light20' FreezeTopRow = $true FreezePane = '3' } if ($catalogResults) { $catalogResults | Export-Excel -Title 'Catalog Results' -WorksheetName 'Catalog' @ExcelOptions} if ($DesktopGroupResults) { $DesktopGroupResults | Export-Excel -Title 'DesktopGroup Results' -WorksheetName DesktopGroup @ExcelOptions } if ($HypervisorConnectionResults) { $HypervisorConnectionResults | Export-Excel -Title 'Hypervisor Connection Results' -WorksheetName Hypervisor @ExcelOptions } if ($InfrastructureResults) { $InfrastructureResults | Export-Excel -Title 'Infrastructure Results' -WorksheetName Infrastructure @ExcelOptions } } if ($Export -eq 'HTML') { New-HTML -TitleText "CitrixFarmDetail-$(Get-Date -Format yyyy.MM.dd-HH.mm)" -FilePath $(Join-Path -Path $ReportPath -ChildPath "\CitrixEnvTestResults-$(Get-Date -Format yyyy.MM.dd-HH.mm).html") { if ($catalogResults) { New-HTMLTab -Name 'Catalog Results' -TextTransform uppercase -IconSolid cloud-sun-rain -TextSize 16 -TextColor $color1 -IconSize 16 -IconColor $color2 -HtmlData {New-HTMLPanel -Content { New-HTMLTable -DataTable $($catalogResults) @TableSettings}}} if ($DesktopGroupResults) { New-HTMLTab -Name 'DesktopGroup Results' -TextTransform uppercase -IconSolid cloud-sun-rain -TextSize 16 -TextColor $color1 -IconSize 16 -IconColor $color2 -HtmlData {New-HTMLPanel -Content { New-HTMLTable -DataTable $($DesktopGroupResults) @TableSettings}}} if ($HypervisorConnectionResults) { New-HTMLTab -Name 'Hypervisor Connection Results' -TextTransform uppercase -IconSolid cloud-sun-rain -TextSize 16 -TextColor $color1 -IconSize 16 -IconColor $color2 -HtmlData {New-HTMLPanel -Content { New-HTMLTable -DataTable $($HypervisorConnectionResults) @TableSettings}}} if ($InfrastructureResults) { New-HTMLTab -Name 'Infrastructure Results' -TextTransform uppercase -IconSolid cloud-sun-rain -TextSize 16 -TextColor $color1 -IconSize 16 -IconColor $color2 -HtmlData {New-HTMLPanel -Content { New-HTMLTable -DataTable $($InfrastructureResults) @TableSettings}}} } -Online -Encoding UTF8 -ShowHTML } if ($Export -eq 'Host') { [pscustomobject]@{ catalogResults = $catalogResults DesktopGroupResults = $DesktopGroupResults HypervisorConnectionResults = $HypervisorConnectionResults InfrastructureResults = $InfrastructureResults } } } #end Function Export-ModuleMember -Function Get-CitrixEnvTestResults #endregion #region Get-CitrixFarmDetail.ps1 ############################################ # source: Get-CitrixFarmDetail.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Get needed Farm details. .DESCRIPTION Get needed Farm details. .PARAMETER AdminServer FQDN of the Citrix Data Collector .EXAMPLE Get-CitrixFarmDetail -AdminServer $CTXDDC #> Function Get-CitrixFarmDetail { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Get-CitrixFarmDetail')] PARAM( [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [string]$AdminServer ) if (-not(Get-PSSnapin -Registered | Where-Object {$_.name -like 'Citrix*'})) {Add-PSSnapin citrix* -ErrorAction SilentlyContinue} #region Site details Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Site Details" $site = Get-BrokerSite -AdminAddress $AdminServer $SiteDetails = New-Object PSObject -Property @{ Summary = $site | Select-Object Name, ConfigLastChangeTime, LicenseEdition, LicenseModel, LicenseServerName AllDetails = $site } #endregion #region Controllers Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Controllers Details" [System.Collections.ArrayList]$Controllers = @() Get-BrokerController -AdminAddress $AdminServer | ForEach-Object { [void]$Controllers.Add([pscustomobject]@{ AllDetails = $_ Summary = New-Object PSObject -Property @{ Name = $_.dnsname 'Desktops Registered' = $_.DesktopsRegistered 'Last Activity Time' = $_.LastActivityTime 'Last Start Time' = $_.LastStartTime State = $_.State ControllerVersion = $_.ControllerVersion } }) } #endregion #region Machines try { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Machines Details" $NonRemotepc = Get-BrokerMachine -MaxRecordCount 1000000 -AdminAddress $AdminServer $UnRegServer = $NonRemotepc | Where-Object { $_.RegistrationState -like 'unreg*' -and $_.DeliveryType -notlike 'DesktopsOnly' } | Select-Object DNSName, CatalogName, DesktopGroupName, FaultState $UnRegDesktop = $NonRemotepc | Where-Object { $_.RegistrationState -like 'unreg*' -and $_.DeliveryType -like 'DesktopsOnly' } | Select-Object DNSName, CatalogName, DesktopGroupName, AssociatedUserNames, FaultState $Machines = New-Object PSObject -Property @{ AllMachines = $NonRemotepc UnRegisteredServers = $UnRegServer UnRegisteredDesktops = $UnRegDesktop } | Select-Object AllMachines, UnRegisteredServers, UnRegisteredDesktops } catch {Write-Warning "`n`tMessage:$($_.Exception.Message)`n`tItem:$($_.Exception.ItemName)"} #endregion #region sessions try { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Sessions Details" $sessions = Get-BrokerSession -MaxRecordCount 1000000 -AdminAddress $AdminServer } catch {Write-Warning "`n`tMessage:$($_.Exception.Message)`n`tItem:$($_.Exception.ItemName)"} #endregion #region del groups try { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] DeliveryGroups Details" $DeliveryGroups = Get-BrokerDesktopGroup -AdminAddress $AdminServer | Select-Object Name, DeliveryType, DesktopKind, IsRemotePC, Enabled, TotalDesktops, DesktopsAvailable, DesktopsInUse, DesktopsUnregistered, InMaintenanceMode, Sessions, SessionSupport, TotalApplicationGroups, TotalApplications } catch {Write-Warning "`n`tMessage:$($_.Exception.Message)`n`tItem:$($_.Exception.ItemName)"} #endregion #region dbconnection try { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] DBConnection Details" $dbconnection = (Test-BrokerDBConnection -DBConnection(Get-BrokerDBConnection -AdminAddress $AdminServer)) if ([bool]($dbconnection.ExtraInfo.'Database.Status') -eq $False) { [string]$dbstatus = 'Unavalable' } else { [string]$dbstatus = $dbconnection.ExtraInfo.'Database.Status' } $CCTXObject = New-Object PSObject -Property @{ 'Service Status' = $dbconnection.ServiceStatus.ToString() 'DB Connection Status' = $dbstatus 'Is Mirroring Enabled' = $dbconnection.ExtraInfo.'Database.IsMirroringEnabled'.ToString() 'DB Last Backup Date' = $dbconnection.ExtraInfo.'Database.LastBackupDate'.ToString() } | Select-Object 'Service Status', 'DB Connection Status', 'Is Mirroring Enabled', 'DB Last Backup Date' $DBConnection = $CCTXObject.psobject.Properties | Select-Object -Property Name, Value } catch {Write-Warning "`n`tMessage:$($_.Exception.Message)`n`tItem:$($_.Exception.ItemName)"} #endregion #region reboots try { [System.Collections.ArrayList]$RebootSchedule = @() Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Reboot Schedule Details" Get-BrokerRebootScheduleV2 -AdminAddress $AdminServer | ForEach-Object { $sched = $_ Get-BrokerMachine -DesktopGroupName $sched.DesktopGroupName | ForEach-Object { [void]$RebootSchedule.Add([pscustomobject]@{ ComputerName = $_.DNSName IP = $_.IPAddress DelGroup = $_.DesktopGroupName Day = $sched.Day Frequency = $sched.Frequency Name = $sched.Name RebootDuration = $sched.RebootDuration StartTime = $sched.StartTime }) } } } catch {Write-Warning "`n`tMessage:$($_.Exception.Message)`n`tItem:$($_.Exception.ItemName)"} #endregion #region counts try { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Session Counts Details" $SessionCounts = New-Object PSObject -Property @{ 'Active Sessions' = ($Sessions | Where-Object -Property Sessionstate -EQ 'Active').count 'Disconnected Sessions' = ($Sessions | Where-Object -Property Sessionstate -EQ 'Disconnected').count 'Unregistered Servers' = ($Machines.UnRegisteredServers | Measure-Object).count 'Unregistered Desktops' = ($Machines.UnRegisteredDesktops | Measure-Object).count } | Select-Object 'Active Sessions', 'Disconnected Sessions', 'Connection Failures', 'Unregistered Servers', 'Unregistered Desktops' } catch {Write-Warning "`n`tMessage:$($_.Exception.Message)`n`tItem:$($_.Exception.ItemName)"} #endregion [PSCustomObject]@{ DateCollected = (Get-Date -Format dd-MM-yyyy_HH:mm).ToString() SiteDetails = $SiteDetails Controllers = $Controllers Machines = $Machines Sessions = $Sessions DeliveryGroups = $DeliveryGroups DBConnection = $DBConnection SessionCounts = $SessionCounts RebootSchedule = $RebootSchedule } } #end Function Export-ModuleMember -Function Get-CitrixFarmDetail #endregion #region Get-CitrixLicenseInformation.ps1 ############################################ # source: Get-CitrixLicenseInformation.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Show Citrix License details .DESCRIPTION Show Citrix License details .PARAMETER AdminServer FQDN of the Citrix Data Collector .PARAMETER Export Export the result to a report file. (Excel, html or Screen) .PARAMETER ReportPath Where to save the report. .EXAMPLE Get-CitrixLicenseInformation -AdminServer $CTXDDC #> Function Get-CitrixLicenseInformation { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Get-CitrixLicenseInformation')] [OutputType([System.Object[]])] PARAM( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [string]$AdminServer, [ValidateSet('Excel', 'HTML')] [string]$Export = 'Host', [ValidateScript( { if (Test-Path $_) { $true } else { New-Item -Path $_ -ItemType Directory -Force | Out-Null; $true } })] [System.IO.DirectoryInfo]$ReportPath = 'C:\Temp' ) if (-not(Get-PSSnapin -Registered | Where-Object {$_.name -like 'Citrix*'})) {Add-PSSnapin citrix* -ErrorAction SilentlyContinue} $licenseServer = (Get-BrokerSite -AdminAddress $AdminServer).LicenseServerName $cert = Get-LicCertificate -AdminAddress "https://$($licenseServer):8083" $ctxlic = Get-LicInventory -AdminAddress "https://$($licenseServer):8083" -CertHash $cert.CertHash | Where-Object { $_.LicensesInUse -ne 0 } [System.Collections.ArrayList]$LicDetails = @() foreach ($lic in $ctxlic) { [void]$LicDetails.Add([pscustomobject]@{ LicenseProductName = $lic.LocalizedLicenseProductName LicenseModel = $lic.LocalizedLicenseModel LicensesInstalled = $lic.LicensesAvailable LicensesInUse = $lic.LicensesInUse LicensesAvailable = ([int]$lic.LicensesAvailable - [int]$lic.LicensesInUse) }) } if ($Export -eq 'Excel') { $ExcelOptions = @{ Path = $(Join-Path -Path $ReportPath -ChildPath "\CitrixLicenseInformation-$(Get-Date -Format yyyy.MM.dd-HH.mm).xlsx") AutoSize = $True AutoFilter = $True TitleBold = $True TitleSize = '28' TitleFillPattern = 'LightTrellis' TableStyle = 'Light20' FreezeTopRow = $True FreezePane = '3' } $LicDetails | Export-Excel -Title CitrixLicenseInformation -WorksheetName CitrixLicenseInformation @ExcelOptions } if ($Export -eq 'HTML') { $LicDetails | Out-HtmlView -DisablePaging -Title 'Lic Details' -HideFooter -SearchHighlight -FixedHeader -FilePath $(Join-Path -Path $ReportPath -ChildPath "\CitrixLicenseInformation-$(Get-Date -Format yyyy.MM.dd-HH.mm).html") } if ($Export -eq 'Host') { $LicDetails } } #end Function Export-ModuleMember -Function Get-CitrixLicenseInformation #endregion #region Get-CitrixMonitoringData.ps1 ############################################ # source: Get-CitrixMonitoringData.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Connects and collects data from the monitoring OData feed. .DESCRIPTION Connects and collects data from the monitoring OData feed. .PARAMETER AdminServer FQDN of the Citrix Data Collector .PARAMETER SessionCount Will collect data for the last x amount of sessions. .PARAMETER AllowUnencryptedAuthentication To use a Unencrypted Authentication .EXAMPLE Get-CitrixMonitoringData -AdminServer $AdminServer -SessionCount 50 #> Function Get-CitrixMonitoringData { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Get-CitrixMonitoringData')] [OutputType([System.Object[]])] PARAM( [Parameter(Mandatory = $true)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [string]$AdminServer, [Parameter(Mandatory = $true)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [int32]$SessionCount, [switch]$AllowUnencryptedAuthentication ) Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Starting] Monitoring data connect" $headers = @{'Accept' = 'application/json;odata=verbose'} $urisettings = @{ UseDefaultCredentials = $true Headers = $headers Method = 'Get' } if ($AllowUnencryptedAuthentication) {$urisettings.Add('AllowUnencryptedAuthentication', $true)} try { [pscustomobject]@{ PSTypeName = 'CTXMonitorData' Sessions = (Invoke-RestMethod -Uri "http://$($AdminServer)/Citrix/Monitor/OData/v3/Data/Sessions?`$top=$($SessionCount)&`$expand=User,SessionMetrics,Machine,Failure,CurrentConnection&`$orderby=CreatedDate desc" @urisettings ).d Connections = (Invoke-RestMethod -Uri "http://$($AdminServer)/Citrix/Monitor/OData/v3/Data/Connections?`$top=$($SessionCount)&`$orderby=CreatedDate desc&`$expand=ConnectionFailureLog,Session" @urisettings ).d } } catch {Write-Warning "Error: `n`tMessage:$($_.Exception.Message)"} } #end Function Export-ModuleMember -Function Get-CitrixMonitoringData #endregion #region Get-CitrixObjects.ps1 ############################################ # source: Get-CitrixObjects.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Get details of citrix objects .DESCRIPTION Get details of citrix objects. (Catalog, Delivery group and published apps) .PARAMETER AdminServer FQDN of the Citrix Data Collector .EXAMPLE Get-CitrixObjects -AdminServer $CTXDDC -RemoteCredentials $CTXAdmin -RunAsPSRemote #> Function Get-CitrixObjects { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Get-CitrixObjects')] PARAM( [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [string]$AdminServer) Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Begining] All Config" if (-not(Get-PSSnapin -Registered | Where-Object {$_.name -like 'Citrix*'})) {Add-PSSnapin citrix* -ErrorAction SilentlyContinue} Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Begining] All Machine Catalogs" $CTXMachineCatalog = @() $MachineCatalogs = Get-BrokerCatalog -AdminAddress $AdminServer foreach ($MachineCatalog in $MachineCatalogs) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Machine Catalog: $($MachineCatalog.name.ToString())" $MasterImage = Get-ProvScheme -AdminAddress $AdminServer | Where-Object -Property IdentityPoolName -Like $MachineCatalog.Name if ($MasterImage.MasterImageVM -notlike '') { $MasterImagesplit = ($MasterImage.MasterImageVM).Split('\') $masterSnapshotcount = ($MasterImagesplit | Where-Object { $_ -like '*.snapshot' }).count $mastervm = ($MasterImagesplit | Where-Object { $_ -like '*.vm' }).Replace('.vm', '') if ($masterSnapshotcount -gt 1) { $masterSnapshot = ($MasterImagesplit | Where-Object { $_ -like '*.snapshot' })[-1].Replace('.snapshot', '') } else { $masterSnapshot = ($MasterImagesplit | Where-Object { $_ -like '*.snapshot' }).Replace('.snapshot', '') } } else { $mastervm = '' $masterSnapshot = '' $masterSnapshotcount = 0 } $CatObject = New-Object PSObject -Property @{ MachineCatalogName = $MachineCatalog.name AllocationType = $MachineCatalog.AllocationType Description = $MachineCatalog.Description IsRemotePC = $MachineCatalog.IsRemotePC MachinesArePhysical = $MachineCatalog.MachinesArePhysical MinimumFunctionalLevel = $MachineCatalog.MinimumFunctionalLevel PersistUserChanges = $MachineCatalog.PersistUserChanges ProvisioningType = $MachineCatalog.ProvisioningType SessionSupport = $MachineCatalog.SessionSupport Uid = $MachineCatalog.Uid UnassignedCount = $MachineCatalog.UnassignedCount UsedCount = $MachineCatalog.UsedCount CleanOnBoot = $MasterImage.CleanOnBoot MasterImageVM = $mastervm MasterImageSnapshotName = $masterSnapshot MasterImageSnapshotCount = $masterSnapshotcount MasterImageVMDate = $MasterImage.MasterImageVMDate UseFullDiskCloneProvisioning = $MasterImage.UseFullDiskCloneProvisioning UseWriteBackCache = $MasterImage.UseWriteBackCache } | Select-Object MachineCatalogName, AllocationType, Description, IsRemotePC, MachinesArePhysical, MinimumFunctionalLevel, PersistUserChanges, ProvisioningType, SessionSupport, Uid, UnassignedCount, UsedCount, CleanOnBoot, MasterImageVM, MasterImageSnapshotName, MasterImageSnapshotCount, MasterImageVMDate, UseFullDiskCloneProvisioning, UseWriteBackCache $CTXMachineCatalog += $CatObject } Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Begining] All Delivery Groups" $BrokerDesktopGroup = Get-BrokerDesktopGroup -AdminAddress $AdminServer $CTXDeliveryGroup = @() foreach ($DesktopGroup in $BrokerDesktopGroup) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Delivery Group: $($DesktopGroup.name.ToString())" $BrokerAccess = @() $BrokerGroups = @() $BrokerAccess = Get-BrokerAccessPolicyRule -DesktopGroupUid $DesktopGroup.Uid -AdminAddress $AdminServer -AllowedConnections ViaAG | ForEach-Object { $_.IncludedUsers | Where-Object { $_.upn -notlike '' } } | Select-Object UPN $BrokerGroups = Get-BrokerAccessPolicyRule -DesktopGroupUid $DesktopGroup.Uid -AdminAddress $AdminServer -AllowedConnections ViaAG | ForEach-Object { $_.IncludedUsers | Where-Object { $_.upn -Like '' } } | Select-Object Name $CusObject = New-Object PSObject -Property @{ DesktopGroupName = $DesktopGroup.name Uid = $DesktopGroup.uid DeliveryType = $DesktopGroup.DeliveryType DesktopKind = $DesktopGroup.DesktopKind Description = $DesktopGroup.Description DesktopsDisconnected = $DesktopGroup.DesktopsDisconnected DesktopsFaulted = $DesktopGroup.DesktopsFaulted DesktopsInUse = $DesktopGroup.DesktopsInUse DesktopsUnregistered = $DesktopGroup.DesktopsUnregistered Enabled = $DesktopGroup.Enabled IconUid = $DesktopGroup.IconUid InMaintenanceMode = $DesktopGroup.InMaintenanceMode SessionSupport = $DesktopGroup.SessionSupport TotalApplicationGroups = $DesktopGroup.TotalApplicationGroups TotalApplications = $DesktopGroup.TotalApplications TotalDesktops = $DesktopGroup.TotalDesktops Tags = @(($DesktopGroup.Tags) | Out-String).Trim() UserAccess = @(($BrokerAccess.UPN) | Out-String).Trim() GroupAccess = @(($BrokerGroups.Name) | Out-String).Trim() } | Select-Object DesktopGroupName, Uid, DeliveryType, DesktopKind, Description, DesktopsDisconnected, DesktopsFaulted, DesktopsInUse, DesktopsUnregistered, Enabled, IconUid, InMaintenanceMode, SessionSupport, TotalApplicationGroups, TotalApplications, TotalDesktops, Tags, UserAccess, GroupAccess $CTXDeliveryGroup += $CusObject } Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Begining] All Application config" $HostedApps = @() foreach ($DeskG in ($CTXDeliveryGroup | Where-Object { $_.DeliveryType -like 'DesktopsAndApps' })) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Delivery Group: $($DeskG.DesktopGroupName.ToString())" $PublishedApps = Get-BrokerApplication -AssociatedDesktopGroupUid $DeskG.Uid -AdminAddress $AdminServer # $PublishedApp = (Get-BrokerApplication -AdminAddress $AdminServer)[27] foreach ($PublishedApp in $PublishedApps) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Published Application: $($DeskG.DesktopGroupName.ToString()) - $($PublishedApp.PublishedName.ToString())" [System.Collections.ArrayList]$PublishedAppGroup = @() [System.Collections.ArrayList]$PublishedAppUser = @($PublishedApp.AssociatedUserNames | Where-Object { $_ -notlike $null }) $index = 0 foreach ($upn in $PublishedApp.AssociatedUserNames) { if ($null -like $upn) { $PublishedAppGroup += @($PublishedApp.AssociatedUserNames)[$index] } $index ++ } $CusObject = New-Object PSObject -Property @{ DesktopGroupName = $DeskG.DesktopGroupName DesktopGroupUid = $DeskG.Uid DesktopGroupUsersAccess = $DeskG.UserAccess DesktopGroupGroupAccess = $DeskG.GroupAccess ApplicationName = $PublishedApp.ApplicationName ApplicationType = $PublishedApp.ApplicationType AdminFolderName = $PublishedApp.AdminFolderName ClientFolder = $PublishedApp.ClientFolder Description = $PublishedApp.Description Enabled = $PublishedApp.Enabled CommandLineExecutable = $PublishedApp.CommandLineExecutable CommandLineArguments = $PublishedApp.CommandLineArguments WorkingDirectory = $PublishedApp.WorkingDirectory Tags = @(($PublishedApp.Tags) | Out-String).Trim() PublishedName = $PublishedApp.PublishedName PublishedAppName = $PublishedApp.Name PublishedAppGroupAccess = @(($PublishedAppGroup) | Out-String).Trim() PublishedAppUserAccess = @(($PublishedAppUser) | Out-String).Trim() } | Select-Object DesktopGroupName, DesktopGroupUid, DesktopGroupUsersAccess, DesktopGroupGroupAccess, ApplicationName, ApplicationType, AdminFolderName, ClientFolder, Description, Enabled, CommandLineExecutable, CommandLineArgument, WorkingDirectory, Tags, PublishedName, PublishedAppName, PublishedAppGroupAccess, PublishedAppUserAccess $HostedApps += $CusObject } } Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Begining] All Server Details" $VDAServers = @() Get-BrokerMachine -AdminAddress $AdminServer -MaxRecordCount 100000 | Where-Object { $_.OSType -like 'Windows 20*' } | ForEach-Object { $VDASCusObject = New-Object PSObject -Property @{ DNSName = $_.DNSName CatalogName = $_.CatalogName DesktopGroupName = $_.DesktopGroupName IPAddress = $_.IPAddress AgentVersion = $_.AgentVersion OSType = $_.OSType RegistrationState = $_.RegistrationState InMaintenanceMode = $_.InMaintenanceMode } | Select-Object DNSName, CatalogName, DesktopGroupName, IPAddress, AgentVersion, OSType, RegistrationState, InMaintenanceMode $VDAServers += $VDASCusObject } Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Begining] All Workstation Details" $VDAWorkstations = @() Get-BrokerMachine -AdminAddress $AdminServer -MaxRecordCount 100000 | Where-Object { $_.OSType -notlike 'Windows 20*' } | ForEach-Object { $VDAWCusObject = New-Object PSObject -Property @{ DNSName = $_.DNSName CatalogName = $_.CatalogName DesktopGroupName = $_.DesktopGroupName IPAddress = $_.IPAddress AgentVersion = $_.AgentVersion AssociatedUserNames = @(($_.AssociatedUserNames) | Out-String).Trim() OSType = $_.OSType RegistrationState = $_.RegistrationState InMaintenanceMode = $_.InMaintenanceMode } | Select-Object DNSName, CatalogName, DesktopGroupName, IPAddress, AgentVersion, AssociatedUserNames, OSType, RegistrationState, InMaintenanceMode $VDAWorkstations += $VDAWCusObject } Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Ending] Published Applications" $CusObject = New-Object PSObject -Property @{ DateCollected = (Get-Date -Format dd-MM-yyyy_HH:mm).ToString() MachineCatalog = $CTXMachineCatalog DeliveryGroups = $CTXDeliveryGroup PublishedApps = $HostedApps VDAServers = $VDAServers VDAWorkstations = $VDAWorkstations } $CusObject } #end Function Export-ModuleMember -Function Get-CitrixObjects #endregion #region Get-CitrixResourceUtilizationSummary.ps1 ############################################ # source: Get-CitrixResourceUtilizationSummary.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Resource Utilization Summary for machines .DESCRIPTION Resource Utilization Summary for machines .PARAMETER AdminServer FQDN of the Citrix Data Collector .PARAMETER hours Limit the report to this time frame .PARAMETER Export Export the result to a report file. (Excel or html) .PARAMETER ReportPath Where to save the report. .EXAMPLE Get-CitrixResourceUtilizationSummary -AdminServer $CTXDDC -hours 24 -Export Excel -ReportPath C:\temp #> Function Get-CitrixResourceUtilizationSummary { [Cmdletbinding(DefaultParameterSetName = 'Set1', HelpURI = 'https://smitpi.github.io/XDHealthCheck/Get-CitrixResourceUtilizationSummary')] [OutputType([System.Object[]])] PARAM( [Parameter(Mandatory = $true, ParameterSetName = 'Fetch odata')] [string]$AdminServer, [Parameter(Mandatory = $true, ParameterSetName = 'Fetch odata')] [int32]$hours, [Parameter(Mandatory = $false, ParameterSetName = 'Got odata')] [Parameter(Mandatory = $false, ParameterSetName = 'Fetch odata')] [ValidateSet('Excel', 'HTML')] [string]$Export = 'Host', [ValidateScript( { if (Test-Path $_) { $true } else { New-Item -Path $_ -ItemType Directory -Force | Out-Null; $true } })] [Parameter(Mandatory = $false, ParameterSetName = 'Got odata')] [Parameter(Mandatory = $false, ParameterSetName = 'Fetch odata')] [System.IO.DirectoryInfo]$ReportPath = 'C:\Temp' ) $now = Get-Date -Format yyyy-MM-ddTHH:mm:ss.ffZ $past = ((Get-Date).AddHours(-$hours)).ToString('yyyy-MM-ddTHH:mm:ss.ffZ') $headers = @{ 'Accept' = 'application/json; odata=verbose'} $urisettings = @{ UseDefaultCredentials = $true headers = $headers Method = 'Get' } $ResourceUtilizationSummary = (Invoke-RestMethod -Uri "http://$($AdminServer)/Citrix/Monitor/OData/v3/Data/ResourceUtilizationSummary?`$filter = CreatedDate ge datetime'$($past)' and CreatedDate le datetime'$($now)'" @urisettings).d [System.Collections.ArrayList]$ResourceUtilization = @() $grouped = $ResourceUtilizationSummary | Group-Object MachineId foreach ($resource in $grouped) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] ResourceUtilization $($grouped.IndexOf($resource)) of $($grouped.count)" $machine = (Invoke-RestMethod -Uri $resource.Group[0].Machine.__deferred.uri @urisettings).d [void]$ResourceUtilization.add([PSCustomObject]@{ Name = $machine.DnsName ObjectCount = ($resource.group | Measure-Object -Property AvgPercentCpu -Average).count AvgPercentCpu = [Decimal]::Round((($resource.group | Measure-Object -Property AvgPercentCpu -Average).Average)) AvgUsedMemory = [Decimal]::Round((($resource.group | Measure-Object -Property AvgUsedMemory -Average).Average) / 1gb, 2) AvgTotalMemory = [Decimal]::Round((($resource.group | Measure-Object -Property AvgTotalMemory -Average).Average) / 1gb, 2) TotalSessionCount = [Decimal]::Round((($resource.group | Measure-Object -Property TotalSessionCount -Average).Average)) }) } if ($Export -eq 'Excel') { $ExcelOptions = @{ Path = $(Join-Path -Path $ReportPath -ChildPath "\CitrixResourceUtilizationSummary-$(Get-Date -Format yyyy.MM.dd-HH.mm).xlsx") AutoSize = $True AutoFilter = $True TitleBold = $True TitleSize = '28' TitleFillPattern = 'LightTrellis' TableStyle = 'Light20' FreezeTopRow = $True FreezePane = '3' } if ($ResourceUtilization) { $ResourceUtilization | Export-Excel -Title MachineFailures -WorksheetName MachineFailures @ExcelOptions } } if ($Export -eq 'HTML') { $ResourceUtilization | Out-HtmlView -DisablePaging -Title 'CitrixResourceUtilizationSummary' -HideFooter -SearchHighlight -FixedHeader -FilePath $(Join-Path -Path $ReportPath -ChildPath "\CitrixResourceUtilizationSummary-$(Get-Date -Format yyyy.MM.dd-HH.mm).html") } if ($Export -eq 'Host') { $ResourceUtilization } } #end Function Export-ModuleMember -Function Get-CitrixResourceUtilizationSummary #endregion #region Get-CitrixServerEventLog.ps1 ############################################ # source: Get-CitrixServerEventLog.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Get windows event log details .DESCRIPTION Get windows event log details .PARAMETER Serverlist List of servers to query. .PARAMETER Days Limit the report to this time frame. .PARAMETER Export Export the result to a report file. (Excel, html or Screen) .PARAMETER ReportPath Where to save the report. .EXAMPLE Get-CitrixServerEventLog -Serverlist $CTXCore -Days 1 #> Function Get-CitrixServerEventLog { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Get-CitrixServerEventLog')] PARAM( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [string[]]$Serverlist, [Parameter(Mandatory = $true, Position = 1)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [int32]$Days, [ValidateSet('Excel', 'HTML')] [string]$Export = 'Host', [ValidateScript( { if (Test-Path $_) { $true } else { New-Item -Path $_ -ItemType Directory -Force | Out-Null; $true } })] [System.IO.DirectoryInfo]$ReportPath = 'C:\Temp' ) [System.Collections.ArrayList]$ServerEvents = @() foreach ($server in $Serverlist) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Starting] Eventlog Details" $eventtime = (Get-Date).AddDays(-$days) $ctxevent = Get-WinEvent -ComputerName $server -FilterHashtable @{LogName = 'Application', 'System'; Level = 2, 3; StartTime = $eventtime } -ErrorAction SilentlyContinue | Select-Object MachineName, TimeCreated, LogName, ProviderName, Id, LevelDisplayName, Message $servererrors = $ctxevent | Where-Object -Property LevelDisplayName -EQ 'Error' $serverWarning = $ctxevent | Where-Object -Property LevelDisplayName -EQ 'Warning' $TopProfider = $ctxevent | Where-Object { $_.LevelDisplayName -EQ 'Warning' -or $_.LevelDisplayName -eq 'Error' } | Group-Object -Property ProviderName | Sort-Object -Property count -Descending | Select-Object Name, Count [void]$ServerEvents.Add([pscustomobject]@{ ServerName = ([System.Net.Dns]::GetHostByName(($server))).hostname Errors = $servererrors.Count Warning = $serverWarning.Count TopProfider = $TopProfider All = $ctxevent }) Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Ending] Eventlog Details" } if ($Export -eq 'Excel') { $ExcelOptions = @{ Path = $(Join-Path -Path $ReportPath -ChildPath "\CitrixServerEventLog-$(Get-Date -Format yyyy.MM.dd-HH.mm).xlsx") AutoSize = $True AutoFilter = $True TitleBold = $True TitleSize = '28' TitleFillPattern = 'LightTrellis' TableStyle = 'Light20' FreezeTopRow = $True FreezePane = '3' } $ServerEvents.TopProfider | Export-Excel -Title 'EventLog Top Profider' -WorksheetName TopProfider @ExcelOptions $ServerEvents.All | Export-Excel -Title 'Citrix Server Event Log' -WorksheetName All @ExcelOptions } if ($Export -eq 'HTML') { New-HTML -TitleText "CitrixServerEventLog-$(Get-Date -Format yyyy.MM.dd-HH.mm)" -FilePath $HTMLPath { $ServerEvents | ForEach-Object { New-HTMLTab -Name "$($_.ServerName)" -TextTransform uppercase -IconSolid cloud-sun-rain -TextSize 16 -TextColor $color1 -IconSize 16 -IconColor $color2 -HtmlData { New-HTMLPanel -Content { New-HTMLTable -DataTable ($($_.TopProfider) | Sort-Object -Property TimeCreated -Descending) @TableSettings} New-HTMLPanel -Content { New-HTMLTable -DataTable ($($_.All) | Sort-Object -Property TimeCreated -Descending) @TableSettings { New-TableCondition -Name LevelDisplayName -ComparisonType string -Operator eq -Value 'Error' -Color GhostWhite -Row -BackgroundColor FaluRed New-TableCondition -Name LevelDisplayName -ComparisonType string -Operator eq -Value 'warning' -Color GhostWhite -Row -BackgroundColor InternationalOrange } }} } } -Online -Encoding UTF8 -ShowHTML } if ($Export -eq 'Host') { $ServerEvents } } #end Function Export-ModuleMember -Function Get-CitrixServerEventLog #endregion #region Get-CitrixServerPerformance.ps1 ############################################ # source: Get-CitrixServerPerformance.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Collects perform data for the core servers. .DESCRIPTION Collects perform data for the core servers. .PARAMETER Export Export the result to a report file. (Excel, html or Screen) .PARAMETER ReportPath Where to save the report. .PARAMETER ComputerName List of Computers to query. .EXAMPLE Get-CitrixServerPerformance -ComputerName $CTXCore #> Function Get-CitrixServerPerformance { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Get-CitrixServerPerformance')] [OutputType([System.Object[]])] PARAM( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [string[]]$ComputerName, [ValidateSet('Excel', 'HTML')] [string]$Export = 'Host', [ValidateScript( { if (Test-Path $_) { $true } else { New-Item -Path $_ -ItemType Directory -Force | Out-Null; $true } })] [System.IO.DirectoryInfo]$ReportPath = 'C:\Temp' ) [System.Collections.ArrayList]$ServerPerfMon = @() foreach ($server in $ComputerName) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Starting] Performance Details for $($server.ToString())" $CtrList = @( '\Processor(_Total)\% Processor Time', '\memory\% committed bytes in use', '\LogicalDisk(C:)\% Free Space' ) $perf = Get-Counter $CtrList -ComputerName $Server -ErrorAction SilentlyContinue | Select-Object -ExpandProperty CounterSamples Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Services Details for $($server.ToString())" $services = [String]::Join(' ; ', ((Get-Service -ComputerName $Server | Where-Object {$_.starttype -eq 'Automatic' -and $_.status -eq 'Stopped'}).DisplayName)) Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Uptime Details for $($server.ToString())" $OS = Get-CimInstance Win32_OperatingSystem -ComputerName $Server | Select-Object * $Uptime = New-TimeSpan -Start $OS.LastBootUpTime -End (Get-Date) $updays = [math]::Round($uptime.Days, 0) [void]$ServerPerfMon.Add([pscustomobject]@{ DateCollected = (Get-Date -Format dd-MM-yyyy_HH:mm).ToString() ServerName = $Server 'CPU %' = [Decimal]::Round(($perf[0].CookedValue), 2).tostring() 'Memory %' = [Decimal]::Round(($perf[1].CookedValue), 2).tostring() 'C Drive % Free' = [Decimal]::Round(($perf[2].CookedValue), 2).tostring() Uptime = $updays.tostring() 'Stopped Services' = $Services }) Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Ending] Performance Details for $($server.ToString())" } $ServerPerfMon if ($Export -eq 'Excel') { $ExcelOptions = @{ Path = $(Join-Path -Path $ReportPath -ChildPath "\CitrixServerPerformance-$(Get-Date -Format yyyy.MM.dd-HH.mm).xlsx") AutoSize = $True AutoFilter = $True TitleBold = $True TitleSize = '28' TitleFillPattern = 'LightTrellis' TableStyle = 'Light20' FreezeTopRow = $True FreezePane = '3' } $ServerPerfMon | Export-Excel -Title CitrixServerPerformance -WorksheetName CitrixServerPerformance @ExcelOptions } if ($Export -eq 'HTML') { $ServerPerfMon | Out-HtmlView -DisablePaging -Title 'Server Performance' -HideFooter -SearchHighlight -FixedHeader -FilePath $(Join-Path -Path $ReportPath -ChildPath "\CitrixServerPerformance-$(Get-Date -Format yyyy.MM.dd-HH.mm).html") } if ($Export -eq 'Host') { $ServerPerfMon } } #end Function Export-ModuleMember -Function Get-CitrixServerPerformance #endregion #region Get-CitrixSessionIcaRtt.ps1 ############################################ # source: Get-CitrixSessionIcaRtt.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Creates a report of users sessions with a AVG IcaRttMS .DESCRIPTION Creates a report of users sessions with a AVG IcaRttMS .PARAMETER MonitorData Use Get-CitrixMonitoringData to create OData, and use that variable in this parameter. .PARAMETER AdminServer FQDN of the Citrix Data Collector .PARAMETER SessionCount Will collect data for the last x amount of sessions. .PARAMETER Export Export the result to a report file. (Excel or html) .PARAMETER ReportPath Where to save the report. .EXAMPLE Get-CitrixSessionIcaRtt -AdminServer $CTXDDC #> Function Get-CitrixSessionIcaRtt { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Get-CitrixSessionIcaRtt')] [OutputType([System.Object[]])] PARAM( [Parameter(Mandatory = $false, ParameterSetName = 'Got odata')] [PSTypeName('CTXMonitorData')]$MonitorData, [Parameter(Mandatory = $true, ParameterSetName = 'Fetch odata')] [string]$AdminServer, [Parameter(Mandatory = $true, ParameterSetName = 'Fetch odata')] [int32]$SessionCount, [Parameter(Mandatory = $false, ParameterSetName = 'Got odata')] [Parameter(Mandatory = $false, ParameterSetName = 'Fetch odata')] [ValidateSet('Excel', 'HTML')] [string]$Export = 'Host', [ValidateScript( { if (Test-Path $_) { $true } else { New-Item -Path $_ -ItemType Directory -Force | Out-Null; $true } })] [Parameter(Mandatory = $false, ParameterSetName = 'Got odata')] [Parameter(Mandatory = $false, ParameterSetName = 'Fetch odata')] [System.IO.DirectoryInfo]$ReportPath = 'C:\Temp' ) if (-not($MonitorData)) { try { $mon = Get-CitrixMonitoringData -AdminServer $AdminServer -SessionCount $SessionCount } catch {$mon = Get-CitrixMonitoringData -AdminServer $AdminServer -SessionCount $SessionCount -AllowUnencryptedAuthentication} } else {$Mon = $MonitorData} [System.Collections.ArrayList]$IcaRttObject = @() $UniqueSession = $mon.Sessions.SessionMetrics | Sort-Object -Property SessionId -Unique foreach ($sessid in $UniqueSession) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Sessions $($UniqueSession.IndexOf($sessid)) of $($UniqueSession.count)" try { $session = $mon.Sessions | Where-Object {$_.SessionKey -like $sessid.SessionId} $user = ($mon.Sessions.User | Where-Object {$_.id -like $session.userid})[0] $Measure = $mon.Sessions.SessionMetrics | Where-Object {$_.SessionId -like $sessid.SessionId} | Measure-Object -Property IcaRttMS -Average [void]$IcaRttObject.Add([pscustomobject]@{ StartDate = [datetime]$session.StartDate EndDate = [datetime]$session.EndDate ObjectCount = $Measure.Count 'AVG IcaRtt' = [math]::Round($Measure.Average) UserName = $user.UserName UPN = $user.Upn }) } catch {Write-Warning "`n`tMessage:$($_.Exception.Message)`n`tItem:$($_.Exception.ItemName)"} } if ($Export -eq 'Excel') { $ExcelOptions = @{ Path = $(Join-Path -Path $ReportPath -ChildPath "\CitrixSessionIcaRtt-$(Get-Date -Format yyyy.MM.dd-HH.mm).xlsx") AutoSize = $True AutoFilter = $True TitleBold = $True TitleSize = '28' TitleFillPattern = 'LightTrellis' TableStyle = 'Light20' FreezeTopRow = $True FreezePane = '3' } $IcaRttObject | Export-Excel -Title CitrixSessionIcaRtt -WorksheetName CitrixSessionIcaRtt @ExcelOptions } if ($Export -eq 'HTML') { $IcaRttObject | Out-HtmlView -DisablePaging -Title 'CitrixSessionIcaRtt' -HideFooter -SearchHighlight -FixedHeader -FilePath $(Join-Path -Path $ReportPath -ChildPath "\CitrixSessionIcaRtt-$(Get-Date -Format yyyy.MM.dd-HH.mm).html") } if ($Export -eq 'Host') { $IcaRttObject } } #end Function Export-ModuleMember -Function Get-CitrixSessionIcaRtt #endregion #region Get-CitrixVDAUptime.ps1 ############################################ # source: Get-CitrixVDAUptime.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Calculate the uptime of VDA Servers. .DESCRIPTION Calculate the uptime of VDA Servers. The script will filter out desktop machines and only report on severs. If the script cant remotely connect to the vda server, then the last registration date will be used. .PARAMETER AdminServer FQDN of the Citrix Data Collector .PARAMETER Export Export the result to a report file. (Excel or html) .PARAMETER ReportPath Where to save the report. .EXAMPLE Get-CitrixVDAUptime -AdminServer $CTXDDC #> Function Get-CitrixVDAUptime { [Cmdletbinding(HelpURI = "https://smitpi.github.io/XDHealthCheck/Get-CitrixVDAUptime")] [OutputType([System.Object[]])] PARAM( [Parameter(Mandatory = $true, Position = 0)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [string]$AdminServer, [ValidateSet('Excel', 'HTML')] [string]$Export = 'Host', [ValidateScript( { if (Test-Path $_) { $true } else { New-Item -Path $_ -ItemType Directory -Force | Out-Null; $true } })] [System.IO.DirectoryInfo]$ReportPath = 'C:\Temp' ) try { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] VDA Uptime" [System.Collections.ArrayList]$VDAUptime = @() Get-BrokerMachine -AdminAddress $AdminServer -MaxRecordCount 1000000 | Where-Object {$_.DesktopGroupName -notlike $null -and $_.OSType -notlike "*10" -and $_.OSType -notlike "*11" } | ForEach-Object { try { $ctxobject = $_ $OS = Get-CimInstance Win32_OperatingSystem -ComputerName $_.DNSName -ErrorAction Stop | Select-Object * $Uptime = New-TimeSpan -Start $OS.LastBootUpTime -End (Get-Date) $updays = [math]::Round($uptime.Days, 0) } catch { try { Write-Warning "Error: `n`tMessage:$($_.Exception.Message)" Write-Warning "Unable to remote to $($ctxobject.DNSName), defaulting uptime to LastRegistrationTime" if ($ctxobject.RegistrationState -like "Registered") { $Uptime = New-TimeSpan -Start $ctxobject.LastRegistrationTime -End (Get-Date) $updays = [math]::Round($uptime.Days, 0) } else {$updays = 'Unknown'} } catch { Write-Warning "Error: `n`tMessage:$($_.Exception.Message)" $updays = 'Unknown'} } [void]$VDAUptime.Add([pscustomobject]@{ ComputerName = $_.dnsname DesktopGroupName = $_.DesktopGroupName SessionCount = $_.SessionCount InMaintenanceMode = $_.InMaintenanceMode MachineInternalState = $_.MachineInternalState Uptime = $updays LastRegistrationTime = $_.LastRegistrationTime }) } } catch {Write-Warning "`n`tMessage:$($_.Exception.Message)`n`tItem:$($_.Exception.ItemName)"} if ($Export -eq 'Excel') { $ExcelOptions = @{ Path = $(Join-Path -Path $ReportPath -ChildPath "\CitrixVDAUptime-$(Get-Date -Format yyyy.MM.dd-HH.mm).xlsx") AutoSize = $True AutoFilter = $True TitleBold = $True TitleSize = '28' TitleFillPattern = 'LightTrellis' TableStyle = 'Light20' FreezeTopRow = $True FreezePane = '3' } $VDAUptime | Export-Excel -Title CitrixVDAUptime -WorksheetName CitrixVDAUptime @ExcelOptions} if ($Export -eq 'HTML') { $VDAUptime | Out-HtmlView -DisablePaging -Title "CitrixVDAUptime" -HideFooter -SearchHighlight -FixedHeader -FilePath $(Join-Path -Path $ReportPath -ChildPath "\CitrixVDAUptime-$(Get-Date -Format yyyy.MM.dd-HH.mm).html") } if ($Export -eq 'Host') { $VDAUptime } } #end Function Export-ModuleMember -Function Get-CitrixVDAUptime #endregion #region Get-CitrixWorkspaceAppVersions.ps1 ############################################ # source: Get-CitrixWorkspaceAppVersions.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Reports on the versions of workspace app your users are using to connect .DESCRIPTION Reports on the versions of workspace app your users are using to connect .EXAMPLE Get-CitrixWorkspaceAppVersions #> <# .SYNOPSIS Reports on the versions of workspace app your users are using to connect .DESCRIPTION Reports on the versions of workspace app your users are using to connect .PARAMETER MonitorData Use Get-CitrixMonitoringData to create OData, and use that variable in this parameter. .PARAMETER AdminServer FQDN of the Citrix Data Collector .PARAMETER SessionCount Will collect data for the last x amount of sessions. .PARAMETER Export Export the result to a report file. (Excel or html) .PARAMETER ReportPath Where to save the report. .EXAMPLE $mon = Get-CitrixMonitoringData -AdminServer $AdminServer -hours $hours Get-CitrixWorkspaceAppVersions -MonitorData $Mon #> Function Get-CitrixWorkspaceAppVersions { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Get-CitrixWorkspaceAppVersions')] [OutputType([System.Object[]])] PARAM( [Parameter(Mandatory = $false, ParameterSetName = 'Got odata')] [PSTypeName('CTXMonitorData')]$MonitorData, [Parameter(Mandatory = $true, ParameterSetName = 'Fetch odata')] [string]$AdminServer, [Parameter(Mandatory = $true, ParameterSetName = 'Fetch odata')] [int32]$SessionCount, [Parameter(Mandatory = $false, ParameterSetName = 'Got odata')] [Parameter(Mandatory = $false, ParameterSetName = 'Fetch odata')] [ValidateSet('Excel', 'HTML')] [string]$Export = 'Host', [ValidateScript( { if (Test-Path $_) { $true } else { New-Item -Path $_ -ItemType Directory -Force | Out-Null; $true } })] [Parameter(Mandatory = $false, ParameterSetName = 'Got odata')] [Parameter(Mandatory = $false, ParameterSetName = 'Fetch odata')] [System.IO.DirectoryInfo]$ReportPath = 'C:\Temp' ) if (-not($MonitorData)) { try { $mon = Get-CitrixMonitoringData -AdminServer $AdminServer -SessionCount $SessionCount } catch {$mon = Get-CitrixMonitoringData -AdminServer $AdminServer -SessionCount $SessionCount -AllowUnencryptedAuthentication} } else {$Mon = $MonitorData} [System.Collections.ArrayList]$ClientObject = @() foreach ($session in $mon.sessions) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Sessions $($mon.Sessions.IndexOf($session)) of $($mon.Sessions.count)" [void]$ClientObject.Add([pscustomobject]@{ Domain = $session.User.domain UserName = $session.User.UserName Upn = $session.User.Upn FullName = $session.User.FullName ClientName = $session.CurrentConnection.ClientName ClientAddress = $session.CurrentConnection.ClientAddress ClientVersion = $session.CurrentConnection.ClientVersion ClientPlatform = $session.CurrentConnection.ClientPlatform Protocol = $session.CurrentConnection.Protocol }) } if ($Export -eq 'Excel') { $ExcelOptions = @{ Path = $(Join-Path -Path $ReportPath -ChildPath "\CitrixWorkspaceAppVersions-$(Get-Date -Format yyyy.MM.dd-HH.mm).xlsx") AutoSize = $True AutoFilter = $True TitleBold = $True TitleSize = '28' TitleFillPattern = 'LightTrellis' TableStyle = 'Light20' FreezeTopRow = $True FreezePane = '3' } $ClientObject | Export-Excel -Title CitrixWorkspaceAppVersions -WorksheetName CitrixWorkspaceAppVersions @ExcelOptions} if ($Export -eq 'HTML') { $ClientObject | Out-HtmlView -DisablePaging -Title 'CitrixWorkspaceAppVersions' -HideFooter -SearchHighlight -FixedHeader -FilePath $(Join-Path -Path $ReportPath -ChildPath "\CitrixWorkspaceAppVersions-$(Get-Date -Format yyyy.MM.dd-HH.mm).html") } if ($Export -eq 'Host') { $ClientObject } } #end Function Export-ModuleMember -Function Get-CitrixWorkspaceAppVersions #endregion #region Get-RDSLicenseInformation.ps1 ############################################ # source: Get-RDSLicenseInformation.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Report on RDS License Usage .DESCRIPTION Report on RDS License Usage .PARAMETER LicenseServer RDS License server name. .PARAMETER Export Export the result to a report file. (Excel, html or Screen) .PARAMETER ReportPath Where to save the report. .EXAMPLE Get-RDSLicenseInformation -LicenseServer $RDSLicenseServer #> Function Get-RDSLicenseInformation { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Get-RDSLicenseInformation')] PARAM( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [ValidateNotNull()] [ValidateNotNullOrEmpty()] [string]$LicenseServer, [ValidateSet('Excel', 'HTML')] [string]$Export = 'Host', [ValidateScript( { if (Test-Path $_) { $true } else { New-Item -Path $_ -ItemType Directory -Force | Out-Null; $true } })] [System.IO.DirectoryInfo]$ReportPath = 'C:\Temp' ) Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Starting] RDS Details" try { $RDSLicense = Get-CimInstance Win32_TSLicenseKeyPack -ComputerName $LicenseServer -ErrorAction stop | Select-Object -Property TypeAndModel, ProductVersion, TotalLicenses, IssuedLicenses, AvailableLicenses } catch {Write-Warning "Unable to connect to RDS License server: $($LicenseServer)"} $CTXObject = New-Object PSObject -Property @{ 'Per Device' = $RDSLicense | Where-Object { $_.TypeAndModel -eq 'RDS Per Device CAL' } 'Per User' = $RDSLicense | Where-Object { $_.TypeAndModel -eq 'RDS Per User CAL' } } Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Ending] RDS Details" if ($Export -eq 'Excel') { $ExcelOptions = @{ Path = $(Join-Path -Path $ReportPath -ChildPath "\RDSLicenseInformation-$(Get-Date -Format yyyy.MM.dd-HH.mm).xlsx") AutoSize = $True AutoFilter = $True TitleBold = $True TitleSize = '28' TitleFillPattern = 'LightTrellis' TableStyle = 'Light20' FreezeTopRow = $True FreezePane = '3' } $CTXObject.'Per Device' | Export-Excel -Title 'Per Device' -WorksheetName 'Per Device' @ExcelOptions $CTXObject.'Per User' | Export-Excel -Title 'Per User' -WorksheetName 'Per User' @ExcelOptions } if ($Export -eq 'HTML') { New-HTML -TitleText "RDSLicenseInformation-$(Get-Date -Format yyyy.MM.dd-HH.mm)" -FilePath $(Join-Path -Path $ReportPath -ChildPath "\RDSLicenseInformation-$(Get-Date -Format yyyy.MM.dd-HH.mm).html") { New-HTMLTab -Name 'Per Device' -TextTransform uppercase -IconSolid cloud-sun-rain -TextSize 16 -TextColor $color1 -IconSize 16 -IconColor $color2 -HtmlData {New-HTMLPanel -Content { New-HTMLTable -DataTable $($CTXObject.'Per Device') @TableSettings}} New-HTMLTab -Name 'Per User' -TextTransform uppercase -IconSolid cloud-sun-rain -TextSize 16 -TextColor $color1 -IconSize 16 -IconColor $color2 -HtmlData { New-HTMLPanel -Content { New-HTMLTable -DataTable $($CTXObject.'Per User') @TableSettings}} } -Online -Encoding UTF8 -ShowHTML } if ($Export -eq 'Host') { $CTXObject } } #end Function Export-ModuleMember -Function Get-RDSLicenseInformation #endregion #region Import-ParametersFile.ps1 ############################################ # source: Import-ParametersFile.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Import the config file and creates the needed variables .DESCRIPTION Import the config file and creates the needed variables .PARAMETER JSONParameterFilePath Path to the json config file, created by Install-ParametersFile .PARAMETER RedoCredentials Deletes the saved credentials, and allow you to recreate them. .EXAMPLE Import-ParametersFile -JSONParameterFilePath $JSONParameterFilePath #> Function Import-ParametersFile { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Import-ParametersFile')] PARAM( [Parameter(Mandatory = $false, Position = 0)] [ValidateScript( { (Test-Path $_) -and ((Get-Item $_).Extension -eq '.json') })] [string]$JSONParameterFilePath = (Get-Item $profile).DirectoryName + '\Parameters.json', [Parameter(Mandatory = $false, Position = 1)] [switch]$RedoCredentials = $false ) $JSONParameter = Get-Content ($JSONParameterFilePath) | ConvertFrom-Json if ($null -eq $JSONParameter) { Write-Error 'Valid Parameters file not found'; break } Write-Color 'Using Variables from Parameters.json: ', $JSONParameterFilePath.ToString() -ShowTime -Color DarkCyan, DarkYellow -LinesAfter 1 Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Starting] Variable Details" $JSONParameter.PSObject.Properties | Where-Object { $_.name -notlike 'TrustedDomains' } | ForEach-Object { Write-Color $_.name, ':', $_.value -Color Yellow, DarkCyan, Green -ShowTime; New-Variable -Name $_.name -Value $_.value -Force -Scope global } New-Variable -Name 'JSONParameterFilePath' -Value $JSONParameterFilePath -Scope global -Force # $global:CTXAdmin = Find-Credential | Where-Object target -Like '*CTXAdmin' | Get-Credential -Store # if ($null -eq $CTXAdmin) { # $AdminAccount = BetterCredentials\Get-Credential -Message 'Admin Account: DOMAIN\Username for CTX Admin' # Set-Credential -Credential $AdminAccount -Target 'CTXAdmin' -Persistence LocalComputer -Description 'Account used for Citrix queries' -Verbose # } # Write-Color 'Citrix Admin Credentials: ', $CTXAdmin.UserName -ShowTime -Color yellow, Green if ($SendEmail) { $global:SMTPClientCredentials = Find-Credential | Where-Object target -Like '*Healthcheck_smtp' | Get-Credential -Store if ($null -eq $SMTPClientCredentials) { $Account = BetterCredentials\Get-Credential -Message 'smtp login for HealthChecks email' Set-Credential -Credential $Account -Target 'Healthcheck_smtp' -Persistence LocalComputer -Description 'Account used for XD health checks' -Verbose } Write-Color 'SMTP Credentials: ', $SMTPClientCredentials.UserName -ShowTime -Color yellow, Green -LinesBefore 2 } if ($RedoCredentials) { foreach ($domain in $JSONParameter.TrustedDomains) { Find-Credential | Where-Object target -Like ('*' + $domain.Description.tostring()) | Remove-Credential -Verbose } Find-Credential | Where-Object target -Like '*CTXAdmin' | Remove-Credential -Verbose Find-Credential | Where-Object target -Like '*NSAdmin' | Remove-Credential -Verbose } } #end Function Export-ModuleMember -Function Import-ParametersFile #endregion #region Install-ParametersFile.ps1 ############################################ # source: Install-ParametersFile.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Create a json config file with all needed farm details. .DESCRIPTION Create a json config file with all needed farm details. .EXAMPLE Install-ParametersFile #> function Install-ParametersFile { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Install-ParametersFile')] param () [string]$CTXDDC = Read-Host 'A Citrix Data Collector FQDN' $CTXStoreFront = @() $ClientInput = '' While ($ClientInput.ToLower() -ne 'n') { $CTXStoreFront += Read-Host 'A Citrix StoreFront FQDN' $ClientInput = Read-Host 'Add more StoreFont Servers (y/n)' } [string]$RDSLicenseServer = Read-Host 'RDS LicenseServer FQDN' Write-Color -Text 'Add RDS License Type' -Color DarkGray -LinesAfter 1 Write-Color '1: ', 'Per Device' -Color Yellow, Green Write-Color '2: ', 'Per User' -Color Yellow, Green $selection = Read-Host 'Please make a selection' switch ($selection) { '1' { [string]$RDSLicenseType = 'Per Device' } '2' { [string]$RDSLicenseType = 'Per User' } } $trusteddomains = @() $ClientInput = '' While ($ClientInput -ne 'n') { If ($null -ne $ClientInput) { $FQDN = Read-Host 'FQDN for the domain' $NetBiosName = Read-Host 'Net Bios Name for Domain ' $CusObject = New-Object PSObject -Property @{ FQDN = $FQDN NetBiosName = $NetBiosName Description = $NetBiosName + '_ServiceAccount' } | Select-Object FQDN, NetBiosName, Description $trusteddomains += $CusObject $ClientInput = Read-Host 'Add more trusted domains? (y/n)' } } $ReportsFolder = Read-Host 'Path to the Reports Folder' $ParametersFolder = Read-Host 'Path to where the Parameters.json will be saved' $DashboardTitle = Read-Host 'Title to be used in the reports and Dashboard' $RemoveOldReports = Read-Host 'Remove Reports older than (in days)' Write-Color -Text 'Save reports to an excel report' -Color DarkGray -LinesAfter 1 Write-Color '1: ', 'Yes' -Color Yellow, Green Write-Color '2: ', 'No' -Color Yellow, Green $selection = Read-Host 'Please make a selection' switch ($selection) { '1' { $SaveExcelReport = $true } '2' { $SaveExcelReport = $false } } Write-Color -Text 'Send Report via email' -Color DarkGray -LinesAfter 1 Write-Color '1: ', 'Yes' -Color Yellow, Green Write-Color '2: ', 'No' -Color Yellow, Green $selection = Read-Host 'Please make a selection' switch ($selection) { '1' { $SendEmail = $true } '2' { $SendEmail = $false } } if ($SendEmail -eq 'true') { $emailFromA = Read-Host 'Email Address of the Sender' $emailFromN = Read-Host 'Full Name of the Sender' $FromAddress = $emailFromN + ' <' + $emailFromA + '>' $ToAddress = @() $ClientInput = '' While ($ClientInput -ne 'n') { If ($null -ne $ClientInput) { $emailtoA = Read-Host 'Email Address of the Recipient' $emailtoN = Read-Host 'Full Name of the Recipient' $ToAddress += $emailtoN + ' <' + $emailtoA + '>' } $ClientInput = Read-Host 'Add more recipients? (y/n)' } $smtpServer = Read-Host 'IP or name of SMTP server' $smtpServerPort = Read-Host 'Port of SMTP server' Write-Color -Text 'Use ssl for SMTP' -Color DarkGray -LinesAfter 1 Write-Color '1: ', 'Yes' -Color Yellow, Green Write-Color '2: ', 'No' -Color Yellow, Green $selection = Read-Host 'Please make a selection' switch ($selection) { '1' { $smtpEnableSSL = $true } '2' { $smtpEnableSSL = $false } } } $AllXDData = New-Object PSObject -Property @{ DateCollected = (Get-Date -Format dd-MM-yyyy_HH:mm).ToString() CTXDDC = $CTXDDC CTXStoreFront = $CTXStoreFront RDSLicenseServer = $RDSLicenseServer RDSLicenseType = $RDSLicenseType TrustedDomains = $trusteddomains ReportsFolder = $ReportsFolder ParametersFolder = $ParametersFolder DashboardTitle = $DashboardTitle RemoveOldReports = $RemoveOldReports SaveExcelReport = $SaveExcelReport SendEmail = $SendEmail EmailFrom = $FromAddress EmailTo = $ToAddress SMTPServer = $smtpServer SMTPServerPort = $smtpServerPort SMTPEnableSSL = $smtpEnableSSL } | Select-Object DateCollected, CTXDDC , CTXStoreFront , RDSLicenseServer , RDSLicenseType, TrustedDomains , ReportsFolder , ParametersFolder , DashboardTitle, RemoveOldReports, SaveExcelReport , SendEmail , EmailFrom , EmailTo , SMTPServer , SMTPServerPort , SMTPEnableSSL $ParPath = Join-Path -Path $ParametersFolder -ChildPath "\Parameters.json" if (Test-Path -Path $ParPath ) { Rename-Item $ParPath -NewName "Parameters_$(Get-Date -Format ddMMyyyy_HHmm).json" } else { $AllXDData | ConvertTo-Json -Depth 5 | Out-File -FilePath $ParPath -Force } Import-ParametersFile -JSONParameterFilePath $ParPath } Export-ModuleMember -Function Install-ParametersFile #endregion #region Set-XDHealthReportColors.ps1 ############################################ # source: Set-XDHealthReportColors.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Set the color and logo for HTML Reports .DESCRIPTION Set the color and logo for HTML Reports. It updates the registry keys in HKCU:\Software\XDHealth with the new details and display a test report. .PARAMETER Color1 New Background Color # code .PARAMETER Color2 New foreground Color # code .PARAMETER LogoURL URL to the new Logo .EXAMPLE Set-XDHealthReportColors -Color1 '#d22c26' -Color2 '#2bb74e' -LogoURL 'https://gist.githubusercontent.com/default-monochrome.png' #> Function Set-XDHealthReportColors { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Set-XDHealthReportColors')] [Cmdletbinding()] PARAM( [string]$Color1 = '#2b1200', [string]$Color2 = '#f37000', [string]$LogoURL = 'https://gist.githubusercontent.com/smitpi/ecdaae80dd79ad585e571b1ba16ce272/raw/6d0645968c7ba4553e7ab762c55270ebcc054f04/default-monochrome.png' ) if (Test-Path HKCU:\Software\XDHealth) { Set-ItemProperty -Path HKCU:\Software\XDHealth -Name Color1 -Value $($Color1) Set-ItemProperty -Path HKCU:\Software\XDHealth -Name Color2 -Value $($Color2) Set-ItemProperty -Path HKCU:\Software\XDHealth -Name LogoURL -Value $($LogoURL) } else { New-Item -Path HKCU:\Software\XDHealth New-ItemProperty -Path HKCU:\Software\XDHealth -Name Color1 -Value $($Color1) New-ItemProperty -Path HKCU:\Software\XDHealth -Name Color2 -Value $($Color2) New-ItemProperty -Path HKCU:\Software\XDHealth -Name LogoURL -Value $($LogoURL) } $global:XDHealth_Color1 = Get-ItemPropertyValue -Path HKCU:\Software\XDHealth -Name Color1 $global:XDHealth_Color2 = Get-ItemPropertyValue -Path HKCU:\Software\XDHealth -Name Color2 $global:XDHealth_LogoURL = Get-ItemPropertyValue -Path HKCU:\Software\XDHealth -Name LogoURL #region Html Settings $global:TableSettings = @{ Style = 'cell-border' TextWhenNoData = 'No Data to display here' Buttons = 'searchBuilder', 'pdfHtml5', 'excelHtml5' FixedHeader = $true HideFooter = $true SearchHighlight = $true PagingStyle = 'full' PagingLength = 10 } $global:SectionSettings = @{ BackgroundColor = 'grey' CanCollapse = $true HeaderBackGroundColor = $XDHealth_Color1 HeaderTextAlignment = 'center' HeaderTextColor = $XDHealth_Color2 HeaderTextSize = '15' BorderRadius = '20px' } $global:TableSectionSettings = @{ BackgroundColor = 'white' CanCollapse = $true HeaderBackGroundColor = $XDHealth_Color2 HeaderTextAlignment = 'center' HeaderTextColor = $XDHealth_Color1 HeaderTextSize = '15' } #endregion [string]$HTMLReportname = $env:TEMP + '\Test-color' + (Get-Date -Format yyyy.MM.dd-HH.mm) + '.html' $HeadingText = 'Test | Report | ' + (Get-Date -Format dd) + ' ' + (Get-Date -Format MMMM) + ',' + (Get-Date -Format yyyy) + ' ' + (Get-Date -Format HH:mm) New-HTML -TitleText 'Report' -FilePath $HTMLReportname -ShowHTML { New-HTMLLogo -RightLogoString $XDHealth_LogoURL New-HTMLHeading -Heading h1 -HeadingText $HeadingText -Color Black New-HTMLSection @SectionSettings -HeaderText 'Test' -Content { New-HTMLSection -HeaderText 'Test2' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable (Get-Process | Select-Object -First 5) } New-HTMLSection -HeaderText 'Test3' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable (Get-Service | Select-Object -First 5) } } } } #end Function Export-ModuleMember -Function Set-XDHealthReportColors #endregion #region Start-CitrixAudit.ps1 ############################################ # source: Start-CitrixAudit.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Creates and distributes a report on catalog, groups and published app config. .DESCRIPTION Creates and distributes a report on catalog, groups and published app config. .PARAMETER JSONParameterFilePath Path to the json config file, created by Install-ParametersFile .EXAMPLE Start-CitrixAudit -JSONParameterFilePath 'C:\temp\Parameters.json' #> function Start-CitrixAudit { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Start-CitrixAudit')] PARAM( [Parameter(Mandatory = $false, Position = 0)] [ValidateScript( { (Test-Path $_) -and ((Get-Item $_).Extension -eq '.json') })] [string]$JSONParameterFilePath = (Get-Item $profile).DirectoryName + '\Parameters.json' ) Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Importing Variables" ########################################## #region xml imports ########################################## Import-ParametersFile -JSONParameterFilePath $JSONParameterFilePath #endregion ########################################## #region checking folders and report names ########################################## Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Starting] Data Collection" if ((Test-Path -Path $ReportsFolder\logs) -eq $false) { New-Item -Path "$ReportsFolder\logs" -ItemType Directory -Force -ErrorAction SilentlyContinue } [string]$Transcriptlog = "$ReportsFolder\logs\XDAudit_TransmissionLogs." + (Get-Date -Format yyyy.MM.dd-HH.mm) + '.log' Start-Transcript -Path $Transcriptlog -IncludeInvocationHeader -Force -NoClobber $timer = [Diagnostics.Stopwatch]::StartNew(); if ((Test-Path -Path $ReportsFolder\XDAudit) -eq $false) { New-Item -Path "$ReportsFolder\XDAudit" -ItemType Directory -Force -ErrorAction SilentlyContinue } if ([bool]$RemoveOldReports) { $oldReports = (Get-Date).AddDays(-$RemoveOldReports) Get-ChildItem $ReportsFolder\XDAudit *.html | Where-Object { $_.LastWriteTime -le $oldReports } | Remove-Item -Force -Verbose Get-ChildItem $ReportsFolder\XDAudit *.xlsx | Where-Object { $_.LastWriteTime -le $oldReports } | Remove-Item -Force -Verbose Get-ChildItem $ReportsFolder\XDAudit *.xml | Where-Object { $_.LastWriteTime -le $oldReports } | Remove-Item -Force -Verbose Get-ChildItem $ReportsFolder\logs\XDAudit_TransmissionLogs* | Where-Object { $_.LastWriteTime -le $oldReports } | Remove-Item -Force -Verbose } [string]$Reportname = $ReportsFolder + '\XDAudit\XD_Audit.' + (Get-Date -Format yyyy.MM.dd-HH.mm) + '.html' [string]$XMLExport = $ReportsFolder + '\XDAudit\XD_Audit.' + (Get-Date -Format yyyy.MM.dd-HH.mm) + '.xml' [string]$ExcelReportname = $ReportsFolder + '\XDAudit\XD_Audit.' + (Get-Date -Format yyyy.MM.dd-HH.mm) + '.xlsx' #endregion ######################################## #region Getting Credentials ######################################### #endregion ######################################## #region Connect and get info ######################################## Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Collecting Farm Details" $CitrixObjects = Get-CitrixObjects -AdminServer $CTXDDC $MachineCatalog = $CitrixObjects.MachineCatalog | Select-Object MachineCatalogName, AllocationType, SessionSupport, UnassignedCount, UsedCount, MasterImageVM, MasterImageSnapshotName, MasterImageSnapshotCount, MasterImageVMDate $DeliveryGroups = $CitrixObjects.DeliveryGroups | Select-Object DesktopGroupName, Enabled, InMaintenanceMode, TotalApplications, TotalDesktops, DesktopsUnregistered, UserAccess, GroupAccess $PublishedApps = $CitrixObjects.PublishedApps | Select-Object DesktopGroupName, DesktopGroupUsersAccess, DesktopGroupGroupAccess, Enabled, ApplicationName, PublishedAppGroupAccess, PublishedAppUserAccess #endregion ######################################## #region saving data to xml ######################################## $AllXDData = New-Object PSObject -Property @{ DateCollected = (Get-Date -Format dd-MM-yyyy_HH:mm).ToString() MachineCatalog = $CitrixObjects.MachineCatalog DeliveryGroups = $CitrixObjects.DeliveryGroups PublishedApps = $CitrixObjects.PublishedApps VDAServers = $CitrixObjects.VDAServers VDAWorkstations = $CitrixObjects.VDAWorkstations MachineCatalogSum = $MachineCatalog DeliveryGroupsSum = $DeliveryGroups PublishedAppsSum = $PublishedApps } if (Test-Path -Path $XMLExport) { Remove-Item $XMLExport -Force -Verbose } $AllXDData | Export-Clixml -Path $XMLExport -Depth 25 -NoClobber -Force #endregion ######################################## #region Setting some table color and settings ######################################## ####################### #region Building HTML the report ####################### Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Building HTML Page" $HeadingText = $DashboardTitle + ' | XenDesktop Audit | ' + (Get-Date -Format dd) + ' ' + (Get-Date -Format MMMM) + ',' + (Get-Date -Format yyyy) + ' ' + (Get-Date -Format HH:mm) New-HTML -TitleText 'XenDesktop Audit' -FilePath $Reportname { New-HTMLLogo -RightLogoString $XDHealth_LogoURL New-HTMLHeading -Heading h1 -HeadingText $HeadingText -Color Black New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'Machine Catalogs' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $MachineCatalog } } New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'Delivery Groups' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $DeliveryGroups } } New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'Published Apps' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $PublishedApps } } } #endregion ####################### #region Saving Excel report ####################### if ($SaveExcelReport) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Processing] Saving Excel Report" $AllXDData.MachineCatalog | Export-Excel -Path $ExcelReportname -AutoSize -AutoFilter -Title 'MachineCatalog' -WorksheetName MachineCatalog -TitleBold -TitleSize 28 -TitleFillPattern LightTrellis -TableStyle Light20 -FreezeTopRow -FreezePane 3 $AllXDData.DeliveryGroups | Export-Excel -Path $ExcelReportname -AutoSize -AutoFilter -Title 'DeliveryGroups' -WorksheetName DeliveryGroups -TitleBold -TitleSize 28 -TitleFillPattern LightTrellis -TableStyle Light20 -FreezeTopRow -FreezePane 3 $AllXDData.PublishedApps | Export-Excel -Path $ExcelReportname -AutoSize -AutoFilter -Title 'PublishedApps' -WorksheetName PublishedApps -TitleBold -TitleSize 28 -TitleFillPattern LightTrellis -TableStyle Light20 -FreezeTopRow -FreezePane 3 $AllXDData.VDAServers | Export-Excel -Path $ExcelReportname -AutoSize -AutoFilter -Title 'VDAServers' -WorksheetName VDAServers -TitleBold -TitleSize 28 -TitleFillPattern LightTrellis -TableStyle Light20 -FreezeTopRow -FreezePane 3 $AllXDData.VDAWorkstations | Export-Excel -Path $ExcelReportname -AutoSize -AutoFilter -Title 'VDAWorkstations' -WorksheetName VDAWorkstations -TitleBold -TitleSize 28 -TitleFillPattern LightTrellis -TableStyle Light20 -FreezeTopRow -FreezePane 3 } #endregion ####################### #region Sending email reports ####################### if ($SendEmail) { $smtpClientCredentials = Find-Credential | Where-Object target -Like '*Healthcheck_smtp' | Get-Credential -Store if ($null -eq $smtpClientCredentials) { $Account = BetterCredentials\Get-Credential -Message 'smtp login for HealthChecks email' Set-Credential -Credential $Account -Target 'Healthcheck_smtp' -Persistence LocalComputer -Description 'Account used for ctx health checks' -Verbose } Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing]Sending Report Email" $emailMessage = New-Object System.Net.Mail.MailMessage $emailMessage.From = $emailFrom $emailTo | ForEach-Object { $emailMessage.To.Add($_) } $emailMessage.Subject = $DashboardTitle + ' - Citrix Audit Results Report on ' + (Get-Date -Format dd) + ' ' + (Get-Date -Format MMMM) + ',' + (Get-Date -Format yyyy) $emailMessage.IsBodyHtml = $true $emailMessage.Body = 'Please see attached reports' $emailMessage.Attachments.Add($Reportname) $emailMessage.Attachments.Add($ExcelReportname) $smtpClient = New-Object System.Net.Mail.SmtpClient( $smtpServer , $smtpServerPort ) $smtpClient.Credentials = [Net.NetworkCredential]$smtpClientCredentials $smtpClient.EnableSsl = $smtpEnableSSL $smtpClient.Timeout = 30000000 $smtpClient.Send( $emailMessage ) } #endregion $timer.Stop() $timer.Elapsed | Select-Object Days, Hours, Minutes, Seconds | Format-List Stop-Transcript } Export-ModuleMember -Function Start-CitrixAudit #endregion #region Start-CitrixHealthCheck.ps1 ############################################ # source: Start-CitrixHealthCheck.ps1 # Module: XDHealthCheck # version: 0.2.12 # Author: Pierre Smit # Company: HTPCZA Tech ############################################# <# .SYNOPSIS Creates and distributes a report on citrix farm health. .DESCRIPTION Creates and distributes a report on citrix farm health. .PARAMETER JSONParameterFilePath Path to the json config file, created by Install-ParametersFile .EXAMPLE Start-CitrixHealthCheck -JSONParameterFilePath 'C:\temp\Parameters.json' #> function Start-CitrixHealthCheck { [Cmdletbinding(HelpURI = 'https://smitpi.github.io/XDHealthCheck/Start-CitrixHealthCheck')] PARAM( [Parameter(Mandatory = $false, Position = 0)] [ValidateScript( { (Test-Path $_) -and ((Get-Item $_).Extension -eq '.json') })] [string]$JSONParameterFilePath = (Get-Item $profile).DirectoryName + '\Parameters.json' ) Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Importing Variables" ########################################## #region xml imports ########################################## Import-ParametersFile -JSONParameterFilePath $JSONParameterFilePath #endregion ########################################## #region checking folders and report names ########################################## Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Starting] Data Collection" if ((Test-Path -Path $ReportsFolder\logs) -eq $false) { New-Item -Path "$ReportsFolder\logs" -ItemType Directory -Force -ErrorAction SilentlyContinue } [string]$Transcriptlog = "$ReportsFolder\logs\XDHealth_TransmissionLogs." + (Get-Date -Format yyyy.MM.dd-HH.mm) + '.log' Start-Transcript -Path $Transcriptlog -IncludeInvocationHeader -Force -NoClobber $timer = [Diagnostics.Stopwatch]::StartNew(); if ((Test-Path -Path $ReportsFolder\XDHealth) -eq $false) { New-Item -Path "$ReportsFolder\XDHealth" -ItemType Directory -Force -ErrorAction SilentlyContinue } if ([bool]$RemoveOldReports) { $oldReports = (Get-Date).AddDays(-$RemoveOldReports) Get-ChildItem $ReportsFolder\XDHealth *.html | Where-Object { $_.LastWriteTime -le $oldReports } | Remove-Item -Force -Verbose Get-ChildItem $ReportsFolder\XDHealth *.xlsx | Where-Object { $_.LastWriteTime -le $oldReports } | Remove-Item -Force -Verbose Get-ChildItem $ReportsFolder\XDHealth *.xml | Where-Object { $_.LastWriteTime -le $oldReports } | Remove-Item -Force -Verbose Get-ChildItem $ReportsFolder\logs\XDHealth_TransmissionLogs* | Where-Object { $_.LastWriteTime -le $oldReports } | Remove-Item -Force -Verbose } [string]$Reportname = $ReportsFolder + '\XDHealth\XD_Healthcheck.' + (Get-Date -Format yyyy.MM.dd-HH.mm) + '.html' [string]$AllXMLExport = $ReportsFolder + '\XDHealth\XD_All_Healthcheck.xml' [string]$ReportsXMLExport = $ReportsFolder + '\XDHealth\XD_Healthcheck.' + (Get-Date -Format yyyy.MM.dd-HH.mm) + '.xml' [string]$ExcelReportname = $ReportsFolder + '\XDHealth\XD_Healthcheck.' + (Get-Date -Format yyyy.MM.dd-HH.mm) + '.xlsx' #endregion ######################################## #region Build other variables ######################################### if (-not(Get-PSSnapin -Registered | Where-Object {$_.name -like 'Citrix*'})) {Add-PSSnapin citrix* -ErrorAction SilentlyContinue} [array]$CTXControllers = (Get-BrokerController -AdminAddress $CTXDDC).dnsname [array]$CTXLicenseServer = (Get-BrokerSite -AdminAddress $AdminServer).LicenseServerName $CTXCore = @() $CTXCore = $CTXControllers + $CTXStoreFront + $CTXLicenseServer | Sort-Object -Unique #endregion ######################################## #region Connect and get info ######################################## Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Collecting License Details" $CitrixLicenseInformation = Get-CitrixLicenseInformation -AdminServer $CTXDDC Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Collecting Farm Details" $CitrixRemoteFarmDetails = Get-CitrixFarmDetail -AdminServer $CTXDDC $TodayReboots = $CitrixRemoteFarmDetails.RebootSchedule | Where-Object {$_.day -like "$((get-date).DayOfWeek)"} Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Collecting Eventlog Details" $CitrixServerEventLogs = Get-CitrixServerEventLog -Serverlist $CTXCore -Days 1 Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Collecting RDS Details" $RDSLicenseInformation = Get-RDSLicenseInformation -LicenseServer $RDSLicenseServer | ForEach-Object { $_.$RDSLicenseType } | Where-Object { $_.TotalLicenses -ne 4294967295 } | Select-Object TypeAndModel, ProductVersion, TotalLicenses, IssuedLicenses, AvailableLicenses Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Collecting Config changes Details" $CitrixConfigurationChanges = Get-CitrixConfigurationChange -AdminServer $CTXDDC -Indays 7 Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Collecting Server Performance Details" $ServerPerformance = Get-CitrixServerPerformance -ComputerName $CTXCore Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Citrix Env Test Results" $CitrixEnvTestResults = Get-CitrixEnvTestResults -AdminServer $CTXDDC -Infrastructure Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Citrix VDA Uptimes" $CitrixVDAUptime = Get-CitrixVDAUptime -AdminServer $CTXDDC Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Monitor Data" $monitor = Get-CitrixMonitoringData -AdminServer $CTXDDC -SessionCount 100 Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Failures" $Failures = Get-CitrixConnectionFailures -MonitorData $monitor Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] app ver" $appver = Get-CitrixWorkspaceAppVersions -MonitorData $monitor | Where-Object {$_.ClientVersion -notlike $null} Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] CitrixSessionIcaRtt" $CitrixSessionIcaRtt = Get-CitrixSessionIcaRtt -MonitorData $monitor Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] CitrixResourceUtilizationSummary" $CitrixResourceUtilizationSummary = get-CitrixResourceUtilizationSummary -AdminServer $CTXDDC -hours 24 #endregion ######################################## #region Adding more reports / scripts ######################################## Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing]Building Red Flags" Function Redflags { $RedFlags = @() $FlagReport = @() $CitrixLicenseInformation | Where-Object LicensesAvailable -LT 500 | ForEach-Object { $RedFlags += 'Citrix License Product: ' + $_.LicenseProductName + ', has ' + $_.LicensesAvailable + ' available licenses' } $RDSLicenseInformation | Where-Object AvailableLicenses -LT 500 | ForEach-Object { $RedFlags += $_.TypeAndModel + ', has ' + $_.AvailableLicenses + ' Licenses Available' } if ($null -eq $CitrixRemoteFarmDetails.SiteDetails.Summary.Name) { $RedFlags += "Could not connect to the Farm with server $CTXDDC" } else { if ($CitrixRemoteFarmDetails.DBConnection[0].Value -NE 'OK') { $RedFlags += 'Farm ' + $CitrixRemoteFarmDetails.SiteDetails.Summary.Name + " can't connect to Database" } $CitrixRemoteFarmDetails.Controllers.Summary | Where-Object 'Desktops Registered' -LT 100 | ForEach-Object { $RedFlags += $_.Name + ' ony have ' + $_.'Desktops Registered' + ' Desktops Registered' } $CitrixRemoteFarmDetails.Controllers.Summary | Where-Object State -NotLike 'Active' | ForEach-Object { $RedFlags += $_.name + ' is not active' } if ($CitrixRemoteFarmDetails.SessionCounts.'Unregistered Servers' -gt 0) { $RedFlags += 'There are ' + $CitrixRemoteFarmDetails.SessionCounts.'Unregistered Servers' + ' Hosted Shared Server(s) Unregistered' } if ($CitrixRemoteFarmDetails.SessionCounts.'Unregistered Desktops' -gt 0) { $RedFlags += 'There are ' + $CitrixRemoteFarmDetails.SessionCounts.'Unregistered Desktops' + ' VDI Desktop(s) Unregistered' } if (($CitrixRemoteFarmDetails.VDAUptime | Where-Object { $_.uptime -gt 7 }).count -gt 0) { $RedFlags += 'There are ' + (($CitrixRemoteFarmDetails.VDAUptime | Where-Object { $_.uptime -gt 7 }).count) + ' VDA servers needed a reboot' } } $CitrixServerEventLogs | Where-Object Errors -GT 100 | ForEach-Object { $RedFlags += $_.'ServerName' + ' have ' + $_.Errors + ' errors in the last 24 hours' } $ServerPerformance | Where-Object 'Stopped Services' -NE $null | ForEach-Object { $RedFlags += $_.Servername + ' has stopped Citrix Services' } foreach ($server in $ServerPerformance) { if ([int]$server.'CDrive % Free' -lt 10) { $RedFlags += $server.Servername + ' has only ' + $server.'CDrive % Free' + ' % free disk space on C Drive' } if ([int]$server.Uptime -gt 20) { $RedFlags += $server.Servername + ' was last rebooted ' + $server.uptime + ' Days ago' } } $index = 0 foreach ($flag in $RedFlags) { $index = $index + 1 $Object = New-Object PSCustomObject $Object | Add-Member -MemberType NoteProperty -Name '#' -Value $index.ToString() $Object | Add-Member -MemberType NoteProperty -Name 'Description' -Value $flag $FlagReport += $Object } $FlagReport } $flags = Redflags #endregion ####################### #region Building HTML the report ####################### Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Building HTML Page" $emailbody = New-HTML -TitleText 'Red Flags' { New-HTMLTable @TableSettings -DataTable $flags } $HeadingText = $DashboardTitle + ' | XenDesktop Report | ' + (Get-Date -Format dd) + ' ' + (Get-Date -Format MMMM) + ',' + (Get-Date -Format yyyy) + ' ' + (Get-Date -Format HH:mm) New-HTML -TitleText 'XenDesktop Report' -FilePath $Reportname { New-HTMLLogo -RightLogoString $XDHealth_LogoURL New-HTMLHeading -Heading h1 -HeadingText $HeadingText -Color Black New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'Citrix Sessions' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $CitrixRemoteFarmDetails.SessionCounts $Conditions_sessions } } New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'Citrix Controllers' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $CitrixRemoteFarmDetails.Controllers.Summary $Conditions_controllers } New-HTMLSection -HeaderText 'Citrix DB Connection' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $CitrixRemoteFarmDetails.DBConnection $Conditions_db } } New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'Citrix Licenses' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $CitrixLicenseInformation $Conditions_ctxlicenses } New-HTMLSection -HeaderText 'RDS Licenses' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable ($RDSLicenseInformation | Select-Object TypeAndModel, ProductVersion, TotalLicenses, IssuedLicenses, AvailableLicenses) } } New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'Citrix Error Counts' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable ($CitrixServerEventLogs | Select-Object ServerName, Errors, Warning) $Conditions_events } New-HTMLSection -HeaderText 'Citrix Events Top Events' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable ($CitrixServerEventLogs.TopProfider | Select-Object -First $CTXCore.count) } } New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'Resource Utilization Summary' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $CitrixResourceUtilizationSummary } New-HTMLSection -HeaderText 'Client Versions' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $AppVer } } New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'Citrix Config Changes in the last 7 days' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable ($CitrixConfigurationChanges.Summary | Where-Object { $_.name -ne '' } | Sort-Object count -Descending | Select-Object -First 5 -Property count, name) } New-HTMLSection -HeaderText 'Citrix Server Performance' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable ($ServerPerformance) $Conditions_performance } } if ($Failures.ConnectionFails -or $CitrixSessionIcaRtt) {New-HTMLSection @SectionSettings -Content { if ($Failures.ConnectionFails) {New-HTMLSection -HeaderText 'Connection Failure' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $Failures.ConnectionFails }} if ($CitrixSessionIcaRtt) {New-HTMLSection -HeaderText 'ICA Rtt' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $CitrixSessionIcaRtt }} }} if (($CitrixVDAUptime | Where-Object {$_.uptime -gt "7"})) {New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'VDA Uptime' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable ($CitrixVDAUptime | Where-Object {$_.uptime -gt "7"})} }} New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'Citrix Delivery Groups' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $CitrixRemoteFarmDetails.DeliveryGroups } } if ($CitrixRemoteFarmDetails.Machines.UnRegisteredDesktops){New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'Citrix UnRegistered Desktops' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $CitrixRemoteFarmDetails.Machines.UnRegisteredDesktops } }} if ($CitrixRemoteFarmDetails.Machines.UnRegisteredServers){New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'Citrix UnRegistered Servers' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $CitrixRemoteFarmDetails.Machines.UnRegisteredServers } }} if ($TodayReboots) {New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText "Today`'s Reboot Schedule" @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $TodayReboots } }} New-HTMLSection @SectionSettings -Content { New-HTMLSection -HeaderText 'Environment Test' @TableSectionSettings { New-HTMLTable @TableSettings -DataTable $CitrixEnvTestResults.InfrastructureResults } } } #endregion ####################### #region Saving Excel report ####################### if ($SaveExcelReport) { Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing] Saving Excel Report" $excelfile = $CitrixServerEventLogs.All | Export-Excel -Path $ExcelReportname -WorksheetName EventsRawData -AutoSize -AutoFilter -Title 'Citrix Events' -TitleBold -TitleSize 20 -FreezePane 3 -IncludePivotTable -TitleFillPattern DarkGrid -PivotTableName 'Events Summery' -PivotRows MachineName, LevelDisplayName, ProviderName -PivotData @{'Message' = 'count' } -NoTotalsInPivot $excelfile += $CitrixConfigurationChanges.Filtered | Export-Excel -Path $ExcelReportname -WorksheetName ConfigChangeRawData -AutoSize -AutoFilter -Title 'Citrix Config Changes' -TitleBold -TitleSize 20 -FreezePane 3 } #endregion ####################### #region Sending email reports ####################### if ($SendEmail) { $smtpClientCredentials = Find-Credential | Where-Object target -Like '*Healthcheck_smtp' | Get-Credential -Store if ($null -eq $smtpClientCredentials) { $Account = BetterCredentials\Get-Credential -Message 'smtp login for HealthChecks email' Set-Credential -Credential $Account -Target 'Healthcheck_smtp' -Persistence LocalComputer -Description 'Account used for ctx health checks' -Verbose } Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Proccessing]Sending Report Email" $emailMessage = New-Object System.Net.Mail.MailMessage $emailMessage.From = $emailFrom $emailTo | ForEach-Object { $emailMessage.To.Add($_) } $emailMessage.Subject = $DashboardTitle + ' - Citrix Health Check Report on ' + (Get-Date -Format dd) + ' ' + (Get-Date -Format MMMM) + ',' + (Get-Date -Format yyyy) $emailMessage.IsBodyHtml = $true $emailMessage.Body = $emailbody $emailMessage.Attachments.Add($Reportname) $emailMessage.Attachments.Add($ExcelReportname) $smtpClient = New-Object System.Net.Mail.SmtpClient( $smtpServer , $smtpServerPort ) $smtpClient.Credentials = [Net.NetworkCredential]$smtpClientCredentials $smtpClient.EnableSsl = $smtpEnableSSL $smtpClient.Timeout = 30000000 $smtpClient.Send( $emailMessage ) } #endregion Write-Verbose "$((Get-Date -Format HH:mm:ss).ToString()) [Ending]Healthcheck Complete" $timer.Stop() $timer.Elapsed | Select-Object Days, Hours, Minutes, Seconds | Format-List Stop-Transcript } Export-ModuleMember -Function Start-CitrixHealthCheck #endregion #endregion |