Jump to content
Welcome to our new Citrix community!
  • 0

powershell script not fetching pvs write cache in details


Sunil Ms1709160707

Question

######
#ICA and network latency tracker script
#
#created by sunil ms
#sunil.ms@infosys.com
#
#
#
#SYNOPSIS Script to show an ICA session's ICA round trip time and network latency. .
#DESCRIPTION The script runs for 20 seconds and measures (once every 2 seconds) the ICA RTT and network latency of the relevant session. 
#The output shows the session info (username, device name/IP, session name/ID) and 10 reads (once every 2 seconds) of the session's
# ICA RTT and network latency in seconds. - 
#
# 10 reads (once every 2 seconds) of session's ICA RTT and network latency.

asnp citrix*
Function Start-Countdown 
{   
    Param(
        [Int32]$Seconds = 10,
        [string]$Message = "Pausing for 10 seconds..."
    )
    ForEach ($Count in (1..$Seconds))
    {   Write-Progress -Id 1 -Activity $Message -Status "Taking ICA RTT and Network Latency sample for $Seconds seconds, $($Seconds - $Count) left" -PercentComplete (($Count / $Seconds) * 100)
        Start-Sleep -Seconds 1
    }
    Write-Progress -Id 1 -Activity $Message -Status "Completed" -PercentComplete 100 -Completed
}

Function CheckCpuUsage() 

    param ($hostname)
    Try { $CpuUsage=(get-counter -ComputerName $hostname -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5 -ErrorAction Stop | select -ExpandProperty countersamples | select -ExpandProperty cookedvalue | Measure-Object -Average).average
       $CpuUsage = "{0:N1}" -f $CpuUsage; return "$CpuUsage %"
    } Catch { "Error returned while checking the CPU usage. Perfmon Counters may be fault" | LogMe -error; return 101 } 
}
#============================================================================================== 
# The function check the memory usage and report the usage value in percentage
Function CheckMemoryUsage() 

    param ($hostname)
   Try 
    {   $SystemInfo = (Get-WmiObject -computername $hostname -Class Win32_OperatingSystem -ErrorAction Stop | Select-Object TotalVisibleMemorySize, FreePhysicalMemory)
       $TotalRAM = $SystemInfo.TotalVisibleMemorySize/1MB 
       $FreeRAM = $SystemInfo.FreePhysicalMemory/1MB 
       $UsedRAM = $TotalRAM - $FreeRAM 
       $RAMPercentUsed = ($UsedRAM / $TotalRAM) * 100 
       $RAMPercentUsed = "{0:N0}" -f $RAMPercentUsed
       return "$RAMPercentUsed %"
    } Catch { "Error returned while checking the Memory usage. Perfmon Counters may be fault" | LogMe -error; return 101 } 
}

 $date=get-date

 [void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') 
$CLT = [Microsoft.VisualBasic.Interaction]::InputBox('Enter user POD specific DC Server Name  : **POD6 [CLT--wsapp8915,,,STL--wsapp8920]-----***POD7 [CLT--wsapp8960,STL--wsapp8963]-----***POD8 [CLT wsapp9010,STL wsapp9013]')


     $CLTHSD=Get-brokersession -AdminAddress $CLT -MaxRecordCount 20000 #|where{$_.HostedMachineName -like "WHSD*" } | Sort-Object -Property @{Expression = "InMaintenanceMode"; Descending = $True}, @{Expression = "RegistrationState"; Descending = $False} #|select HostedMachineName,RegistrationState,InMaintenanceMode
    $STLHSD=Get-brokersession -AdminAddress $STL -MaxRecordCount 20000 #|where{$_.HostedMachineName -like "WHSD*" } | Sort-Object -Property @{Expression = "InMaintenanceMode"; Descending = $True}, @{Expression = "RegistrationState"; Descending = $False} #|select HostedMachineName,RegistrationState,InMaintenanceMode
    
[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') 
$session_name = [Microsoft.VisualBasic.Interaction]::InputBox('Enter Afftected user D ID  example- d944771')

[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') 
$sampletime = [Microsoft.VisualBasic.Interaction]::InputBox('Enter ICA Packets Sample interval time in Sec example:  5')

[void][System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') 
$sampletime1 = [Microsoft.VisualBasic.Interaction]::InputBox('Enter ICA Packets Sample interval counts in Sec example:  10')

$server=($CLTHSD|where{$_.BrokeringUserName -match $session_name}).IPAddress
$server1=($CLTHSD |where{$_.BrokeringUserName -match "$session_name"}).Hostedmachinename
$hanu=$CLTHSD |where{$_.BrokeringUserName -match "$session_name" -and $_.HostedMachineName -match "$server1" } 
#$sessionID = $hanu.SessionId
if ($session_name -Match 'RDP') {
Write-Host "There is no ICA RTT data on an RDP session. Please choose an ICA session."
exit
}
if (-Not (Get-WmiObject -ComputerName $server -Namespace root\Citrix\euem Citrix_Euem_ClientConnect)) {
Write-host "Couldn't find EUEM data. It's available on XenApp\XenDesktop 7.x and higher versions."
exit}
Else {
$final_obj = @()
$obj = New-Object PSObject
 # Check the AvgCPU value for 5 seconds
 $AvgCPUval = 0
       $AvgCPUval = CheckCpuUsage ($server)
        
        

       # Check the Physical Memory usage   
       $UsedMemory = 0      
       $UsedMemory = CheckMemoryUsage ($server)
       
    $session_info1 = (Get-WmiObject -ComputerName $server -Namespace root\Citrix\euem Citrix_Euem_ClientConnect).count     
$session_info = Get-WmiObject -ComputerName $server -Namespace root\Citrix\euem Citrix_Euem_ClientConnect | where {$_.UserName -eq $session_name} #| select username, ClientMachineIP, ClientMachineName, PSComputerName
$sessionID=$session_info.SessionID
$obj | Add-Member "Username" $session_info.username
$obj | Add-Member "Device Name" $session_info.ClientMachineName
$obj | Add-Member "Device IP" $session_info.ClientMachineIP
$obj | Add-Member "Connected To" $session_info.PSComputerName
$obj | Add-Member "Session Name" $session_name
$obj | Add-Member "Session ID" $sessionID
$obj | Add-Member "Avarage CPU"  $AvgCPUval 
$obj | Add-Member "Avarage Memory"  $UsedMemory 
$obj | Add-Member "User Session count on HSD"  $session_info1
#$obj| ft -AutoSize
$suni= $session_info.ClientMachineName
$suni1=$session_info.PSComputerName
For ($i=0; $i -le $sampletime1; $i++) {
$temp_obj = New-Object PSObject
$temp = Get-WmiObject -ComputerName $server -Namespace root\Citrix\euem citrix_euem_RoundTrip | where {$_.SessionID -eq $sessionID} #| select NetworkLatency, RoundtripTime
$time = Get-Date -Format T
#$temp_obj | Add-Member "Session ID" $temp.SessionID
# Check the AvgCPU value for 5 seconds
$AvgCPUval = 0
       $AvgCPUval = CheckCpuUsage ($server)
        
        

       # Check the Physical Memory usage   
       $UsedMemory = 0     
       $UsedMemory = CheckMemoryUsage ($server)
       
         
$temp_obj | Add-Member "HSD Machine" $temp.PSComputerName
$temp_obj | Add-Member "Time" $time
$temp_obj | Add-Member "ICA RTT in ms" $temp.RoundtripTime
$temp_obj | Add-Member "Network Latency in ms" $temp.NetworkLatency
$temp_obj | Add-Member "Avarage CPU"  $AvgCPUval 
$temp_obj | Add-Member "Avarage Memory"  $UsedMemory
$temp_obj | Add-Member "User Session count on HSD"  $session_info1 
#$temp_obj | ft -AutoSize
$final_obj += $temp_obj
Write-Output $final_obj | Format-Table -AutoSize
Start-Countdown -Seconds $sampletime -Message "taking $session_name ICA RTT Network Latency samples "
}

write-host $session_info.UserName " user ICA session's ICA round trip time and network latency (10 samples) Report $date" -ForegroundColor DarkRed -BackgroundColor DarkGray
Write-Output $obj | Format-Table -AutoSize
Write-Output $final_obj | Format-Table -AutoSize
#$obj| Out-GridView -Title "$session_name user connected HSD server $suni1 From $suni Client Machine " -passthru
write-host "ICA Round-Trip Time (ICA RTT) is used to quantify the user experience" -ForegroundColor DarkMagenta -BackgroundColor DarkYellow
write-host "ICA RTT in ms Up to 150ms: great user experience" -ForegroundColor DarkGreen #-BackgroundColor DarkYellow
write-host "ICA RTT in ms 150ms – 400ms: good/acceptable user experience" -ForegroundColor DarkYellow #-BackgroundColor DarkGray
write-host "ICA RTT in ms Over 400ms: degraded user experience" -ForegroundColor DarkRed  -BackgroundColor DarkGray
}

#pause
$final_obj| Out-GridView -Title "$session_name user connected HSD server $suni1 From $suni Client Machine and ICA session's ICA RTT and network latency packets $sampletime sec sample for $sampletime1 interval  $date  " -passthru
pause

ICA RTT.docx

Edited by Sunil Ms
Link to comment

23 answers to this question

Recommended Posts

  • 0

$wmiOSBlock = {param($computer)
  try { $wmi=Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computer -ErrorAction Stop }
  catch { $wmi = $null }
  return $wmi
}
$ControllerResults = @{}
$Controllers = Get-BrokerController #-AdminAddress $AdminAddress

foreach ($Controller in $Controllers) {
$tests = @{}
  Clear-Content $resultsHTM -Force
#Name of $Controller
$ControllerDNS = $Controller | ForEach-Object{ $_.DNSName }
"Controller: $ControllerDNS" | LogMe -display -progress
  
#Ping $Controller
$result = Ping $ControllerDNS 100
if ($result -ne "SUCCESS") { $tests.Ping = "Error", $result }
else { $tests.Ping = "SUCCESS", $result 

#Now when Ping is ok also check this:
  
#State of this controller
$ControllerState = $Controller | ForEach-Object{ $_.State }
"State: $ControllerState" | LogMe -display -progress
if ($ControllerState -ne "Active") { $tests.State = "ERROR", $ControllerState }
else { $tests.State = "SUCCESS", $ControllerState }


  
#DesktopsRegistered on this controller
$ControllerDesktopsRegistered = $Controller | ForEach-Object{ $_.DesktopsRegistered }
"Registered: $ControllerDesktopsRegistered" | LogMe -display -progress
$tests.DesktopsRegistered = "NEUTRAL", $ControllerDesktopsRegistered
  
#ActiveSiteServices on this controller
$ActiveSiteServices = $Controller | ForEach-Object{ $_.ActiveSiteServices }
"ActiveSiteServices $ActiveSiteServices" | LogMe -display -progress
$tests.ActiveSiteServices = "NEUTRAL", $ActiveSiteServices


#==============================================================================================
#               CHECK CPU AND MEMORY USAGE 
#==============================================================================================

        # Check the AvgCPU value for 5 seconds
        $AvgCPUval = CheckCpuUsage ($ControllerDNS)
        #$VDtests.LoadBalancingAlgorithm = "SUCCESS", "LB is set to BEST EFFORT"} 
            
        if( [int] $AvgCPUval -lt 75) { "CPU usage is normal [ $AvgCPUval % ]" | LogMe -display; $tests.AvgCPU = "SUCCESS", "$AvgCPUval %" }
        elseif([int] $AvgCPUval -lt 85) { "CPU usage is medium [ $AvgCPUval % ]" | LogMe -warning; $tests.AvgCPU = "WARNING", "$AvgCPUval %" }       
        elseif([int] $AvgCPUval -lt 95) { "CPU usage is high [ $AvgCPUval % ]" | LogMe -error; $tests.AvgCPU = "ERROR", "$AvgCPUval %" }
        elseif([int] $AvgCPUval -eq 101) { "CPU usage test failed" | LogMe -error; $tests.AvgCPU = "ERROR", "Err" }
        else { "CPU usage is Critical [ $AvgCPUval % ]" | LogMe -error; $tests.AvgCPU = "ERROR", "$AvgCPUval %" }   
        $AvgCPUval = 0

        # Check the Physical Memory usage       
        $UsedMemory = CheckMemoryUsage ($ControllerDNS)
        if( $UsedMemory -lt 75) { "Memory usage is normal [ $UsedMemory % ]" | LogMe -display; $tests.MemUsg = "SUCCESS", "$UsedMemory %" }
        elseif( [int] $UsedMemory -lt 85) { "Memory usage is medium [ $UsedMemory % ]" | LogMe -warning; $tests.MemUsg = "WARNING", "$UsedMemory %" }       
        elseif( [int] $UsedMemory -lt 95) { "Memory usage is high [ $UsedMemory % ]" | LogMe -error; $tests.MemUsg = "ERROR", "$UsedMemory %" }
        elseif( [int] $UsedMemory -eq 101) { "Memory usage test failed" | LogMe -error; $tests.MemUsg = "ERROR", "Err" }
        else { "Memory usage is Critical [ $UsedMemory % ]" | LogMe -error; $tests.MemUsg = "ERROR", "$UsedMemory %" }   
        $UsedMemory = 0  

       
            # Check Disk Usage 
            $HardDisk = CheckHardDiskUsage -hostname $ControllerDNS -deviceID "C:"
            if ($null -ne $HardDisk) {    
                $XAPercentageDS = $HardDisk.PercentageDS
                $frSpace = $HardDisk.frSpace
            
                If ( [int] $XAPercentageDS -gt 15) { "Disk Free is normal [ $XAPercentageDS % ]" | LogMe -display; $tests."CdriveFreespace" = "SUCCESS", "$frSpace GB" } 
                ElseIf ([int] $XAPercentageDS -eq 0) { "Disk Free test failed" | LogMe -error; $tests."CdriveFreespace" = "ERROR", "Err" }
                ElseIf ([int] $XAPercentageDS -lt 5) { "Disk Free is Critical [ $XAPercentageDS % ]" | LogMe -error; $tests."CdriveFreespace" = "ERROR", "$frSpace GB" } 
                ElseIf ([int] $XAPercentageDS -lt 15) { "Disk Free is Low [ $XAPercentageDS % ]" | LogMe -warning; $tests."CdriveFreespace" = "WARNING", "$frSpace GB" }     
                Else { "Disk Free is Critical [ $XAPercentageDS % ]" | LogMe -error; $tests."CdriveFreespace" = "ERROR", "$frSpace GB" }  
        
                $XAPercentageDS = 0
                $frSpace = 0
                $HardDisk = $null
            }
        
        $LBTime=$null
    # Check uptime (Query over WMI)
    $tests.WMI = "ERROR","Error"
    try { $wmi=Get-WmiObject -class Win32_OperatingSystem -computer $ControllerDNS }
    catch { $wmi = $null }

    # Perform WMI related checks
    if ($null -ne $wmi) {
        $tests.WMI = "SUCCESS", "Success"
        $LBTime=$wmi.ConvertToDateTime($wmi.Lastbootuptime)
        [TimeSpan]$uptime=New-TimeSpan $LBTime $(get-date)

        if ($uptime.days -lt $minUpTimeDaysDDC){
            "reboot warning, last reboot: {0:D}" -f $LBTime | LogMe -display -warning
            $tests.Uptime = "WARNING", (ToHumanReadable($uptime))
        }
        else { $tests.Uptime = "SUCCESS", (ToHumanReadable($uptime)) }
    }
    else { "WMI connection failed - check WMI for corruption" | LogMe -display -error }
}


" --- " | LogMe -display -progress
#Fill $tests into array
############## END PVS SECTION #############
  
# Check services
$services = Get-Service -ComputerName $ControllerDNS
  
if (($services | Where-Object {$_.Name -eq "CitrixBrokerService"}).Status -Match "Running") {
    "SPOOLER service running..." | LogMe
    $tests.Brokersrv = "SUCCESS","Success"
}
else {
    "SPOOLER service stopped" | LogMe -display -error
    $tests.Brokersrv  = "ERROR","Error"
}


 if (($services | Where-Object {$_.Name -eq "CitrixHostService"}).Status -Match "Running") {
    "SPOOLER service running..." | LogMe
    $tests.Host_srv = "SUCCESS","Success"
}
else {
    "SPOOLER service stopped" | LogMe -display -error
    $tests.Host_srv  = "ERROR","Error"
}
 

  if (($services | Where-Object {$_.Name -eq "CitrixADIdentityService"}).Status -Match "Running") {
    "SPOOLER service running..." | LogMe
    $tests.ADIdentity_srv = "SUCCESS","Success"
}
else {
    "SPOOLER service stopped" | LogMe -display -error
    $tests.ADIdentity_srv  = "ERROR","Error"
}
 

 
  if (($services | Where-Object {$_.Name -eq "CitrixMachineCreationService"}).Status -Match "Running") {
    "SPOOLER service running..." | LogMe
    $tests.MCS_srv = "SUCCESS","Success"
}
else {
    "SPOOLER service stopped" | LogMe -display -error
    $tests.MCS_srv  = "ERROR","Error"
}
 
   if (($services | Where-Object {$_.Name -eq "CitrixStorefront"}).Status -Match "Running") {
    "SPOOLER service running..." | LogMe
    $tests.Storefront_srv = "SUCCESS","Success"
}
else {
    "SPOOLER service stopped" | LogMe -display -error
    $tests.Storefront_srv  = "ERROR","Error"
}
 

$ControllerResults.$ControllerDNS = $tests

}

writeTableHeader $resultsHTM $XDControllerFirstheaderName $XDControllerHeaderNames $XDControllerHeaderWidths $XDControllerTableWidth
$ControllerResults | sort-object -property XDControllerFirstheaderName | ForEach-Object{ writeData $ControllerResults $resultsHTM $XDControllerHeaderNames }

writeTableFooter $resultsHTM
$rajj= gc $resultsHTM

$body1=calculate $POD6CLT "USSECXDA_P"
$body3=calculate2 $POD6CLT "USSECXDA_P"
$body5 =calculate4 $POD6CLT "USSECXDA_P"
$DEDB=calculate7 $POD6CLT "USSECXDA_P"
$body2=calculate1 $POD6STL "USSECXDA_P"

$body4=calculate3 $POD6STL "DEFRAXDA_P"

$body6=calculate5 $POD6STL "DEFRAXDA_P"
#invoke-expression -Command "D:\sunil\ghostsession1.ps1" 
$USDB=calculate417 $POD6STL "DEFRAXDA_P"
$currentDir1 = Split-Path $MyInvocation.MyCommand.Path
$outputpath1 = Join-Path $currentDir "" #add here a custom output folder if you wont have it on the same directory
$logfile1 = Join-Path $outputpath ("us.log")
$resultsHTM1 = Join-Path $outputpath ("us.htm") #add $outputdate in filename if you like

$XDControllerFirstheaderName1 = "ControllerServer"
$XDControllerHeaderNames1 = "Ping",     "State","DesktopsRegistered","CdriveFreespace","Brokersrv","Host_srv","ADIdentity_srv","MCS_srv","Storefront_srv"
$XDControllerHeaderWidths1 = "2",    "2",     "2"    ,"2","2",    "2",     "2"    ,"2","2"                
$XDControllerTableWidth1= 800
foreach ($disk1 in $diskLettersControllers1)
{
    $XDControllerHeaderNames1 += "$($disk)Freespace"
    $XDControllerHeaderWidths1 += "2"
}
$XDControllerHeaderNames1 +=      "AvgCPU",     "MemUsg",     "Uptime"
$XDControllerHeaderWidths1 +=    "2",        "2",        "2"

$ControllerResults1 = @{}
$Controllers1 = Get-BrokerController -AdminAddress "USPILVMPCXIXD01.ey.net"

foreach ($Controller1 in $Controllers1) {
$tests1 = @{}
  Clear-Content $resultsHTM1 -Force
#Name of $Controller
$ControllerDNS1 = $Controller1 | ForEach-Object{ $_.DNSName }
"Controller: $ControllerDNS1" | LogMe -display -progress
  
#Ping $Controller
$result1 = Ping $ControllerDNS1 100
if ($result1 -ne "SUCCESS") { $tests1.Ping = "Error", $result1 }
else{$tests1.Ping = "SUCCESS", $result1 }
 #

#Now when Ping is ok also check this:
  
#State of this controller
$ControllerState1 = $Controller1 | ForEach-Object{ $_.State }
"State: $ControllerState1" | LogMe -display -progress
if ($ControllerState1 -ne "Active") { $tests1.State = "ERROR", $ControllerState1 }
else { $tests1.State = "SUCCESS", $ControllerState1 }


  
#DesktopsRegistered on this controller
$ControllerDesktopsRegistered1 = $Controller1 | ForEach-Object{ $_.DesktopsRegistered }
"Registered: $ControllerDesktopsRegistered1" | LogMe -display -progress
$tests1.DesktopsRegistered = "NEUTRAL", $ControllerDesktopsRegistered1
  
#ActiveSiteServices on this controller
$ActiveSiteServices1 = $Controller1 | ForEach-Object{ $_.ActiveSiteServices }
"ActiveSiteServices $ActiveSiteServices1" | LogMe -display -progress
$tests1.ActiveSiteServices = "NEUTRAL", $ActiveSiteServices1


#==============================================================================================
#               CHECK CPU AND MEMORY USAGE 
#==============================================================================================

        # Check the AvgCPU value for 5 seconds
        $AvgCPUval1 = CheckCpuUsage ($ControllerDNS1)
        #$VDtests.LoadBalancingAlgorithm = "SUCCESS", "LB is set to BEST EFFORT"} 
            
        if( [int] $AvgCPUval1 -lt 75) { "CPU usage is normal [ $AvgCPUval1 % ]" | LogMe -display; $tests1.AvgCPU = "SUCCESS", "$AvgCPUval1 %" }
        elseif([int] $AvgCPUval1 -lt 85) { "CPU usage is medium [ $AvgCPUval1 % ]" | LogMe -warning; $tests1.AvgCPU = "WARNING", "$AvgCPUval1 %" }       
        elseif([int] $AvgCPUval1 -lt 95) { "CPU usage is high [ $AvgCPUval1 % ]" | LogMe -error; $tests1.AvgCPU = "ERROR", "$AvgCPUval1 %" }
        elseif([int] $AvgCPUval1 -eq 101) { "CPU usage test failed" | LogMe -error; $tests1.AvgCPU = "ERROR", "Err" }
        else { "CPU usage is Critical [ $AvgCPUval % ]" | LogMe -error; $tests1.AvgCPU = "ERROR", "$AvgCPUval1 %" }   
        $AvgCPUval1 = 0

        # Check the Physical Memory usage       
        $UsedMemory1 = CheckMemoryUsage ($ControllerDNS1)
        if( $UsedMemory1 -lt 75) { "Memory usage is normal [ $UsedMemory1 % ]" | LogMe -display; $tests1.MemUsg = "SUCCESS", "$UsedMemory1 %" }
        elseif( [int] $UsedMemory1 -lt 85) { "Memory usage is medium [ $UsedMemory1 % ]" | LogMe -warning; $tests1.MemUsg = "WARNING", "$UsedMemory1 %" }       
        elseif( [int] $UsedMemory1 -lt 95) { "Memory usage is high [ $UsedMemory1 % ]" | LogMe -error; $tests1.MemUsg = "ERROR", "$UsedMemory1 %" }
        elseif( [int] $UsedMemory1 -eq 101) { "Memory usage test failed" | LogMe -error; $tests1.MemUsg = "ERROR", "Err" }
        else { "Memory usage is Critical [ $UsedMemory % ]" | LogMe -error; $tests1.MemUsg = "ERROR", "$UsedMemory1 %" }   
        $UsedMemory1 = 0  

       
            # Check Disk Usage 
            $HardDisk1 = CheckHardDiskUsage -hostname $ControllerDNS1 -deviceID "C:"
            if ($null -ne $HardDisk1) {    
                $XAPercentageDS1 = $HardDisk1.PercentageDS
                $frSpace1 = $HardDisk1.frSpace
            
                If ( [int] $XAPercentageDS1 -gt 15) { "Disk Free is normal [ $XAPercentageDS1 % ]" | LogMe -display; $tests1."CdriveFreespace" = "SUCCESS", "$frSpace1 GB" } 
                ElseIf ([int] $XAPercentageDS1 -eq 0) { "Disk Free test failed" | LogMe -error; $tests1."CdriveFreespace" = "ERROR", "Err" }
                ElseIf ([int] $XAPercentageDS1 -lt 5) { "Disk Free is Critical [ $XAPercentageDS1 % ]" | LogMe -error; $tests1."CdriveFreespace" = "ERROR", "$frSpace1 GB" } 
                ElseIf ([int] $XAPercentageDS1 -lt 15) { "Disk Free is Low [ $XAPercentageDS1 % ]" | LogMe -warning; $tests1."CdriveFreespace" = "WARNING", "$frSpace1 GB" }     
                Else { "Disk Free is Critical [ $XAPercentageDS1 % ]" | LogMe -error; $tests1."CdriveFreespace" = "ERROR", "$frSpace1 GB" }  
        
                $XAPercentageDS1 = 0
                $frSpace1 = 0
                $HardDisk1 = $null
            }
        
        
    # Check uptime (Query over WMI)
    $tests1.WMI = "ERROR","Error"
    try { $wmi1=Get-WmiObject -class Win32_OperatingSystem -computer $ControllerDNS1 }
    catch { $wmi1 = $null }

    # Perform WMI related checks
    if ($null -ne $wmi1) {
        $tests1.WMI = "SUCCESS", "Success"
        $LBTime1=$wmi1.ConvertToDateTime($wmi.Lastbootuptime)
        [TimeSpan]$uptime1=New-TimeSpan $LBTime1 $(get-date)

        if ($uptime1.days -lt $minUpTimeDaysDDC1){
            "reboot warning, last reboot: {0:D}" -f $LBTime1 | LogMe -display -warning
            $tests1.Uptime = "WARNING", (ToHumanReadable($uptime1))
        }
        else { $tests1.Uptime = "SUCCESS", (ToHumanReadable($uptime1)) }
    }
    else { "WMI connection failed - check WMI for corruption" | LogMe -display -error }


" --- " | LogMe -display -progress
#Fill $tests into array
  
# Check services
$services1 = Get-Service -ComputerName $ControllerDNS1
  
if (($services1 | Where-Object {$_.Name -eq "CitrixBrokerService"}).Status -Match "Running") {
    "SPOOLER service running..." | LogMe
    $tests1.Brokersrv = "SUCCESS","Success"
}
else {
    "SPOOLER service stopped" | LogMe -display -error
    $tests1.Brokersrv  = "ERROR","Error"
}


 if (($services1 | Where-Object {$_.Name -eq "CitrixHostService"}).Status -Match "Running") {
    "SPOOLER service running..." | LogMe
    $tests1.Host_srv = "SUCCESS","Success"
}
else {
    "SPOOLER service stopped" | LogMe -display -error
    $tests1.Host_srv  = "ERROR","Error"
}
 

  if (($services1 | Where-Object {$_.Name -eq "CitrixADIdentityService"}).Status -Match "Running") {
    "SPOOLER service running..." | LogMe
    $tests1.ADIdentity_srv = "SUCCESS","Success"
}
else {
    "SPOOLER service stopped" | LogMe -display -error
    $tests1.ADIdentity_srv  = "ERROR","Error"
}
 

 
  if (($services1 | Where-Object {$_.Name -eq "CitrixMachineCreationService"}).Status -Match "Running") {
    "SPOOLER service running..." | LogMe
    $tests1.MCS_srv = "SUCCESS","Success"
}
else {
    "SPOOLER service stopped" | LogMe -display -error
    $tests1.MCS_srv  = "ERROR","Error"
}
 
   if (($services1 | Where-Object {$_.Name -eq "CitrixStorefront"}).Status -Match "Running") {
    "SPOOLER service running..." | LogMe
    $tests1.Storefront_srv = "SUCCESS","Success"
}
else {
    "SPOOLER service stopped" | LogMe -display -error
    $tests1.Storefront_srv  = "ERROR","Error"
}
 

$ControllerResults1.$ControllerDNS1 = $tests1

}
writeTableHeader $resultsHTM1 $XDControllerFirstheaderName1 $XDControllerHeaderNames1 $XDControllerHeaderWidths1 $XDControllerTableWidth1
$ControllerResults1 | sort-object -property XDControllerFirstheaderName | ForEach-Object{ writeData $ControllerResults1 $resultsHTM1 $XDControllerHeaderNames1 }

writeTableFooter $resultsHTM1
$rajj1= gc $resultsHTM1

$king=Get-Date -Format g

#$message= New-Object net.mail.Mailmessage 
#$message.To.Add("ML_EYOMC_VDI.GID@ey.net")
$Stamp=[System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($(Get-Date), [System.TimeZoneInfo]::Local.Id, 'India Standard Time')

$k=$jj
$k1=$jj1
 [array]$s=($jj ,$jj1,$jj2,$jj3)

$message="Hi Team,<br><br> Kindly find the summary of EY DE/US site Persistant/Pool VDI avaiablity status at :: $stamp    <br><br>DE Site VDI sessions summary "+ "$body5<br><br> </table> <br><br>DE Static VDI Summary"  + "$body1<br><br> </table> <br><br>DE Pool VDI Avaiablity in % Summary" + "$body3<br><br> </table> <br><br> DE Site Database and Hosting Unit summary Dashboard"+ "$DEDB<br><br> </table> <br><br>DE DDC and Broker services status Dashboard" + "$rajj<br><br> </table> <br>US Site VDI sessions summary"+ "$body6<br><br> </table> <br><br>US Static VDI Summary"    + "$body2<br><br> </table> <br><br>US Pool VDI Avaiablity in % Summary" + "$body4<br><br> </table> <br><br>US Site DataBase and Hosting Unit status Dashboard" + "$USDB<br><br> </table> <br><br>US Site DDC and Broker services health status Dashboard" + "$rajj1<br><br> </table> <br><br>Regards," + "<br>VDI Automation<br>"


#Send-MailMessage -From "US_DE_Site_summary@ey.com" -SmtpServer "smtp.ey.net" -subject "US/DE Site VDI summary on $Stamp IST"  -Body $message -BodyAsHtml:$true -To "sunil.saroja@ey.com" -Cc "sunil.saroja@ey.com" -Attachments $s -Priority High
#Send-MailMessage -From "US_DE_Site_summary@ey.com" -SmtpServer "smtp.ey.net" -subject "US/DE Site VDI summary on $Stamp IST"  -Body $message -BodyAsHtml:$true -To "sunil.saroja@ey.com" -Cc "sunil.saroja@ey.com" -Attachments $s -Priority High


Send-MailMessage -From "US_DE_Site_summary@ey.com" -SmtpServer "smtp.ey.net" -subject "US/DE Site VDI summary on $Stamp IST"  -Body $message -BodyAsHtml:$true -To "ML_EYOMC_VDI.GID@ey.net" -Cc "sunil.saroja@ey.com" -Attachments $s -Priority High
#Send-MailMessage -From "US_DE_Site_summary@ey.com" -SmtpServer "smtp.ey.net" -subject "US/DE Site VDI summary on $Stamp IST"  -Body $message -BodyAsHtml:$true -To "sunil.saroja@ey.com" -Cc "sunil.saroja@ey.com" -Attachments $s -Priority High
 

Link to comment
  • 0


$currentDir = Split-Path $MyInvocation.MyCommand.Path
$logfile = Join-Path $currentDir ("StorefrontHealthCheck.log")
$resultsHTM = Join-Path $currentDir ("StorefrontReport.htm")
$errorsHTM = Join-Path $currentDir ("StorefrontHealthCheckErrors.htm") 

#Header for Table 1 "DeploymentCheck Get-STFDeployment""
$DeploymentFirstHeaderName = "SiteId"
$DeploymentHeaderName = "HostbaseUrl", "URLReachable", "LastSourceServer","LastSyncStatus","LastSyncTime"
$DeploymentWidths = "4", "4", "4", "4", "4","4"
$DeploymentTableWidth  = 800

#Header for Table 2 "Clustermembers"
$ClusterMemberFirstFarmheaderName = "StoreFrontServer"
$ClusterMemberHeaderNames = "CitrixCredentialWalletSvC", "CitrixPeerResolutionSvC","WWWService","CFreespace","EFreespace","AvgCPU","MemUsg","Uptime"
$ClusterMemberWidths = "4", "4", "4", "4", "4", "4", "4", "4"
$ClusterMemberTablewidth  = 800


#==============================================================================================
#log function
function LogMe() {
Param(
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $logEntry,
[switch]$display,
[switch]$error,
[switch]$warning,
[switch]$progress
)

if ($error) {
$logEntry = "[ERROR] $logEntry" ; Write-Host "$logEntry" -Foregroundcolor Red}
elseif ($warning) {
Write-Warning "$logEntry" ; $logEntry = "[WARNING] $logEntry"}
elseif ($progress) {
Write-Host "$logEntry" -Foregroundcolor Green}
elseif ($display) {
Write-Host "$logEntry" }

#$logEntry = ((Get-Date -uformat "%D %T") + " - " + $logEntry)
$logEntry | Out-File $logFile -Append
}
#==============================================================================================
function Ping([string]$hostname, [int]$timeout = 200) {
$ping = new-object System.Net.NetworkInformation.Ping #creates a ping object
try {
$result = $ping.send($hostname, $timeout).Status.ToString()
} catch {
$result = "Failure"
}
return $result
}
#==============================================================================================
# The function will check the processor counter and check for the CPU usage. Takes an average CPU usage for 5 seconds. It check the current CPU usage for 5 secs.
Function CheckCpuUsage() 

    param ($hostname)
    Try { $CpuUsage=(get-counter -ComputerName $hostname -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5 -ErrorAction Stop | select -ExpandProperty countersamples | select -ExpandProperty cookedvalue | Measure-Object -Average).average
       $CpuUsage = "{0:N1}" -f $CpuUsage; return $CpuUsage
    } Catch { "Error returned while checking the CPU usage. Perfmon Counters may be fault" | LogMe -error; return 101 } 
}
#============================================================================================== 
# The function check the memory usage and report the usage value in percentage
Function CheckMemoryUsage() 

    param ($hostname)
   Try 
    {   $SystemInfo = (Get-WmiObject -computername $hostname -Class Win32_OperatingSystem -ErrorAction Stop | Select-Object TotalVisibleMemorySize, FreePhysicalMemory)
       $TotalRAM = $SystemInfo.TotalVisibleMemorySize/1MB 
       $FreeRAM = $SystemInfo.FreePhysicalMemory/1MB 
       $UsedRAM = $TotalRAM - $FreeRAM 
       $RAMPercentUsed = ($UsedRAM / $TotalRAM) * 100 
       $RAMPercentUsed = "{0:N0}" -f $RAMPercentUsed
       return $RAMPercentUsed
    } Catch { "Error returned while checking the Memory usage. Perfmon Counters may be fault" | LogMe -error; return 101 } 
}
#==============================================================================================
Function writeHtmlHeader
{
param($title, $fileName)
$date = ( Get-Date -format R)
$head = @"
<html>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>
<title>$title</title>
<STYLE TYPE="text/css">
<!--
td {
font-family: Tahoma;
font-size: 11px;
border-top: 1px solid #999999;
border-right: 1px solid #999999;
border-bottom: 1px solid #999999;
border-left: 1px solid #999999;
padding-top: 0px;
padding-right: 0px;
padding-bottom: 0px;
padding-left: 0px;
overflow: hidden;
}
body {
margin-left: 5px;
margin-top: 5px;
margin-right: 0px;
margin-bottom: 10px;
table {
table-layout:fixed; 
border: thin solid #000000;
}
-->
</style>
</head>
<body>
<table width='1200'>
<tr bgcolor='#CCCCCC'>
<td colspan='7' height='48' align='center' valign="middle">
<font face='tahoma' color='#003399' size='4'>
<strong>$title - $date</strong></font>
</td>
</tr>
</table>
"@
$head | Out-File $fileName
}
# ==============================================================================================
Function writeTableHeader
{
param($fileName, $firstheaderName, $headerNames, $headerWidths, $tablewidth)
$tableHeader = @"
<table width='$tablewidth'><tbody>
<tr bgcolor=#CCCCCC>
<td width='6%' align='center'><strong>$firstheaderName</strong></td>
"@
$i = 0
while ($i -lt $headerNames.count) {
$headerName = $headerNames[$i]
$headerWidth = $headerWidths[$i]
$tableHeader += "<td width='" + $headerWidth + "%' align='center'><strong>$headerName</strong></td>"
$i++
}
$tableHeader += "</tr>"
$tableHeader | Out-File $fileName -append
}
# ==============================================================================================
Function writeTableFooter
{
param($fileName)
"</table><br/>"| Out-File $fileName -append
}
#==============================================================================================
Function writeData
{
param($data, $fileName, $headerNames)

$data.Keys | sort | foreach {
$tableEntry += "<tr>"
$computerName = $_
$tableEntry += ("<td bgcolor='#CCCCCC' align=center><font color='#003399'>$computerName</font></td>")
#$data.$_.Keys | foreach {
$headerNames | foreach {
#"$computerName : $_" | LogMe -display
try {
if ($data.$computerName.$_[0] -eq "SUCCESS") { $bgcolor = "#387C44"; $fontColor = "#FFFFFF" }
elseif ($data.$computerName.$_[0] -eq "WARNING") { $bgcolor = "#FF7700"; $fontColor = "#FFFFFF" }
elseif ($data.$computerName.$_[0] -eq "ERROR") { $bgcolor = "#FF0000"; $fontColor = "#FFFFFF" }
else { $bgcolor = "#CCCCCC"; $fontColor = "#003399" }
$testResult = $data.$computerName.$_[1]
}
catch {
$bgcolor = "#CCCCCC"; $fontColor = "#003399"
$testResult = ""
}

$tableEntry += ("<td bgcolor='" + $bgcolor + "' align=center><font color='" + $fontColor + "'>$testResult</font></td>")
}

$tableEntry += "</tr>"


}

$tableEntry | Out-File $fileName -append
}
# ==============================================================================================
 

Link to comment
  • 0

function DeploymentCheck {
# =======  Check ====================================================================
"Read some Deployment Parameters" | LogMe -display -progress
" " | LogMe -display -progress

$global:DeploymentResults = @{}
$STFDeployment = Get-STFDeployment

$global:DeploymentSiteId = $STFDeployment.SiteId 
"StoreFront Deployment SiteId: $global:DeploymentSiteId" | LogMe -display -progress

$STFDeploymenttests = @{}

$DeploymentHostbaseUrl = $STFDeployment | %{ $_.HostbaseUrl}
$STFDeploymenttests.HostbaseUrl = "NEUTRAL", $DeploymentHostbaseUrl
"StoreFront HostbaseUrl: $DeploymentHostbaseUrl" | LogMe -display -progress

#HTTP Check
# => currently not working
   $HTTPS_Request = [System.Net.WebRequest]::Create($DeploymentHostbaseUrl)
   $httpsstatus = $HTTPS_Request.HaveResponse
   $httpsstatus = $HTTPS_Request.GetResponse() | select StatusCode
   $httpsstatus.StatusCode

if ($httpsstatus.StatusCode -ne "OK") { $STFDeploymenttests.URLReachable = "ERROR", $httpsstatus.StatusCode }
else { $STFDeploymenttests.URLReachable = "SUCCESS", $httpsstatus.StatusCode}


#ReplicationChecks (Registry)
$ConfigurationReplicationSource = (Get-ItemProperty  HKLM:\SOFTWARE\Citrix\DeliveryServices\ConfigurationReplication -Name LastSourceServer).LastSourceServer
$syncsctate = (Get-ItemProperty  HKLM:\SOFTWARE\Citrix\DeliveryServices\ConfigurationReplication -Name LastUpdateStatus).LastUpdateStatus
$endsyncdate = (Get-ItemProperty  HKLM:\SOFTWARE\Citrix\DeliveryServices\ConfigurationReplication -Name LastEndTime).LastEndTime

$STFDeploymenttests.LastSourceServer = "NEUTRAL", $ConfigurationReplicationSource
if ($syncsctate -ne "Complete") { $STFDeploymenttests.LastSyncStatus = "ERROR", $syncsctate }
else { $STFDeploymenttests.LastSyncStatus = "SUCCESS", $syncsctate}
$STFDeploymenttests.LastSyncTime = "NEUTRAL", $endsyncdate

$global:DeploymentResults.$global:DeploymentSiteId = $STFDeploymenttests
}


function ClusterMemberCheck {
# =======  Check ====================================================================
"Read some Deployment Parameters" | LogMe -display -progress
" " | LogMe -display -progress

$global:ClusterMemberResults = @{}
$Clustermembers = Get-DSClusterMembersName
$hostnames = $clustermembers | %{ $_.Hostnames }

foreach($STFServerName in $hostnames){
$ClusterMembertests = @{}

$STFServerName
"StoreFront-Server: $STFServerName " | LogMe -display -progress


# Ping server 
$result = Ping $STFServerName  100
"Ping: $result" | LogMe -display -progress
if ($result -ne "SUCCESS") { $ClusterMembertests.Ping = "ERROR", $result }
else { $ClusterMembertests.Ping = "SUCCESS", $result 

# Check services
        if ((Get-Service -Name "Citrix Credential Wallet" -ComputerName $STFServerName).Status -Match "Running") {
            "CitrixCredentialWalletSvC running..." | LogMe
            $ClusterMembertests.CitrixCredentialWalletSvC = "SUCCESS", "Success"
        } else {
            "CitrixCredentialWalletSvC service stopped"  | LogMe -display -error
            $ClusterMembertests.CitrixCredentialWalletSvC = "ERROR", "Error"
        }
            
        if ((Get-Service -Name "Citrix Peer Resolution Service" -ComputerName $STFServerName).Status -Match "Running") {
            "Citrix Peer Resolution Service running..." | LogMe
            $ClusterMembertests.CitrixPeerResolutionSvC = "SUCCESS","Success"
        } else {
            "Citrix Peer Resolution Service service stopped"  | LogMe -display -error
            $ClusterMembertests.CitrixPeerResolutionSvC = "ERROR","Error"
        }


            if ((Get-Service -Name "W3SVC" -ComputerName $STFServerName).Status -Match "Running") {
            "World Wide Web Publishing Service service running..." | LogMe
            $ClusterMembertests.WWWService = "SUCCESS","Success"
        } else {
            "CWorld Wide Web Publishing Service stopped"  | LogMe -display -error
            $ClusterMembertests.WWWService = "ERROR","Error"
        }


        #==============================================================================================
        #               CHECK CPU AND MEMORY USAGE 
        #==============================================================================================

       # Check the AvgCPU value for 5 seconds
       $AvgCPUval = CheckCpuUsage ($STFServerName)
        if( [int] $AvgCPUval -lt 75) { "CPU usage is normal [ $AvgCPUval % ]" | LogMe -display; $ClusterMembertests.AvgCPU = "SUCCESS", "$AvgCPUval %" }
        elseif([int] $AvgCPUval -lt 85) { "CPU usage is medium [ $AvgCPUval % ]" | LogMe -warning; $ClusterMembertests.AvgCPU = "WARNING", "$AvgCPUval %" }       
        elseif([int] $AvgCPUval -lt 95) { "CPU usage is high [ $AvgCPUval % ]" | LogMe -error; $ClusterMembertests.AvgCPU = "ERROR", "$AvgCPUval %" }
        elseif([int] $AvgCPUval -eq 101) { "CPU usage test failed" | LogMe -error; $ClusterMembertests.AvgCPU = "ERROR", "Err" }
       else { "CPU usage is Critical [ $AvgCPUval % ]" | LogMe -error; $ClusterMembertests.AvgCPU = "ERROR", "$AvgCPUval %" }   
        $AvgCPUval = 0

       # Check the Physical Memory usage       
       $UsedMemory = CheckMemoryUsage ($STFServerName)
       if( [int] $UsedMemory -lt 75) { "Memory usage is normal [ $UsedMemory % ]" | LogMe -display; $ClusterMembertests.MemUsg = "SUCCESS", "$UsedMemory %" }
        elseif([int] $UsedMemory -lt 85) { "Memory usage is medium [ $UsedMemory % ]" | LogMe -warning; $ClusterMembertests.MemUsg = "WARNING", "$UsedMemory %" }       
        elseif([int] $UsedMemory -lt 95) { "Memory usage is high [ $UsedMemory % ]" | LogMe -error; $ClusterMembertests.MemUsg = "ERROR", "$UsedMemory %" }
        elseif([int] $UsedMemory -eq 101) { "Memory usage test failed" | LogMe -error; $ClusterMembertests.MemUsg = "ERROR", "Err" }
       else { "Memory usage is Critical [ $UsedMemory % ]" | LogMe -error; $ClusterMembertests.MemUsg = "ERROR", "$UsedMemory %" }   
        $UsedMemory = 0  

       # Check C Disk Usage 
        $ClusterMembertests.CFreespace = "NEUTRAL", "N/A" 
       $HardDisk = Get-WmiObject Win32_LogicalDisk -ComputerName $STFServerName -Filter "DeviceID='C:'" | Select-Object Size,FreeSpace 
       $DiskTotalSize = $HardDisk.Size 
       $DiskFreeSpace = $HardDisk.FreeSpace 
       $frSpace=[Math]::Round(($DiskFreeSpace/1073741824),2)

       $PercentageDS = (($DiskFreeSpace / $DiskTotalSize ) * 100); $PercentageDS = "{0:N2}" -f $PercentageDS 

       If ( [int] $PercentageDS -gt 15) { "Disk Free is normal [ $PercentageDS % ]" | LogMe -display; $ClusterMembertests.CFreespace = "SUCCESS", "$frSpace GB" } 
        ElseIf ([int] $PercentageDS -lt 15) { "Disk Free is Low [ $PercentageDS % ]" | LogMe -warning; $ClusterMembertests.CFreespace = "WARNING", "$frSpace GB" }     
        ElseIf ([int] $PercentageDS -lt 5) { "Disk Free is Critical [ $PercentageDS % ]" | LogMe -error; $ClusterMembertests.CFreespace = "ERROR", "$frSpace GB" } 
        ElseIf ([int] $PercentageDS -eq 0) { "Disk Free test failed" | LogMe -error; $ClusterMembertests.CFreespace = "ERROR", "Err" } 
       Else { "Disk Free is Critical [ $PercentageDS % ]" | LogMe -error; $ClusterMembertests.CFreespace = "ERROR", "$frSpace GB" }   
       $PercentageDS = 0     


         # Check E Disk Usage 
        $ClusterMembertests.EFreespace = "NEUTRAL", "N/A" 
       $HardDisk = Get-WmiObject Win32_LogicalDisk -ComputerName $STFServerName -Filter "DeviceID='E:'" | Select-Object Size,FreeSpace 
       $DiskTotalSize = $HardDisk.Size 
       $DiskFreeSpace = $HardDisk.FreeSpace 
       $frSpace=[Math]::Round(($DiskFreeSpace/1073741824),2)

       $PercentageDS = (($DiskFreeSpace / $DiskTotalSize ) * 100); $PercentageDS = "{0:N2}" -f $PercentageDS 

       If ( [int] $PercentageDS -gt 15) { "Disk Free is normal [ $PercentageDS % ]" | LogMe -display; $ClusterMembertests.EFreespace = "SUCCESS", "$frSpace GB" } 
        ElseIf ([int] $PercentageDS -lt 15) { "Disk Free is Low [ $PercentageDS % ]" | LogMe -warning; $ClusterMembertests.EFreespace = "WARNING", "$frSpace GB" }     
        ElseIf ([int] $PercentageDS -lt 5) { "Disk Free is Critical [ $PercentageDS % ]" | LogMe -error; $ClusterMembertests.EFreespace = "ERROR", "$frSpace GB" } 
        ElseIf ([int] $PercentageDS -eq 0) { "Disk Free test failed" | LogMe -error; $ClusterMembertests.EFreespace = "ERROR", "Err" } 
       Else { "Disk Free is Critical [ $PercentageDS % ]" | LogMe -error; $ClusterMembertests.EFreespace = "ERROR", "$frSpace GB" }   
       $PercentageDS = 0     
        
        #==============================================================================================
        #               CHECK EventLog Last 24 h
        #==============================================================================================
        $LogEventsLast24 = ""
        $LogEventsLast24 = Invoke-Command -ComputerName $STFServerName -ScriptBlock {Get-EventLog 'Citrix Delivery Services' -After (Get-Date).AddHours(-24)}
        $ClusterMembertests.EventsLogLast24h = "NEUTRAL", $LogEventsLast24.Count  

            
        
    }


$global:ClusterMemberResults.$STFServerName = $ClusterMembertests
}
}

Link to comment
  • 0

#==============================================================================================
#HTML function
function WriteHTML() {

# ======= Write all results to an html file =================================================
Write-Host ("Saving results to html report: " + $resultsHTM)
writeHtmlHeader "StoreFront  Report " $resultsHTM

writeTableHeader $resultsHTM $DeploymentFirstHeaderName $DeploymentHeaderName $DeploymentWidths $DeploymentTableWidth
$global:DeploymentResults | sort-object -property SiteId | % { writeData $DeploymentResults $resultsHTM $DeploymentHeaderName}
writeTableFooter $resultsHTM


writeTableHeader $resultsHTM $ClusterMemberFirstFarmheaderName $ClusterMemberHeaderNames $ClusterMemberWidths $ClusterMemberTablewidth
$global:DeploymentResults | sort-object -property STFServerName | % { writeData $ClusterMemberResults $resultsHTM $ClusterMemberHeaderNames}
writeTableFooter $resultsHTM


writeHtmlFooter $resultsHTM
#send email
$emailSubject = ("$emailSubjectStart - StoreFront - " + (Get-Date -format R))
$global:mailMessageParameters = @{
From = $emailFrom
To = $emailTo
Subject = $emailSubject
SmtpServer = $smtpServer
Body = (gc $resultsHTM) | Out-String
Attachment = $resultsHTM
}
}
#==============================================================================================
#Mail function
# Send mail 
function SendMail() {
Send-MailMessage @global:mailMessageParameters -BodyAsHtml -Priority $mailprio
}

#==============================================================================================
# == MAIN SCRIPT ==
#==============================================================================================
$scriptstart = Get-Date
rm $logfile -force -EA SilentlyContinue
"Begin with Citrix StoreFront HealthCheck" | LogMe -display -progress
" " | LogMe -display -progress

DeploymentCheck
ClusterMemberCheck
WriteHTML

if ($PerformSendMail -eq "yes") {
"Initiate send of Email " | LogMe
SendMail
} else {
"send of Email  skipped" | LogMe
}

$scriptend = Get-Date
$scriptruntime =  $scriptend - $scriptstart | select TotalSeconds
$scriptruntimeInSeconds = $scriptruntime.TotalSeconds
#Write-Host $scriptruntime.TotalSeconds
"Script was running for $scriptruntimeInSeconds " | LogMe -display -progress
 

Link to comment
  • 0

Function writeHtmlFooter
{
param($fileName)
@"
<table>
<table width='1200'>
<tr bgcolor='#CCCCCC'>
<td colspan='7' height='25' align='left'>
<br>
<font face='courier' color='#000000' size='2'><strong>Retry Threshold =</strong></font><font color='#003399' face='courier' size='2'> $retrythresholdWarning<tr></font><br>
<tr bgcolor='#CCCCCC'>
</td>
</tr>
<tr bgcolor='#CCCCCC'>
</tr>
</table>
</body>
</html>

"@ | Out-File $FileName -append

}

Link to comment
  • 0

$Uris = 'www.google.com', 'www.microsoft.com', 'www.stackoverflow.com', 'www.example.com', 'www.example1234567.com','www.bing.com'
 $PathTxt = "C:\junk\Uri.txt"
 $PathCsv = "C:\junk\Uri.csv"
 $uriList = @()
    
   foreach ($uri in $Uris) {
     $uriObject = New-Object PSObject
     $Response = ""
     try {
     $Response = Invoke-WebRequest -Uri $uri -ErrorAction SilentlyContinue -UseBasicParsing -DisableKeepAlive
     $uriObject | Add-Member -MemberType NoteProperty -Name "URL" -Value $Response.BaseResponse.ResponseUri
     $uriObject | Add-Member -MemberType NoteProperty -Name "Status" -Value $Response.StatusCode
     $uriObject | Add-Member -MemberType NoteProperty -Name "Description" -Value $Response.StatusDescription
     Write-Host $Response.StatusCode $Response.StatusDescription $Response.BaseResponse.ResponseUri -ForegroundColor Green
     $uriList += $uriObject
   }
   catch {
     Write-Host "Unable to reach $uri" -ForegroundColor Red
     $uriObject | Add-Member -MemberType NoteProperty -Name "URL" -Value $uri
     $uriObject | Add-Member -MemberType NoteProperty -Name "Status" -Value "Not Responding"
     $uriObject | Add-Member -MemberType NoteProperty -Name "Description" -Value "Error"
     $uriList += $uriObject
   }
 }
    
 $uriList | Out-File -FilePath $PathTxt
 $uriList | Export-Csv -Path $PathCsv -NoTypeInformation

Link to comment
  • 0

$maxUpTimeDays = 10


#Import-Module 'C:\Program Files\Citrix\Provisioning Services Console\Citrix.PVS.SnapIn.dll'

asnp *citrix*

Set-PvsConnection -Server tor163pvs01.bell.corp.bce.ca -port 54321

$maxUpTimeDays = 10

#Set-StrictMode -Version Latest

$ReportDate = (Get-Date   -UFormat "%A, %d. %B %Y %R")
#==============================================================================================
 
$currentDir = Split-Path $MyInvocation.MyCommand.Path
$outputpath = Join-Path $currentDir "" #add here a custom output folder if you wont have it on the same directory
$outputdate = Get-Date -Format 'yyyyMMddHHmm'
$logfile = Join-Path $outputpath ("PVSHealthCheck.log")
$resultsHTM = Join-Path $outputpath ("PVSFarmReport.htm") #add $outputdate in filename if you like
$errorsHTM = Join-Path $currentDir ("PVSHealthCheckErrors.htm") 

#Header for Table 3 "PV Server"
$PVSfirstheaderName = "PVS Server"
$PVSHeaderNames =  "Active", "deviceCount","SoapService","StreamService"
$PVSheaderWidths =  "4", "4","4","4"
$PVStablewidth = 1200
foreach ($disk in $diskLetters)
{
    $PVSHeaderNames += "$($disk)Freespace"
    $PVSheaderWidths += "4"
}
$PVSHeaderNames += "AvgCPU","MemUsg", "Uptime"
$PVSheaderWidths += "4","4","4"


#Header for Table 4 "Farm"
$PVSFirstFarmheaderName = "Farm"
$PVSFarmHeaderNames = "sites","DBServerName", "DatabaseName", "OfflineDB", "LicenseServer","Totaltargetdevice","TotalVdisk"
$PVSFarmWidths = "3", "3", "3", "3", "3", "3", "3"
$PVSFarmTablewidth = 1200

 
#==============================================================================================
#log function
function LogMe() {
Param(
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $logEntry,
[switch]$display,
[switch]$error,
[switch]$warning,
[switch]$progress
)
 
 if ($error) {
$logEntry = "[ERROR] $logEntry" ; Write-Host "$logEntry" -Foregroundcolor Red}
elseif ($warning) {
Write-Warning "$logEntry" ; $logEntry = "[WARNING] $logEntry"}
elseif ($progress) {
Write-Host "$logEntry" -Foregroundcolor Green}
elseif ($display) {
Write-Host "$logEntry" }
  
 #$logEntry = ((Get-Date -uformat "%D %T") + " - " + $logEntry)
$logEntry | Out-File $logFile -Append
}
#==============================================================================================
Function CheckCpuUsage() 

    param ($hostname)
    Try { $CpuUsage=(get-counter -ComputerName $hostname -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5 -ErrorAction Stop | select -ExpandProperty countersamples | select -ExpandProperty cookedvalue | Measure-Object -Average).average
        $CpuUsage = "{0:N1}" -f $CpuUsage; return $CpuUsage
    } Catch { "Error returned while checking the CPU usage. Perfmon Counters may be at fault." | LogMe -error; return 101 } 
}

#============================================================================================== 
# This function check the memory usage and report the usage value in percentage
#==============================================================================================
Function CheckMemoryUsage() 

    param ($hostname)
    Try 
    {   $SystemInfo = (Get-WmiObject -computername $hostname -Class Win32_OperatingSystem -ErrorAction Stop | Select-Object TotalVisibleMemorySize, FreePhysicalMemory)
        $TotalRAM = $SystemInfo.TotalVisibleMemorySize/1MB 
        $FreeRAM = $SystemInfo.FreePhysicalMemory/1MB 
        $UsedRAM = $TotalRAM - $FreeRAM 
        $RAMPercentUsed = ($UsedRAM / $TotalRAM) * 100 
        $RAMPercentUsed = "{0:N2}" -f $RAMPercentUsed
        return $RAMPercentUsed     } 
    Catch { "Error returned while checking the Memory usage. Perfmon Counters may be at fault" | LogMe -error; return 101 } 
}
#==============================================================================================
Function writeHtmlHeader
{
param($title, $fileName)
$date = Get-Date -Format g 
$head =@"
    <html>
    <head>
    <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>
    <meta http-equiv="refresh" content="900">
    <title>$title</title>
    <STYLE TYPE="text/css">
    <!--
    td {
        font-family: Lao UI;
        font-size: 11px;
        border-top: 1px solid #999999;
        border-right: 1px solid #999999;
        border-bottom: 1px solid #999999;
        border-left: 1px solid #999999;
        padding-top: 0px;
        padding-right: 0px;
        padding-bottom: 0px;
        padding-left: 0px;
        overflow: hidden;}

    .header {
        font-family: Tahoma;
        font-size: 40px;
        font-weight:bold;
        border-top: 1px solid #999999;
        border-right: 1px solid #999999;
        border-bottom: 1px solid #999999;
        border-left: 1px solid #999999;
        padding-top: 0px;
        padding-right: 0px;
        padding-bottom: 0px;
        padding-left: 0px;
        overflow: hidden;
        color:#FFFFFF;
        text-shadow:2px 2px 10px #000000;

        }
    body {
        margin-left: 5px;
        margin-top: 5px;
        margin-right: 0px;
        margin-bottom: 10px;
        table {
            table-layout:fixed;
            border: thin solid #FFFFFF;}
    .shadow {
        height: 1em;
        filter: Glow(Color=#000000,
        Direction=135,
        Strength=5);}
        -->
    </style>
    </head>
    <body>
       <table class="header" width='100%'>
        <tr bgcolor='#FAFAFA'>
        
        
<td class="header" width='1000' align='center' valign="middle" style="background-image:url('$itag1'); background-repeat: repeat; background-position: center; ">
        <p class="shadow"><font face='Tahoma' color='#021399' size='200'><strong>BELL PVS Farm Health Status - $date EST</strong></font>
        </td>
</tr>
</table>
"@
$head | Out-File $fileName
}
# ==============================================================================================
Function writeTableHeader
{
param($fileName, $firstheaderName, $headerNames, $headerWidths, $tablewidth)
$tableHeader = @"
<table width='$tablewidth'><tbody>
<tr bgcolor=#CCCCCC>
<td width='6%' align='center'><strong>$firstheaderName</strong></td>
"@
$i = 0
while ($i -lt $headerNames.count) {
$headerName = $headerNames[$i]
$headerWidth = $headerWidths[$i]
$tableHeader += "<td width='" + $headerWidth + "%' align='center'><strong>$headerName</strong></td>"
$i++
}
$tableHeader += "</tr>"
$tableHeader | Out-File $fileName -append
}
# ==============================================================================================
Function writeTableFooter
{
param($fileName)
"</table><br/>"| Out-File $fileName -append
}
#==============================================================================================
Function writeData
{
param($data, $fileName, $headerNames)
  
 $data.Keys | sort | foreach {
$tableEntry += "<tr>"
$computerName = $_
$tableEntry += ("<td bgcolor='#CCCCCC' align=center><font color='#003399'>$computerName</font></td>")
#$data.$_.Keys | foreach {
$headerNames | foreach {
#"$computerName : $_" | LogMe -display
try {
if ($data.$computerName.$_[0] -eq "SUCCESS") { $bgcolor = "#387C44"; $fontColor = "#FFFFFF" }
elseif ($data.$computerName.$_[0] -eq "WARNING") { $bgcolor = "#FF7700"; $fontColor = "#FFFFFF" }
elseif ($data.$computerName.$_[0] -eq "ERROR") { $bgcolor = "#FF0000"; $fontColor = "#FFFFFF" }
else { $bgcolor = "#CCCCCC"; $fontColor = "#003399" }
$testResult = $data.$computerName.$_[1]
}
catch {
$bgcolor = "#CCCCCC"; $fontColor = "#003399"
$testResult = ""
}
  
 $tableEntry += ("<td bgcolor='" + $bgcolor + "' align=center><font color='" + $fontColor + "'>$testResult</font></td>")
}
  
 $tableEntry += "</tr>"
  
  
 }
  
 $tableEntry | Out-File $fileName -append
}
# ==============================================================================================
 

Link to comment
  • 0


 function Farmcheck() {
# ======= PVS Farm Check ====================================================================
"Read some PVS Farm Parameters" | LogMe -display -progress
" " | LogMe -display -progress
$global:PVSFarmResults = @{}
$PVSfarm = Get-PVSFarm
$site = Get-PvsSite
$tdevice = Get-PvsDevice
$tdisk = Get-PvsDiskInfo
$global:farmname_short = $PVSfarm.FarmName 
$PVSFarmtests = @{}

$site1 = $site | %{ $_.SiteName }
$PVSFarmtests.sites = "NEUTRAL", $site1

$tdivecount = $tdevice | %{ $_.DeviceName }
$tdivecount = ($tdivecount).count
$PVSFarmtests.Totaltargetdevice = "NEUTRAL", $tdivecount

$Tdiskcount = $tdisk| %{ $_.DiskLocatorName }
$Tdiskcount = ($Tdiskcount).count
$PVSFarmtests.TotalVdisk = "NEUTRAL", $Tdiskcount


$DBServer = $PVSFarm | %{ $_.DatabaseServerName }
$PVSFarmtests.DBServerName = "NEUTRAL", $DBServer

$dbname = $PVSFarm | %{ $_.DatabaseName }
$PVSFarmtests.databaseName = "NEUTRAL", $dbname

$OfflineDB = $PVSFarm | %{ $_.OfflineDatabaseSupportEnabled }
$PVSFarmtests.OfflineDB = "NEUTRAL", $OfflineDB

$LicenseServer = $PVSFarm | %{ $_.LicenseServer }
$PVSFarmtests.LicenseServer = "NEUTRAL", $LicenseServer

$global:PVSFarmResults.$global:farmname_short = $PVSFarmtests
}


function PVSServerCheck() {
# ======= PVS Server Check ==================================================================
"Check PVS Servers" | LogMe -display -progress
" " | LogMe -display -progress
 
$global:PVSResults = @{}
$ServerName = gc "D:\sunil\jenkins\infra\pvs\PVSSERVER.txt"
#$allPVSServer =  Get-PvsServer -Servername MTRL163PVS01.bell.corp.bce.ca -port 54321 -ServerName $ServerName |Select-Object Servername

#foreach ($server in $servers) {
#$server | LogMe -display
    

    #$tests = @{}

foreach($PVServerName_short in $ServerName){
$PVStests = @{}
  
#$PVServerName_short = $PVServerName | %{ $_.ServerName }
#"PVS-Server: $PVServerName_short" | LogMe -display -progress
#$PVServerName_short

# Ping server 

# Check services
        if ((Get-Service -Name "soapserver" -ComputerName $PVServerName_short).Status -Match "Running") {
            "SoapService running..." | LogMe
            $PVStests.SoapService = "SUCCESS", "Success"
        } else {
            "SoapService service stopped"  | LogMe -display -error
            $PVStests.SoapService = "ERROR", "Error"
        }
            
        if ((Get-Service -Name "StreamService" -ComputerName $PVServerName_short).Status -Match "Running") {
            "StreamService service running..." | LogMe
            $PVStests.StreamService = "SUCCESS","Success"
        } else {
            "StreamService service stopped"  | LogMe -display -error
            $PVStests.StreamService = "ERROR","Error"
        }
            
        if ((Get-Service -Name "BNTFTP" -ComputerName $PVServerName_short).Status -Match "Running") {
            "TFTP service running..." | LogMe
            $PVStests.TFTPService = "SUCCESS","Success"
        } else {
            "TFTP  service stopped"  | LogMe -display -error
            $PVStests.TFTPService = "ERROR","Error"
        
 }
 
 
 
       #==============================================================================================
        #               CHECK CPU AND MEMORY USAGE 
        #==============================================================================================
        # Check the AvgCPU value for 5 seconds
        $AvgCPUval = CheckCpuUsage ($PVServerName_short)
        if( [int] $AvgCPUval -lt 80) { "CPU usage is normal [ $AvgCPUval % ]" | LogMe -display; $PVStests.AvgCPU = "SUCCESS", " $AvgCPUval % " }
        elseif([int] $AvgCPUval -lt 90) { "CPU usage is medium [ $AvgCPUval % ]" | LogMe -warning; $PVStests.AvgCPU = "WARNING", " $AvgCPUval % " }       
        elseif([int] $AvgCPUval -lt 95) { "CPU usage is high [ $AvgCPUval % ]" | LogMe -error; $PVStests.AvgCPU = "ERROR", " $AvgCPUval % " }
        elseif([int] $AvgCPUval -eq 101) { "CPU usage test failed" | LogMe -error; $PVStests.AvgCPU = "ERROR", "Err" }
        else { "CPU usage is Critical [ $AvgCPUval % ]" | LogMe -error; $PVStests.AvgCPU = "ERROR", ($AvgCPUval) }   
        $AvgCPUval = 0
 
        # Check the Physical Memory usage       
        $UsedMemory = CheckMemoryUsage ($PVServerName_short)
        if( [int] $UsedMemory -lt 80) { "Memory usage is normal [ $UsedMemory % ]" | LogMe -display; $PVStests.MemUsg = "SUCCESS", " $UsedMemory % " }
        elseif([int] $UsedMemory -lt 85) { "Memory usage is medium [ $UsedMemory % ]" | LogMe -warning; $PVStests.MemUsg = "WARNING",  "$UsedMemory % " }       
        elseif([int] $UsedMemory -lt 90) { "Memory usage is high [ $UsedMemory % ]" | LogMe -error; $PVStests.MemUsg = "ERROR", " $UsedMemory % " }
        elseif([int] $UsedMemory -eq 101) { "Memory usage test failed" | LogMe -error; $PVStests.MemUsg = "ERROR", "Err" }
        else { "Memory usage is Critical [ $UsedMemory % ]" | LogMe -error; $PVStests.MemUsg = "ERROR", " $UsedMemory % " }   
        $UsedMemory = 0  

        foreach ($disk in $diskLetters)
        {
            # Check Disk Usage 
            $HardDisk = Get-WmiObject Win32_LogicalDisk -ComputerName $PVServerName_short -Filter "DeviceID='$($disk):'" | Select-Object Size,FreeSpace 
            $DiskTotalSize = $HardDisk.Size 
            $DiskFreeSpace = $HardDisk.FreeSpace 
            $frSpace=[Math]::Round(($DiskFreeSpace/1073741824),2)

            $PercentageDS = (($DiskFreeSpace / $DiskTotalSize ) * 100); $PercentageDS = "{0:N2}" -f $PercentageDS 

            If ( [int] $PercentageDS -gt 15) { "Disk Free is normal [ $PercentageDS % ]" | LogMe -display; $PVStests."$($disk)Freespace" = "SUCCESS", "$frSpace GB" } 
            ElseIf ([int] $PercentageDS -lt 15) { "Disk Free is Low [ $PercentageDS % ]" | LogMe -warning; $PVStests."$($disk)Freespace" = "WARNING", "$frSpace GB" }     
            ElseIf ([int] $PercentageDS -lt 5) { "Disk Free is Critical [ $PercentageDS % ]" | LogMe -error; $PVStests."$($disk)Freespace" = "ERROR", "$frSpace GB" } 
            ElseIf ([int] $PercentageDS -eq 0) { "Disk Free test failed" | LogMe -error; $PVStests."$($disk)Freespace" = "ERROR", "Err" } 
            Else { "Disk Free is Critical [ $PercentageDS % ]" | LogMe -error; $PVStests."$($disk)Freespace" = "ERROR", "$frSpace GB" }   
            $PercentageDS = 0   
        }
         # Check uptime (Query over WMI)
    #$PVStests.WMI = "ERROR","Error"
    try { $wmi=Get-WmiObject -class Win32_OperatingSystem -computer $PVServerName_short }
    catch { $wmi = $null }

    # Perform WMI related checks
    if ($wmi -ne $null) {
        $PVStests.WMI = "SUCCESS", "Success"
        $LBTime=$wmi.ConvertToDateTime($wmi.Lastbootuptime)
        [TimeSpan]$uptime=New-TimeSpan $LBTime $(get-date)

        if ($uptime.days -lt $maxUpTimeDays){
            "reboot warning, last reboot: {0:D}" -f $LBTime | LogMe -display -warning
            $PVStests.Uptime = "WARNING", (ToHumanReadable($uptime))
        }
        else { $PVStests.Uptime = "SUCCESS", (ToHumanReadable($uptime)) }
    }
   # else { "WMI connection failed - check WMI for corruption" | LogMe -display -error }

        
            
         


   
  
#Check PVS Activity Status (over PVS Framework)
$serverstatus = Get-PvsServerStatus -ServerName $PVServerName_short
$actviestatus = $serverstatus.Status
if ($actviestatus -eq 1) { $PVStests.Active = "SUCCESS", "active" }
else { $PVStests.Active = "Error","inactive" }
"PVS-Active-Status: $actviestatus" | LogMe -display -progress


  
#Check PVS deviceCount
$numberofdevices = $serverstatus.DeviceCount
if ($numberofdevices -ge 1) { $PVStests.deviceCount = "SUCCESS", " $numberofdevices active" }
else { $PVStests.deviceCount = "WARNING","No devices on this server" }
"Number of devices: $numberofdevices" | LogMe -display -progress

$global:PVSResults.$PVServerName_short = $PVStests
  
}
}


#==============================================================================================
#HTML function
function WriteHTML() {
 
    # ======= Write all results to an html file =================================================
    Write-Host ("Saving results to html report: " + $resultsHTM)
    # STBE writeHtmlHeader "PVS Farm Report $global:farmname_short" $resultsHTM
    #$EnvironmentName = "$EnvironmentName $global:farmname_short"
    writeHtmlHeader "$EnvName" $resultsHTM


    writeTableHeader $resultsHTM $PVSFirstFarmheaderName $PVSFarmHeaderNames $PVSFarmWidths $PVSFarmTablewidth
    $global:PVSFarmResults | % { writeData $PVSFarmResults $resultsHTM $PVSFarmHeaderNames}
    writeTableFooter $resultsHTM

    writeTableHeader $resultsHTM $PVSFirstheaderName $PVSheaderNames $PVSheaderWidths $PVStablewidth
    $global:PVSResults | sort-object -property PVServerName_short | % { writeData $PVSResults $resultsHTM $PVSheaderNames}
    writeTableFooter $resultsHTM

    
 
    

    }

    #writeHtmlFooter $resultsHTM

#==============================================================================================
# == MAIN SCRIPT ==
#==============================================================================================
$scriptstart = Get-Date
rm $logfile -force -EA SilentlyContinue
"Begin with Citrix Provisioning Services HealthCheck" | LogMe -display -progress
" " | LogMe -display -progress

Farmcheck
$EnvName = "$EnvironmentName $farmname_short"
$emailSubject = ("$EnvName - " + $ReportDate)
 

PVSServerCheck
WriteHTML

$scriptend = Get-Date
$scriptruntime =  $scriptend - $scriptstart | select TotalSeconds
$scriptruntimeInSeconds = $scriptruntime.TotalSeconds
#Write-Host $scriptruntime.TotalSeconds
"Script was running for $scriptruntimeInSeconds " | LogMe -display -progress


 

Link to comment
  • 0


# License Server Name for license usage.
$LicenseServer = "SERVERNAME"

# License Type to be defined 
# Example: @("MPS_PLT_CCU", "MPS_ENT_CCU", "XDT_ENT_UD") 
#$LicenseTypes = @("MPS_ENT_CCU")
$LicenseTypes = @("XDT_ENT_UD")


Function CheckLicense() 
    {

    If (!$LicenseServer) { "No License Server Name defined" | LogMe -Error; $LicenseResult = " Error; Check Detailed Logs "; return $LicenseResult }
    If (!$LicenseTypes) { "No License Type defined" | LogMe -Error; $LicenseResult = " Error; Check Detailed Logs "; return $LicenseResult }
    
    [int]$TotalLicense = 0; [int]$InUseLicense = 0; [int]$PercentageLS = 0; $LicenseResult = " "
    
    Try 
        {
        
        If (Get-Service -Display "Citrix Licensing" -ComputerName $LicenseServer -ErrorAction Stop) { "Citrix Licensing service is available." | LogMe -Display }
        Else { "Citrix Licensing' service is NOT available." | LogMe -Error; Return "Error; Check Logs" }

        Try
            {
             
            If ($licensePool = gwmi -class "Citrix_GT_License_Pool" -Namespace "ROOT\CitrixLicensing" -comp $LicenseServer -ErrorAction Stop) 
                {
                
                "License Server WMI Class file found" | LogMe -Display 
                $LicensePool | ForEach-Object{ 
                    
                    Foreach ($Ltype in $LicenseTypes)
                        {

                        If ($_.PLD -match $Ltype) { $TotalLicense = $TotalLicense + $_.count; $InUseLicense = $InUseLicense + $_.InUseCount }

                        }

                    }
    
                "The total number of licenses available: $TotalLicense " | LogMe -Display
                "The number of licenses are in use: $InUseLicense " | LogMe -Display
                If (!(($InUseLicense -eq 0) -or ($TotalLicense -eq 0 ))) { $PercentageLS = (($InUseLicense / $TotalLicense ) * 100); $PercentageLS = "{0:N2}" -f $PercentageLS }
                
                If ($PercentageLS -gt 90) { "The License usage is $PercentageLS % " | LogMe -Error }
                ElseIf ($PercentageLS -gt 80) { "The License usage is $PercentageLS % " | LogMe -Warning }
                Else { "The License usage is $PercentageLS % " | LogMe -Display }
    
                $LicenseResult = "$InUseLicense/$TotalLicense [ $PercentageLS % ]"; return $LicenseResult

                }

            }
        Catch
            { 
            
            $ErrorMessage = $_.Exception.Message
            $FailedItem = $_.Exception.ItemName
            "License Server WMI Class file failed. An Error Occured while capturing the License information" | LogMe -Error
            "You may need to uninstall your License Server and reinstall." | LogMe -Error
            "There are known issues with doing an in place upgrade of the license service." | LogMe -Error
            $LicenseResult = " Error; Check Detailed Logs "; return $LicenseResult 

            }

        }
    Catch { "Error returned while checking the Licensing Service. Server may be down or some permission issue" | LogMe -error; return "Error; Check Detailed Logs" }

    } #End Function: CheckLicense

"Checking Citrix License usage on $LicenseServer" | LogMe -Display 
$LicenseReport = CheckLicense

Link to comment
  • 0

Add-PSSnapin citrix*
#"">>"C:\0_Scripts\HSD status\test.txt"
#Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
$POD6CLT="USPILVMPCXIXD01.ey.net"
$POD6STL="DEFRNVMPCXIXD11.ey.net"


$server1="USPILVMPCXIXD01.ey.net"
$server2="DEFRNVMPCXIXD11.ey.net"

$POD8CLT="USSECVMPCXIXD03"
$POD8STL="USSECVMPCXIXD03"
Clear-Content -Path D:\healthcheckreports\consolidatedhealthcheck\sitelog.txt -Verbose
$hanu1=$null
$hanu2=$null
$hanu3=$null
 
$sitename = Get-BrokerSite -AdminAddress $server1  |select name
            $GhostSession = Get-BrokerSession -AdminAddress $server1 -MaxRecordCount 10000 | Where-Object {$_.SessionState -eq 'connected' } | Select-Object MachineName,DesktopGroupName,SessionState | ft -AutoSize  #-ExpandProperty MachineName -ExcludeProperty DesktopGroupName 
             $unregisteredVMs=Get-BrokerDesktop -AdminAddress $server1 -MaxRecordCount 10000 | Where-Object {($_.DesktopGroupName -ne"W10_THK_PKG_BASE") -and ($_.DesktopGroupName -ne "W7_THK_PKG_BASE") -and($_.RegistrationState -match 'Unregistered') -and ($_.PowerState -eq 'On') } |Select DNSName,DesktopGroupName,RegistrationState,PowerState,AssociatedUserFullNames |ft -AutoSize
            $disconnected=Get-Brokersession -AdminAddress $server1 -MaxRecordCount 10000  -SessionState Disconnected -Filter { SessionStateChangeTime -lt "-3:00" } |Select-Object UserName,MachineName,SessionState,SessionStateChangeTime|ft -AutoSize
                  $MMm=Get-BrokerDesktop -AdminAddress $server1 -MaxRecordCount 10000  -InMaintenanceMode 1 -PowerState On | Where-Object {($_.Tags -notcontains 'MaintenanceMode')} |Select-Object HostedMachineName,InMaintenanceMode,RegistrationState,Tags |ft -AutoSize
                          $Powerun1= Get-BrokerDesktop -AdminAddress $server1  -MaxRecordCount 10000 -PowerState Unknown |Select-Object DNSName,PowerState,HypervisorConnectionName |ft -AutoSize

             [array]$hanu1 += ($GhostSession +$sitename+$Powerun1+$unregisteredVMs+$MMm+ $disconnected)
$sitename1 = Get-BrokerSite -AdminAddress $server2  |select name
            $GhostSession1 = Get-BrokerSession -AdminAddress $server2 -MaxRecordCount 10000 | Where-Object {$_.SessionState -eq 'connected' } | Select-Object MachineName,DesktopGroupName,SessionState | ft -AutoSize  #-ExpandProperty MachineName -ExcludeProperty DesktopGroupName 
             $unregisteredVMs1=Get-BrokerDesktop -AdminAddress $server2 -MaxRecordCount 10000 | Where-Object {($_.DesktopGroupName -ne"W10_THK_PKG_BASE") -and ($_.DesktopGroupName -ne "W7_THK_PKG_BASE") -and ($_.RegistrationState -match 'Unregistered') -and ($_.PowerState -eq 'On') }  |Select DNSName,DesktopGroupName,RegistrationState,PowerState,AssociatedUserFullNames |ft -AutoSize
                       $MMm1=Get-BrokerDesktop -AdminAddress $server2 -MaxRecordCount 10000 -PowerState On -InMaintenanceMode 1 | Where-Object {($_.Tags -notcontains 'MaintenanceMode')} |Select-Object HostedMachineName,InMaintenanceMode,RegistrationState,Tags |ft -AutoSize
                          $Powerun2= Get-BrokerDesktop -AdminAddress $server2 -MaxRecordCount 10000 -PowerState Unknown |Select-Object DNSName,PowerState,HypervisorConnectionName |ft -AutoSize

            $disconnected1=Get-Brokersession -AdminAddress $server2 -MaxRecordCount 10000  -SessionState Disconnected -Filter { SessionStateChangeTime -lt "-3:00" }  |Select-Object UserName,MachineName,SessionState,SessionStateChangeTime|ft -AutoSize

              [array]$hanu2 += ($GhostSession1+$sitename1+$Powerun2+$unregisteredVMs1+$MMm1+$disconnected1  )

              [array]$hanu3 += ( $hanu1 + $hanu2)

             $hanu3 |Out-File -FilePath D:\healthcheckreports\consolidatedhealthcheck\sitelog.txt -Verbose

#$hanu1=$null
function calculate($server,$POD)
{

    $dgq=Get-BrokerDesktopGroup -AdminAddress $server -InMaintenanceMode $false -DesktopKind Private |select name
        #$dgq1=Get-BrokerDesktopGroup -AdminAddress $server -InMaintenanceMode $false -DesktopKind Shared |select name
    $deliverygroup=$dgq.name
    $deliverygroup1=$dgq1.name

    $table="<table border='1' style='border-collapse:collapse;width:95%;background-color:#edf4f5;'> <tr style='background-color:#0a2ebf;color:white;height:20px;'><th>Static Delivery group </th> <th> Total machines&nbsp</th> <th> In Maintenance &nbsp</th> <th>Active Session</th><th> User Dis_sessions</th><th>Unregister</th><th> Poweroff</th><th> PS Unknown</th></tr>"
   # $table1="<table border='1' style='border-collapse:collapse;width:95%;background-color:#edf4f5;'> <tr style='background-color:#0a2ebf;color:white;height:20px;'><th> Delivery group </th> <th> Total machines&nbsp</th> <th> In Maintenance &nbsp</th> <th>Active Session</th><th> User Dis_sessions</th><th>Unregister</th><th> Poweroff</th></tr>"

    foreach($delivery in $deliverygroup)
    {
    
        $dg=$delivery
        if($dg -ne "W10_THK_PKG_BASE" -and $dg -ne "W7_THK_PKG_BASE")
        {
            $totalmachine=Get-BrokerDesktop -AdminAddress $server -MaxRecordCount 10000 -DesktopGroupName $dg 
            
      $suni= (Get-BrokerDesktop -AdminAddress $server -MaxRecordCount 10000 -DesktopGroupName $dg | Where-Object {($_.RegistrationState -match 'Unregistered') -and ($_.PowerState -eq 'On')  } ) #Select-Object DNSName,IPAddress |ft -AutoSize #| ForEach-Object { Restart-Service -InputObject $(Get-Service -Computer $_.DNSName -Name *citrix*) -Force -Verbose}  -Verbose)


           #sleep -Seconds 3 -Verbose

$totalactivesession=Get-BrokerDesktop -AdminAddress $server -MaxRecordCount 10000 -DesktopGroupName $dg -SessionState Active
            $totaldisconnected=Get-Brokersession -AdminAddress $server -MaxRecordCount 10000 -DesktopGroupName $dg -SessionState Disconnected -Filter { SessionStateChangeTime -lt "-2:30" }
            $unreg=Get-BrokerDesktop -AdminAddress $server -MaxRecordCount 10000 -DesktopGroupName $dg | Where-Object {($_.RegistrationState -match 'Unregistered') -and ($_.PowerState -eq 'On') }
            $MM=Get-BrokerDesktop -AdminAddress $server -MaxRecordCount 10000  -DesktopGroupName $dg -PowerState On -InMaintenanceMode 1 | Where-Object {($_.Tags -notcontains 'MaintenanceMode')}
            $Poweroff= Get-BrokerDesktop -AdminAddress $server  -MaxRecordCount 10000 -DesktopGroupName $dg -PowerState Off
                        $Powerun= Get-BrokerDesktop -AdminAddress $server  -MaxRecordCount 10000 -DesktopGroupName $dg -PowerState Unknown

            $TotalMachines=$totalmachine.count
            $InMaintanance=[int]$MM.count
           $InPoweroff=[int]$Poweroff.count
           $inpowerun=[int]$Powerun.count
           $totalactive=[int]$totalactivesession.count
           $totaldisc=[int]$totaldisconnected.count
             $totalunreg=  [int]$unreg.count
             Write-Host "unreg" $totalunreg
             Write-Host "powerstate" $InPoweroff
      if($TotalMachines -ne 0)
      {  

    
        if($InMaintanance -ne 0)
        {
            $stylem=";background-color:#FFBF00;color:white;" #change the background color to red
        }
        else
        {
            $stylem=";background-color:#1fa626;color:white;font-weight:bold;" #change the background color to green
        }
        
        if($InPoweroff -ne 0 )
        {
           $stylep=";background-color:#1fa626;color:white;font-weight:bold"
        }
            
        else
        {
         $stylep=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        }

        if($inpowerun -ne 0 )
        {
           $stylep33=";background-color:#f5053d;color:white;" #chage the background color to red
        }
            
        else
        {
         $stylep33=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        }

        if($totalactive -ne 0 )
        {
          $stylep2=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        }
            
        else
        {

         $stylep2=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        }
        
        if($totaldisc -ne 0 )
        {
          $stylep1=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        }
            
        else
        {
         $stylep1=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        }
         
        if($totalunreg-ne 0 )
        {
          $stylep9=";background-color:#f5053d;color:white;" #chage the background color to red
        }
            
        else
        {
         $stylep9=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        }

        $stylem7=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        $table+="<tr> <td>$dg</td> <td style='text-align:center$stylem7'> $TotalMachines </td> <td style='text-align:center$stylem'> $InMaintanance </td> <td style='text-align:center$stylep2'> $totalactive </td> <td style='text-align:center$stylep1'> $totaldisc </td> <td style='text-align:center$stylep9'> $totalunreg </td> </td> <td style='text-align:center$stylep'> $InPoweroff </td></td> <td style='text-align:center$stylep33'> $inpowerun </td></tr>"
       }
       }
     }

     write-host "Done with $server Static VDI"
     return  $table

}
function calculate1($server,$POD)
{

    $dgq=Get-BrokerDesktopGroup -AdminAddress $server -InMaintenanceMode $false -DesktopKind Shared |select name
        #$dgq1=Get-BrokerDesktopGroup -AdminAddress $server -InMaintenanceMode $false -DesktopKind Shared |select name
    $deliverygroup=$dgq.name
    $deliverygroup1=$dgq1.name

    $table="<table border='1' style='border-collapse:collapse;width:95%;background-color:#edf4f5;'> <tr style='background-color:#0a2ebf;color:white;height:20px;'><th>Static Delivery group </th> <th> Total machines&nbsp</th> <th> In Maintenance &nbsp</th> <th>Active Session</th><th> User Dis_sessions</th><th>Unregister</th><th> Poweroff</th><th> PS Unknown</th></tr>"
    #$table1="<table border='1' style='border-collapse:collapse;width:95%;background-color:#edf4f5;'> <tr style='background-color:#0a2ebf;color:white;height:20px;'><th> Delivery group </th> <th> Total machines&nbsp</th> <th> In Maintenance &nbsp</th> <th>Active Session</th><th> User Dis_sessions</th><th>Unregister</th><th> Poweroff</th></tr>"

    foreach($delivery in $deliverygroup)
    {
    
        $dg=$delivery
           if($dg -ne "W10_THK_PKG_BASE" -and $dg -ne "W7_THK_PKG_BASE")
        {
            $totalmachine=Get-BrokerDesktop -AdminAddress $server -MaxRecordCount 10000 -DesktopGroupName $dg 
            $totalactivesession=Get-BrokerDesktop -AdminAddress $server -MaxRecordCount 10000 -DesktopGroupName $dg -SessionState Active
            $totaldisconnected=Get-Brokersession -AdminAddress $server -MaxRecordCount 10000 -DesktopGroupName $dg -SessionState Disconnected -Filter { SessionStateChangeTime -lt "-3:00" }
      $suni= (Get-BrokerDesktop -AdminAddress $server -MaxRecordCount 10000 -DesktopGroupName $dg | Where-Object {($_.RegistrationState -match 'Unregistered') -and ($_.PowerState -eq 'On')  } ) #Select-Object DNSName,IPAddress |ft -AutoSize #| ForEach-Object { Restart-Service -InputObject $(Get-Service -Computer $_.DNSName -Name *citrix*) -Force -Verbose}  -Verbose)


           #sleep -Seconds 3 -Verbose

            $unreg=Get-BrokerDesktop -AdminAddress $server -MaxRecordCount 10000 -DesktopGroupName $dg | Where-Object {($_.RegistrationState -match 'Unregistered') -and ($_.PowerState -eq 'On') } 

                                    $Powerun= Get-BrokerDesktop -AdminAddress $server  -MaxRecordCount 10000 -DesktopGroupName $dg -PowerState Unknown


            $MM=Get-BrokerDesktop -AdminAddress $server -MaxRecordCount 10000  -DesktopGroupName $dg -PowerState On -InMaintenanceMode 1 | Where-Object {($_.Tags -notcontains 'MaintenanceMode')}
            $Poweroff= Get-BrokerDesktop -AdminAddress $server  -MaxRecordCount 10000 -DesktopGroupName $dg -PowerState Off
            $TotalMachines=$totalmachine.count
            $InMaintanance=[int]$MM.count
           $InPoweroff=[int]$Poweroff.count
           $inpowerun=[int]$Powerun.count
           $totalactive=[int]$totalactivesession.count
           $totaldisc=[int]$totaldisconnected.count
             $totalunreg=  [int]$unreg.count
             Write-Host "unreg" $totalunreg
             Write-Host "powerstate" $InPoweroff
      if($TotalMachines -ne 0)
      {  

    
        if($InMaintanance -ne 0)
        {
            $stylem=";background-color:#FFBF00;color:white;" #change the background color to red
        }
        else
        {
            $stylem=";background-color:#1fa626;color:white;font-weight:bold;" #change the background color to green
        }
        
        if($InPoweroff -ne 0 )
        {
           $stylep=";background-color:#1fa626;color:white;font-weight:bold"
        }
            
        else
        {
         $stylep=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        }
        if($inpowerun -ne 0 )
        {
           $stylep33=";background-color:#f5053d;color:white;" #chage the background color to red
        }
            
        else
        {
         $stylep33=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        }
        if($totalactive -ne 0 )
        {
          $stylep2=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        }
            
        else
        {

         $stylep2=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        }
        
        if($totaldisc -ne 0 )
        {
          $stylep1=";background-color:#f5053d;color:white;" #chage the background color to red
        }
            
        else
        {
         $stylep1=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        }
         
        if($totalunreg-ne 0 )
        {
          $stylep9=";background-color:#f5053d;color:white;" #chage the background color to red
        }
            
        else
        {
         $stylep9=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        }

        $stylem7=";background-color:#1fa626;color:white;font-weight:bold" #change the background color to green
        $table+="<tr> <td>$dg</td> <td style='text-align:center$stylem7'> $TotalMachines </td> <td style='text-align:center$stylem'> $InMaintanance </td> <td style='text-align:center$stylep2'> $totalactive </td> <td style='text-align:center$stylep1'> $totaldisc </td> <td style='text-align:center$stylep9'> $totalunreg </td> </td> <td style='text-align:center$stylep'> $InPoweroff </td></td> <td style='text-align:center$stylep33'> $inpowerun </td></tr>"
       }
       }
     }

     write-host "Done with $server pooled VDI"
     return  $table

}
$body1=calculate $POD6CLT "USSECXDA_P"
$body2=calculate1 $POD6CLT "USSECXDA_P"
$body3=calculate $POD6STL "DEFRAXDA_P"
$body4=calculate1 $POD6STL "DEFRAXDA_P"
#$body2=calculate $POD6STL "DEFRAXDA_P"


$king=Get-Date -Format g

$message= New-Object net.mail.Mailmessage 
$message.To.Add("ML_EYOMC_VDI.GID@ey.net")
#$message.To.Add("sunil.saroja@ey.com")

$message.From="EY_Citrix_Healthstatus@ey.com"
$message.IsBodyHtml=$true


$path02 = "D:\healthcheckreports\consolidatedhealthcheck\sitelog.txt";
$Attachments = @($path02)
$message.CC.Add("sunil.saroja@ey.com")

$message.Body="Hi Team,<br><br> Kindly find the status of EY US/DE sites machines on $king <br><br>"+ "USSECXDA_P Static VDI/Sessions Details<br><br>$body1 </table> <br><br>" +"USSECXDA_P Pooled VDI/Sessions Details<br><br>$body2</table><br><br>" + "DEFRAXDA_P Static VDI/Sessions Details<br><br>$body3 </table> <br><br>" +"DEFRAXDA_P Pooled VDI/Sessions Details<br><br>$body4</table><br><br>" 

$message.Subject="EY US/DE Citrix Estate Status on $king Eastern Time (US & Canada)" 
$message.Attachments.Add($path02);

write-host "send mail"
$ser="smtp.ey.net"
$smtp=New-Object net.mail.smtpclient($ser,25)

$smtp.Send($message)
 

Link to comment
  • 0

[CmdletBinding()]

Param
(
    [Parameter(Mandatory)]
    [string]$ddc ,
    [double]$daysAgo = 1 ,
    [string]$username
)

$VerbosePreference = $(if( $PSBoundParameters[ 'verbose' ] ) { $VerbosePreference } else { 'SilentlyContinue' })
$DebugPreference = $(if( $PSBoundParameters[ 'debug' ] ) { $DebugPreference } else { 'SilentlyContinue' })
$ErrorActionPreference = $(if( $PSBoundParameters[ 'erroraction' ] ) { $ErrorActionPreference } else { 'Stop' })
$ProgressPreference = 'SilentlyContinue'
$Hash = @()

[int]$outputWidth = 400
[bool]$join = $true
[string]$query = 'ConnectionFailureLogs'
[string]$protocol = 'http'
[int]$oDataVersion = 4 ## if this fails will try lower versions
## map tables to the date stamp we will filter on
[hashtable]$dateFields = @{
     'Session' = 'StartDate'
     'Connection' = 'BrokeringDate'
     'ConnectionFailureLog' = 'FailureDate'
}

[hashtable]$connectionFailureCodes = @{}

# Altering the size of the PS Buffer
if( ( $PSWindow = (Get-Host).UI.RawUI ) -and ($WideDimensions = $PSWindow.BufferSize) )
{
    $WideDimensions.Width = $outputWidth
    $PSWindow.BufferSize = $WideDimensions
}

## Modified from code at https://jasonconger.com/2013/10/11/using-powershell-to-retrieve-citrix-monitor-data-via-odata/
Function Invoke-ODataTransform
{
    Param
    (
        [Parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true)]
        $records
    )

    Begin
    {
        $propertyNames = $null

        [int]$timeOffset = if( (Get-Date).IsDaylightSavingTime() ) { 1 } else { 0 }
    }

    Process
    {
        if( $records -and $records.PSObject.Properties[ 'content' ] )
        {
            if( ! $propertyNames )
            {
                $properties = ($records | Select -First 1).content.properties
                if( $properties )
                {
                    $propertyNames = $properties | Get-Member -MemberType Properties | Select -ExpandProperty name
                }
                else
                {
                    // v4+
                    $propertyNames = 'NA' -as [string]
                }
            }
            if( $propertyNames -is [string] )
            {
                $records | Select -ExpandProperty value
            }
            else
            {
                ForEach( $record in $records )
                {
                    $h = @{ 'ID' = $record.ID }
                    $properties = $record.content.properties

                    ForEach( $propertyName in $propertyNames )
                    {
                        $targetProperty = $properties.$propertyName
                        if($targetProperty -is [Xml.XmlElement])
                        {
                            try
                            {
                                $h.$propertyName = $targetProperty.'#text'
                                ## see if we need to adjust for daylight savings
                                if( $timeOffset -and ! [string]::IsNullOrEmpty( $h.$propertyName ) -and $targetProperty.type -match 'DateTime' )
                                {
                                    $h.$propertyName = (Get-Date -Date $h.$propertyName).AddHours( $timeOffset )
                                }
                            }
                            catch
                            {
                                ##$_
                            }
                        }
                        else
                        {
                            $h.$propertyName = $targetProperty
                        }
                    }

                    [PSCustomObject]$h
                }
            }
        }
        elseif( $records -and $records.PSObject.Properties[ 'value' ] ) ##JSON
        {
            $records.value
        }
    }
}

Function Get-DateRanges
{
    Param
    (
        [string]$query ,
        $from ,
        $to ,
        [switch]$selective ,
        [int]$oDataVersion
    )
    
    $field = $dateFields[ ($query -replace 's$' , '') ]
    if( ! $field )
    {
        if( $selective )
        {
            return $null ## only want specific ones
        }
        $field = 'CreatedDate'
    }
    if( $oDataVersion -ge 4 )
    {
        if( $from )
        {
            "()?`$filter=$field ge $(Get-Date -date $from -format s)Z"
        }
        if( $to )
        {
            "and $field le $(Get-Date -date $to -format s)Z"
        }
    }
    else
    {
        if( $from )
        {
            "()?`$filter=$field ge datetime'$(Get-Date -date $from -format s)'"
        }
        if( $to )
        {
            "and $field le datetime'$(Get-Date -date $to -format s)'"
        }
    }
}

Function Resolve-CrossReferences
{
    Param
    (
        [Parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true)]
        $properties ,
        [switch]$cloud
    )
    
    Process
    {
        $properties | Where-Object { ( $_.Name -match '^(.*)Id$' -or $_.Name -match '^(SessionKey)$' ) -and ! [string]::IsNullOrEmpty( $Matches[1] ) }  | Select-Object -Property Name | ForEach-Object `
        {
            [string]$id = $Matches[1]
            [bool]$current = $false
            if( $id -match '^Current(.*)$' )
            {
                $current = $true
                $id = $Matches[1]
            }
            elseif( $id -eq 'SessionKey' )
            {
                $id = 'Session'
            }

            if( ! $tables[ $id ] -and ! $alreadyFetched[ $id ] )
            {
                if( $cloud )
                {
                    $params.uri = ( "{0}://{1}.xendesktop.net/Citrix/Monitor/OData/v{2}/Data/{3}s" -f $protocol , $customerid , $version ,  $id ) ## + (Get-DateRanges -query $id -from $from -to $to -selective -oDataVersion $oDataVersion)
                }
                else
                {
                    $params.uri = ( "{0}://{1}/Citrix/Monitor/OData/v{2}/Data/{3}s" -f $protocol , $ddc , $version , $id ) ## + (Get-DateRanges -query $id -from $from -to $to -selective -oDataVersion $oDataVersion)
                }

                ## save looking up again, especially if it errors as we are not looking up anything valid
                $alreadyFetched.Add( $id , $id )

                ## if it's a high volume, time specific table then we will filter it
                if( $dateFields[ $id ] )
                {
                    $params.uri += Get-DateRanges -query $id -from $from -to $to -oDataVersion $oDataVersion
                }

                [hashtable]$table = @{}
                try
                {
                    Invoke-RestMethod @params | Invoke-ODataTransform | ForEach-Object `
                    {
                        ## add to hash table keyed on its id
                        ## ToDo we need to go recursive to see if any of these have Ids that we need to resolve without going infintely recursive
                        $object = $_
                        [string]$thisId = $null
                        [string]$keyName = $null

                        if( $object.PSObject.Properties[ 'id' ] )
                        {
                            $thisId = $object.Id
                            $keyName = 'id'
                        }
                        elseif( $object.PSObject.Properties[ 'SessionKey' ] )
                        {
                            $thisId = $object.SessionKey
                            $keyname = 'SessionKey'
                        }

                        if( $thisId )
                        {
                            [string]$key = $(if( $thisId -match '\(guid''(.*)''\)$' )
                                {
                                    $Matches[ 1 ]
                                }
                                else
                                {
                                    $thisId
                                })
                            $object.PSObject.properties.remove( $key )
                            $table.Add( $key , $object )
                        }

                        ## Look at other properties to figure if it too is an id and grab that table too if we don't have it already
                        ForEach( $property in $object.PSObject.Properties )
                        {
                            if( $property.MemberType -eq 'NoteProperty' -and $property.Name -ne $keyName -and $property.Name -ne 'sid' -and $property.Name -match '(.*)Id$' )
                            {
                                $property | Resolve-CrossReferences -cloud:$cloud
                            }
                        }
                    }
                    if( $table.Count )
                    {
                        Write-Verbose -Message "Adding table $id with $($table.Count) entries"
                        $tables.Add( $id , $table )
                    }
                }
                catch
                {
                    $nop = $null
                }
            }
        }
    }
}

Function Resolve-NestedProperties
{
    Param
    (
        [Parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true)]
        $properties ,
        $previousProperties
    )
    
    Process
    {
        $properties | Where-Object { $_.Name -ne 'sid' -and ( $_.Name -match '^(.*)Id$' -or $_.Name -match '^(Session)Key$' -or $_.Name -match '(EnumValue)' ) -and ! [string]::IsNullOrEmpty( $Matches[1] ) } | . { Process `
        {
            $property = $_
            if( ( $matchedEnum = $Matches[1] ) -eq 'EnumValue' )
            {
                ## http://grl-xaddc01/Citrix/Monitor/OData/v3/Methods/GetAllMonitoringEnums('SessionFailureCode')/Values
                ## need to find a generic way of doing this
                $lookupTable = $null
                if( $property.Name -eq 'ConnectionFailureEnumValue' )
                {
                    if( ! $connectionFailureCodes -or ! $connectionFailureCodes.Count )
                    {
                        Write-Verbose -Message "Resolving enum $($property.Name)"
                        ## v4 equivalent??
                        $params[ 'uri' ] = ( "{0}://{1}/Citrix/Monitor/OData/v3/Methods/GetAllMonitoringEnums('SessionFailureCode')/Values" -f $protocol , $ddc )
                        if( $enums = Invoke-RestMethod @params )
                        {
                            ForEach( $enum in $enums )
                            {
                                ## http://grl-xaddc01/Citrix/Monitor/OData/v3/Methods/MonitoringEnumItems(0)
                                if( $enum.id -match '\((\d+)\)$' )
                                {
                                    $connectionFailureCodes.Add( $Matches[1] , ( $enum.content.properties | Select-Object -expandProperty Name ) )
                                }
                            }
                        }
                    }
                    $lookupTable = $connectionFailureCodes
                }
                if( $lookupTable )
                {
                    if( [string]$expandedEnum = $connectionFailureCodes[ $property.Value.ToString() ] )
                    {
                        [pscustomobject]@{ ( $property.Name -replace $matchedEnum ) = ( $expandedEnum -creplace '([a-z])([A-Z])' , '$1 $2' ) }
                    }
                    else
                    {
                        Write-Warning -Message "Unable to find enum value $($property.Value) for enum $($property.Name)"
                    }
                }
                else
                {
                    Write-Warning -Message "Unable to lookup enumeration $($property.Name)"
                }
            }
            elseif( ! [string]::IsNullOrEmpty( ( $id = ( $Matches[1] -replace '^Current' , '')) ))
            {
                if ( $table = $tables[ $id ] )
                {
                    if( $property.Value -and ( $item = $table[ ($property.Value -as [string]) ]))
                    {
                        $datum.PSObject.properties.remove( $property )
                        $item.PSObject.Properties | ForEach-Object `
                        {
                            [pscustomobject]@{ "$id.$($_.Name)" = $_.Value }
                            if( $_.Name -ne $property.Name -and ( ! $previousProperties -or ! ( $previousProperties | Where-Object Name -eq $_.Name ))) ## don't lookup self or a key if it was one we previously looked up
                            {
                                Resolve-NestedProperties -properties $_ -previousProperties $properties
                            }
                        }
                    }
                }
            }
        }}
    }
}

[hashtable]$params = @{ 'ErrorAction' = 'SilentlyContinue' }
[hashtable]$alreadyFetched = @{}
$credential = $null

if( $PSBoundParameters[ 'XDusername' ] )
{
    if( ! [string]::IsNullOrEmpty( $XDpassword ) )
    {
        $credential = New-Object System.Management.Automation.PSCredential( $XDusername , ( ConvertTo-SecureString -AsPlainText -String $XDpassword -Force ) )
        $XDpassword = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
    }
    else
    {
        Throw "Must specify password when using -username either via -password or %RandomKey%"
    }
}

if( $credential )
{
    $params.Add( 'Credential' , $credential )
}
 else
{
    $params.Add( 'UseDefaultCredentials' , $true )
}

## used to try and figure out the highest supported oData version but proved problematic
[int]$highestVersion = $oDataVersion ## if( $oDataVersion -le 0 ) { 10 } else { -1 }
$fatalException = $null
[int]$version = $oDataVersion

$services = $null
## queries are case sensitive so help people who don't know this but don't do it for everything as would break items like DesktopGroups
if( $query -cmatch '^[a-z]' )
{
    $TextInfo = (Get-Culture).TextInfo
    $query = $TextInfo.ToTitleCase( $query ).ToString()
}

if( $PsCmdlet.ParameterSetName -eq 'cloud' )
{
    if( ! $PSBoundParameters[ 'authtoken' ] )
    {
        Add-PSSnapin -Name Citrix.Sdk.Proxy.*
        if( ! ( Get-Command -Name Get-XDAuthentication -ErrorAction SilentlyContinue ) )
        {
            Throw "Unable to find the Get-XDAuthentication cmdlet - is the Virtual Apps and Desktops Remote PowerShell SDK installed ?"
        }
        Get-XDAuthentication -CustomerId $customerid
        if( ! $? )
        {
            Throw "Failed to get authentication token for Cloud customer id $customerid"
        }
        $authtoken = $GLOBAL:XDAuthToken
    }
    $params.Add( 'Headers' , @{ 'Customer' = $customerid ; 'Authorization' = $authtoken } )
    $protocol = 'https'
}

[bool]$cloud = $false

[datetime]$from = ($to = Get-Date).AddMinutes(-120)

[array]$data = @( do
{
    if( $oDataVersion -le 0 )
    {
        ## Figure out what the latest OData version supported is. Could get via remoting but remoting may not be enabled
        if( $highestVersion -le 0 )
        {
            break
        }
        $version = $highestVersion--
    }
    
    if( $PsCmdlet.ParameterSetName -eq 'cloud' )
    {
        $params[ 'Uri' ] = ( "{0}://{1}.xendesktop.net/Citrix/Monitor/OData/v{2}/Data/{3}" -f $protocol , $customerid , $version , $query ) + (Get-DateRanges -query $query -from $from -to $to -oDataVersion $oDataVersion)
        $cloud = $true
    }
    else
    {
        $params[ 'Uri' ] = ( "{0}://{1}/Citrix/Monitor/OData/v{2}/Data/{3}" -f $protocol , $ddc , $version , $query ) + (Get-DateRanges -query $query -from $from -to $to -oDataVersion $oDataVersion)
    }

    Write-Verbose "URL : $($params.Uri)"

    try
    {
        Invoke-RestMethod @params | Invoke-ODataTransform

        $fatalException = $null
        break ## since call succeeded so that we don't report for lower versions
    }
    catch
    {
        $fatalException = $_
        if( $_.Exception.response.StatusCode -eq 'Unauthorized' )
        {
            Throw $fatalException
        }
        elseif( $_.Exception.response.StatusCode -eq 'NotFound' )
        {
            $fatalException = $null
            $oDataVersion = --$version ## try lower OData version
        }
    }
} while ( $highestVersion -gt 0 -and $version -gt 0 -and ! $fatalException ))

if( $fatalException )
{
    Throw $fatalException
}

if( $services )
{
    if( $services.PSObject.Properties[ 'service' ] )
    {
        $services.service.workspace.collection | Select-Object -Property 'title' | Sort-Object -Property 'title'
    }
    else
    {
        $services.value | Sort-Object -Property 'name'
    }
}
elseif( $data -and $data.Count )
{
    [hashtable]$tables = @{}

    ## now figure out what other tables we need in order to satisfy these ids (not interested in id on it's own)
    $data[0].PSObject.Properties | Resolve-CrossReferences -cloud:$cloud

    [int]$originalPropertyCount = $data[0].PSObject.Properties.GetEnumerator()|Measure-Object |Select-Object -ExpandProperty Count
    [int]$finalPropertyCount = -1

    ## now we need to add these cross referenced items
    [array]$results = @( ForEach( $datum in $data )
    {
        $datum.PSObject.Properties | Where-Object { $_.Name -ne 'sid' -and ( $_.Name -match '^(.*)Id$' -or $_.Name -match '^(Session)Key$' -or $_.Name -match '(EnumValue)' ) -and ! [string]::IsNullOrEmpty( $Matches[1] ) } | . { Process `
        {
            $property = $_
            Resolve-NestedProperties $property | ForEach-Object `
            {
                $_.PSObject.Properties | Where-Object MemberType -eq 'NoteProperty' | ForEach-Object `
                {
                    Add-Member -InputObject $datum -MemberType NoteProperty -Name $_.Name -Value $_.Value
                }
            }
        }}

        if( $finalPropertyCount -lt 0 )
        {
            $finalPropertyCount = $datum.PSObject.Properties.GetEnumerator()|Measure-Object |Select-Object -ExpandProperty Count
            Write-Verbose -Message "Expanded from $originalPropertyCount properties to $finalPropertyCount"
        }

        $datum
    })
    if( $results -and $results.Count )
    {
        Write-Verbose -Message "Start date is $(Get-Date -Date $from -Format G)"
      $kiki=  $results | Where-Object { $_.FailureDate -as [datetime] -ge $from -and ( ( [string]::IsNullOrEmpty( $username ) -and $null -ne $_.PSObject.Properties[ 'User.UserName' ] ) -or ( $null -ne $_.PSObject.Properties[ 'User.UserName' ] -and $_.'UserName' -eq $username ) ) } | Select-Object -Property 'User.UserName' , @{n='Date';e={$_.FailureDate -as [datetime]}} , ConnectionFailure , @{n='Delivery Group';e={$_.'DesktopGroup.Name'}} , 'Machine.Name' , 'Connection.ClientAddress' , 'Connection.IsReconnect'  | Format-Table -AutoSize
    $tata=$kiki

        $suni= $results | Where-Object { $_.FailureDate -as [datetime] -ge $from -and ( ( [string]::IsNullOrEmpty( $username ) -and $null -ne $_.PSObject.Properties[ 'User.UserName' ] ) -or ( $null -ne $_.PSObject.Properties[ 'User.UserName' ] -and $_.'UserName' -eq $username ) ) } |Select-Object User.Upn,User.UserName,Machine.Name,DesktopGroup.Name,ConnectionFailure, @{n='Date';e={$_.FailureDate -as [datetime]}} 
        $suni #|ft -AutoSize


        
    }
    else
    {
        Write-Warning "No data returned"
    }
}
else
{
    Write-Warning "No data returned"
}

Link to comment
  • 0


#html logo path
$ImageName1="C:\tmp\test\logo.png"
$ImageName2="C:\tmp\test\sss.png"
$OutputPath="C:\tmp\test"

$date=Get-Date -Format g


asnp citrix*
## Begining of the loop. At the bottom of the script the loop is broken if the refresh option is not configured.
#Do
#{
    ## If CSV is configured, setting the location and name of the report output. If CSV is not configured output a HTML file.
    If ($Csv)
    {
        $OutputFile = "$OutputPath\WinServ-Status-Report.csv"
        
        ## If the CSV file already exists, clear it
        $CsvT = Test-Path -Path $OutputFile

        If ($CsvT)
        {
            Clear-Content -Path $OutputFile
        }
    }

    Else
    {
        $OutputFile = "$OutputPath\WinServ-Status-Report.htm"
    }

        $PingStatus = "true"#Test-Connection -ComputerName $ServerName -Count 1 -Quiet

        ## If server responds, get the stats for the server.
        If ($PingStatus)
        {
            $CpuAlert = $false
            $MemAlert = $false
            $DiskAlert = $false


#####################


   }
    
  

    ## If there is a result put the report together.
    If ($Result -ne $null)
    {
        ## If CSV report is specified, output a CSV file. If CSV is not configured output a HTML file.
        If ($Csv)
        {
            ForEach($Entry in $Result)
            {
                If ($Entry.Status -eq $True)
                {
                    Add-Content -Path "$OutputFile" -Value "$($Entry.ServerName),Online,CPU: $($Entry.CpuUsage),RAM: $($Entry.MemUsage),$($Entry.DiskUsage),$($Entry.Uptime),RAM: $($Entry.MemUsage),$($Entry.DiskUsage),$($Entry.Uptime)"
                }

                Else
                {
                    Add-Content -Path "$OutputFile" -Value "$($Entry.ServerName),Offline"
                }
            }
        }

        Else
        {
            ## If the light theme is specified, use a lighter css theme. If not, use the dark css theme.
            If ($Light)
            {
                $HTML = '<style type="text/css">
                    p {font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;font-size:14px}
                    p {color:#000000;}
                    #Header{font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;width:100%;border-collapse:collapse;}
                    #Header td, #Header th {font-size:15px;text-align:left;border:1px solid #1a1a1a;padding:2px 2px 2px 7px 2px 2px 2px 7px ;color:#ffffff;}
                    #Header tr.alt td {color:#ffffff;background-color:#404040;}
                    #Header tr:nth-child(even) {background-color:#404040;}
                    #Header tr:nth-child(even) {background-color:#404040;}
                    #Header tr:nth-child(odd) {background-color:#737373;}
                    body {background-color: #d9d9d9;}
                    div {background-color: #d9d9d9;-webkit-animation-name: alert;animation-duration: 4s;animation-iteration-count: infinite;animation-direction: alternate;}
                    @-webkit-keyframes alert {from {background-color:rgba(217,0,0,0);} to {background-color:rgba(217,0,0,1);}
                    @keyframes alert {from {background-color:rgba(217,0,0,0);} to {background-color:rgba(217,0,0,1);}
                    </style>
                    <head><meta http-equiv="refresh" content="300"></head>'

                $HTML += "<html><body>
                    <p><font color=#$Black>Status refreshed on: $(Get-Date -Format G)</font></p>
                    <table border=0 cellpadding=0 cellspacing=0 id=header>"
            }

            Else
            {
                $HTML = '<style type="text/css">
                    p {font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;font-size:14px}
                    p {color:#ffffff;}
                    #Header{font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;width:100%;border-collapse:collapse;}
                    #Header td, #Header th {font-size:25px;text-align:left;border:1px solid #1a1a1a;padding:2px 2px 2px 7px 2px 2px 2px 7px ;color:#ffffff;}
                    #Header tr.alt td {color:#ffffff;background-color:#1a1a1a;}
                    #Header tr:nth-child(even) {background-color:#1a1a1a;}
                    #Header tr:nth-child(odd) {background-color:#3b3b3b;}
                    body {background-color: #1a1a1a;}
                    div {background-color: #1a1a1a;-webkit-animation-name: alert;animation-duration: 4s;animation-iteration-count: infinite;animation-direction: alternate;}
                    @-webkit-keyframes alert {from {background-color:rgba(217,0,0,0);} to {background-color:rgba(217,0,0,1);}
                    @keyframes alert {from {background-color:rgba(217,0,0,0);} to {background-color:rgba(217,0,0,1);}
                    </style>
                    <head><meta http-equiv="refresh" content="300"></head>'

$HTML += @"
    <html>
    <head>
    <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>
    <meta http-equiv="refresh" content="60">
    <title>Sssss</title>
    <STYLE TYPE="text/css">
    <!--
    td {
        font-family: Lao UI;
        font-size: 11px;
        border-top: 1px solid #999999;
        border-right: 1px solid #999999;
        border-bottom: 1px solid #999999;
        border-left: 1px solid #999999;
        padding-top: 0px;
        padding-right: 0px;
        padding-bottom: 0px;
        padding-left: 0px;
        overflow: hidden;}

    .header {
   font-family: Tahoma;
font-size: 40px;
font-weight:bold;
border-top: 1px solid #999999;
border-right: 1px solid #999999;
border-bottom: 1px solid #999999;
border-left: 1px solid #999999;
padding-top: 0px;
padding-right: 0px;
padding-bottom: 0px;
padding-left: 0px;
        overflow: hidden;
color:#FFFFFF;
text-shadow:2px 2px 10px #000000;

        }
    body {
        margin-left: 5px;
        margin-top: 5px;
        margin-right: 0px;
        margin-bottom: 10px;
        table {
            table-layout:fixed;
            border: thin solid #FFFFFF;}
.shadow {
height: 1em;
filter: Glow(Color=#000000,
Direction=135,
Strength=5);}
        -->
demotext {
color: #131313;
background: #e7e5e4;
letter-spacing: .15em;
text-shadow: 1px -1px 0 #767676, -1px 2px 1px #737272, -2px 4px 1px #767474, -3px 6px 1px #787777, -4px 8px 1px #7b7a7a, -5px 10px 1px #7f7d7d, -6px 12px 1px #828181, -7px 14px 1px #868585, -8px 16px 1px #8b8a89, -9px 18px 1px #8f8e8d, -10px 20px 1px #949392, -11px 22px 1px #999897, -12px 24px 1px #9e9c9c, -13px 26px 1px #a3a1a1, -14px 28px 1px #a8a6a6, -15px 30px 1px #adabab, -16px 32px 1px #b2b1b0, -17px 34px 1px #b7b6b5, -18px 36px 1px #bcbbba, -19px 38px 1px #c1bfbf, -20px 40px 1px #c6c4c4, -21px 42px 1px #cbc9c8, -22px 44px 1px #cfcdcd, -23px 46px 1px #d4d2d1, -24px 48px 1px #d8d6d5, -25px 50px 1px #dbdad9, -26px 52px 1px #dfdddc, -27px 54px 1px #e2e0df, -28px 56px 1px #e4e3e2;
color: #131313;
background: #e7e5e4;
}
    </style>
    </head>
    <body>
        <table class="header" width='100%'>
        <tr bgcolor='#FAFAFA'>
           <td width=23% align='center' valign="middle">
        <img src="s.png">
        </td>
        <td class="header" width='8262' align='center' valign="middle" style="background-image: url('ekg_wide.jpg'); background-repeat: no-repeat; background-position: center; ">

<div id="demotext">TELSTRA DVP Citrix Envernment Health Status DashBoard</div>
       
 <font face='Tahoma' color='#8A0808' size='2'>
        </tr>
        </table>
        <table width='100%'>
        <tr bgcolor='#CCCCCC'>
       <td width=15% align='center' valign="middle">
        <font face='Tahoma' color='#8A0808' size='2'><strong>Farm Last Queried: $date</strong></font>

        </td>
        <td width=15% align='center' valign="middle">
        <font face='Tahoma' color='#8A0808' size='2'>
        <strong>Page Last Refresfed: 
        <script type="text/javascript">
        <!--
        var currentTime = new Date()
        var month = currentTime.getMonth() + 1
        var day = currentTime.getDate()
        var year = currentTime.getFullYear()
        var hours = currentTime.getHours()
        var minutes = currentTime.getMinutes()
        if (minutes < 10){
        minutes = "0" + minutes
        }
        document.write(month + "/" + day + "/" + year + " " + hours + ":" + minutes + " ")
        if(hours > 11){
        document.write("PM")
        } else {
        document.write("AM")
        }
        //-->
        </script>
        </strong>
        </td>
        <td width=70% align='center' valign="middle">
        <font face='Tahoma' color='#8A0808' size='12'>
        <strong>Auto Refresh in <span id="CDTimer">180</span> secs.</font></strong>
        <script language="JavaScript" type="text/javascript">
        /*<![CDATA[*/
        var TimerVal = 600;
        var TimerSPan = document.getElementById("CDTimer");
        function CountDown(){
        setTimeout( "CountDown()", 1000 );
        TimerSPan.innerHTML=TimerVal;
        TimerVal=TimerVal-1;
        } CountDown() /*]]>*/ </script>
        
        
        </td>
        </tr>
        </table>
"@
  $headerNames  = "Admin_DB","Broker_DB","Acct_DB","Monitor_DB","Prov_DB","Trust_DB","Monitor_srv","EnvTest_DB", "Config_srv", "Admin_srv", "EnvTest_srv", "Hyp_srv", "SF_srv", "Acct_srv", "Log_srv", "Trust_srv","prov_srv","broker_srv"
$headerWidths = "2","2" ,"2","2" , "2","2" ,"2","2" ,"2" ,"2","2" , "2","2" ,"2","2"  ,"2","2" ,"2"
    

$HTML +=  @"
    <table width='100%'><tbody>
   <tr bgcolor='#FAFAFA'>

    <td width='13%' align='center' valign="middle"><strong>PODS</strong></td>
        <font face='Tahoma' color='#8A0808' size='20'>

        

"@
 $i = 0
    while ($i -lt $headerNames.count) {
        $headerName = $headerNames[$i]
        $headerWidth = $headerWidths[$i]

         $HTML += "<td width='" + $headerWidth + "%'font face='Tahoma' color='#8A0808' size='200' align='center'><strong>$headerName</strong></td>"
        #$HTML += " <font face='Tahoma' color='#8A0808' size='2'align='center'><strong>$headerName</strong></td>"
        $i++ }


 
 
                $HTML += "<html><body>
                    
                    <table border=0 cellpadding=0 cellspacing=0 id=header>"

                  
     }

            ## Finish the HTML file.
            $HTML += "</table></body></html>

Link to comment
  • 0

gggg

 ## Finish the HTML file.
            $HTML += "</table></body></html>

## Output the HTML file
            $HTML | Out-File $OutputFile
        }

        ## If email was configured, set the variables for the email subject and body.
     
        ## If the refresh time option is configured, wait the specifed number of seconds then loop.
        If ($RefreshTime -ne 0)
        {
          # Start-Sleep -Seconds $RefreshTime
        }
    }
    

Link to comment
  • 0


if ($null -eq (Get-PSSnapin "Citrix.*" -EA silentlycontinue)) {
try { Add-PSSnapin Citrix.* -ErrorAction Stop }
catch { write-error "Error Get-PSSnapin Citrix.Broker.Admin.* Powershell snapin"; Return }
}

$ReportDate = (Get-Date -UFormat "%A, %d. %B %Y %R")


$currentDir = Split-Path $MyInvocation.MyCommand.Path
$outputpath = Join-Path $currentDir "" #add here a custom output folder if you wont have it on the same directory
$logfile = Join-Path $outputpath ("TCSGDSD_CHINACTXXDHealthChedck.log")
$resultsHTM = Join-Path $outputpath ("TCSGDSD_CHINARdeport.htm") #add $outputdate in filename if you like
  
$VDIfirstheaderName = "virtualDesktops"

$VDIHeaderNames = "DNSName","CatalogName","DeliveryGroup","PowerState", "Ping", "MaintMode","NoofCore", "AvgCPU", "MemoryGB",    "MemUsg", "AvailDiskSpace",     "Uptime","LastConnect",     "RegState","VDAVersion","Mail",  "CitrixLetency", "CitrixICARTT", "ClientWorkspace", "MSTeam", "HDX", "CQI", "VDAWorkspace"
$VDIHeaderWidths = "4","4",  "4", "4",        "4","4", "4",        "4","4",     "4",        "4","4",                  "4",         "4","4",                 "4",              "4",              "4",              "4",              "4", "4", "4",         "4", "4"

$VDItablewidth = 1200
  

function LogMe() {
Param(
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $logEntry,
[switch]$display,
[switch]$error,
[switch]$warning,
[switch]$progress
)
  
if ($error) { $logEntry = "[ERROR] $logEntry" ; Write-Host "$logEntry" -Foregroundcolor Red }
elseif ($warning) { Write-Warning "$logEntry" ; $logEntry = "[WARNING] $logEntry" }
elseif ($progress) { Write-Host "$logEntry" -Foregroundcolor Green }
elseif ($display) { Write-Host "$logEntry" }
  
#$logEntry = ((Get-Date -uformat "%D %T") + " - " + $logEntry)
$logEntry | Out-File $logFile -Append
}
  
function Ping([string]$IPAddress, [int]$timeout = 10) {
#$ping =Get-WmiObject -Query "Select * from win32_PingStatus where Address='$IPAddress'"
$ping = new-object System.Net.NetworkInformation.Ping #creates a ping object
try { $result = $ping.send($IPAddress, $timeout).Status.ToString() }
catch { $result = "Failure" }
return $result
}
Function CheckCpuUsage() 

    param ($IPAddress)
    Try { $CpuUsage=(Get-WmiObject -computer $IPAddress -class win32_processor | Measure-Object -property LoadPercentage -Average | Select-Object -ExpandProperty Average)
    $CpuUsage = [math]::round($CpuUsage, 1); return $CpuUsage


    } Catch { "Error returned while checking the CPU usage. Perfmon Counters may be fault" | LogMe -error; return 101 } 
}
Function CheckMemoryUsage()

    param ($IPAddress)
    Try 
    {   $SystemInfo = (Get-WmiObject -computername $IPAddress -Class Win32_OperatingSystem -ErrorAction Stop | Select-Object TotalVisibleMemorySize, FreePhysicalMemory)
        $TotalRAM = $SystemInfo.TotalVisibleMemorySize/1MB 
        $FreeRAM = $SystemInfo.FreePhysicalMemory/1MB 
        $UsedRAM = $TotalRAM - $FreeRAM 
        $RAMPercentUsed = ($UsedRAM / $TotalRAM) * 100 
        $RAMPercentUsed = [math]::round($RAMPercentUsed, 2);
        return $RAMPercentUsed
    } Catch { "Error returned while checking the Memory usage. Perfmon Counters may be fault" | LogMe -error; return 101 } 
}

Function CheckHardDiskUsage() 

    param ($IPAddress, $deviceID)
    Try 
    {   
        $HardDisk = $null
        $HardDisk = Get-WmiObject Win32_LogicalDisk -ComputerName $IPAddress -Filter "DeviceID='$deviceID'" -ErrorAction Stop | Select-Object Size,FreeSpace
        if ($null -ne $HardDisk)
        {
        $DiskTotalSize = $HardDisk.Size 
        $DiskFreeSpace = $HardDisk.FreeSpace 
        $frSpace=[Math]::Round(($DiskFreeSpace/1073741824),2)
        $PercentageDS = (($DiskFreeSpace / $DiskTotalSize ) * 100); $PercentageDS = [math]::round($PercentageDS, 2)
        
        Add-Member -InputObject $HardDisk -MemberType NoteProperty -Name PercentageDS -Value $PercentageDS
        Add-Member -InputObject $HardDisk -MemberType NoteProperty -Name frSpace -Value $frSpace
        } 
        
        return $HardDisk
    } Catch { "Error returned while checking the Hard Disk usage. Perfmon Counters may be fault" | LogMe -error; return $null } 
}

Function writeHtmlHeader
{
param($title, $fileName)
$date = $ReportDate
$head = @"
<html>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>
<title>$title</title>
<STYLE TYPE="text/css">
<!--
td {
font-family: Tahoma;
font-size: 11px;
border-top: 1px solid #999999;
border-right: 1px solid #999999;
border-bottom: 1px solid #999999;
border-left: 1px solid #999999;
padding-top: 0px;
padding-right: 0px;
padding-bottom: 0px;
padding-left: 0px;
overflow: hidden;
}
body {
margin-left: 5px;
margin-top: 5px;
margin-right: 0px;
margin-bottom: 10px;
table {
table-layout:fixed;
border: thin solid #000000;
}
-->
</style>
</head>
<body>
<table width='1200'>
<tr bgcolor='#CCCCCC'>
<td colspan='7' height='48' align='center' valign="middle">
<font face='tahoma' color='#003399' size='4'>
<strong>FrankFurt TCSGDSD_CHINA VDI Report - $ReportDate</strong></font>
</td>
</tr>
</table>
"@
$head | Out-File $fileName
}
Function writeTableHeader
{
param($fileName, $firstheaderName, $headerNames, $headerWidths, $tablewidth)
$tableHeader = @"
  
<table width='$tablewidth'><tbody>
<tr bgcolor=#CCCCCC>
<td width='6%' align='center'><strong>$firstheaderName</strong></td>
"@
  
$i = 0
while ($i -lt $headerNames.count) {
$headerName = $headerNames[$i]
$headerWidth = $headerWidths[$i]
$tableHeader += "<td width='" + $headerWidth + "%' align='center'><strong>$headerName</strong></td>"
$i++
}
  
$tableHeader += "</tr>"
  
$tableHeader | Out-File $fileName -append
}
Function writeTableFooter
{
param($fileName)
"</table><br/>"| Out-File $fileName -append
}
  
Function writeData
{
param($data, $fileName, $headerNames)

$tableEntry  =""  
$data.Keys | Sort-Object | ForEach-Object {
$tableEntry += "<tr>"
$computerName = $_
$tableEntry += ("<td bgcolor='#CCCCCC' align=center><font color='#003399'>$computerName</font></td>")
#$data.$_.Keys | foreach {
$headerNames | ForEach-Object {
#"$computerName : $_" | LogMe -display
try {
if ($data.$computerName.$_[0] -eq "SUCCESS") { $bgcolor = "#387C44"; $fontColor = "#FFFFFF" }
elseif ($data.$computerName.$_[0] -eq "WARNING") { $bgcolor = "#FF7700"; $fontColor = "#FFFFFF" }
elseif ($data.$computerName.$_[0] -eq "ERROR") { $bgcolor = "#FF0000"; $fontColor = "#FFFFFF" }
else { $bgcolor = "#CCCCCC"; $fontColor = "#003399" }
$testResult = $data.$computerName.$_[1]
}
catch {
$bgcolor = "#CCCCCC"; $fontColor = "#003399"
$testResult = ""
}
$tableEntry += ("<td bgcolor='" + $bgcolor + "' align=center><font color='" + $fontColor + "'>$testResult</font></td>")
}
$tableEntry += "</tr>"
}
$tableEntry | Out-File $fileName -append
}
 

Link to comment
  • 0

Function ToHumanReadable()
{
  param($timespan)
  
  If ($timespan.TotalHours -lt 1) {
    return $timespan.Minutes + "minutes"
  }

  $sb = New-Object System.Text.StringBuilder
  If ($timespan.Days -gt 0) {
    [void]$sb.Append($timespan.Days)
    [void]$sb.Append(" days")
    [void]$sb.Append(", ")    
  }
  If ($timespan.Hours -gt 0) {
    [void]$sb.Append($timespan.Hours)
    [void]$sb.Append(" hours")
  }
  If ($timespan.Minutes -gt 0) {
    [void]$sb.Append(" and ")
    [void]$sb.Append($timespan.Minutes)
    [void]$sb.Append(" minutes")
  }
  return $sb.ToString()
}


function Get-CitrixMaintenanceInfo {
    [CmdletBinding()]
    [OutputType([System.Management.Automation.PSCustomObject])]
    param
    (
        [Parameter(Mandatory = $false,
                   ValueFromPipeline = $true,
                   Position = 0)]
        [System.String[]]$AdminAddress = 'localhost',
        [Parameter(Mandatory = $false,
                   ValueFromPipeline = $true,
                   Position = 1)]
        [System.Management.Automation.PSCredential]$Credential
    ) # Param
    
    Try {
        $PSSessionParam = @{ }
        If ($null -ne $Credential) { $PSSessionParam['Credential'] = $Credential } #Splatting
        If ($null -ne $AdminAddress) { $PSSessionParam['ComputerName'] = $AdminAddress } #Splatting
        
        # Create Session
        $Session = New-PSSession -ErrorAction Stop @PSSessionParam
        
        # Create script block for invoke command
        $ScriptBlock = {
            if ($null -eq (Get-PSSnapin "Get-PSSnapin Citrix.ConfigurationLogging.Admin.*" -ErrorAction silentlycontinue)) {
                try { Add-PSSnapin Citrix.ConfigurationLogging.Admin.* -ErrorAction Stop } catch { write-error "Error Get-PSSnapin Citrix.ConfigurationLogging.Admin.* Powershell snapin"; Return }
            } #If
            
            $Date = Get-Date
            $StartDate = $Date.AddDays(-7) # Hard coded value for how many days back
            $EndDate = $Date
            
            # Command to get the informations from log
            $LogEntrys = Get-LogLowLevelOperation -MaxRecordCount 1000000 -Filter { StartTime -ge $StartDate -and EndTime -le $EndDate } | Where-Object { $_.Details.PropertyName -eq 'MAINTENANCEMODE' } | Sort-Object EndTime -Descending
            
            # Build an object with the data for the output
            [array]$arrMaintenance = @()
            ForEach ($LogEntry in $LogEntrys) {
                $TempObj = New-Object -TypeName psobject -Property @{
                    User = $LogEntry.User
                    TargetName = $LogEntry.Details.TargetName
                    NewValue = $LogEntry.Details.NewValue
                    PreviousValue = $LogEntry.Details.PreviousValue
                    StartTime = $LogEntry.Details.StartTime
                    EndTime = $LogEntry.Details.EndTime
                } #TempObj
                $arrMaintenance += $TempObj
            } #ForEach                
            $arrMaintenance
        } # ScriptBlock
        
        # Run the script block with invoke-command, return the values and close the session
        $MaintLogs = Invoke-Command -Session $Session -ScriptBlock $ScriptBlock -ErrorAction Stop
        Write-Output $MaintLogs
        Remove-PSSession -Session $Session -ErrorAction SilentlyContinue
        
    } Catch {
        Write-Warning "Error occurs: $_"
    } # Try/Catch


$wmiOSBlock = {param($IPAddress)
  try { $wmi=Get-WmiObject -class Win32_OperatingSystem -computer $IPAddress }
  catch { $wmi = $null }
  return $wmi
}

Remove-Item $logfile -force -EA SilentlyContinue
Remove-Item $resultsHTM -force -EA SilentlyContinue


  
$allResults = @{}
#givemachine list
 #$machines = Get-Content "D:\Scripts\Subodh\Uptime\Machine3.txt"
#$machines = Get-BrokerMachine -MaxRecordCount 10000 | Where-Object {$_.DesktopGroupName -eq "W10_THK_BASE_MicrolandBCP_DE" }
$machines = Get-BrokerMachine -MaxRecordCount 100000 | Where-Object {($_.DesktopGroupName -like "W10_MCS_POOL_NX_TCSGSDCHINA_PL") -and ($_.PowerState -eq "On" -or $_.PowerState -eq "Off")}
#$machines = Get-BrokerMachine -MaxRecordCount 10000 | Where-Object {$_.DesktopGroupName -eq "W10_THK_BASE_MicrolandBCP_DE"}
#$machines = Get-BrokerMachine -MaxRecordCount 10000 | Where-Object {$_.IpAddress -eq "10.151.83.46" }
$Maintenance = Get-CitrixMaintenanceInfo -AdminAddress hostname

foreach($machine in $machines.IPAddress) {
$tests = @{}
#convert machine list
    $machine= Get-BrokerMachine -MaxRecordCount 10000 -IPAddress $machine | Where-Object {($_.DesktopGroupName -eq "W10_MCS_POOL_NX_TCSGSDCHINA_PL") -and ($_.PowerState -eq "On" -or $_.PowerState -eq "Off")}  | select -First 1
  #Clear-Variable $machine
  #$machine=$machine1
$ErrorVDI = 0
$machineDNS = $machine | ForEach-Object{ $_.IpAddress }
"Machine: $machineDNS" | LogMe -display -progress
$CatalogName = $machine | ForEach-Object{ $_.CatalogName }
"Catalog: $CatalogName" | LogMe -display -progress
$tests.CatalogName = "NEUTRAL", $CatalogName
$DeliveryGroup = $machine | ForEach-Object{ $_.DesktopGroupName }
"DeliveryGroup: $DeliveryGroup" | LogMe -display -progress
$tests.DeliveryGroup = "NEUTRAL", $DeliveryGroup
$DNSName = $machine | ForEach-Object{ $_.DNSName }
"MachineName: $DNSName" | LogMe -display -progress
$tests.DNSName = "NEUTRAL", $DNSName
$Powered = $machine | ForEach-Object{ $_.PowerState }
"PowerState: $Powered" | LogMe -display -progress
$tests.PowerState = "NEUTRAL", $Powered

if ($Powered -eq "Off" -OR $Powered -eq "Unknown") {
$tests.PowerState = "NEUTRAL", $Powered
}

if ($Powered -eq "On") {
$tests.PowerState = "SUCCESS", $Powered
}

if ($Powered -eq "On" -OR $Powered -eq "Unknown" -OR $Powered -eq "Unmanaged") {

$result = Ping $machineDNS 100
if ($result -eq "SUCCESS") {
  $tests.Ping = "SUCCESS", $result
  
  $tests.WMI = "ERROR","Error"
  $job = Start-Job -ScriptBlock $wmiOSBlock -ArgumentList $machineDNS
  $wmi = Wait-job $job -Timeout 15 | Receive-Job
  if ($null -ne $wmi) {
    $tests.WMI = "SUCCESS", "Success"
    $LBTime=[Management.ManagementDateTimeConverter]::ToDateTime($wmi.Lastbootuptime)
    [TimeSpan]$uptime=New-TimeSpan $LBTime $(get-date)
  
    if ($uptime.days -gt $maxUpTimeDays) {
      "reboot warning, last reboot: {0:D}" -f $LBTime | LogMe -display -warning
      $tests.Uptime = "WARNING", $uptime.days
      $ErrorVDI = $ErrorVDI + 1
    } else { 
      $tests.Uptime = "SUCCESS", $uptime.days 
    }
  } else { 
    "WMI connection failed - check WMI for corruption" | LogMe -display -error
    stop-job $job
  }

  
$MachineOSVersion = "N/A"
$MachineOSVersion = (Get-ItemProperty -Path "\\$machineDNS\C$\WINDOWS\System32\hal.dll" -ErrorAction SilentlyContinue).VersionInfo.FileVersion.Split()[0]
$tests.OSBuild = "NEUTRAL", $MachineOSVersion


  
  }
else {
$tests.Ping = "Error", $result
$ErrorVDI = $ErrorVDI + 1
}


$RegistrationState = $machine | ForEach-Object{ $_.RegistrationState }
"State: $RegistrationState" | LogMe -display -progress
if ($RegistrationState -ne "Registered") {
$tests.RegState = "ERROR", $RegistrationState
$ErrorVDI = $ErrorVDI + 1
}
else { $tests.RegState = "SUCCESS", $RegistrationState }


 
$MaintenanceMode = $machine | ForEach-Object{ $_.InMaintenanceMode }
"MaintenanceMode: $MaintenanceMode" | LogMe -display -progress
if ($MaintenanceMode) {
    $objMaintenance = $Maintenance | Where-Object { $_.TargetName.ToUpper() -eq $machine.MachineName.ToUpper() } | Select-Object -First 1
    If ($null -ne $objMaintenance){$MaintenanceModeOn = ("ON, " + $objMaintenance.User)} Else {$MaintenanceModeOn = "ON"}
    "MaintenanceModeInfo: $MaintenanceModeOn" | LogMe -display -progress
    $tests.MaintMode = "WARNING", $MaintenanceModeOn
    $ErrorVDI = $ErrorVDI + 1
}
else { $tests.MaintMode = "SUCCESS", "OFF" }
  
$HostedOn = $machine | ForEach-Object{ $_.HostingServerName }
"HostedOn: $HostedOn" | LogMe -display -progress
$tests.HostedOn = "NEUTRAL", $HostedOn

$VDAVersion = $machine | ForEach-Object{ $_.AgentVersion }
"VDAVersion: $VDAVersion" | LogMe -display -progress
$tests.VDAVersion = "NEUTRAL", $VDAVersion

$AssociatedUserNames = $machine | ForEach-Object{ $_.AssociatedUserUPNs }
"Assigned to $AssociatedUserNames" | LogMe -display -progress
$tests.Mail = "NEUTRAL", $AssociatedUserNames
         
$Tags = $machine | ForEach-Object{ $_.Tags }
"Tags: $Tags" | LogMe -display -progress
$tests.Tags = "NEUTRAL", $Tags

$MCSVDIImageOutOfDate = $machine | ForEach-Object{ $_.ImageOutOfDate }
"ImageOutOfDate: $MCSVDIImageOutOfDate" | LogMe -display -progress
if ($MCSVDIImageOutOfDate -eq $true) { $tests.MCSImageOutOfDate = "ERROR", $MCSVDIImageOutOfDate }
elseif ($MCSVDIImageOutOfDate -eq $false) { $tests.MCSImageOutOfDate = "SUCCESS", $MCSVDIImageOutOfDate  }
else { $tests.MCSVDIImageOutOfDate = "NEUTRAL", $MCSVDIImageOutOfDate }
$machineDNS = $machine | ForEach-Object{ $_.IpAddress }
"Machine: $machineDNS" | LogMe -display -progress

$NoofCore = Get-WmiObject -class Win32_ComputerSystem -computer $machineDNS  | select -expandproperty numberoflogicalprocessors -first 1

"Assigned core $NoofCore" | LogMe -display -progress
$tests.NoofCore = "NEUTRAL", $NoofCore

$Nw = Get-WmiObject -Namespace root\Citrix\euem -Class citrix_euem_RoundTrip -cn $machineDNS  | select NetworkLatency

"latency $Nw" | LogMe -display -progress
$tests.CitrixLetency = "NEUTRAL", $Nw.NetworkLatency


$Nw1 = Get-WmiObject -Namespace root\Citrix\euem -Class citrix_euem_RoundTrip -cn $machineDNS  | select RoundtripTime

"latency $Nw1" | LogMe -display -progress
$tests.CitrixICARTT = "NEUTRAL", $Nw1.RoundtripTime


# Column AssociatedUserNames
$ctx =Get-Brokersession -IpAddress $machineDNS | Select ClientVersion

"latency $ctx" | LogMe -display -progress
$tests.ClientWorkspace = "NEUTRAL", $ctx.ClientVersion
$filename = "\Program Files (x86)\Microsoft\Teams\current\Teams.exe"
$filename1= "Program Files (x86)\Citrix\HDX RealTime Connector\MediaEngineService.exe"
$filename2= "Program Files (x86)\Citrix\Connection Quality Indicator\Citrix.CQI.exe"
$filename3 = "\Program Files (x86)\Citrix\ICA Client\CDViewer.exe"
        $file = Get-Item "\\$machineDNS\c$\$filename" -ErrorAction SilentlyContinue
        $file1 = Get-Item "\\$machineDNS\c$\$filename1" -ErrorAction SilentlyContinue
        $file2 = Get-Item "\\$machineDNS\c$\$filename2" -ErrorAction SilentlyContinue
        $file3 = Get-Item "\\$machineDNS\c$\$filename3" -ErrorAction SilentlyContinue
$FileVersion=($file.VersionInfo).fileversion
$tests.MSTeam= "NEUTRAL", $FileVersion
        $HDXVersion=($file1.VersionInfo).fileversion
        $tests.HDX = "NEUTRAL",  $HDXVersion
        $CQIVersion=($file2.VersionInfo).fileversion
        $tests.CQI = "NEUTRAL", $CQIVersion
        $WorkSpaceVersion=($file3.VersionInfo).fileversion
        $tests.VDAWorkspace = "NEUTRAL", $WorkSpaceVersion

$Noofmemory=get-wmiobject Win32_ComputerSystem -cn $machineDNS | select @{name="PhysicalMemory";Expression={"{0:N2}" -f($_.TotalPhysicalMemory/1gb).tostring("N0")}}| select -expandproperty PhysicalMemory -first 1
"Assigned core $Noofmemory" | LogMe -display -progress
$tests.MemoryGB = "NEUTRAL", $Noofmemory
        $XAAvgCPUval = CheckCpuUsage ($machineDNS)
        #$VDtests.LoadBalancingAlgorithm = "SUCCESS", "LB is set to BEST EFFORT"} 
            
        if( [int] $XAAvgCPUval -lt 75) { "CPU usage is normal [ $XAAvgCPUval % ]" | LogMe -display; $tests.AvgCPU = "SUCCESS", "$XAAvgCPUval %" }
        elseif([int] $XAAvgCPUval -lt 85) { "CPU usage is medium [ $XAAvgCPUval % ]" | LogMe -warning; $tests.AvgCPU = "WARNING", "$XAAvgCPUval %" }       
        elseif([int] $XAAvgCPUval -lt 95) { "CPU usage is high [ $XAAvgCPUval % ]" | LogMe -error; $tests.AvgCPU = "ERROR", "$XAAvgCPUval %" }
        elseif([int] $XAAvgCPUval -eq 101) { "CPU usage test failed" | LogMe -error; $tests.AvgCPU = "ERROR", "Err" }
        else { "CPU usage is Critical [ $XAAvgCPUval % ]" | LogMe -error; $tests.AvgCPU = "ERROR", "$XAAvgCPUval %" }   
        $XAAvgCPUval = 0

        # Check the Physical Memory usage       
        [int] $XAUsedMemory = CheckMemoryUsage ($machineDNS)
        if( [int] $XAUsedMemory -lt 75) { "Memory usage is normal [ $XAUsedMemory % ]" | LogMe -display; $tests.MemUsg = "SUCCESS", "$XAUsedMemory %" }
        elseif( [int] $XAUsedMemory -lt 85) { "Memory usage is medium [ $XAUsedMemory % ]" | LogMe -warning; $tests.MemUsg = "WARNING", "$XAUsedMemory %" }       
        elseif( [int] $XAUsedMemory -lt 95) { "Memory usage is high [ $XAUsedMemory % ]" | LogMe -error; $tests.MemUsg = "ERROR", "$XAUsedMemory %" }
        elseif( [int] $XAUsedMemory -eq 101) { "Memory usage test failed" | LogMe -error; $tests.MemUsg = "ERROR", "Err" }
        else { "Memory usage is Critical [ $XAUsedMemory % ]" | LogMe -error; $tests.MemUsg = "ERROR", "$XAUsedMemory %" }   
        $XAUsedMemory = 0  

       
            # Check Disk Usage 
            $HardDisk = CheckHardDiskUsage -IpAddress $machineDNS -deviceID "C:"
            if ($null -ne $HardDisk) {    
                $XAPercentageDS = $HardDisk.PercentageDS
                $frSpace = $HardDisk.frSpace

                If ( [int] $XAPercentageDS -gt 15) { "Disk Free is normal [ $XAPercentageDS % ]" | LogMe -display; $tests.AvailDiskSpace = "SUCCESS", "$frSpace GB" } 
                ElseIf ([int] $XAPercentageDS -eq 0) { "Disk Free test failed" | LogMe -error; $tests.CFreespace = "ERROR", "Err" }
                ElseIf ([int] $XAPercentageDS -lt 5) { "Disk Free is Critical [ $XAPercentageDS % ]" | LogMe -error; $tests.AvailDiskSpace = "ERROR", "$frSpace GB" } 
                ElseIf ([int] $XAPercentageDS -lt 15) { "Disk Free is Low [ $XAPercentageDS % ]" | LogMe -warning; $tests.AvailDiskSpace = "WARNING", "$frSpace GB" }     
                Else { "Disk Free is Critical [ $XAPercentageDS % ]" | LogMe -error; $tests.AvailDiskSpace = "ERROR", "$frSpace GB" }
            
                $XAPercentageDS = 0
                $frSpace = 0
                $HardDisk = $null
            }
        
        

$yellow =((Get-Date).AddMonths(-1).ToString('yyyy-MM-dd HH:mm:s'))
$red =((Get-Date).AddMonths(-3).ToString('yyyy-MM-dd HH:mm:s'))

$machineLastConnect = $machine | ForEach-Object{ $_.LastConnectionTime }

if ([string]::IsNullOrWhiteSpace($machineLastConnect))
    {
        $tests.LastConnect = "NEUTRAL", "NO DATA"
    }
elseif ($machineLastConnect -lt $red)
    {
        "LastConnect: $machineLastConnect" | LogMe -display -ERROR
        $tests.LastConnect = "ERROR", $machineLastConnect
    }     
elseif ($machineLastConnect -lt $yellow)
    {
        "LastConnect: $machineLastConnect" | LogMe -display -WARNING
        $tests.LastConnect = "WARNING", $machineLastConnect
    }
else 
    {
        $tests.LastConnect = "SUCCESS", $machineLastConnect
        "LastConnect: $machineLastConnect" | LogMe -display -progress
    }
$sessionUser = $machine | ForEach-Object{ $_.SessionUserName }

  
  
" --- " | LogMe -display -progress
  
if ($ExcludedCatalogs -contains $CatalogName) {
"$machineDNS in excluded folder - skipping" | LogMe -display -progress
}
else {
# Check if error exists on this vdi
if ($ShowOnlyErrorVDI -eq 0 ) { $allResults.$machineDNS = $tests }
else {
if ($ErrorVDI -gt 0) { $allResults.$machineDNS = $tests }
else { "$machineDNS is ok, no output into HTML-File" | LogMe -display -progress }
}
}
}


  
#XDmajor, $XDminor = $controllerversion.Split(".")[0..1]
#$XDVersion = "$XDmajor.$XDminor"
$EnvironmentNameOut = "$EnvironmentName"
$emailSubject = ("US TCSGSD CHINA Farm Report - " + $ReportDate)

Write-Host ("Saving results to html report: " + $resultsHTM)
writeHtmlHeader "US TCSGSD CHINA Farm Report" $resultsHTM

writeTableHeader $resultsHTM $VDIFirstheaderName $VDIHeaderNames $VDIHeaderWidths $VDItablewidth
$allResults | sort-object -property CatalogName | ForEach-Object{ writeData $allResults $resultsHTM $VDIHeaderNames }
writeTableFooter $resultsHTM

  
 


#$scriptend = Get-Date
#$scriptruntime =  $scriptend - $scriptstart | Select-Object TotalSeconds
#$scriptruntimeInSeconds = $scriptruntime.TotalSeconds
#
#"Script was running for $scriptruntimeInSeconds " | LogMe -display -progress

#send email

###########################################################################
# E-mail report details
$logTime =  Get-Date -f "yy.MM.dd HH:mm:ss"
$emailFrom     = "DEFRAVMPCXIXD06@ey.com"
#$emailTo       = "sunil.saroja@ey.com"
$emailTo       = "ML_EYOMC_VDI.GID@ey.net"
$smtpServer    = "smtp.ey.net"
############################################################################

$emailMessage = New-Object System.Net.Mail.MailMessage
$emailMessage.From = $emailFrom
$emailMessage.To.Add( $emailTo )
$emailMessage.Subject = $emailSubject 
$emailMessage.IsBodyHtml = $true
$emailMessage.Body = (Get-Content $resultsHTM) | Out-String
$emailMessage.Attachments.Add($resultsHTM)

$smtpClient = New-Object System.Net.Mail.SmtpClient( $smtpServer )
#$smtpClient.EnableSsl = $smtpEnableSSL

$smtpClient.Send( $emailMessage )

Link to comment
  • 0

asnp citrix*
$out =$null
$out = @()
foreach ($g in (Get-Brokermachine -MaxRecordCount 20000000 )) {
foreach ($u in $g) {
$d = “” | select User,VDI_Assigned,Machinecatalog,DesktopGroup,LastConnectedUser,GeoLocation,city,mail,HRGPN,Title
 #username,Account_status,VDI,fullname,LastConnectedTime,Email,HRGPN,DG,ADGroup,type,Title,City,country
Write-Host $g.DesktopGroupName

$king = $null
$king = "0"

$king1=$u|select -ExpandProperty AssociatedUserFullNames -First 1


#import-module activedirectory
$LocalSite = (Get-ADDomainController -Discover).Site
$NewTargetGC = Get-ADDomainController -Discover -Service 6 -SiteName $LocalSite
IF (!$NewTargetGC)
{ $NewTargetGC = Get-ADDomainController -Discover -Service 6 -NextClosestSite }
$NewTargetGCHostName = $NewTargetGC.HostName
$LocalGC = “$NewTargetGCHostName” + “:3268”
Write-Output “Identify User and Computer Objects with configured Service Principal Names `r ”
#Get-ADUser $king1 -Server “$LocalGC”
$raju=Get-ADUser -Server $LocalGC -filter { sAMAccountName -eq $king1} -Properties * |Select-Object city,co,samaccountname,UserPrincipalName,Enabled,DisplayName,"ey-HRGPN",Title 

$D.HRGPN =$raju."ey-HRGPN"
$D.Title  =$raju."Title"

$d.User = $king1
$d.VDI_Assigned = $u.HostedMachineName 
Write-Host $u.HostedMachineName
$d.Machinecatalog = $u.CatalogName
$d.DesktopGroup = $u.DesktopGroupName 
$d.LastConnectedUser = $u.LastConnectionUser

$d.GeoLocation=$raju.co
$d.city=$raju.city
$d.mail=$raju.UserPrincipalName

write-Host End -BackgroundColor DarkCyan
$out += $d
}
}
$out|export-csv -Path "c:\temp\newcontractor.csv" -NoTypeInformation -Force
 

Link to comment
  • 0

#Import-Module ActiveDirectory

asnp citrix*

$POD6CLT="USSECVMPCXIXD03"
$POD6STL="defravmpcxixd06"
$Lastused =((Get-Date).AddMonths(-15).ToString('yyyy-MM-dd HH:mm:s'))
$used = gc C:\Temp\out.txt

#$used=Get-BrokerMachine -MaxRecordCount 2000| Where-Object {($_.LastConnectionTime -lt $Lastused) -and ($_.DesktopKind -match 'Private') -and ($_.LastConnectionTime -notcontains $null) } #|Select-Object HostedMachineName,AssociatedUserUPNs,LastConnectionTime #-ExpandProperty AssociatedUserUPNs

 foreach($item in $used)
    {

   # $item = "US0PROD1803VA"

    $table="<table border='1' style='border-collapse:collapse;width:95%;background-color:#edf4f5;'> <tr style='background-color:#0a2ebf;color:white;height:20px;'><th>VDI_NAME</th><th>User</th><th>Last_VDI_Used</th></tr>"


        
            $user1=Get-BrokerDesktop -MaxRecordCount 100000 -DNSName $item|Select-Object AssociatedUserUPNs -ExpandProperty AssociatedUserUPNs
            $lasttime=Get-BrokerDesktop -MaxRecordCount 100000 -DNSName $item |Select-Object LastConnectionTime -ExpandProperty LastConnectionTime
            $user=$user1 |select -First 1
           $name1= Get-BrokerDesktop -MaxRecordCount 100000 -DNSname $item |Select-Object AssociatedUserFullNames -ExpandProperty AssociatedUserFullNames
           $name=$name1|select -First 1
           $VDI =Get-BrokerDesktop -MaxRecordCount 100000 -DNSname $item |Select-Object HostedMachineName -ExpandProperty HostedMachineName
 $StartDate=(GET-DATE)

$EndDate=[datetime]$lasttime

$days=NEW-TIMESPAN –Start $StartDate –End $EndDate
$days1=$days.Days

        $stylem7=";background-color:##333333;color:black;font-weight:bold" #change the background color to green
        $table+="<tr> <td>$dg</td> <td style='text-align:center$stylem7'>  $user </td> <td style='text-align:center$stylem7'> $lasttime($days1 Days)"
       
       
     $user2=$user1|select -First 1

      
      $firstName = $user2.split(".")[0]

     #$user1.Substring(0,5)
     #Get-ADUser -Identity $name1
     #$user2|select -First 1
     write-host "Done with $user$VDI VDI"
     #return  $table
     

$body = @"
<html>

<head>
<meta http-equiv=Content-Type content="text/html; charset=windows-1252">
<meta name=Generator content="Microsoft Word 15 (filtered)">
<style>
<!--
 /* Font Definitions */
 @font-face
    {font-family:"Cambria Math";
    panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
    {font-family:Calibri;
    panose-1:2 15 5 2 2 2 4 3 2 4;}
 /* Style Definitions */
 p.MsoNormal, li.MsoNormal, div.MsoNormal
    {margin-top:0in;
    margin-right:0in;
    margin-bottom:8.0pt;
    margin-left:0in;
    line-height:107%;
    font-size:11.0pt;
    font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
    {color:blue;
    text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
    {color:#954F72;
    text-decoration:underline;}
.MsoChpDefault
    {font-family:"Calibri",sans-serif;}
.MsoPapDefault
    {margin-bottom:8.0pt;
    line-height:107%;}
@page WordSection1
    {size:8.5in 11.0in;
    margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
    {page:WordSection1;}
-->
</style>

</head>

<body lang=EN-US link=blue vlink="#954F72">

<div class=WordSection1>

<p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;line-height:
normal'><span style='color:black'>&nbsp;</span></p>

<table class=MsoNormalTable border=0 cellspacing=0 cellpadding=0 align=left
 style='border-collapse:collapse;margin-left:6.75pt;margin-right:6.75pt'>
 <tr style='height:467.55pt'>
  <td width=642 valign=top style='width:476.2pt;border:solid windowtext 1.0pt;
  background:white;padding:0in 5.4pt 0in 5.4pt;height:467.55pt'>
  <table class=MsoNormalTable border=0 cellspacing=0 cellpadding=0 align=right
   width=0 style='width:469.85pt;border-collapse:collapse;margin-left:6.75pt;
   margin-right:6.75pt'>
   <tr style='height:30.3pt'>
    <td width=626 valign=top style='width:469.85pt;border:solid windowtext 1.0pt;
    border-bottom:none;background:#333333;padding:2.85pt 5.4pt 4.25pt 5.4pt;
    height:30.3pt'>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'><b><span style='color:white'><img width=50 height=34
    id="Picture 2" src='cid:lowIssues.png' alt=''></span></b><b><span
    style='color:white'>Zoom out to get a better view if you’re reading this on
    a
    smartphone&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></b></p>
    </td>
   </tr>
  </table>
  <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;line-height:
  normal'><b>&nbsp;</b></p>
  <table class=MsoNormalTable border=0 cellspacing=0 cellpadding=0 width=0
   style='width:459.25pt;border-collapse:collapse'>
   <tr style='height:42.2pt'>
    <td width=220 valign=top style='width:164.9pt;border:none;border-bottom:
    solid #A6A6A6 1.0pt;padding:0in 0in 11.35pt 0in;height:42.2pt'>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'><b>&nbsp;</b><b><img width=171 height=81 id="Picture 1"
    src='cid:moderateIssues.png' alt=''></b></p>
    </td>
    <td width=378 valign=bottom style='width:283.4pt;border:none;border-bottom:
    solid #A6A6A6 1.0pt;padding:0in 0in 11.35pt 0in;height:42.2pt'>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'><b><span style='font-size:18.0pt'>Virtual Desktop</span></b></p>
    </td>
    <td width=10 style='width:7.85pt;padding:0in 0in 0in 0in;height:42.2pt'>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'><b>&nbsp;</b></p>
    </td>
    <td width=4 style='width:2.95pt;padding:0in 0in 0in 0in;height:42.2pt'>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'><b>&nbsp;</b></p>
    </td>
   </tr>
   <tr style='height:268.45pt'>
    <td width=612 colspan=4 valign=top style='width:459.25pt;padding:11.35pt 11.35pt 11.35pt 11.35pt;
    height:268.45pt'>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'><b><u>VDI Decommissioning</u></b></p>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'>&nbsp;</p>
    <p class=MsoNormal style='margin-bottom:12.0pt;line-height:normal'><b>Assigned
    VDI in your Name $VDI<b> has not been accessed since $lasttime($days1 Days)</p>
    <p class=MsoNormal style='margin-bottom:12.0pt;line-height:normal'>With the
    process in place, the unused VDI for more than 4 weeks will be decommissioned.
    Hence, VDI mentioned above will be decommissioned.</p>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'>&nbsp;</p>
    <table class=MsoNormalTable border=0 cellspacing=0 cellpadding=0
     style='border-collapse:collapse'>
     <tr style='height:80.8pt'>
      <td width=577 valign=top style='width:432.55pt;border:solid windowtext 1.0pt;
      padding:0in 5.4pt 0in 5.4pt;height:80.8pt'>
      <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
      line-height:normal'><b>&nbsp;</b></p>
      <table class=MsoNormalTable border=0 cellspacing=0 cellpadding=0
       style='border-collapse:collapse'>
       <tr style='height:16.45pt'>
        <td width=302 valign=top style='width:226.6pt;border:solid windowtext 1.0pt;
        padding:0in 5.4pt 0in 5.4pt;height:16.45pt'>
        <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
               line-height:normal'><b><u>VDI Name:</u></b></p>
        </td>
        <td width=238 valign=top style='width:178.5pt;border:solid windowtext 1.0pt;
        border-left:none;padding:0in 5.4pt 0in 5.4pt;height:16.45pt'>
        <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
        line-height:normal'><b>&nbsp;$VDI</b></p>
        </td>
       </tr>
       <tr style='height:15.55pt'>
        <td width=302 valign=top style='width:226.6pt;border:solid windowtext 1.0pt;
        border-top:none;padding:0in 5.4pt 0in 5.4pt;height:15.55pt'>
        <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
        line-height:normal'><b><u>User:</u></b></p>
        </td>
        <td width=238 valign=top style='width:178.5pt;border-top:none;
        border-left:none;border-bottom:solid windowtext 1.0pt;border-right:
        solid windowtext 1.0pt;padding:0in 5.4pt 0in 5.4pt;height:15.55pt'>
        <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
        line-height:normal'><b>&nbsp;$user</b></p>
        </td>
       </tr>
       <tr style='height:15.55pt'>
        <td width=302 valign=top style='width:226.6pt;border:solid windowtext 1.0pt;
        border-top:none;padding:0in 5.4pt 0in 5.4pt;height:15.55pt'>
        <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
        line-height:normal'><b><u>Last Connected</u></b></p>
        </td>
        <td width=238 valign=top style='width:178.5pt;border-top:none;
        border-left:none;border-bottom:solid windowtext 1.0pt;border-right:
        solid windowtext 1.0pt;padding:0in 5.4pt 0in 5.4pt;height:15.55pt'>
        <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
        line-height:normal'><b>&nbsp;$EndDate</b></p>
        </td>
       </tr>
      </table>
      <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
      line-height:normal'><b>&nbsp;</b></p>
      <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
      line-height:normal'><b>&nbsp;</b></p>

      </td>
     </tr>
    </table>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'><b>&nbsp;</b></p>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'><b><u>Note</u></b><u>:</u></p>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'>If you wish to retain this VDI please email to&nbsp;<a
    href="mailto:IT_CITRIXVDI_MANAGEMENT.GID@ey.net"><span style='color:#0563C1'>IT_CITRIXVDI_MANAGEMENT.GID@ey.net</span></a>&nbsp;&nbsp;within
    next 7 days, else it will be deleted. Once VDI is deleted it can’t be
    retrieved.</p>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'>&nbsp;</p>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'>&nbsp;</p>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'><b>Regards,</b></p>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'><b>Virtual Desktop Team</b></p>
    <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;
    line-height:normal'><b>&nbsp;</b></p>
    </td>
   </tr>
  </table>
  <p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;line-height:
  normal'><b><span style='color:black'>Please do not reply to this message.
  Replies to this message are routed to an unmonitored mailbox</span></b></p>
  </td>
 </tr>
</table>

<p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;line-height:
normal'><span lang=EN-GB style='color:black'>&nbsp;</span></p>

<p class=MsoNormal style='margin-bottom:0in;margin-bottom:.0001pt;line-height:
normal'><span style='color:black'>&nbsp;</span></p>

<p class=MsoNormal>&nbsp;</p>

</div>

</body>

</html>

Link to comment
  • 0


#################################################
#PVS Health check script with automated retrys fixes
#created by Sunil MS
#DVP Support
#==============================================================================================
if ((Get-PSSnapin "Citrix.PVS.SnapIn" -EA silentlycontinue) -eq $null) {
try { Add-PSSnapin Citrix.PVS.SnapIn -ErrorAction silentlycontinue }
catch { write-error "Error loading Citrix.PVS.SnapIn PowerShell snapin"; Return }
}
Add-PSSnapin Citrix*
Add-PSSnapIN MCLIPSSnapIn
#==============================================================================================
# Import Variables from XML:
If (![string]::IsNullOrEmpty($hostinvocation)) {
    [string]$Script:ScriptPath = [System.IO.Path]::GetDirectoryName([System.Windows.Forms.Application]::ExecutablePath)
    [string]$Script:ScriptFile = [System.IO.Path]::GetFileName([System.Windows.Forms.Application]::ExecutablePath)
    [string]$Script:ScriptName = [System.IO.Path]::GetFileNameWithoutExtension([System.Windows.Forms.Application]::ExecutablePath)
} ElseIf ($Host.Version.Major -lt 3) {
    [string]$Script:ScriptPath = Split-Path -parent $MyInvocation.MyCommand.Definition
    [string]$Script:ScriptFile = Split-Path -Leaf $script:MyInvocation.MyCommand.Path
    [string]$Script:ScriptName = $ScriptFile.Split('.')[0].Trim()
} Else {
    [string]$Script:ScriptPath = $PSScriptRoot
    [string]$Script:ScriptFile = Split-Path -Leaf $PSCommandPath
    [string]$Script:ScriptName = $ScriptFile.Split('.')[0].Trim()
}

#Set-StrictMode -Version Latest

# Import parameter file
$Script:ParameterFile = $ScriptName + "_Parameters.xml"
$Script:ParameterFilePath = $ScriptPath
[xml]$cfg = Get-Content ($ParameterFilePath + "\" + $ParameterFile) # Read content of XML file

# Import variables
Function New-XMLVariables {
    # Create a variable reference to the XML file
    $cfg.Settings.Variables.Variable | foreach {
        # Set Variables contained in XML file
        $VarValue = $_.Value
        $CreateVariable = $True # Default value to create XML content as Variable
        switch ($_.Type) {
            # Format data types for each variable 
            '[string]' { $VarValue = [string]$VarValue } # Fixed-length string of Unicode characters
            '[char]' { $VarValue = [char]$VarValue } # A Unicode 16-bit character
            '[byte]' { $VarValue = [byte]$VarValue } # An 8-bit unsigned character
            '[bool]' { If ($VarValue.ToLower() -eq 'false'){$VarValue = [bool]$False} ElseIf ($VarValue.ToLower() -eq 'true'){$VarValue = [bool]$True} } # An boolean True/False value
            '[int]' { $VarValue = [int]$VarValue } # 32-bit signed integer
            '[long]' { $VarValue = [long]$VarValue } # 64-bit signed integer
            '[decimal]' { $VarValue = [decimal]$VarValue } # A 128-bit decimal value
            '[single]' { $VarValue = [single]$VarValue } # Single-precision 32-bit floating point number
            '[double]' { $VarValue = [double]$VarValue } # Double-precision 64-bit floating point number
            '[DateTime]' { $VarValue = [DateTime]$VarValue } # Date and Time
            '[Array]' { $VarValue = [Array]$VarValue.Split(',') } # Array
            '[Command]' { $VarValue = Invoke-Expression $VarValue; $CreateVariable = $False } # Command
        }
        If ($CreateVariable) { New-Variable -Name $_.Name -Value $VarValue -Scope $_.Scope -Force }
    }
}

New-XMLVariables
#define PVS server
$admin ="wsapp4460"
$ReportDate = (Get-Date -UFormat "%A, %d. %B %Y %R")
#==============================================================================================
 
$currentDir = Split-Path $MyInvocation.MyCommand.Path
$outputpath = Join-Path $currentDir "" #add here a custom output folder if you wont have it on the same directory
$outputdate = Get-Date -Format 'yyyyMMddHHmm'
$logfile = Join-Path $outputpath ("PVSHealthCheck.log")
$resultsHTM = Join-Path $outputpath ("PVSFarmReport.htm") #add $outputdate in filename if you like
$errorsHTM = Join-Path $currentDir ("PVSHealthCheckErrors.htm") 

if ($PerformPVSTargetCheck -eq "yes") {
#Header for Table 1 "Target Device Checks"
$TargetfirstheaderName = "TargetDeviceName"
$TargetheaderNames =  "WriteCacheFreespace", "WriteCache", "PVSServer"
$TargetheaderWidths = "4", "4", "4" 
$Targettablewidth = 1200
}

if ($PerformPVSvDiskCheck -eq "yes") {
#Header for Table 2 "vDisk Checks"
$vDiksFirstheaderName = "vDiskName"
$vDiskheaderNames = "Site", "Store", "vDiskFileName", "deviceCount", "CreateDate" , "ReplState",  "WriteCacheType"
$vDiskheaderWidths = "4","4", "8", "2","4", "4", "4"
$vDisktablewidth = 1200
}

#Header for Table 3 "PV Server"
$PVSfirstheaderName = "PVS Server"
$PVSHeaderNames = "Ping", "Active", "deviceCount","SoapService","StreamService"
$PVSheaderWidths = "8", "4", "4","4","4"
$PVStablewidth = 800
foreach ($disk in $diskLetters)
{
    $PVSHeaderNames += "$($disk)Freespace"
    $PVSheaderWidths += "4"
}
$PVSHeaderNames += "AvgCPU","MemUsg"
$PVSheaderWidths += "4","4"


#Header for Table 4 "Farm"


 
#==============================================================================================
#log function
function LogMe() {
Param(
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $logEntry,
[switch]$display,
[switch]$error,
[switch]$warning,
[switch]$progress
)
 
 if ($error) {
$logEntry = "[ERROR] $logEntry" ; Write-Host "$logEntry" -Foregroundcolor Red}
elseif ($warning) {
Write-Warning "$logEntry" ; $logEntry = "[WARNING] $logEntry"}
elseif ($progress) {
Write-Host "$logEntry" -Foregroundcolor Green}
elseif ($display) {
Write-Host "$logEntry" }
  
 #$logEntry = ((Get-Date -uformat "%D %T") + " - " + $logEntry)
$logEntry | Out-File $logFile -Append
}
#==============================================================================================
function Ping([string]$hostname, [int]$timeout = 200) {
$ping = new-object System.Net.NetworkInformation.Ping #creates a ping object
try {
$result = $ping.send($hostname, $timeout).Status.ToString()
} catch {
$result = "Failure"
}
return $result
}

Function Start-Countdown 
{   
    Param(
        [Int32]$Seconds = 10,
        [string]$Message = "Pausing for 10 seconds..."
    )
    ForEach ($Count in (1..$Seconds))
    {   Write-Progress -Id 1 -Activity $Message -Status "Waiting for $Seconds seconds, $($Seconds - $Count) left" -PercentComplete (($Count / $Seconds) * 100)
        Start-Sleep -Seconds 1
    }
    Write-Progress -Id 1 -Activity $Message -Status "Completed" -PercentComplete 100 -Completed
}
#==============================================================================================
# The function will check the processor counter and check for the CPU usage. Takes an average CPU usage for 5 seconds. It check the current CPU usage for 5 secs.
Function CheckCpuUsage() 

    param ($hostname)
    Try { $CpuUsage=(get-counter -ComputerName $hostname -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5 -ErrorAction Stop | select -ExpandProperty countersamples | select -ExpandProperty cookedvalue | Measure-Object -Average).average
        $CpuUsage = "{0:N1}" -f $CpuUsage; return $CpuUsage
    } Catch { "Error returned while checking the CPU usage. Perfmon Counters may be fault" | LogMe -error; return 101 } 
}
#============================================================================================== 
# The function check the memory usage and report the usage value in percentage
Function CheckMemoryUsage() 

    param ($hostname)
   Try 
    {   $SystemInfo = (Get-WmiObject -computername $hostname -Class Win32_OperatingSystem -ErrorAction Stop | Select-Object TotalVisibleMemorySize, FreePhysicalMemory)
       $TotalRAM = $SystemInfo.TotalVisibleMemorySize/1MB 
       $FreeRAM = $SystemInfo.FreePhysicalMemory/1MB 
       $UsedRAM = $TotalRAM - $FreeRAM 
       $RAMPercentUsed = ($UsedRAM / $TotalRAM) * 100 
       $RAMPercentUsed = "{0:N0}" -f $RAMPercentUsed
       return $RAMPercentUsed
    } Catch { "Error returned while checking the Memory usage. Perfmon Counters may be fault" | LogMe -error; return 101 } 
}
#==============================================================================================
Function writeHtmlHeader
{
param($title, $fileName)
$date = $ReportDate
$head = @"
<html>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>
<title>$title</title>
<STYLE TYPE="text/css">
<!--
td {
font-family: Tahoma;
font-size: 11px;
border-top: 1px solid #999999;
border-right: 1px solid #999999;
border-bottom: 1px solid #999999;
border-left: 1px solid #999999;
padding-top: 0px;
padding-right: 0px;
padding-bottom: 0px;
padding-left: 0px;
overflow: hidden;
}
body {
margin-left: 5px;
margin-top: 5px;
margin-right: 0px;
margin-bottom: 10px;
table {
table-layout:fixed; 
border: thin solid #000000;
}
-->
</style>
</head>
<body>
<table width='1200'>
<tr bgcolor='#CCCCCC'>
<td colspan='7' height='48' align='center' valign="middle">
<font face='tahoma' color='#003399' size='4'>
<strong>$title - $date</strong></font>
</td>
</tr>
</table>
"@
$head | Out-File $fileName
}
# ==============================================================================================
Function writeTableHeader
{
param($fileName, $firstheaderName, $headerNames, $headerWidths, $tablewidth)
$tableHeader = @"
<table width='$tablewidth'><tbody>
<tr bgcolor=#CCCCCC>
<td width='6%' align='center'><strong>$firstheaderName</strong></td>
"@
$i = 0
while ($i -lt $headerNames.count) {
$headerName = $headerNames[$i]
$headerWidth = $headerWidths[$i]
$tableHeader += "<td width='" + $headerWidth + "%' align='center'><strong>$headerName</strong></td>"
$i++
}
$tableHeader += "</tr>"
$tableHeader | Out-File $fileName -append
}
# ==============================================================================================
Function writeTableFooter
{
param($fileName)
"</table><br/>"| Out-File $fileName -append
}
#==============================================================================================
Function writeData
{
param($data, $fileName, $headerNames)
  
 $data.Keys | sort | foreach {
$tableEntry += "<tr>"
$computerName = $_
$tableEntry += ("<td bgcolor='#CCCCCC' align=center><font color='#003399'>$computerName</font></td>")
#$data.$_.Keys | foreach {
$headerNames | foreach {
#"$computerName : $_" | LogMe -display
try {
if ($data.$computerName.$_[0] -eq "SUCCESS") { $bgcolor = "#387C44"; $fontColor = "#FFFFFF" }
elseif ($data.$computerName.$_[0] -eq "WARNING") { $bgcolor = "#FF7700"; $fontColor = "#FFFFFF" }
elseif ($data.$computerName.$_[0] -eq "ERROR") { $bgcolor = "#FF0000"; $fontColor = "#FFFFFF" }
else { $bgcolor = "#CCCCCC"; $fontColor = "#003399" }
$testResult = $data.$computerName.$_[1]
}
catch {
$bgcolor = "#CCCCCC"; $fontColor = "#003399"
$testResult = ""
}
  
 $tableEntry += ("<td bgcolor='" + $bgcolor + "' align=center><font color='" + $fontColor + "'>$testResult</font></td>")
}
  
 $tableEntry += "</tr>"
  
  
 }
  
 $tableEntry | Out-File $fileName -append
}
# ==============================================================================================
Function writeHtmlFooter
{
param($fileName)
@"
<table>
<table width='1200'>
<tr bgcolor='#CCCCCC'>
<td colspan='7' height='25' align='left'>
<br>
<font face='courier' color='#000000' size='2'><strong>Retry Threshold =</strong></font><font color='#003399' face='courier' size='2'> $retrythresholdWarning<tr></font><br>
<tr bgcolor='#CCCCCC'>
</td>
</tr>
<tr bgcolor='#CCCCCC'>
</tr>
</table>
</body>
</html>

Link to comment
  • 0

"@ | Out-File $FileName -append
}
 
 function Farmcheck() {
# ======= PVS Farm Check ====================================================================
"Read some PVS Farm Parameters" | LogMe -display -progress
" " | LogMe -display -progress
$global:PVSFarmResults = @{}
$PVSfarm = Get-PVSFarm

$global:farmname_short = $PVSfarm.FarmName 
$PVSFarmtests = @{}

$DBServer = $PVSFarm | %{ $_.DatabaseServerName }
$PVSFarmtests.DBServerName = "NEUTRAL", $DBServer

$dbname = $PVSFarm | %{ $_.DatabaseName }
$PVSFarmtests.databaseName = "NEUTRAL", $dbname

$OfflineDB = $PVSFarm | %{ $_.OfflineDatabaseSupportEnabled }
$PVSFarmtests.OfflineDB = "NEUTRAL", $OfflineDB

$LicenseServer = $PVSFarm | %{ $_.LicenseServer }
$PVSFarmtests.LicenseServer = "NEUTRAL", $LicenseServer

$global:PVSFarmResults.$global:farmname_short = $PVSFarmtests
}

function PVSServerCheck() {
# ======= PVS Server Check ==================================================================
"Check PVS Servers" | LogMe -display -progress
" " | LogMe -display -progress
 
$global:PVSResults = @{}
$allPVSServer = Get-PvsServer

foreach($PVServerName in $allPVSServer){
$PVStests = @{}
  
$PVServerName_short = $PVServerName | %{ $_.ServerName }
"PVS-Server: $PVServerName_short" | LogMe -display -progress
$PVServerName_short

# Ping server 
$result = Ping $PVServerName_short 100
"Ping: $result" | LogMe -display -progress
if ($result -ne "SUCCESS") { $PVStests.Ping = "ERROR", $result }
else { $PVStests.Ping = "SUCCESS", $result 


# Check services
        if ((Get-Service -Name "soapserver" -ComputerName $PVServerName_short).Status -Match "Running") {
            "SoapService running..." | LogMe
            $PVStests.SoapService = "SUCCESS", "Success"
        } else {
            "SoapService service stopped"  | LogMe -display -error
            $PVStests.SoapService = "ERROR", "Error"
        }
            
        if ((Get-Service -Name "StreamService" -ComputerName $PVServerName_short).Status -Match "Running") {
            "StreamService service running..." | LogMe
            $PVStests.StreamService = "SUCCESS","Success"
        } else {
            "StreamService service stopped"  | LogMe -display -error
            $PVStests.StreamService = "ERROR","Error"
        }
            
        
        
 }
 
 
 #==============================================================================================
#               CHECK CPU AND MEMORY USAGE 
#==============================================================================================
#$PVServerName_short='WSAPP4470'
        # Check the AvgCPU value for 5 seconds
        $AvgCPUval = CheckCpuUsage ($PVServerName_short)
        #$VDtests.LoadBalancingAlgorithm = "SUCCESS", "LB is set to BEST EFFORT"} 
            
        if( [int] $AvgCPUval -lt 75) { "CPU usage is normal [ $AvgCPUval % ]" | LogMe -display; $PVStests.AvgCPU = "SUCCESS", "$AvgCPUval %" }
        elseif([int] $AvgCPUval -lt 85) { "CPU usage is medium [ $AvgCPUval % ]" | LogMe -warning; $PVStests.AvgCPU = "WARNING", "$AvgCPUval %" }       
        elseif([int] $AvgCPUval -lt 95) { "CPU usage is high [ $AvgCPUval % ]" | LogMe -error; $PVStests.AvgCPU = "ERROR", "$AvgCPUval %" }
        elseif([int] $AvgCPUval -eq 101) { "CPU usage test failed" | LogMe -error; $PVStests.AvgCPU = "ERROR", "Err" }
        else { "CPU usage is Critical [ $AvgCPUval % ]" | LogMe -error; $PVStests.AvgCPU = "ERROR", "$AvgCPUval %" }   
        $AvgCPUval = 0

        # Check the Physical Memory usage       
              $UsedMemory = CheckMemoryUsage ($PVServerName_short)
       if( [int] $UsedMemory -lt 75) { "Memory usage is normal [ $UsedMemory % ]" | LogMe -display; $PVStests.MemUsg = "SUCCESS", "$UsedMemory %" }
        elseif([int] $UsedMemory -lt 85) { "Memory usage is medium [ $UsedMemory % ]" | LogMe -warning; $PVStests.MemUsg = "WARNING", "$UsedMemory %" }       
        elseif([int] $UsedMemory -lt 95) { "Memory usage is high [ $UsedMemory % ]" | LogMe -error; $PVStests.MemUsg = "ERROR", "$UsedMemory %" }
        elseif([int] $UsedMemory -eq 101) { "Memory usage test failed" | LogMe -error; $PVStests.MemUsg = "ERROR", "Err" }
       else { "Memory usage is Critical [ $UsedMemory % ]" | LogMe -error; $PVStests.MemUsg = "ERROR", "$UsedMemory %" }   
        $UsedMemory = 0  


        foreach ($disk in $diskLetters)
        {
            # Check Disk Usage 
            $HardDisk = Get-WmiObject Win32_LogicalDisk -ComputerName $PVServerName_short -Filter "DeviceID='$($disk):'" | Select-Object Size,FreeSpace 
            $DiskTotalSize = $HardDisk.Size 
            $DiskFreeSpace = $HardDisk.FreeSpace 
            $frSpace=[Math]::Round(($DiskFreeSpace/1073741824),2)

            $PercentageDS = (($DiskFreeSpace / $DiskTotalSize ) * 100); $PercentageDS = "{0:N2}" -f $PercentageDS 

            If ( [int] $PercentageDS -gt 15) { "Disk Free is normal [ $PercentageDS % ]" | LogMe -display; $PVStests."$($disk)Freespace" = "SUCCESS", "$frSpace GB" } 
            ElseIf ([int] $PercentageDS -lt 15) { "Disk Free is Low [ $PercentageDS % ]" | LogMe -warning; $PVStests."$($disk)Freespace" = "WARNING", "$frSpace GB" }     
            ElseIf ([int] $PercentageDS -lt 5) { "Disk Free is Critical [ $PercentageDS % ]" | LogMe -error; $PVStests."$($disk)Freespace" = "ERROR", "$frSpace GB" } 
            ElseIf ([int] $PercentageDS -eq 0) { "Disk Free test failed" | LogMe -error; $PVStests."$($disk)Freespace" = "ERROR", "Err" } 
            Else { "Disk Free is Critical [ $PercentageDS % ]" | LogMe -error; $PVStests."$($disk)Freespace" = "ERROR", "$frSpace GB" }   
            $PercentageDS = 0   
        }


   
  
#Check PVS Activity Status (over PVS Framework)
$serverstatus = Get-PvsServerStatus -ServerName $PVServerName_short
$actviestatus = $serverstatus.Status
if ($actviestatus -eq 1) { $PVStests.Active = "SUCCESS", "active" }
else { $PVStests.Active = "Error","inactive" }
"PVS-Active-Status: $actviestatus" | LogMe -display -progress


  
#Check PVS deviceCount
$numberofdevices = $serverstatus.DeviceCount
if ($numberofdevices -ge 1) { $PVStests.deviceCount = "SUCCESS", " $numberofdevices active" }
else { $PVStests.deviceCount = "WARNING","No devices on this server" }
"Number of devices: $numberofdevices" | LogMe -display -progress

$global:PVSResults.$PVServerName_short = $PVStests
  
}
}


Function Get-PageFileInfo 
{

[CmdletBinding()]
Param(
    [Parameter(Mandatory=$True,ValueFromPipeline=$True)]  
    [string[]]$ComputerName
)

# Main Part of function


Foreach ($computer in $ComputerName)
{

  $online= Test-Connection -ComputerName $computer -Count 2 -Quiet
    if ($online -eq $true)
     {
      $PageFileResults = Get-CimInstance -Class Win32_PageFileUsage -ComputerName $Computer | Select-Object *
      $CompSysResults = Get-CimInstance win32_computersystem -ComputerName $computer -Namespace 'root\cimv2'
    
      $PageFileStats = [PSCustomObject]@{
        Computer = $computer
        FilePath = $PageFileResults.Description
        AutoManagedPageFile = $CompSysResults.AutomaticManagedPagefile
        "TotalSize(in MB)" = $PageFileResults.AllocatedBaseSize
        "CurrentUsage(in MB)"  = $PageFileResults.CurrentUsage
        "PeakUsage(in MB)" = $PageFileResults.PeakUsage
        TempPageFileInUse = $PageFileResults.TempPageFile
      } #END PSCUSTOMOBJECT
     } #END IF
    else
     {
        # Computer is not reachable!
        Write-Host "Error: $computer not online" -Foreground white -BackgroundColor Red
     } # END ELSE


  $PageFileStats
 
} #END FOREACH



function PVSTargetCheck() {
# ======= PVS Target Device Check ========
"Check PVS Target Devices" | LogMe -display -progress
" " | LogMe -display -progress

$global:allResults = @{}
#Get-PvsDeviceStatus  |select status |Sort-Object status –Descending
$pvsdevices = Get-PvsDevice| Where-Object {$_.Active -Match "True" -and $_.Enabled -Match "True" }
$pvsdevices1=$pvsdevices.name
foreach($target in $pvsdevices) {
$tests = @{} 
    
    # Check to see if the server is in an excluded folder path
    $CollectionName = $target | %{ $_.CollectionName }
  
        #Only Check Servers in defined Collections: 
        if ($Collections -contains $CollectionName -Or $Collections -contains "every") { 
    
    
        $targetName = $target | %{ $_.Name }
        
        $targetName
        
        #Name of CollectionName
        $CollectionName = $target | %{ $_.CollectionName }
        "Collection: $CollectionName" | LogMe -display -progress
        $tests.CollectionName = "NEUTRAL", "$CollectionName"
        
        $targetNamePvsDeviceInfo = Get-PvsDeviceInfo -Name $targetName
        
        
        $DeviceUsedDiskVersion = $targetNamePvsDeviceInfo.DiskVersion
        "Used DiskVersion: $DeviceUsedDiskVersion" | LogMe -display -progress
        $tests.vDisk_Version = "NEUTRAL", "$DeviceUsedDiskVersion"
        
        $DeviceUsedServerName= $targetNamePvsDeviceInfo.ServerName
        "Used Server: $DeviceUsedServerName" | LogMe -display -progress
        $tests.PVSServer = "NEUTRAL", "$DeviceUsedServerName"
        
        $targetNamePvsDeviceStatus = Get-PvsDeviceStatus -Name $targetName  |Sort-Object status –Descending
        
        $RetryStatus = $targetNamePvsDeviceStatus.Status
        
    #$targetName ="WVDI7S53133"    
#$RetryStatus='121'        
$hanu= "50" 
asnp citrix*
$hanu1 =(Get-BrokerSession -AdminAddress $admin -HostedMachineName $targetName | Where-Object {$_.SessionState -Match "Active" }).count
#Write-Host $hanu1 -ForegroundColor DarkYellow
"Retry: $RetryStatus" | LogMe -display -warning
"user count: $hanu1" | LogMe -display -progress

$hanu2 ="0"
if ([int]$RetryStatus -cle $hanu) {
            "Retry: $RetryStatus" | LogMe -display -progress
            $tests.Retry = "SUCCESS", "$RetryStatus"
        } 
elseif([int]$RetryStatus -gt $hanu -and [int]$hanu1 -eq $hanu2  ) {

  $suni1=Get-BrokerMachine -AdminAddress $admin -HostedMachineName $targetName | Set-BrokerMachine -AdminAddress $admin -InMaintenanceMode 1
  "Turning ON MM mode for $targetName VDi " | LogMe -display -warning
  sleep 5
   " Preparining $targetName for Reboot" | LogMe -display -error
     #Restart-Computer -force -ComputerName $targetName
      

             Start-Countdown -Seconds 200 -Message "Waiting for $targetName Registration"

                 #$ServerStatus=Get-BrokerMachine -AdminAddress $admin -DNSName $targetName

              

         
             $hanu3 = (Get-PvsDeviceStatus -Name $targetName).Status 
  IF ($ServerStatus.RegistrationState -eq "Registered" -and [int]$hanu3 -cle $hanu)

         {
          $suni="-Auto Fixed"
         #Write-Log -Msg "Machine $MaintDevice has successfully registered. MaintenanceMode will be turned off." -ShowConsole -Color green 
         Get-BrokerMachine -AdminAddress $admin -HostedMachineName $targetName | Set-BrokerMachine -AdminAddress $admin -InMaintenanceMode 0
          "Turning OFF MM mode for $targetName VDi " | LogMe -display -progress
         "Retry: $hanu3 $suni" | LogMe -display -progress
            $tests.Retry = "SUCCESS", "$hanu3","$suni"

                               }
                               else {

                              
                               $hanu5 = (Get-PvsDeviceStatus -Name $targetName).Status 

                                Get-BrokerMachine -AdminAddress $admin -HostedMachineName $targetName | Set-BrokerMachine -AdminAddress $admin -InMaintenanceMode 1
                                " MM mode Still Turnned ON for $targetName VDi " | LogMe -display -warning
                               "Retry: $hanu5 No action" | LogMe -display -error
            $tests.Retry = "ERROR", "$hanu5","$suni"
                               }
}

else {
$hanu4 = (Get-PvsDeviceStatus -Name $targetName).Status 

            "Retry: $hanu4" | LogMe -display -error
            $tests.Retry = "ERROR", "$hanu4"
        }

#QUser /server:$targetName
         # Ping target 
        #$result = Ping $targetName 100
        #if ($result -ne "SUCCESS") { $tests.Ping = "ERROR", $result }
        #else { $tests.Ping = "SUCCESS", $result 
        #}

        $DiskFileNameStatus = $targetNamePvsDeviceStatus.DiskFileName
        "Retry: $DiskFileNameStatus" | LogMe -display -progress
        $tests.vDisk_PVS = "NEUTRAL", "$DiskFileNameStatus"
    $result = Ping $targetName 100
if ($result -ne "SUCCESS") { $tests.WriteCacheFreespace = "WARNING","0 GB"  }
  else {
        ################ PVS WriteCache SECTION ###############    
        $short_diskLocatorID = $targetNamePvsDeviceInfo.DiskLocatorId
        $diskinfo = Get-PvsDiskInfo -DiskLocatorId $short_diskLocatorID
        $short_DeviceWriteCacheType = $diskinfo.WriteCacheType
        
        if (test-path \\$targetName\c$\Personality.ini) #checks if Personality file is present and accessible and then checks 3 WCtypes
        #if ($short_DeviceWriteCacheType -eq "4")
        {

            $wconhd = ""
            
            #$job = Start-Job { #Job-Created but never received
            $wconhd = Get-Content \\$targetName\c$\Personality.ini | Where-Object  {$_.Contains("WriteCacheType") } -ErrorAction SilentlyContinue 
            #}
            #$res = Wait-Job $job -timeout 3
            #if(-not $res) {Write-Host "Timeout"}

            
            If ($wconhd -eq '$WriteCacheType=4')
            {Write-Host Cache on HDD
            
            #WWC on HD is $wconhd 4=DeviceHD

                # Relative path to the PVS vDisk write cache file
                $PvsWriteCache   = "d$\.vdiskcache"
                # Size of the local PVS write cache drive
                $PvsWriteMaxSize = 7gb # size in GB
            
                $PvsWriteCacheUNC = Join-Path "\\$targetName" $PvsWriteCache 
                $CacheDiskexists  = Test-Path $PvsWriteCacheUNC -ErrorAction SilentlyContinue 
                if ($CacheDiskexists -eq $True)
                {
                    $CacheDisk = [long] ((get-childitem $PvsWriteCacheUNC -force -ErrorAction SilentlyContinue ).length  ) 
                    $CacheDiskGB = "{0:n2}GB" -f($CacheDisk / 1GB)
                    "PVS Cache file size: {0:n2}GB" -f($CacheDisk / 1GB) | LogMe
                    #"PVS Cache max size: {0:n2}GB" -f($PvsWriteMaxSize / 1GB) | LogMe -display
                    

                    if($CacheDiskGB -lt ($PvsWriteMaxSize * 0.5))
                    {
                       "WriteCache on HD file size is low with $CacheDiskGB" | LogMe -display -progress
                       $tests.WriteCache = "SUCCESS", $CacheDiskGB
                    }
                    elseif($CacheDiskGB -lt ($PvsWriteMaxSize * 0.8))
                    {
                       "WriteCache on HD file size moderate with $CacheDiskGB" | LogMe -display -warning
                       $tests.WriteCache = "WARNING", $CacheDiskGB
                    }   
                    else
                    {
                       "WriteCache on HD file size is high with $CacheDiskGB" | LogMe -display -error
                       $tests.WriteCache = "ERROR", $CacheDiskGB
                    }
                }              
    
                $Cachedisk = 0
               
                $VDISKImage = get-content \\$targetName\c$\Personality.ini | Select-String "Diskname" | Out-String | % { $_.substring(12)} -ErrorAction SilentlyContinue 
                if($VDISKImage -Match $DefaultVDISK){
                    "Default vDisk detected" | LogMe
                    $tests.vDisk  = "SUCCESS", $VDISKImage
                } else {
                    "vDisk unknown"  | LogMe -display -error
                    $tests.vDisk = "SUCCESS", $VDISKImage
                }   
            
            }
            elseif ($wconhd -eq '$WriteCacheType=9')
                    {
                        Write-Host Cache on RAM with overflow
                #RAMCache 9=RamOfToHD
                #Get-RamCache from each target, code from Matthew Nics http://mattnics.com/?p=414
                $RAMCache = [math]::truncate((Get-WmiObject Win32_PerfFormattedData_PerfOS_Memory -ComputerName $targetName -ErrorAction SilentlyContinue ).PoolNonPagedBytes /1MB) 
            
                        # Relative path to the PVS vDisk write cache file
                $PvsWriteCache   = "d$\vdiskdif.vhdx"
                # Size of the local PVS write cache drive
                $PvsWriteMaxSize = 4gb # size in GB

                 $HDDwarning = $false
                  $HDDerror = $false
            
                $PvsWriteCacheUNC = Join-Path "\\$targetName" $PvsWriteCache 
                $CacheDiskexists  = Test-Path $PvsWriteCacheUNC -ErrorAction SilentlyContinue 
                if ($CacheDiskexists -eq $True)
                {
                    $CacheDisk = [long] ((get-childitem $PvsWriteCacheUNC -force -ErrorAction SilentlyContinue ).length)
                    $CacheDiskGB = "{0:n2} GB" -f($CacheDisk / 1GB)
                    "PVS Cache file size: {0:n2} GB" -f($CacheDisk / 1GB) | LogMe
                    #"PVS Cache max size: {0:n2}GB" -f($PvsWriteMaxSize / 1GB) | LogMe -display
                    if($CacheDiskGB -lt ($PvsWriteMaxSize * 0.5))
                    {
                       "WriteCache on RAM size is low with $CacheDiskGB" | LogMe -display -progress

                        $tests.WriteCache = "SUCCESS", $CacheDiskGB
                    }
                    elseif($CacheDiskGB -lt ($PvsWriteMaxSize * 0.8))
                    {
                       "WriteCache on RAM size moderate with $CacheDiskGB" | LogMe -display -warning
                       $HDDwarning = $true
                       $tests.WriteCache = "WARNING", $CacheDiskGB
                    }   
                    else
                    {
                       "WriteCache on RAM  size is high with $CacheDiskGB" | LogMe -display -error
                       $HDDerror = $true
                       $tests.WriteCache = "ERROR", $CacheDiskGB
                    }
                }              
               
                $Cachedisk = 0
               
                $VDISKImage = get-content \\$targetName\c$\Personality.ini | Select-String "Diskname" | Out-String | % { $_.substring(12)} -ErrorAction SilentlyContinue 
                Get-PageFileInfo $targetName| Format-Table
"$CacheDiskGB on HHD"| LogMe -display -progress
            " $RamCache MB on Ram"| LogMe -display -progress
if($VDISKImage -Match $DefaultVDISK){
                    "Default vDisk detected" | LogMe
                    $tests.vDisk = "SUCCESS", $VDISKImage
                } else {
                    "vDisk unknown"  | LogMe -display -error
                    $tests.vDisk = "SUCCESS", $VDISKImage
                }  

                #merge HDD and RAM data
                if ($HDDwarning)
                {$tests.WriteCache = "WARNING", "$CacheDiskGB on HHD, $RamCache MB on Ram"}
                elseif ($HDDerror)
                {$tests.WriteCache = "ERROR", "$CacheDiskGB on HHD, $RamCache MB on Ram"}
                else
                {$tests.WriteCache = "SUCCESS", "$CacheDiskGB on HHD, $RamCache MB on Ram"}
                 
                 

            }
            elseif ($wconhd -eq '$WriteCacheType=3')
            {Write-Host Cache on Ram
            
            #RAMCache
            #Get-RamCache from each target, code from Matthew Nics http://mattnics.com/?p=414
            $RAMCache = [math]::truncate((Get-WmiObject Win32_PerfFormattedData_PerfOS_Memory -ComputerName $targetName -ErrorAction SilentlyContinue ).PoolNonPagedBytes /1MB)
            $tests.WriteCache = "Neutral", "$RamCache MB on Ram"
        
            }
            else
            {
            Write-Host Cache on Ram
            $tests.WriteCache = "WARNING", "Other WriteCache Type"
            }
        
        }
        else 
        {Write-Host WriteCache not readable
        $tests.WriteCache = "Neutral", "Cache not readable"    
        }
        ############## END PVS WriteCache SECTION #############
    $disk='D'           

         # Check D Disk Usage 
        
            # Check Disk Usage 
            $HardDisk = Get-WmiObject Win32_LogicalDisk -ComputerName $targetName -Filter "DeviceID='$($disk):'" | Select-Object Size,FreeSpace -ErrorAction SilentlyContinue 
            $DiskTotalSize = $HardDisk.Size 
            $DiskFreeSpace = $HardDisk.FreeSpace 
            $frSpace=[Math]::Round(($DiskFreeSpace/1073741824),2)
            
            $PercentageDS = (($DiskFreeSpace / $DiskTotalSize ) * 100); $PercentageDS = "{0:N2}" -f $PercentageDS 

            If ( [int] $PercentageDS -gt 15) { "Disk Free is normal [ $PercentageDS % ]" | LogMe -display; $tests.WriteCacheFreespace = "SUCCESS", "$frSpace GB" } 
            ElseIf ([int] $PercentageDS -lt 15) { "Disk Free is Low [ $PercentageDS % ]" | LogMe -warning; $tests.WriteCacheFreespace = "WARNING", "$frSpace GB" }     
            ElseIf ([int] $PercentageDS -lt 5) { "Disk Free is Critical [ $PercentageDS % ]" | LogMe -error; $tests.WriteCacheFreespace = "ERROR", "$frSpace GB" } 
            ElseIf ([int] $PercentageDS -eq 0) { "Disk Free test failed" | LogMe -error; $tests.WriteCacheFreespace = "ERROR", "Err" } 
            Else { "Disk Free is Critical [ $PercentageDS % ]" | LogMe -error; $tests.WriteCacheFreespace = "ERROR", "$frSpace GB" }   
            $PercentageDS = 0   
        }


    $global:allResults.$targetName = $tests
    }
}
 
 
 
 }


#==============================================================================================
#HTML function
function WriteHTML() {
 
    # ======= Write all results to an html file =================================================
    Write-Host ("Saving results to html report: " + $resultsHTM)
    # STBE writeHtmlHeader "PVS Farm Report $global:farmname_short" $resultsHTM
    #$EnvironmentName = "$EnvironmentName $global:farmname_short"
    writeHtmlHeader "$EnvName" $resultsHTM

    if ($PerformPVSvDiskCheck -eq "yes") {
    writeTableHeader $resultsHTM $vDiksFirstheaderName $vDiskheaderNames $vDiskheaderWidths $vDisktablewidth
    $global:vdiskResults | sort-object -property ReplState | % { writeData $vdiskResults $resultsHTM $vDiskheaderNames }
    writeTableFooter $resultsHTM
    }

    writeTableHeader $resultsHTM $PVSFirstheaderName $PVSheaderNames $PVSheaderWidths $PVStablewidth
    $global:PVSResults | sort-object -property PVServerName_short | % { writeData $PVSResults $resultsHTM $PVSheaderNames}
    writeTableFooter $resultsHTM
 
    writeTableHeader $resultsHTM $PVSFirstFarmheaderName $PVSFarmHeaderNames $PVSFarmWidths $PVSFarmTablewidth
    $global:PVSFarmResults | % { writeData $PVSFarmResults $resultsHTM $PVSFarmHeaderNames}
    writeTableFooter $resultsHTM

    if ($PerformPVSTargetCheck -eq "yes") {
    writeTableHeader $resultsHTM $TargetFirstheaderName $TargetheaderNames $TargetheaderWidths $TargetTablewidth
    $allResults | sort-object -property collectionName | % { writeData $allResults $resultsHTM $TargetheaderNames}
    writeTableFooter $resultsHTM
    }

    writeHtmlFooter $resultsHTM
# STBE    #send email
# STBE    $emailSubject = ("$emailSubjectStart - $global:farmname_short - " + (Get-Date -format R))
# STBE    $global:mailMessageParameters = @{
# STBE        From = $emailFrom
# STBE        To = $emailTo
# STBE        Subject = $emailSubject
# STBE        SmtpServer = $smtpServer
# STBE        Body = (gc $resultsHTM) | Out-String
# STBE        Attachment = $resultsHTM
# STBE    }
}
#==============================================================================================
#Mail function
# Send mail 
function SendMail() {
    Param (
        [Parameter(
            Mandatory = $True,
            Position = 0,
            ValueFromPipeline = $True
        )]
        [System.String[]]
        $Subject
    )
    #send email
    $emailMessage = New-Object System.Net.Mail.MailMessage
    $emailMessage.From = $emailFrom
    $emailMessage.To.Add( $emailTo )
    $emailMessage.Subject = $Subject
    $emailMessage.IsBodyHtml = $true
    $emailMessage.Body = (gc $resultsHTM) | Out-String
    $emailMessage.Attachments.Add($resultsHTM)
    $emailMessage.Priority = ($emailPrio)

    $smtpClient = New-Object System.Net.Mail.SmtpClient( $smtpServer , $smtpServerPort )
    $smtpClient.EnableSsl = $smtpEnableSSL

    # If you added username an password, add this to smtpClient
    If ((![string]::IsNullOrEmpty($smtpUser)) -and (![string]::IsNullOrEmpty($smtpPW))){
        $pass = $smtpPW | ConvertTo-SecureString -key $smtpKey
        $cred = New-Object System.Management.Automation.PsCredential($smtpUser,$pass)

        $Ptr = [System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($cred.Password)
        $smtpUserName = $cred.Username
        $smtpPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($Ptr)

        $smtpClient.Credentials = New-Object System.Net.NetworkCredential( $smtpUserName , $smtpPassword );
    }

    $smtpClient.Send( $emailMessage )
}


#==============================================================================================
# == MAIN SCRIPT ==
#==============================================================================================
$scriptstart = Get-Date
rm $logfile -force -EA SilentlyContinue
"Begin with Citrix Provisioning Services HealthCheck" | LogMe -display -progress
" " | LogMe -display -progress

#Farmcheck
$EnvName = "$EnvironmentName $farmname_short"
$emailSubject = ("$EnvName - " + $ReportDate)
 
#if ($PerformPVSTargetCheck -eq "yes") {
#"Initiate PVS Target check" | LogMe
#PVSTargetCheck
#} else {
#" PVS Target check skipped" | LogMe
#}

#if ($PerformPVSvDiskCheck -eq "yes") {
#"Initiate PVS Target check" | LogMe
#PVSvDiskCheck
#} else {
#" PVSvDiskCheck check skipped" | LogMe
#}
PVSTargetCheck
PVSvDiskCheck
PVSServerCheck
WriteHTML


if ($PerformSendMail -eq "yes") {
"Initiate send of Email " | LogMe
SendMail -Subject $emailSubject
} else {
"send of Email  skipped" | LogMe
}

$scriptend = Get-Date
$scriptruntime =  $scriptend - $scriptstart | select TotalSeconds
$scriptruntimeInSeconds = $scriptruntime.TotalSeconds
#Write-Host $scriptruntime.TotalSeconds
"Script was running for $scriptruntimeInSeconds " | LogMe -display -progress
 

Link to comment
  • 0

<#

Grab all PVS devices and their collections, machine catalogues, delivery groups and more and output to csv or a grid view

Use of this script is entirely at the risk of the user. The author accepts no liability or responsibility for any undesired issues it may cause

Guy Leech, 2018

Modification history:

19/01/18 GL Fixed issue where not showing not booted off latest if different vdisk assigned from what is booted off

Fixed issue with PoSH v5

Added -name filter and -tags

Added warning for pre PoSH v5 and more than 30 columns in Out-GridView

Added error trapping when fails to retrive boot time

22/01/18 GL Added action GUI

26/01/18 GL Added ability to match AD group regex and output group memberships

01/02/18 GL Added disk version descriptions and dns lookup option

22/02/18 GL Added capability to include machines retrieved from DDCs which are not present in PVS

Changed main device collection to .NET array so we can add potential orphans to it

Added MaxRecordCount parameter for DDC

25/02/18 GL Added GUI options to remove from PVS and DDC

27/02/18 GL Added saving DDC and PVS servers to registry

14/03/18 GL Changed AD Account Exists to Created date

16/03/18 GL "Remove from AD" and "Remove from Hypervisor" (VMware) actions added

Added ability to identify orphaned VMs in VMware

Include VM information if have hypervisor connection (VMware)

Added progress bar

17/03/18 GL Completion message after actions completed with error count

18/03/18 GL Disable Remove from Hypervisor and AD buttons if not available

Added profiling and optimised

Made MessageBox calls app modal as well as system

20/03/18 GL Added remote memory, CPU and hard disk status via WMI/CIM

Added -help option

21/03/18 GL Changed action UI to use context menus rather than buttons

Added vdisk file size

06/04/18 GL Retry count, device IP and PVS server booted from fields added

-noGridview option added and will produce both csv and gridview if -csv specified unless -nogridview used

17/04/18 GL Implemented timeout when getting remote information as Invoke-Command can take up to 25 minutes to timeout.

Added remote domain health check via Test-ComputerSecureChannel and AD account modification time

30/05/18 GL Added Load indexes

01/06/18 GL Changed to run device queries in runspaces to speed up

19/06/18 GL Split main code into module so can re-use in other scripts

20/06/18 GL Added option to split VMware VM names in case have description, etc after an _ character or similar

21/01/21 GL Added remove from delivery group option

16/06/21 GL Bug fixes where full domain name passed as part of machine name to DDC actions

13/09/21 GL Added menu item to delete DNS entry

14/09/21 GL Added "Erase from Existence" menu item to delete from PVS, DDC, AD, DNS and VMware. Use with caution!

#>

<#

Copyright © 2021 Guy Leech

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction,

including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.

IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#>

<#

.SYNOPSIS

Produce grid view or csv report of Citrix Provisioning Services devices, cross referenced with information from Delivery Controllers and Active Directory

.DESCRIPTION

Alows easy identification of potential problems such as devices booted off the wrong vdisk or vdisk version, devices with no AD account or devices overdue a reboot.

Devices can be selected in the grid view and then shutdown, restarted, booted or maintenance mode turned on or off from a simple GUI

.PARAMETER pvsServers

Comma separated list of PVS servers to contact. Do not specify multiple servers if they use the same SQL database

.PARAMETER ddcs

Comma separated list of Delivery Controllers to contact. Do not specify multiple servers if they use the same SQL database

.PARAMETER registry

Pick up the PVS and DDC servers from the registry rather than the command line. Must previously have saved them with -save and an optional server set name via -serverSet

.PARAMETER serverSet

Pick up the PVS and DDC servers from the registry sub key specified in this paramter rather than the command line. Must previously have saved them with -save and this server set name

.PARAMETER domain

The FQDN domain to search for the selected PVS devices when deleting DNS records. If not specified, the DNS domain for the user running the script will be used

.PARAMETER save

Save the PVS and DDC servers to the registry for later use with -registry. Use -serverSet to specify named sets of servers, e.g. preproduction and production

.PARAMETER csv

Path to a csv file that will have the results written to it.

.PARAMETER noGridView

Do not produce an on screen grid view containing the results. Default behaviour will display one.

.PARAMETER cpuSamples

The number of CPU usage samples to gather from a remote system if -noRemoting is not specified. The higher the number, the more accurate the figure but the longer it will take.

Set to zero to not gather CPU but still gather other remote data like memory usage and free disk space

.PARAMETER noRemoting

Will not try and contact active devices to gather information like last boot time. If WinRM not setup correctly or other issues mean remote calls will fail then this option can speed the script up

.PARAMETER noOrphans

Do not display machines which are present on DDCs/Studio but not in PVS. May be physical, MCS or VMs with local disks or could be orphans that did exist in PVS but do not any longer.

.PARAMETER jobTimeout

Timeout in seconds for the command to retrieve information remotely from a device.

.PARAMETER maxRecordCount

Citrix DDC cmdlets by default only return 250 items so if there are more machines than this on a DDC then this parameter may need to be increased

.PARAMETER name

Only show devices whose name matches the regular expression specified

.PARAMETER tags

Adds a column containing Citrix tags, if present, for each device

.PARAMETER ADGroups

A regular expression of AD groups to match and will output ones that match for which the device is a member

.PARAMETER noProgress

Do not show a progress bar

.PARAMETER hypervisors

Comma separated list of VMware vSphere servers to connect to in order to gather VM information such as memory and CPU configuration.

Will prompt for authentication if no pass thru or saved credentials

.PARAMETER vmPattern

When hypervisors are specified, or retrieved from the registry, a regular expression must be specified to match the VM names in vCenter to prevent all VMs

being included rather than just XenApp/XenDesktop ones

.PARAMETER provisioningType

The type of catalogue provisioning to check for in machines that are suspected of being orphaned from PVS as they were found on a DDC but not PVS

.PARAMETER dns

Perform DNS ipV4 lookup. This can cause the script to run slower.

.PARAMETER noMenu

Do not display "OK" and "Cancel" buttons on grid view so no action menu is presented after grid view is closed

.PARAMETER messageText

Text to send to users in the action GUI. If none specified and the message option is clicked then you will be prompted for the text

.PARAMETER messageCaption

Caption of the message to send to users in the action GUI. If none specified and the message option is clicked then you will be prompted for the text

.PARAMETER timeout

Timeout in seconds for PVS power commands

.PARAMETER splitVM

Split VMware name on a specific character such as an underscore where the second and subsequent parts of the name are descriptions, etc

.PARAMETER help

Show full help via Get-Help cmdlet

.EXAMPLE

& '.\Get PVS device info.ps1' -pvsservers pvsprod01,pvstest01 -ddcs ddctest02,ddcprod03

Retrieve devices from the listed PVS servers, cross reference to the listed DDCs (list order does not matter) and display on screen in a sortable and filterable grid view

.EXAMPLE

& '.\Get PVS device info.ps1' -pvsservers pvsprod01 -ddcs ddcprod03 -name CTXUAT -tags -dns -csv h:\pvs.ctxuat.csv

Retrieve devices matching regular expression CTXUAT from the listed PVS server, cross reference to the listed DDC and output to the given csv file, including Citrix tag information and IPv4 address from DNS query

.EXAMPLE

& '.\Get PVS device info.ps1' -pvsservers pvsprod01 -ddcs ddcprod03 -ADGroups '^GRP-' -hypervisors vmware01 -vmPattern '^CTX[15]\d{3}$'

Retrieve all devices from the listed PVS server, cross reference to the listed DDC and VMware, including AD groups which start withg "GRP", and output to an on-screen grid view

.NOTES

Uses local PowerShell cmdlets for PVS, DDCs and VMware, as well as Active Directory, so run from a machine where both PVS and Studio consoles and the VMware PowerCLI are installed.

#>

[CmdletBinding()]

Param

(

[Parameter(ParameterSetName='Manual',mandatory=$true,HelpMessage='Comma separated list of PVS servers')]

[string[]]$pvsServers ,

[Parameter(ParameterSetName='Manual',mandatory=$true,HelpMessage='Comma separated list of Delivery controllers')]

[string[]]$ddcs ,

[Parameter(ParameterSetName='Manual',mandatory=$false)]

[switch]$save ,

[Parameter(ParameterSetName='Registry',mandatory=$true,HelpMessage='Use default server set name from registry')]

[switch]$registry ,

[string]$serverSet = 'Default' ,

[string[]]$hypervisors ,

[string]$csv ,

[switch]$dns ,

[string]$name ,

[switch]$tags ,

[string]$ADgroups ,

[switch]$noProgress ,

[switch]$noRemoting ,

[switch]$noMenu ,

[switch]$noOrphans ,

[switch]$noGridView ,

[ValidateSet('PVS','MCS','Manual','Any')]

[string]$provisioningType = 'PVS' ,

[string]$configRegKey = 'HKCU:\software\Guy Leech\PVS Fetcher' ,

[string]$messageText ,

[string]$messageCaption ,

[string]$domain ,

[int]$maxRecordCount = 2000 ,

[int]$jobTimeout = 120 ,

[int]$timeout = 60 ,

[int]$cpuSamples = 2 ,

[int]$maxThreads = 10 ,

[string]$pvsShare ,

[string]$splitVM ,

[switch]$help ,

[string[]]$snapins = @( 'Citrix.Broker.Admin.*' ) ,

[string[]]$modules = @( 'ActiveDirectory', 'DNSServer' , "$env:ProgramFiles\Citrix\Provisioning Services Console\Citrix.PVS.SnapIn.dll" , 'Guys.Common.Functions.psm1' ) ,

[string]$vmWareModule = 'VMware.VimAutomation.Core'

)

if( $help )

{

Get-Help -Name ( & { $myInvocation.ScriptName } ) -Full

return

}

[string]$script:dnsserver = $null

$columns = [System.Collections.Generic.List[string]]( @( 'Name','DomainName','Description','PVS Server','DDC','SiteName','CollectionName','Machine Catalogue','Delivery Group','Load Index','Load Indexes','Registration State','Maintenance_Mode','User_Sessions','devicemac','active','enabled',

'Store Name','Disk Version Access','Disk Version Created','AD Account Created','AD Account Modified','Domain Membership','AD Last Logon','AD Description','Disk Name','Booted off vdisk','Booted Disk Version','Vdisk Production Version','Vdisk Latest Version','Latest Version Description','Override Version',

'Retries','Booted Off','Device IP','Booted off latest','Disk Description','Cache Type','Disk Size (GB)','vDisk Size (GB)','Write Cache Size (MB)' ) )

if( $dns )

{

$columns.Add( 'IPv4 address' )

}

if( $tags )

{

$columns.Add( 'Tags')

}

if( ! [string]::IsNullOrEmpty( $ADgroups ) )

{

$columns.Add( 'AD Groups')

}

if( ! $noRemoting )

{

$columns.Add( 'Boot_Time' )

$columns.Add( 'Available Memory (GB)' )

$columns.Add( 'Committed Memory %' )

$columns.Add( 'Free disk space %' )

if( $cpuSamples -gt 0 )

{

$columns.Add( 'CPU Usage %' )

}

}

if( $snapins -and $snapins.Count -gt 0 )

{

ForEach( $snapin in $snapins )

{

Add-PSSnapin $snapin -ErrorAction Continue

}

}

ForEach( $module in $modules )

{

Import-Module $module -ErrorAction SilentlyContinue

[bool]$loaded = $?

if( ! $loaded -and $module -notmatch '^[a-z]:\\' -and $module -notmatch '^\\\\' ) ## only check script folder if not an absolute or UNC path

{

## try same folder as the script if there is no path in the module name

Import-Module (Join-Path ( & { Split-Path -Path $myInvocation.ScriptName -Parent } ) $module ) -ErrorAction Continue

$loaded = $?

}

if( ! $loaded )

{

Write-Warning "Unable to load module `"$module`" so functionality may be limited"

}

}

$messageWindowXAML = @"

<Window x:Class="Direct2Events.MessageWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:local="clr-namespace:Direct2Events"

mc:Ignorable="d"

Title="Send Message" Height="414.667" Width="309.333">

<Grid>

<TextBox x:Name="txtMessageCaption" HorizontalAlignment="Left" Height="53" Margin="85,20,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="180"/>

<Label Content="Caption" HorizontalAlignment="Left" Height="24" Margin="10,20,0,0" VerticalAlignment="Top" Width="63"/>

<TextBox x:Name="txtMessageBody" HorizontalAlignment="Left" Height="121" Margin="85,171,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="180"/>

<Label Content="Message" HorizontalAlignment="Left" Height="25" Margin="10,167,0,0" VerticalAlignment="Top" Width="56" RenderTransformOrigin="0.47,1.333"/>

<StackPanel Orientation="Horizontal" Height="43" Margin="10,332,0,0" Width="283">

<Button x:Name="btnMessageOk" Content="OK" Height="20" Width="89"/>

<Button x:Name="btnMessageCancel" Content="Cancel" Height="20" Margin="50,0,0,0" Width="89"/>

</StackPanel>

<ComboBox x:Name="comboMessageStyle" HorizontalAlignment="Left" Height="27" Margin="85,98,0,0" VerticalAlignment="Top" Width="180">

<ComboBoxItem Content="Information" IsSelected="True"/>

<ComboBoxItem Content="Question"/>

<ComboBoxItem Content="Exclamation"/>

<ComboBoxItem Content="Critical"/>

</ComboBox>

<Label Content="Level" HorizontalAlignment="Left" Height="27" Margin="15,98,0,0" VerticalAlignment="Top" Width="58"/>

</Grid>

</Window>

"@

$pvsDeviceActionerXAML = @'

<Window x:Name="formDeviceActioner" x:Class="PVSDeviceViewerActions.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:local="clr-namespace:PVSDeviceViewerActions"

mc:Ignorable="d"

Title="PVS Device Actioner" Height="522.565" Width="401.595">

<Grid Margin="0,0,-20.333,-111.667">

<ListView x:Name="lstMachines" HorizontalAlignment="Left" Height="452" Margin="23,22,0,0" VerticalAlignment="Top" Width="346">

<ListView.ContextMenu>

<ContextMenu>

<MenuItem Header="VMware" Name="VMwareContextMenu" >

<MenuItem Header="Power On" Name="VMwarePowerOnContextMenu" />

<MenuItem Header="Power Off" Name="VMwarePowerOffContextMenu" />

<MenuItem Header="Restart" Name="VMwareRestartContextMenu" />

<MenuItem Header="Delete" Name="VMwareDeleteContextMenu" />

<MenuItem Header="Reconfigure" >

<MenuItem Header="CPUs" Name="VMwareReconfigCPUsContextMenu" />

<MenuItem Header="Memory" Name="VMwareReconfigMemoryContextMenu" />

</MenuItem>

</MenuItem>

<MenuItem Header="PVS" Name="PVSContextMenu">

<MenuItem Header="Boot" Name="PVSBootContextMenu" />

<MenuItem Header="Shutdown" Name="PVSShutdownContextMenu" />

<MenuItem Header="Restart" Name="PVSRestartContextMenu" />

<MenuItem Header="Delete" Name="PVSDeleteContextMenu" />

</MenuItem>

<MenuItem Header="DDC" Name="DDCContextMenu" >

<MenuItem Header="Maintenance Mode On" Name="DDCMaintModeOnContextMenu" />

<MenuItem Header="Maintenance Mode Off" Name="DDCMaintModeOffContextMenu" />

<MenuItem Header="Message Users" Name="DDCMessageUsersContextMenu" />

<MenuItem Header="Remove from Delivery Group" Name="DDCRemoveDeliveryGroupContextMenu" />

<MenuItem Header="Delete" Name="DDCDeleteContextMenu" />

</MenuItem>

<MenuItem Header="AD" Name="ADContextMenu">

<MenuItem Header="Delete Computer" Name="ADDeleteComputerContextMenu" />

<MenuItem Header="Delete DNS Record" Name="ADDeleteDNSContextMenu" />

</MenuItem>

<MenuItem Header="Windows" Name="WindowsContextMenu">

<MenuItem Header="Shutdown" Name="WinShutdownModeOnContextMenu" />

<MenuItem Header="Restart" Name="WinRestartContextMenu" />

</MenuItem>

<MenuItem Header="Erase from Existence" Name="EraseFromExistenceContextMenu" />

</ContextMenu>

</ListView.ContextMenu>

<ListView.View>

<GridView>

<GridViewColumn Header="Device" DisplayMemberBinding ="{Binding 'Name'}" />

<GridViewColumn Header="Boot Time" DisplayMemberBinding ="{Binding 'Boot_Time'}" />

<GridViewColumn Header="Users" DisplayMemberBinding ="{Binding 'User_Sessions'}" />

<GridViewColumn Header="Maintenance Mode" DisplayMemberBinding ="{Binding 'Maintenance_Mode'}" />

</GridView>

</ListView.View>

<ListBoxItem Content="Device"/>

</ListView>

</Grid>

</Window>

'@

## Adding so we can make it app modal as well as system

Add-Type @'

using System;

using System.Runtime.InteropServices;

namespace PInvoke.Win32

{

public static class Windows

{

[DllImport("user32.dll")]

public static extern int MessageBox(int hWnd, String text, String caption, uint type);

}

}

'@

Add-Type -TypeDefinition @'

public enum MessageBoxReturns

{

IDYES = 6,

IDNO = 7 ,

IDOK = 1 ,

IDABORT = 3,

IDCANCEL = 2 ,

IDCONTINUE = 11 ,

IDIGNORE = 5 ,

IDRETRY = 4 ,

IDTRYAGAIN = 10

}

'@

Function Save-ConfigToRegistry( [string]$serverSet = 'Default' , [string[]]$DDCs , [string[]]$PVSServers , [string[]]$hypervisors )

{

[string]$key = Join-Path $configRegKey $serverSet

if( ! ( Test-Path $key -ErrorAction SilentlyContinue ) )

{

$null = New-Item -Path $key -Force

}

Set-ItemProperty -Path $key -Name 'DDC' -Value $DDCs

Set-ItemProperty -Path $key -Name 'PVS' -Value $PVSServers

Set-ItemProperty -Path $Key -Name 'Hypervisor' -Value $hypervisors

}

Function Get-ConfigFromRegistry

{

Param

(

[string]$serverSet = 'Default' ,

[ref]$DDCs ,

[ref]$PVSServers ,

[ref]$hypervisors

)

[string]$key = Join-Path $configRegKey $serverSet

if( ! ( Test-Path $key -ErrorAction SilentlyContinue ) )

{

Write-Warning "Config registry key `"$key`" does not exist"

}

$hypervisors.value = Get-ItemProperty -Path $key -Name 'Hypervisor' -ErrorAction SilentlyContinue | select -ExpandProperty 'Hypervisor'

$DDCs.value = Get-ItemProperty -Path $key -Name 'DDC' -ErrorAction SilentlyContinue | select -ExpandProperty 'DDC'

$PVSServers.value = Get-ItemProperty -Path $key -Name 'PVS' -ErrorAction SilentlyContinue | select -ExpandProperty 'PVS'

}

Function Load-GUI( $inputXaml )

{

$form = $NULL

$inputXaml = $inputXaml -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace '^<Win.*', '<Window'

[xml]$XAML = $inputXaml

$reader = New-Object Xml.XmlNodeReader $xaml

try

{

$Form = [Windows.Markup.XamlReader]::Load( $reader )

}

catch

{

Write-Error "Unable to load Windows.Markup.XamlReader. Double-check syntax and ensure .NET is installed.`n$_"

return $null

}

$xaml.SelectNodes("//*[@Name]") | ForEach-Object `

{

Set-Variable -Name "WPF$($_.Name)" -Value $Form.FindName($_.Name) -Scope Script

}

return $form

}

Function Display-MessageBox( $window , $text , $caption , [System.Windows.MessageBoxButton]$buttons , [System.Windows.MessageBoxImage]$icon )

{

if( $window -and $window.Handle )

{

[int]$modified = switch( $buttons )

{

'OK' { [System.Windows.MessageBoxButton]::OK }

'OKCancel' { [System.Windows.MessageBoxButton]::OKCancel }

'YesNo' { [System.Windows.MessageBoxButton]::YesNo }

'YesNoCancel' { [System.Windows.MessageBoxButton]::YesNo }

}

[int]$choice = [PInvoke.Win32.Windows]::MessageBox( $Window.handle , $text , $caption , ( ( $icon -as [int] ) -bor $modified ) ) ## makes it app modal so UI blocks

switch( $choice )

{

([MessageBoxReturns]::IDYES -as [int]) { 'Yes' }

([MessageBoxReturns]::IDNO -as [int]) { 'No' }

([MessageBoxReturns]::IDOK -as [int]) { 'Ok' }

([MessageBoxReturns]::IDABORT -as [int]) { 'Abort' }

([MessageBoxReturns]::IDCANCEL -as [int]) { 'Cancel' }

([MessageBoxReturns]::IDCONTINUE -as [int]) { 'Continue' }

([MessageBoxReturns]::IDIGNORE -as [int]) { 'Ignore' }

([MessageBoxReturns]::IDRETRY -as [int]) { 'Retry' }

([MessageBoxReturns]::IDTRYAGAIN -as [int]) { 'TryAgain' }

}

}

else

{

[Windows.MessageBox]::Show( $text , $caption , $buttons , $icon )

}

}

Function Perform-Action( [string]$action , $form , [bool]$prompt = $true)

{

if( $_ -and $_.PSObject.Properties[ 'Handled' ] )

{

$_.Handled = $true

}

## Get HWND so we can make app modal dialogues

$thisWindow = [System.Windows.Interop.WindowInteropHelper]::new($form)

if( $prompt )

{

$answer = Display-MessageBox -text "Are you sure you want to $action these $($WPFlstMachines.SelectedItems.Count) devices ?" -caption 'Confirm' -buttons YesNo -icon Question -window $thisWindow

if( $answer -ne 'Yes' )

{

return

}

}

if( $action -eq 'Message' )

{

$messageForm = Load-GUI $messageWindowXAML

if( $messageForm )

{

## Load up caption and text and set callbacks

$WPFtxtMessageCaption.Text = if( [string]::IsNullOrEmpty( $messageCaption ) ) { "Message from $env:USERNAME at $(Get-Date -Format F)" } else { $messageCaption }

$WPFtxtMessageBody.Text = $messageText

$WPFbtnMessageOk.Add_Click({

$messageForm.DialogResult = $true

$messageForm.Close()

})

$WPFbtnMessageOk.IsDefault = $true

$WPFbtnMessageCancel.Add_Click({ $messageForm.Close() })

$WPFbtnMessageCancel.IsCancel = $true

$result = $messageForm.ShowDialog()

if( ! $result )

{

return

}

}

}

if( $form -and $prompt )

{

$oldCursor = $form.Cursor

$form.Cursor = [Windows.Input.Cursors]::Wait

}

[int]$errors = 0

ForEach( $device in $WPFlstMachines.SelectedItems )

{

Write-Verbose "Action $action on $($device.Name)"

[string]$FQDN = $device.Name

if( ! [string]::IsNullOrEmpty( $device.DomainName ) )

{

$FQDN = ( $device.DomainName -split '\.')[0] + '\' + $device.Name

}

if( $prompt -and $action -imatch 'remove|erase|reboot|shutdown|restart|off' -and $device.user_sessions -ge 1 )

{

if( ( $answer = Display-MessageBox -text "$FQDN has $($device.user_sessions) user sessions - are you sure you want to $action ?" -caption 'Confirm' -buttons YesNo -icon Question -window $thisWindow ) -ne 'Yes' )

{

continue

}

}

switch -regex ( $action )

{

'Erase from Existence' `

{

Perform-Action -action 'Remove From Delivery Group' -form $form -prompt $false

Perform-Action -action 'Remove From DDC' -form $form -prompt $false

Perform-Action -action 'VMware Power Off' -form $form -prompt $false

Perform-Action -action 'Remove from Hypervisor' -form $form -prompt $false

Perform-Action -action 'Remove From AD' -form $form -prompt $false

Perform-Action -action 'Remove From DNS' -form $form -prompt $false

Perform-Action -action 'Remove From PVS' -form $form -prompt $false

break

}

'Message' { Get-BrokerSession -AdminAddress $device.ddc -MachineName $FQDN | Send-BrokerSessionMessage -AdminAddress $device.ddc -Title $WPFtxtMessageCaption.Text -Text $WPFtxtMessageBody.Text -MessageStyle ($WPFcomboMessageStyle.SelectedItem.Content) ;break }

'Remove From AD' { Remove-ADComputer -Identity $device.Name -Confirm:$False ;break }

'Remove From DNS' { Get-DnsServerResourceRecord -Name $device.Name -ComputerName $script:dnsserver -ZoneName $device.DomainName | Remove-DnsServerResourceRecord -ZoneName $device.DomainName -ComputerName $script:dnsserver -Force ;break }

'Remove From Delivery Group' { Remove-BrokerMachine -Force -DesktopGroup $device.'Delivery Group' -AdminAddress $device.ddc -MachineName $FQDN ; break }

'Remove From DDC' { Remove-BrokerMachine -Force -AdminAddress $device.ddc -MachineName $FQDN ; break }

'Remove From PVS' { Set-PvsConnection -Server $device.'PVS Server'; Remove-PvsDevice -DeviceName $device.Name ; break }

'Maintenance Mode On' { Set-BrokerMachine -AdminAddress $device.ddc -InMaintenanceMode $true -MachineName $FQDN ; break }

'Maintenance Mode Off' { Set-BrokerMachine -AdminAddress $device.ddc -InMaintenanceMode $false -MachineName $FQDN ; break }

'Reboot' { Restart-Computer -ComputerName $device.Name ; break }

'Shutdown' { Stop-Computer -ComputerName $device.Name ; break }

'Remove from Hypervisor' { Get-VM -Name $device.Name | Remove-VM -DeletePermanently -Confirm:$false ;break }

'VMware Boot' { Get-VM -Name $device.name | Start-VM -Confirm:$false ;break }

'VMware Power Off' { Get-VM -Name $device.name | Stop-VM -Confirm:$false ;break }

'VMware Restart' { Get-VM -Name $device.name | Restart-VM -Confirm:$false ;break }

'PVS Boot|PVS Power Off|PVS Restart' ` ## Can't use New-BrokerHostingPowerAction as may not be known to DDC

{

Set-PvsConnection -Server $device.'PVS Server'

if( $_ -match 'Boot' )

{

if( [string]::IsNullOrEmpty( $device.'Disk Name' ) )

{

$answer = Display-MessageBox -window $thisWindow -text "$($device.Name) has no vdisk assigned so may not boot - continue ?" -caption 'Confirm' -buttons YesNo -icon Question

if( $answer -ne 'Yes' )

{

continue

}

}

$thePvsTask = Start-PvsDeviceBoot -DeviceName $device.Name

}

elseif( $_ -match 'Off' )

{

$thePvsTask = Start-PvsDeviceShutdown -DeviceName $device.Name

}

elseif( $_ -match 'Restart' )

{

$thePvsTask = Start-PvsDeviceReboot -DeviceName $device.Name

}

$timer = [Diagnostics.Stopwatch]::StartNew()

[bool]$timedOut = $false

while ( $thePvsTask -and $thePvsTask.State -eq 0 )

{

$percentFinished = Get-PvsTaskStatus -Object $thePvsTask

if( ! $percentFinished -or $percentFinished.ToString() -ne 100 )

{

Start-Sleep -timer 500

if( $timer.Elapsed.TotalSeconds -gt $timeout )

{

$timeOut = $true

break

}

}

$thePvsTask = Get-PvsTask -Object $thePvsTask

}

$timer.Stop()

if ( $timedOut )

{

Display-MessageBox -window $thisWindow -text "Failed to perform action on $($device.Name) - timed out after $timeout seconds" -caption $action -buttons OK -icon Error

$errors++

}

elseif ( ! $thePvsTask -or $thePvsTask.State -ne 2)

{

Display-MessageBox -window $thisWindow -text "Failed to perform action on $($device.Name)" -caption $action -buttons OK -icon Error

$errors++

}

}

default { Write-Warning "Unknown command `"$action`"" }

}

if( ! $? )

{

$errors++

}

}

if( $prompt )

{

if( $form )

{

$form.Cursor = $oldCursor

}

[string]$status = [System.Windows.MessageBoxImage]::Information

if( $errors )

{

$status = [System.Windows.MessageBoxImage]::Error

}

Display-MessageBox -window $thisWindow -text "$errors / $($WPFlstMachines.SelectedItems.Count) errors" -caption "Finished $action" -buttons OK -icon $status

}

}

if( $noProgress )

{

$ProgressPreference = 'SilentlyContinue'

}

if( $registry )

{

Get-ConfigFromRegistry -serverSet $serverSet -DDCs ( [ref] $ddcs ) -PVSServers ( [ref] $pvsServers ) -hypervisors ( [ref] $hypervisors )

if( ! $ddcs -or ! $ddcs.Count -or ! $pvsServers -or ! $pvsServers.Count )

{

Write-Warning "Failed to get PVS and/or DDC servers from registry key `"$configRegKey`" for server set `"$serverSet`""

return

}

}

elseif( $save )

{

Save-ConfigToRegistry -serverSet $serverSet -DDCs $ddcs -PVSServers $pvsServers -hypervisors $hypervisors

}

if( $hypervisors -and $hypervisors.Count )

{

if( [string]::IsNullOrEmpty( $name ) )

{

Write-Error "Must specify a VM name pattern via -name when cross referencing to VMware"

exit

}

Import-Module $vmWareModule -ErrorAction Stop

Write-Progress -Activity "Connecting to hypervisors $($hypervisors -split ' ')" -PercentComplete 1

$null = Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false

if( Connect-VIserver -Server $hypervisors )

{

$columns.Add( 'CPUs')

$columns.Add( 'Memory (GB)')

$columns.Add( 'Hard Drives (GB)')

$columns.Add( 'NICs')

$columns.Add( 'Hypervisor')

## we pass the modules list to our function so that they can be loaded into the runspaces

$modules += $vmWareModule

}

else

{

Write-Error "Failed to connect to vmware $($hypervisors -join ' ')"

exit

}

}

if( $PSVersionTable.PSVersion.Major -lt 5 -and $columns.Count -gt 30 -and [string]::IsNullOrEmpty( $csv ) )

{

Write-Warning "This version of PowerShell limits the number of columns in a grid view to 30 and we have $($columns.Count) so those from `"$($columns[30])`" will be lost in grid view"

}

[datetime]$startTime = Get-Date

[hashtable]$devices = Get-PVSDevices -pvsservers $pvsServers -ddcs $ddcs -hypervisors $hypervisors -dns:$dns -name $name -tags:$tags -adgroups $ADgroups -noRemoting:$noRemoting -cpusamples:$cpuSamples -noProgress:$noprogress `

-maxThreads $maxThreads -timeout $timeout -jobTimeout $jobTimeout -maxRecordCount $maxRecordCount -provisioningType $provisioningType -noOrphans:$noOrphans -pvsShare $pvsShare -modules $modules -splitVM $splitVM

if( $devices -and $devices.Count )

{

if( ! [string]::IsNullOrEmpty( $csv ) )

{

$devices.GetEnumerator() | ForEach-Object { $_.Value } | Select-Object -Property $columns | Sort-Object -Property Name | Export-Csv -Path $csv -NoTypeInformation -NoClobber

}

if( ! $noGridView )

{

[hashtable]$params = @{}

if( $noMenu )

{

$params.Add( 'Wait' , $true )

}

else

{

$params.Add( 'PassThru' , $true )

}

[string]$title = "$(Get-Date -Format G) : $($devices.count) PVS devices via $($pvsServers -join ' ') & ddc $($ddcs -join ' ')"

if( ! [string]::IsNullOrEmpty( $name ) )

{

$title += " matching `"$name`""

}

[array]$selected = @( $devices.GetEnumerator() | ForEach-Object { $_.Value } | Select-Object -Property $columns | Sort-Object -Property Name | Out-GridView -Title $title @Params )

if( $selected -and $selected.Count )

{

if( -Not ( $mainForm = Load-GUI $pvsDeviceActionerXAML ) )

{

return

}

$mainForm.Title += " - $($selected.Count) devices"

if( $hypervisors -and $hypervisors.Count )

{

$WPFVMwarePowerOnContextMenu.Add_Click({ Perform-Action -action 'VMware Boot' -form $mainForm })

$WPFVMwarePowerOffContextMenu.Add_Click({ Perform-Action -action 'VMware Power Off' -form $mainForm })

$WPFVMwareRestartContextMenu.Add_Click({ Perform-Action -action 'VMware Restart' -form $mainForm })

$WPFVMwareDeleteContextMenu.Add_Click({ Perform-Action -action 'Remove From Hypervisor' -form $mainForm })

}

else

{

$WPFVMwareContextMenu.IsEnabled = $false

}

[int]$ADmenuItems = 0

if( Get-Module -Name ActiveDirectory -ErrorAction SilentlyContinue )

{

$WPFADDeleteComputerContextMenu.Add_Click({ Perform-Action -action 'Remove From AD' -form $mainForm })

$ADmenuItems++

}

if( Get-Module -Name DNSServer -ErrorAction SilentlyContinue )

{

## need to get DNS server so we can remote the delete DNS entry menu item to it

[hashtable]$ipaddressesChecked = @{}

if( -Not $PSBoundParameters[ 'domain' ] )

{

$domain = $env:USERDNSDOMAIN

}

Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter 'IPEnabled = TRUE' | Select-Object -ExpandProperty DNSServerSearchOrder | ForEach-Object `

{

$ipaddress = $_

if( [string]::IsNullOrEmpty( $script:dnsserver ) -and -Not $ipaddressesChecked.ContainsKey( $ipaddress ) )

{

Write-Verbose -Message "Checking DNS server IP $ipaddress"

if( $dnsinfo = Get-DnsServer -ComputerName $ipaddress -ErrorAction SilentlyContinue )

{

ForEach( $zone in $dnsinfo.ServerZone )

{

if( $zone.ZoneName -ieq $domain )

{

$script:dnsserver = $ipaddress

Write-Verbose -Message "Using $script:dnsserver for DNS"

}

}

}

$ipaddressesChecked.Add( $ipaddress , $ipaddress ) ## so we only check any given IP address once

}

}

if( [string]::IsNullOrEmpty( $script:dnsserver ) )

{

Write-Warning -Message "Unable to find DNS server for domain $domain"

}

else

{

$WPFADDeleteDNSContextMenu.Add_Click({ Perform-Action -action 'Remove From DNS' -form $mainForm })

$ADmenuItems++

}

}

if( $ADmenuItems -eq 0 )

{

$WPFADContextMenu.IsEnabled = $false

}

$WPFDDCMessageUsersContextMenu.Add_Click({ Perform-Action -action Message -form $mainForm })

$WPFDDCRemoveDeliveryGroupContextMenu.Add_Click({ Perform-Action -action 'Remove From Delivery Group' -form $mainForm })

$WPFDDCDeleteContextMenu.Add_Click({ Perform-Action -action 'Remove From DDC' -form $mainForm })

$WPFDDCMaintModeOffContextMenu.Add_Click({ Perform-Action -action 'Maintenance Mode Off' -form $mainForm })

$WPFDDCMaintModeOnContextMenu.Add_Click({ Perform-Action -action 'Maintenance Mode On' -form $mainForm })

$WPFWinShutdownModeOnContextMenu.Add_Click({ Perform-Action -action Shutdown -form $mainForm })

$WPFWinRestartContextMenu.Add_Click({ Perform-Action -action Reboot -form $mainForm })

$WPFPVSDeleteContextMenu.Add_Click({ Perform-Action -action 'Remove From PVS' -form $mainForm })

$WPFPVSBootContextMenu.Add_Click({ Perform-Action -action 'PVS Boot' -form $mainForm })

$WPFPVSShutdownContextMenu.Add_Click({ Perform-Action -action 'PVS Power Off' -form $mainForm })

$WPFPVSRestartContextMenu.Add_Click({ Perform-Action -action 'PVS Restart' -form $mainForm })

$WPFEraseFromExistenceContextMenu.Add_Click({ Perform-Action -action 'Erase from Existence' -form $mainForm })

$WPFlstMachines.Items.Clear()

$WPFlstMachines.ItemsSource = $selected

## Select all items since already selected them in grid view

$WPFlstMachines.SelectAll()

$null = $mainForm.ShowDialog()

## Put in clipboard so we can paste into something if we need to

if( $selected )

{

$selected | Clip.exe

}

}

}

}

else

{

Write-Warning "No PVS devices found via $($pvsServers -join ' ')"

}

if( ( Get-Variable global:DefaultVIServers -ErrorAction SilentlyContinue ) -and $global:DefaultVIServers.Count )

{

Disconnect-VIServer -Server $hypervisors -Force -Confirm:$false

}

# SIG # Begin signature block

# MIINRQYJKoZIhvcNAQcCoIINNjCCDTICAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB

# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR

# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU81BUw+pErsGyjWnY17lG+3NS

# Jc2gggqHMIIFMDCCBBigAwIBAgIQBAkYG1/Vu2Z1U0O1b5VQCDANBgkqhkiG9w0B

# AQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD

# VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk

# IElEIFJvb3QgQ0EwHhcNMTMxMDIyMTIwMDAwWhcNMjgxMDIyMTIwMDAwWjByMQsw

# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu

# ZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQg

# Q29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA

# +NOzHH8OEa9ndwfTCzFJGc/Q+0WZsTrbRPV/5aid2zLXcep2nQUut4/6kkPApfmJ

# 1DcZ17aq8JyGpdglrA55KDp+6dFn08b7KSfH03sjlOSRI5aQd4L5oYQjZhJUM1B0

# sSgmuyRpwsJS8hRniolF1C2ho+mILCCVrhxKhwjfDPXiTWAYvqrEsq5wMWYzcT6s

# cKKrzn/pfMuSoeU7MRzP6vIK5Fe7SrXpdOYr/mzLfnQ5Ng2Q7+S1TqSp6moKq4Tz

# rGdOtcT3jNEgJSPrCGQ+UpbB8g8S9MWOD8Gi6CxR93O8vYWxYoNzQYIH5DiLanMg

# 0A9kczyen6Yzqf0Z3yWT0QIDAQABo4IBzTCCAckwEgYDVR0TAQH/BAgwBgEB/wIB

# ADAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwMweQYIKwYBBQUH

# AQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYI

# KwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFz

# c3VyZWRJRFJvb3RDQS5jcnQwgYEGA1UdHwR6MHgwOqA4oDaGNGh0dHA6Ly9jcmw0

# LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwOqA4oDaG

# NGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RD

# QS5jcmwwTwYDVR0gBEgwRjA4BgpghkgBhv1sAAIEMCowKAYIKwYBBQUHAgEWHGh0

# dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCgYIYIZIAYb9bAMwHQYDVR0OBBYE

# FFrEuXsqCqOl6nEDwGD5LfZldQ5YMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6en

# IZ3zbcgPMA0GCSqGSIb3DQEBCwUAA4IBAQA+7A1aJLPzItEVyCx8JSl2qB1dHC06

# GsTvMGHXfgtg/cM9D8Svi/3vKt8gVTew4fbRknUPUbRupY5a4l4kgU4QpO4/cY5j

# DhNLrddfRHnzNhQGivecRk5c/5CxGwcOkRX7uq+1UcKNJK4kxscnKqEpKBo6cSgC

# PC6Ro8AlEeKcFEehemhor5unXCBc2XGxDI+7qPjFEmifz0DLQESlE/DmZAwlCEIy

# sjaKJAL+L3J+HNdJRZboWR3p+nRka7LrZkPas7CM1ekN3fYBIM6ZMWM9CBoYs4Gb

# T8aTEAb8B4H6i9r5gkn3Ym6hU/oSlBiFLpKR6mhsRDKyZqHnGKSaZFHvMIIFTzCC

# BDegAwIBAgIQBP3jqtvdtaueQfTZ1SF1TjANBgkqhkiG9w0BAQsFADByMQswCQYD

# VQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGln

# aWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgQ29k

# ZSBTaWduaW5nIENBMB4XDTIwMDcyMDAwMDAwMFoXDTIzMDcyNTEyMDAwMFowgYsx

# CzAJBgNVBAYTAkdCMRIwEAYDVQQHEwlXYWtlZmllbGQxJjAkBgNVBAoTHVNlY3Vy

# ZSBQbGF0Zm9ybSBTb2x1dGlvbnMgTHRkMRgwFgYDVQQLEw9TY3JpcHRpbmdIZWF2

# ZW4xJjAkBgNVBAMTHVNlY3VyZSBQbGF0Zm9ybSBTb2x1dGlvbnMgTHRkMIIBIjAN

# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr20nXdaAALva07XZykpRlijxfIPk

# TUQFAxQgXTW2G5Jc1YQfIYjIePC6oaD+3Zc2WN2Jrsc7bj5Qe5Nj4QHHHf3jopLy

# g8jXl7Emt1mlyzUrtygoQ1XpBBXnv70dvZibro6dXmK8/M37w5pEAj/69+AYM7IO

# Fz2CrTIrQjvwjELSOkZ2o+z+iqfax9Z1Tv82+yg9iDHnUxZWhaiEXk9BFRv9WYsz

# qTXQTEhv8fmUI2aZX48so4mJhNGu7Vp1TGeCik1G959Qk7sFh3yvRugjY0IIXBXu

# A+LRT00yjkgMe8XoDdaBoIn5y3ZrQ7bCVDjoTrcn/SqfHvhEEMj1a1f0zQIDAQAB

# o4IBxTCCAcEwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0O

# BBYEFE16ovlqIk5uX2JQy6og0OCPrsnJMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUE

# DDAKBggrBgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdp

# Y2VydC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2Ny

# bDQuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUw

# QzA3BglghkgBhv1sAwEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNl

# cnQuY29tL0NQUzAIBgZngQwBBAEwgYQGCCsGAQUFBwEBBHgwdjAkBggrBgEFBQcw

# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tME4GCCsGAQUFBzAChkJodHRwOi8v

# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEyQXNzdXJlZElEQ29kZVNp

# Z25pbmdDQS5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAU9zO

# 9UpTkPL8DNrcbIaf1w736CgWB5KRQsmp1mhXbGECUCCpOCzlYFCSeiwH9MT0je3W

# aYxWqIpUMvAI8ndFPVDp5RF+IJNifs+YuLBcSv1tilNY+kfa2OS20nFrbFfl9QbR

# 4oacz8sBhhOXrYeUOU4sTHSPQjd3lpyhhZGNd3COvc2csk55JG/h2hR2fK+m4p7z

# sszK+vfqEX9Ab/7gYMgSo65hhFMSWcvtNO325mAxHJYJ1k9XEUTmq828ZmfEeyMq

# K9FlN5ykYJMWp/vK8w4c6WXbYCBXWL43jnPyKT4tpiOjWOI6g18JMdUxCG41Hawp

# hH44QHzE1NPeC+1UjTGCAigwggIkAgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUwEwYD

# VQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAv

# BgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EC

# EAT946rb3bWrnkH02dUhdU4wCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwxCjAI

# oAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIB

# CzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFAWkHKMJIAuRNUFb4UEk

# xZ04HhsjMA0GCSqGSIb3DQEBAQUABIIBAHxwqTERSzA3ZPtCGDQiSvjOAn63BnIq

# MXshN2QPzWg8kValXq0AoafSvAhaK6GDxGUSeLELcfSWRDfgcsdouPZ9D5i/1v4V

# MiDJnRiMJIjs7PWjiDzA6RHG4jkUiVmCd+psxzQFUQjh7YNo7mWOVkFwUZDTZmvY

# iFNTqXRop+GFVdqNeuFsRRuaUJE6ckeqjTIYa8eh8hlnpNQxQ/TdWlTUvRlfOnlE

# WYUjIWlX9N0dxsUZXrO2xYB6eSjIastD/KVyOIh1h80Cg2PJ7Lz/soawOoOglqvn

# 45WciJPCAbIOd0cC4JLXlYdz7xXJXOm5jPTXYD8+iLWHLB7Iu3gYzhs=

# SIG # End signature block

Footer

Link to comment
  • 0

<#

Common functions used by multiple scripts

Guy Leech, 2018

Modification history:

20/06/18 GL Added function to get Citrix PVS devices

12/12/18 GL Added -quiet switch to Get-RemoteInfo

#>

## internal use only

Function Extract-RemoteInfo( [array]$remoteInfo )

{

[hashtable]$fields = @{}

if( $remoteInfo -and $remoteInfo.Count )

{

$osinfo,$logicalDisks,$cpu,$domainMembership = $remoteInfo

$fields += `

@{

'Boot_Time' = [Management.ManagementDateTimeConverter]::ToDateTime( $osinfo.LastBootUpTime )

'Available Memory (GB)' = [Math]::Round( $osinfo.FreePhysicalMemory / 1MB , 1 )

'Committed Memory %' = 100 - [Math]::Round( ( $osinfo.FreeVirtualMemory / $osinfo.TotalVirtualMemorySize ) * 100 , 1 )

'CPU Usage %' = $cpu

'Free disk space %' = ( $logicalDisks | Sort DeviceID | ForEach-Object { [Math]::Round( ( $_.FreeSpace / $_.Size ) * 100 , 1 ) }) -join ' '

'Domain Membership' = $domainMembership

}

}

else

{

Write-Warning "Get-RemoteInfo failed: $remoteError"

}

$fields

}

Function Get-RemoteInfo

{

[CmdletBinding()]

Param

(

[Parameter(Mandatory=$true)]

[string]$computer ,

[int]$jobTimeout = 60 ,

[Parameter(Mandatory=$true)]

[scriptblock]$work ,

[switch]$quiet ,

[int]$miscparameter1 ## passed through to the script block

)

$results = $null

[scriptblock]$code = `

{

Param([string]$computer,[scriptblock]$work,[int]$miscparameter1)

Invoke-Command -ComputerName $computer -ScriptBlock $work

}

try

{

## use a runspace so we can have a timeout

$runspace = [RunspaceFactory]::CreateRunspace()

$runspace.Open()

$command = [PowerShell]::Create().AddScript($code)

$command.Runspace = $runspace

$null = $command.AddParameters( @( $computer , $work , $miscparameter1 ) )

$job = $command.BeginInvoke()

## wait for command to finish

$wait = $job.AsyncWaitHandle.WaitOne( $jobTimeout * 1000 , $false )

if( $wait -or $job.IsCompleted )

{

if( $command.HadErrors )

{

if( ! $quiet )

{

Write-Warning "Errors occurred in remote command on $computer :`n$($command.Streams.Error)"

}

}

else

{

$results = $command.EndInvoke($job)

if( ! $results -and ! $quiet )

{

Write-Warning "No data returned from remote command on $computer"

}

}

## if we do these after timeout too then takes an age to return which defeats the point of running via a runspace

$command.Dispose()

$runSpace.Dispose()

}

else

{

if( ! $quiet )

{

Write-Warning "Job to retrieve info from $computer is still running after $jobTimeout seconds so aborting"

}

$null = $command.BeginStop($null,$null)

## leaking command and runspace but if we dispose it hangs

}

}

catch

{

if( ! $quiet )

{

Write-Error "Failed to get remote info from $computer : $($_.ToString())"

}

}

$results

}

Function Get-PVSDevices

{

Param

(

[Parameter(ParameterSetName='Manual',mandatory=$true,HelpMessage='Comma separated list of PVS servers')]

[string[]]$pvsServers ,

[Parameter(ParameterSetName='Manual',mandatory=$true,HelpMessage='Comma separated list of Delivery controllers')]

[string[]]$ddcs ,

[string[]]$hypervisors ,

[switch]$dns ,

[string]$name ,

[switch]$tags ,

[string]$ADgroups ,

[switch]$noRemoting ,

[switch]$noOrphans ,

[switch]$noProgress ,

[ValidateSet('PVS','MCS','Manual','Any')]

[string]$provisioningType = 'PVS' ,

[int]$maxRecordCount = 2000 ,

[int]$jobTimeout = 120 ,

[int]$timeout = 60 ,

[int]$cpuSamples = 2 ,

[int]$maxThreads = 10 ,

[string]$splitVM ,

[string]$pvsShare ,

[string[]]$modules ## need to import them into runspaces even though they are already loaded

)

if( $noProgress )

{

$ProgressPreference = 'SilentlyContinue'

}

Write-Progress -Activity "Caching information" -PercentComplete 0

## Get all information from DDCs so we can lookup locally

[hashtable]$machines = @{}

ForEach( $ddc in $ddcs )

{

$machines.Add( $ddc , [System.Collections.ArrayList] ( Get-BrokerMachine -AdminAddress $ddc -MaxRecordCount $maxRecordCount -ErrorAction SilentlyContinue ) )

}

## Make a hashtable so we can index quicker when cross referencing to DDC & VMware

[hashtable]$devices = @{}

[hashtable]$vms = @{}

if( $hypervisors -and $hypervisors.Count )

{

## Cache all VMs for efficiency

Get-VM | Where-Object { $_.Name -match $name } | ForEach-Object `

{

$vms.Add( $_.Name , $_ )

}

Write-Verbose "Got $($vms.Count) vms matching `"$name`" from $($hypervisors -split ' ')"

}

[scriptblock]$remoteWork = if( ! $noRemoting )

{

{

$osinfo = Get-WmiObject -Class Win32_OperatingSystem

$logicalDisks = Get-WmiObject -Class Win32_logicaldisk -Filter 'DriveType = 3'

$cpu = $(if( $using:miscparameter1 -gt 0 ) { [math]::Round( ( Get-Counter -Counter '\Processor(*)\% Processor Time' -SampleInterval 1 -MaxSamples $using:miscparameter1 |select -ExpandProperty CounterSamples| Where-Object { $_.InstanceName -eq '_total' } | select -ExpandProperty CookedValue | Measure-Object -Average ).Average , 1 ) }) -as [int]

$domainMembership = Test-ComputerSecureChannel

$osinfo,$logicalDisks,$cpu,$domainMembership

}

}

[int]$pvsServerCount = 0

[hashtable]$adparams = @{ 'Properties' = @( 'Created' , 'LastLogonDate' , 'Description' , 'Modified' ) }

if( ! [string]::IsNullOrEmpty( $ADgroups ) )

{

$adparams[ 'Properties' ] += 'MemberOf'

}

## https://blogs.technet.microsoft.com/heyscriptingguy/2015/11/28/beginning-use-of-powershell-runspaces-part-3/

$SessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()

## one downside of runspaces is that we don't get modules passed through

ForEach( $module in $modules )

{

[void]$sessionstate.ImportPSModule( $module )

}

[void]$sessionstate.ImportPSModule( 'Citrix.PVS.SnapIn' )

## also need to import the functions we need from this module

@( 'Get-ADMachineInfo' , 'Get-RemoteInfo' ,'Extract-RemoteInfo' ) | ForEach-Object `

{

$function = $_

$Definition = Get-Content Function:\$function -ErrorAction Continue

$SessionStateFunction = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList $function , $Definition

$sessionState.Commands.Add($SessionStateFunction)

}

$RunspacePool = [runspacefactory]::CreateRunspacePool(

1, #Min Runspaces

$maxThreads ,

$sessionstate ,

$host

)

$PowerShell = [PowerShell]::Create()

$PowerShell.RunspacePool = $RunspacePool

$RunspacePool.Open()

$jobs = New-Object System.Collections.ArrayList

ForEach( $pvsServer in $pvsServers )

{

$pvsServerCount++

Set-PvsConnection -Server $pvsServer

if( ! $? )

{

Write-Output "Cannot connect to PVS server $pvsServer - aborting"

continue

}

## Get Device info in one go as quite slow

[hashtable]$deviceInfos = @{}

Get-PvsDeviceInfo | ForEach-Object `

{

$deviceInfos.Add( $_.DeviceId , $_ )

}

## Cache store locations so we can look up vdisk sizes

[hashtable]$stores = @{}

Get-PvsStore | ForEach-Object `

{

$stores.Add( $_.StoreName , $_.Path )

}

## Get all devices so we can do progress

$pvsDevices = @( Get-PvsDevice | Where-Object { $_.Name -match $name })

[decimal]$eachDevicePercent = 100 / [Math]::Max( $pvsDevices.Count , 1 ) ## avoid divide by zero if no devices found

[int]$counter = 0

# Cache all disk and version info

[hashtable]$diskVersions = @{}

Get-PvsSite | ForEach-Object `

{

Get-PvsDiskInfo -SiteId $_.SiteId | ForEach-Object `

{

$diskVersions.Add( $_.DiskLocatorId , @( Get-PvsDiskVersion -DiskLocatorId $_.DiskLocatorId ) )

}

}

# Get all sites that we can see on this server and find all devices and cross ref to Citrix for catalogues and delivery groups

$pvsDevices | ForEach-Object `

{

$counter++

$device = $_

Write-Verbose "$counter / $($pvsDevices.Count) : $($device.Name)"

[decimal]$percentComplete = $counter * $eachDevicePercent

Write-Progress -Activity "Processing $($pvsDevices.Count) devices from PVS server $pvsServer" -Status "$($device.name)" -PercentComplete $percentComplete

$vm = if( $vms -and $vms.count )

{

$vms[ $device.Name ]

}

[hashtable]$parameters = @{

'Device' = $device

'vm' = $vm

'NoRemoting' = $noRemoting

'CPUSamples' = $cpuSamples

'DiskVersions' = $diskVersions

'deviceInfos' = $deviceInfos

'stores' = $stores

'pvsserver' = $pvsServer

'adparams' = $adparams

'ADGroups' = $ADgroups

'ddcs' = $ddcs

'machines' = $machines

'dns' = $dns

'tags' = $tags

'jobTimeout' = $jobTimeout

'RemoteWork' = $remoteWork

}

$PowerShell = [PowerShell]::Create()

$PowerShell.RunspacePool = $RunspacePool

[void]$PowerShell.AddScript({

Param (

$device,

$vm ,

$noRemoting ,

$cpuSamples ,

$diskVersions ,

$deviceInfos ,

$stores ,

$pvsserver ,

$adparams ,

$ADgroups ,

$ddcs ,

$machines ,

$dns ,

$tags ,

$jobTimeout ,

$remoteWork

)

[string[]]$cacheTypes =

@(

'Standard Image' ,

'Cache on Server',

'Standard Image' ,

'Cache in Device RAM',

'Cache on Device Hard Disk',

'Standard Image' ,

'Device RAM Disk',

'Cache on Server, Persistent',

'Standard Image' ,

'Cache in Device RAM with Overflow on Hard Disk'

)

[string[]]$accessTypes =

@(

'Production',

'Maintenance',

'Maintenance Highest Version',

'Override',

'Merge',

'MergeMaintenance',

'MergeTest'

'Test'

)

[int]$bootVersion = -1

## Can't easily cache this since needs each device's deviceid

$vDisk = Get-PvsDiskInfo -DeviceId $device.DeviceId

[hashtable]$fields = @{}

if( $vm )

{

$error.Clear()

$fields += @{

'CPUs' = $vm.NumCpu

'Memory (GB)' = $vm.MemoryGB

'Hard Drives (GB)' = $( ( Get-HardDisk -VM $vm -ErrorAction SilentlyContinue | sort CapacityGB | select -ExpandProperty CapacityGB ) -join ' ' )

'NICS' = $( ( Get-NetworkAdapter -VM $vm -ErrorAction SilentlyContinue | Sort Type | Select -ExpandProperty Type ) -join ' ' )

'Hypervisor' = $vm.VMHost

}

if( $error[0] )

{

Write-Warning "VMware error: $($error[0])"

}

}

if( $device.Active -and ! $noRemoting )

{

$remoteInfo = Get-RemoteInfo -computer $device.Name -miscparameter1 $cpuSamples -jobTimeout $jobTimeout -work $remoteWork -ErrorVariable RemoteError

$fields += Extract-RemoteInfo $remoteInfo

}

$fields.Add( 'PVS Server' , $pvsServer )

$versions = $null

if( $vdisk )

{

$fields += @{

'Disk Name' = $vdisk.Name

'Store Name' = $vdisk.StoreName

'Disk Description' = $vdisk.Description

'Cache Type' = $cacheTypes[$vdisk.WriteCacheType]

'Disk Size (GB)' = ([math]::Round( $vdisk.DiskSize / 1GB , 2 ))

'Write Cache Size (MB)' = $vdisk.WriteCacheSize }

$versions = $diskVersions[ $vdisk.DiskLocatorId ] ## Get-PvsDiskVersion -DiskLocatorId $vdisk.DiskLocatorId ##

if( $versions )

{

## Now get latest production version of this vdisk

$override = $versions | Where-Object { $_.Access -eq 3 }

$vdiskFile = $null

$latestProduction = $versions | Where-Object { $_.Access -eq 0 } | Sort Version -Descending | Select -First 1

if( $latestProduction )

{

$vdiskFile = $latestProduction.DiskFileName

$latestProductionVersion = $latestProduction.Version

}

else

{

$latestProductionVersion = $null

}

if( $override )

{

$bootVersion = $override.Version

$vdiskFile = $override.DiskFileName

}

else

{

## Access: Read-only access of the Disk Version. Values are: 0 (Production), 1 (Maintenance), 2 (MaintenanceHighestVersion), 3 (Override), 4 (Merge), 5 (MergeMaintenance), 6 (MergeTest), and 7 (Test) Min=0, Max=7, Default=0

$bootVersion = $latestProductionVersion

}

if( $vdiskFile)

{

## Need to see if Store path is local to the PVS server and if so convert to a share so we can get vdisk file info

if( $stores[ $vdisk.StoreName ] -match '^([A-z]):(.*$)' )

{

if( [string]::IsNullOrEmpty( $pvsShare ) )

{

$vdiskfile = Join-Path ( Join-Path ( '\\' + $pvsServer + '\' + "$($Matches[1])`$" ) $Matches[2] ) $vdiskFile ## assume regular admin share

}

else

{

$vdiskfile = Join-Path ( Join-Path ( '\\' + $pvsServer + '\' + $pvsShare ) ) $vdiskFile

}

}

else

{

$vdiskFile = Join-Path $stores[ $vdisk.StoreName ] $vdiskFile

}

if( ( Test-Path $vdiskFile -ErrorAction SilentlyContinue ) )

{

$fields += @{ 'vDisk Size (GB)' = [math]::Round( (Get-ItemProperty -Path $vdiskFile).Length / 1GB ) }

}

else

{

Write-Warning "Could not find disk `"$vdiskFile`" for $($device.name)"

}

}

if( $latestProductionVersion -eq $null -and $override )

{

## No production version, only an override so this must be the latest production version

$latestProductionVersion = $override.Version

}

$fields += @{

'Override Version' = $( if( $override ) { $bootVersion } else { $null } )

'Vdisk Latest Version' = $latestProductionVersion

'Latest Version Description' = $( $versions | Where-Object { $_.Version -eq $latestProductionVersion } | Select -ExpandProperty Description )

}

}

else

{

Write-Output "Failed to get vdisk versions for id $($vdisk.DiskLocatorId) for $($device.Name):$($error[0])"

}

$fields.Add( 'Vdisk Production Version' ,$bootVersion )

}

else

{

Write-Output "Failed to get vdisk for device id $($device.DeviceId) device $($device.Name)"

}

$deviceInfo = $deviceInfos[ $device.DeviceId ]

if( $deviceInfo )

{

$fields.Add( 'Disk Version Access' , $accessTypes[ $deviceInfo.DiskVersionAccess ] )

$fields.Add( 'Booted Off' , $deviceInfo.ServerName )

$fields.Add( 'Device IP' , $deviceInfo.IP )

if( ! [string]::IsNullOrEmpty( $deviceInfo.Status ) )

{

$fields.Add( 'Retries' , ($deviceInfo.Status -split ',')[0] -as [int] ) ## second value is supposedly RAM cache used percent but I've not seen it set

}

if( $device.Active )

{

## Check if booting off the disk we should be as previous info is what is assigned, not what is necessarily being used (e.g. vdisk changed for device whilst it is booted)

$bootedDiskName = (( $diskVersions[ $deviceInfo.DiskLocatorId ] | Select -First 1 | Select -ExpandProperty Name ) -split '\.')[0]

$fields.Add( 'Booted Disk Version' , $deviceInfo.DiskVersion )

if( $bootVersion -ge 0 )

{

Write-Verbose "$($device.Name) booted off $bootedDiskName, disk configured $($vDisk.Name)"

$fields.Add( 'Booted off latest' , ( $bootVersion -eq $deviceInfo.DiskVersion -and $bootedDiskName -eq $vdisk.Name ) )

$fields.Add( 'Booted off vdisk' , $bootedDiskName )

}

}

if( $versions )

{

try

{

$fields.Add( 'Disk Version Created' ,( $versions | Where-Object { $_.Version -eq $deviceInfo.DiskVersion } | select -ExpandProperty CreateDate ) )

}

catch

{

$_

}

}

}

else

{

Write-Warning "Failed to get PVS device info for id $($device.DeviceId) device $($device.Name)"

}

$fields += Get-ADMachineInfo -machineName $device.Name -adparams $adparams -adGroups $ADgroups

if( $device.Active -and $dns )

{

[array]$ipv4Address = @( Resolve-DnsName -Name $device.Name -Type A )

$fields.Add( 'IPv4 address' , ( ( $ipv4Address | Select -ExpandProperty IPAddress ) -join ' ' ) )

}

if( $machines -and $machines.Count )

{

## Need to find a ddc that will return us information on this device

ForEach( $ddc in $ddcs )

{

## can't use HostedMachineName as only populated if registered

$machine = $machines[ $ddc ] | Where-Object { $_.MachineName -eq ( ($device.DomainName -split '\.')[0] + '\' + $device.Name ) }

if( $machine )

{

$fields += @{

'Machine Catalogue' = $machine.CatalogName

'Delivery Group' = $machine.DesktopGroupName

'Registration State' = $machine.RegistrationState

'User_Sessions' = $machine.SessionCount

'Load Index' = $machine.LoadIndex

'Load Indexes' = $machine.LoadIndexes -join ','

'Maintenance_Mode' = $( if( $machine.InMaintenanceMode ) { 'On' } else { 'Off' } )

'DDC' = $ddc

}

if( $tags )

{

$fields.Add( 'Tags' , ( $machine.Tags -join ',' ) )

}

break

}

}

}

Add-Member -InputObject $device -NotePropertyMembers $fields

$device ## return

})

[void]$PowerShell.AddParameters( $Parameters )

$Handle = $PowerShell.BeginInvoke()

$temp = '' | Select PowerShell,Handle

$temp.PowerShell = $PowerShell

$temp.handle = $Handle

[void]$jobs.Add($Temp)

}

}

[array]$results = @( $jobs | ForEach-Object `

{

$_.powershell.EndInvoke($_.handle)

$_.PowerShell.Dispose()

})

$jobs.clear()

$results | ForEach-Object `

{

if( Get-Member -InputObject $_ -Name 'Name' -ErrorAction SilentlyContinue )

{

$devices.Add( $_.Name , ( $_ | Select * ) )

}

else

{

Write-Warning $_

}

}

## See if we have any devices from DDC machine list which are marked as being in PVS catalogues but not in our devices list so are orphans

if( ! $noOrphans )

{

$machines.GetEnumerator() | ForEach-Object `

{

$ddc = $_.Key

Write-Progress -Activity "Checking for orphans on DDC $ddc" -PercentComplete 98

## Cache machine catalogues so we can check provisioning type

[hashtable]$catalogues = @{}

Get-BrokerCatalog -AdminAddress $ddc | ForEach-Object { $catalogues.Add( $_.Name , $_ ) }

## Add to devices so we can display as much detail as possible if PVS provisioned

$_.Value | ForEach-Object `

{

$machine = $_

$domainName,$machineName = $machine.MachineName -split '\\'

if( [string]::IsNullOrEmpty( $machineName ) )

{

$machineName = $domainName

$domainName = $null

}

if( [string]::IsNullOrEmpty( $name ) -or $machineName -match $name )

{

## Now see if have this in devices in which case we ignore it - domain name in device record may be FQDN but domain from catalogue will not be (may also be missing in device)

#$device = $devices | Where-Object { $_.Name -eq $machineName -and ( ! $domainName -or ! $_.DomainName -or ( $domainName -eq ( $_.DomainName -split '\.' )[0] ) ) }

$device = $devices[ $machineName ]

if( $device ) ## check same domain

{

if( $domainName -and $device.DomainName -and $domainName -ne ( $device.DomainName -split '\.' )[0] )

{

$device = $null ## doesn't quite match

}

}

if( ! $device )

{

## Now check machine catalogues so if ProvisioningType = PVS then we will look to see if it an orphan

$catalogue = $catalogues[ $machine.CatalogName ]

if( ! $catalogue -or $provisioningType -eq 'Any' -or $catalogue.ProvisioningType -match $provisioningType )

{

$newItem = [pscustomobject]@{

'Name' = ( $machine.MachineName -split '\\' )[-1]

'DomainName' = if( $machine.MachineName.IndexOf( '\' ) -gt 0 )

{

($machine.MachineName -split '\\')[0]

}

else

{

$null

}

'DDC' = $ddc ;

'Machine Catalogue' = $machine.CatalogName

'Delivery Group' = $machine.DesktopGroupName

'Registration State' = $machine.RegistrationState

'Maintenance_Mode' = $( if( $machine.InMaintenanceMode ) { 'On' } else { 'Off' } )

'User_Sessions' = $machine.SessionCount ; }

[hashtable]$adFields = Get-ADMachineInfo -machineName $newItem.Name -adparams $adparams -adGroups $ADgroups

if( $adFields -and $adFields.Count )

{

Add-Member -InputObject $newItem -NotePropertyMembers $adfields

}

if( ! $noRemoting )

{

[hashtable]$fields = Extract-RemoteInfo (Get-RemoteInfo -computer $newItem.Name -miscparameter1 $cpuSamples -jobTimeout $jobTimeout -work $remoteWork)

if( $fields -and $fields.Count )

{

Add-Member -InputObject $newItem -NotePropertyMembers $fields

}

}

if( $tags )

{

Add-Member -InputObject $newItem -MemberType NoteProperty -Name 'Tags' -Value ( $machine.Tags -join ',' )

}

if( $dns )

{

[array]$ipv4Address = @( Resolve-DnsName -Name $newItem.Name -Type A )

Add-Member -InputObject $newItem -MemberType NoteProperty -Name 'IPv4 address' -Value ( ( $ipv4Address | Select -ExpandProperty IPAddress ) -join ' ' )

}

$devices.Add( $newItem.Name , $newItem )

}

}

}

}

}

## if we have VMware details then get those VMs and add if not present here

if( $hypervisors -and $hypervisors.Count )

{

## will already be connected as have already grabbed VMs

Write-Progress -Activity "Checking for orphans on hypervisor $($hypervisors -split ' ')" -PercentComplete 99

[int]$vmCount = 0

$vms.GetEnumerator() | ForEach-Object `

{

$vmwareVM = $_.Value

$vmCount++

[string]$vmName = $vmwareVM.Name

## name may not be just netbios but may have other infor after a separator character like _

if( ! [string]::IsNullOrEmpty( $splitVM ) )

{

$vmName = ($vmName -split $splitVM)[0]

}

$existingDevice = $devices[ $vmname ]

## Now have to see if we have restricted the PVS device retrieval via -name making $devices a subset of all PVS devices

if( ! $existingDevice -and ! [string]::IsNullOrEmpty( $name ) )

{

$existingDevice = $vmName -notmatch $name

}

if( ! $existingDevice )

{

$newItem = [pscustomobject]@{

'Name' = $vmName

'Description' = $vmwareVM.Notes

'CPUs' = $vmwareVM.NumCpu

'Memory (GB)' = $vmwareVM.MemoryGB

'Hard Drives (GB)' = $( ( Get-HardDisk -VM $vmwareVM -ErrorAction SilentlyContinue | sort CapacityGB | select -ExpandProperty CapacityGB ) -join ' ' )

'NICS' = $( ( Get-NetworkAdapter -VM $vmwareVM -ErrorAction SilentlyContinue | Sort Type | Select -ExpandProperty Type ) -join ' ' )

'Hypervisor' = $vmwareVM.VMHost

'Active' = $( if($vmwareVM.PowerState -eq 'PoweredOn') { $true } else { $false } )

}

[hashtable]$adFields = Get-ADMachineInfo -machineName $newItem.Name -adparams $adparams -adGroups $ADgroups

if( $adFields -and $adFields.Count )

{

Add-Member -InputObject $newItem -NotePropertyMembers $adfields

}

if( $vmwareVM.PowerState -eq 'PoweredOn' )

{

if( ! $noRemoting )

{

[hashtable]$fields = Extract-RemoteInfo (Get-RemoteInfo -computer $newItem.Name -miscparameter1 $cpuSamples -jobTimeout $jobTimeout -work $remoteWork)

if( $fields -and $fields.Count )

{

Add-Member -InputObject $newItem -NotePropertyMembers $fields

}

}

if( $dns )

{

[array]$ipv4Address = @( Resolve-DnsName -Name $newItem.Name -Type A )

Add-Member -InputObject $newItem -MemberType NoteProperty -Name 'IPv4 address' -Value ( ( $ipv4Address | Select -ExpandProperty IPAddress ) -join ' ' )

}

}

$devices.Add( $newItem.Name , $newItem )

}

if( ! $vmCount )

{

Write-Warning "Found no VMs on $($hypervisors -split ',') matching regex `"$name`""

}

}

}

}

Write-Progress -Activity 'Finished' -Completed -PercentComplete 100

$devices # return

}

Function Get-ADMachineInfo

{

Param

(

[hashtable]$adparams ,

[string]$adGroups ,

[string]$machineName

)

[hashtable]$fields = @{}

try

{

$adaccount = Get-ADComputer $machineName -ErrorAction SilentlyContinue @adparams

if( $adaccount )

{

[string]$groups = $null

if( ! [string]::IsNullOrEmpty( $ADgroups ) )

{

$groups = ( ( $adAccount | select -ExpandProperty MemberOf | ForEach-Object { (( $_ -split '^CN=')[1] -split '\,')[0] } | Where-Object { $_ -match $ADgroups } ) -join ' ' )

}

$fields += `

@{

'AD Account Created' = $adAccount.Created

'AD Last Logon' = $adAccount.LastLogonDate

'AD Account Modified' = $adaccount.Modified

'AD Description' = $adAccount.Description

'AD Groups' = $groups

}

}

}

catch {}

$fields # return

}

Export-ModuleMember -Function Get-RemoteInfo,Get-PVSDevices,Get-ADMachineInfo

Link to comment
  • 0

[CmdletBinding()]

Param
(
    [Parameter(Mandatory)]
    [string]$ddc ,
    [double]$daysAgo = 1 ,
    [string]$username
)

$VerbosePreference = $(if( $PSBoundParameters[ 'verbose' ] ) { $VerbosePreference } else { 'SilentlyContinue' })
$DebugPreference = $(if( $PSBoundParameters[ 'debug' ] ) { $DebugPreference } else { 'SilentlyContinue' })
$ErrorActionPreference = $(if( $PSBoundParameters[ 'erroraction' ] ) { $ErrorActionPreference } else { 'Stop' })
$ProgressPreference = 'SilentlyContinue'
$Hash = @()

[int]$outputWidth = 400
[bool]$join = $true
[string]$query = 'ConnectionFailureLogs'
[string]$protocol = 'http'
[int]$oDataVersion = 4 ## if this fails will try lower versions
## map tables to the date stamp we will filter on
[hashtable]$dateFields = @{
     'Session' = 'StartDate'
     'Connection' = 'BrokeringDate'
     'ConnectionFailureLog' = 'FailureDate'
}

[hashtable]$connectionFailureCodes = @{}

# Altering the size of the PS Buffer
if( ( $PSWindow = (Get-Host).UI.RawUI ) -and ($WideDimensions = $PSWindow.BufferSize) )
{
    $WideDimensions.Width = $outputWidth
    $PSWindow.BufferSize = $WideDimensions
}

## Modified from code at https://jasonconger.com/2013/10/11/using-powershell-to-retrieve-citrix-monitor-data-via-odata/
Function Invoke-ODataTransform
{
    Param
    (
        [Parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true)]
        $records
    )

    Begin
    {
        $propertyNames = $null

        [int]$timeOffset = if( (Get-Date).IsDaylightSavingTime() ) { 1 } else { 0 }
    }

    Process
    {
        if( $records -and $records.PSObject.Properties[ 'content' ] )
        {
            if( ! $propertyNames )
            {
                $properties = ($records | Select -First 1).content.properties
                if( $properties )
                {
                    $propertyNames = $properties | Get-Member -MemberType Properties | Select -ExpandProperty name
                }
                else
                {
                    // v4+
                    $propertyNames = 'NA' -as [string]
                }
            }
            if( $propertyNames -is [string] )
            {
                $records | Select -ExpandProperty value
            }
            else
            {
                ForEach( $record in $records )
                {
                    $h = @{ 'ID' = $record.ID }
                    $properties = $record.content.properties

                    ForEach( $propertyName in $propertyNames )
                    {
                        $targetProperty = $properties.$propertyName
                        if($targetProperty -is [Xml.XmlElement])
                        {
                            try
                            {
                                $h.$propertyName = $targetProperty.'#text'
                                ## see if we need to adjust for daylight savings
                                if( $timeOffset -and ! [string]::IsNullOrEmpty( $h.$propertyName ) -and $targetProperty.type -match 'DateTime' )
                                {
                                    $h.$propertyName = (Get-Date -Date $h.$propertyName).AddHours( $timeOffset )
                                }
                            }
                            catch
                            {
                                ##$_
                            }
                        }
                        else
                        {
                            $h.$propertyName = $targetProperty
                        }
                    }

                    [PSCustomObject]$h
                }
            }
        }
        elseif( $records -and $records.PSObject.Properties[ 'value' ] ) ##JSON
        {
            $records.value
        }
    }
}

Function Get-DateRanges
{
    Param
    (
        [string]$query ,
        $from ,
        $to ,
        [switch]$selective ,
        [int]$oDataVersion
    )
    
    $field = $dateFields[ ($query -replace 's$' , '') ]
    if( ! $field )
    {
        if( $selective )
        {
            return $null ## only want specific ones
        }
        $field = 'CreatedDate'
    }
    if( $oDataVersion -ge 4 )
    {
        if( $from )
        {
            "()?`$filter=$field ge $(Get-Date -date $from -format s)Z"
        }
        if( $to )
        {
            "and $field le $(Get-Date -date $to -format s)Z"
        }
    }
    else
    {
        if( $from )
        {
            "()?`$filter=$field ge datetime'$(Get-Date -date $from -format s)'"
        }
        if( $to )
        {
            "and $field le datetime'$(Get-Date -date $to -format s)'"
        }
    }
}

Function Resolve-CrossReferences
{
    Param
    (
        [Parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true)]
        $properties ,
        [switch]$cloud
    )
    
    Process
    {
        $properties | Where-Object { ( $_.Name -match '^(.*)Id$' -or $_.Name -match '^(SessionKey)$' ) -and ! [string]::IsNullOrEmpty( $Matches[1] ) }  | Select-Object -Property Name | ForEach-Object `
        {
            [string]$id = $Matches[1]
            [bool]$current = $false
            if( $id -match '^Current(.*)$' )
            {
                $current = $true
                $id = $Matches[1]
            }
            elseif( $id -eq 'SessionKey' )
            {
                $id = 'Session'
            }

            if( ! $tables[ $id ] -and ! $alreadyFetched[ $id ] )
            {
                if( $cloud )
                {
                    $params.uri = ( "{0}://{1}.xendesktop.net/Citrix/Monitor/OData/v{2}/Data/{3}s" -f $protocol , $customerid , $version ,  $id ) ## + (Get-DateRanges -query $id -from $from -to $to -selective -oDataVersion $oDataVersion)
                }
                else
                {
                    $params.uri = ( "{0}://{1}/Citrix/Monitor/OData/v{2}/Data/{3}s" -f $protocol , $ddc , $version , $id ) ## + (Get-DateRanges -query $id -from $from -to $to -selective -oDataVersion $oDataVersion)
                }

                ## save looking up again, especially if it errors as we are not looking up anything valid
                $alreadyFetched.Add( $id , $id )

                ## if it's a high volume, time specific table then we will filter it
                if( $dateFields[ $id ] )
                {
                    $params.uri += Get-DateRanges -query $id -from $from -to $to -oDataVersion $oDataVersion
                }

                [hashtable]$table = @{}
                try
                {
                    Invoke-RestMethod @params | Invoke-ODataTransform | ForEach-Object `
                    {
                        ## add to hash table keyed on its id
                        ## ToDo we need to go recursive to see if any of these have Ids that we need to resolve without going infintely recursive
                        $object = $_
                        [string]$thisId = $null
                        [string]$keyName = $null

                        if( $object.PSObject.Properties[ 'id' ] )
                        {
                            $thisId = $object.Id
                            $keyName = 'id'
                        }
                        elseif( $object.PSObject.Properties[ 'SessionKey' ] )
                        {
                            $thisId = $object.SessionKey
                            $keyname = 'SessionKey'
                        }

                        if( $thisId )
                        {
                            [string]$key = $(if( $thisId -match '\(guid''(.*)''\)$' )
                                {
                                    $Matches[ 1 ]
                                }
                                else
                                {
                                    $thisId
                                })
                            $object.PSObject.properties.remove( $key )
                            $table.Add( $key , $object )
                        }

                        ## Look at other properties to figure if it too is an id and grab that table too if we don't have it already
                        ForEach( $property in $object.PSObject.Properties )
                        {
                            if( $property.MemberType -eq 'NoteProperty' -and $property.Name -ne $keyName -and $property.Name -ne 'sid' -and $property.Name -match '(.*)Id$' )
                            {
                                $property | Resolve-CrossReferences -cloud:$cloud
                            }
                        }
                    }
                    if( $table.Count )
                    {
                        Write-Verbose -Message "Adding table $id with $($table.Count) entries"
                        $tables.Add( $id , $table )
                    }
                }
                catch
                {
                    $nop = $null
                }
            }
        }
    }
}

Function Resolve-NestedProperties
{
    Param
    (
        [Parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true)]
        $properties ,
        $previousProperties
    )
    
    Process
    {
        $properties | Where-Object { $_.Name -ne 'sid' -and ( $_.Name -match '^(.*)Id$' -or $_.Name -match '^(Session)Key$' -or $_.Name -match '(EnumValue)' ) -and ! [string]::IsNullOrEmpty( $Matches[1] ) } | . { Process `
        {
            $property = $_
            if( ( $matchedEnum = $Matches[1] ) -eq 'EnumValue' )
            {
                ## http://grl-xaddc01/Citrix/Monitor/OData/v3/Methods/GetAllMonitoringEnums('SessionFailureCode')/Values
                ## need to find a generic way of doing this
                $lookupTable = $null
                if( $property.Name -eq 'ConnectionFailureEnumValue' )
                {
                    if( ! $connectionFailureCodes -or ! $connectionFailureCodes.Count )
                    {
                        Write-Verbose -Message "Resolving enum $($property.Name)"
                        ## v4 equivalent??
                        $params[ 'uri' ] = ( "{0}://{1}/Citrix/Monitor/OData/v3/Methods/GetAllMonitoringEnums('SessionFailureCode')/Values" -f $protocol , $ddc )
                        if( $enums = Invoke-RestMethod @params )
                        {
                            ForEach( $enum in $enums )
                            {
                                ## http://grl-xaddc01/Citrix/Monitor/OData/v3/Methods/MonitoringEnumItems(0)
                                if( $enum.id -match '\((\d+)\)$' )
                                {
                                    $connectionFailureCodes.Add( $Matches[1] , ( $enum.content.properties | Select-Object -expandProperty Name ) )
                                }
                            }
                        }
                    }
                    $lookupTable = $connectionFailureCodes
                }
                if( $lookupTable )
                {
                    if( [string]$expandedEnum = $connectionFailureCodes[ $property.Value.ToString() ] )
                    {
                        [pscustomobject]@{ ( $property.Name -replace $matchedEnum ) = ( $expandedEnum -creplace '([a-z])([A-Z])' , '$1 $2' ) }
                    }
                    else
                    {
                        Write-Warning -Message "Unable to find enum value $($property.Value) for enum $($property.Name)"
                    }
                }
                else
                {
                    Write-Warning -Message "Unable to lookup enumeration $($property.Name)"
                }
            }
            elseif( ! [string]::IsNullOrEmpty( ( $id = ( $Matches[1] -replace '^Current' , '')) ))
            {
                if ( $table = $tables[ $id ] )
                {
                    if( $property.Value -and ( $item = $table[ ($property.Value -as [string]) ]))
                    {
                        $datum.PSObject.properties.remove( $property )
                        $item.PSObject.Properties | ForEach-Object `
                        {
                            [pscustomobject]@{ "$id.$($_.Name)" = $_.Value }
                            if( $_.Name -ne $property.Name -and ( ! $previousProperties -or ! ( $previousProperties | Where-Object Name -eq $_.Name ))) ## don't lookup self or a key if it was one we previously looked up
                            {
                                Resolve-NestedProperties -properties $_ -previousProperties $properties
                            }
                        }
                    }
                }
            }
        }}
    }
}

[hashtable]$params = @{ 'ErrorAction' = 'SilentlyContinue' }
[hashtable]$alreadyFetched = @{}
$credential = $null

if( $PSBoundParameters[ 'XDusername' ] )
{
    if( ! [string]::IsNullOrEmpty( $XDpassword ) )
    {
        $credential = New-Object System.Management.Automation.PSCredential( $XDusername , ( ConvertTo-SecureString -AsPlainText -String $XDpassword -Force ) )
        $XDpassword = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
    }
    else
    {
        Throw "Must specify password when using -username either via -password or %RandomKey%"
    }
}

if( $credential )
{
    $params.Add( 'Credential' , $credential )
}
 else
{
    $params.Add( 'UseDefaultCredentials' , $true )
}

## used to try and figure out the highest supported oData version but proved problematic
[int]$highestVersion = $oDataVersion ## if( $oDataVersion -le 0 ) { 10 } else { -1 }
$fatalException = $null
[int]$version = $oDataVersion

$services = $null
## queries are case sensitive so help people who don't know this but don't do it for everything as would break items like DesktopGroups
if( $query -cmatch '^[a-z]' )
{
    $TextInfo = (Get-Culture).TextInfo
    $query = $TextInfo.ToTitleCase( $query ).ToString()
}

if( $PsCmdlet.ParameterSetName -eq 'cloud' )
{
    if( ! $PSBoundParameters[ 'authtoken' ] )
    {
        Add-PSSnapin -Name Citrix.Sdk.Proxy.*
        if( ! ( Get-Command -Name Get-XDAuthentication -ErrorAction SilentlyContinue ) )
        {
            Throw "Unable to find the Get-XDAuthentication cmdlet - is the Virtual Apps and Desktops Remote PowerShell SDK installed ?"
        }
        Get-XDAuthentication -CustomerId $customerid
        if( ! $? )
        {
            Throw "Failed to get authentication token for Cloud customer id $customerid"
        }
        $authtoken = $GLOBAL:XDAuthToken
    }
    $params.Add( 'Headers' , @{ 'Customer' = $customerid ; 'Authorization' = $authtoken } )
    $protocol = 'https'
}

[bool]$cloud = $false

[datetime]$from = ($to = Get-Date).AddMinutes(-120)

[array]$data = @( do
{
    if( $oDataVersion -le 0 )
    {
        ## Figure out what the latest OData version supported is. Could get via remoting but remoting may not be enabled
        if( $highestVersion -le 0 )
        {
            break
        }
        $version = $highestVersion--
    }
    
    if( $PsCmdlet.ParameterSetName -eq 'cloud' )
    {
        $params[ 'Uri' ] = ( "{0}://{1}.xendesktop.net/Citrix/Monitor/OData/v{2}/Data/{3}" -f $protocol , $customerid , $version , $query ) + (Get-DateRanges -query $query -from $from -to $to -oDataVersion $oDataVersion)
        $cloud = $true
    }
    else
    {
        $params[ 'Uri' ] = ( "{0}://{1}/Citrix/Monitor/OData/v{2}/Data/{3}" -f $protocol , $ddc , $version , $query ) + (Get-DateRanges -query $query -from $from -to $to -oDataVersion $oDataVersion)
    }

    Write-Verbose "URL : $($params.Uri)"

    try
    {
        Invoke-RestMethod @params | Invoke-ODataTransform

        $fatalException = $null
        break ## since call succeeded so that we don't report for lower versions
    }
    catch
    {
        $fatalException = $_
        if( $_.Exception.response.StatusCode -eq 'Unauthorized' )
        {
            Throw $fatalException
        }
        elseif( $_.Exception.response.StatusCode -eq 'NotFound' )
        {
            $fatalException = $null
            $oDataVersion = --$version ## try lower OData version
        }
    }
} while ( $highestVersion -gt 0 -and $version -gt 0 -and ! $fatalException ))

if( $fatalException )
{
    Throw $fatalException
}

if( $services )
{
    if( $services.PSObject.Properties[ 'service' ] )
    {
        $services.service.workspace.collection | Select-Object -Property 'title' | Sort-Object -Property 'title'
    }
    else
    {
        $services.value | Sort-Object -Property 'name'
    }
}
elseif( $data -and $data.Count )
{
    [hashtable]$tables = @{}

    ## now figure out what other tables we need in order to satisfy these ids (not interested in id on it's own)
    $data[0].PSObject.Properties | Resolve-CrossReferences -cloud:$cloud

    [int]$originalPropertyCount = $data[0].PSObject.Properties.GetEnumerator()|Measure-Object |Select-Object -ExpandProperty Count
    [int]$finalPropertyCount = -1

    ## now we need to add these cross referenced items
    [array]$results = @( ForEach( $datum in $data )
    {
        $datum.PSObject.Properties | Where-Object { $_.Name -ne 'sid' -and ( $_.Name -match '^(.*)Id$' -or $_.Name -match '^(Session)Key$' -or $_.Name -match '(EnumValue)' ) -and ! [string]::IsNullOrEmpty( $Matches[1] ) } | . { Process `
        {
            $property = $_
            Resolve-NestedProperties $property | ForEach-Object `
            {
                $_.PSObject.Properties | Where-Object MemberType -eq 'NoteProperty' | ForEach-Object `
                {
                    Add-Member -InputObject $datum -MemberType NoteProperty -Name $_.Name -Value $_.Value
                }
            }
        }}

        if( $finalPropertyCount -lt 0 )
        {
            $finalPropertyCount = $datum.PSObject.Properties.GetEnumerator()|Measure-Object |Select-Object -ExpandProperty Count
            Write-Verbose -Message "Expanded from $originalPropertyCount properties to $finalPropertyCount"
        }

        $datum
    })
    if( $results -and $results.Count )
    {
        Write-Verbose -Message "Start date is $(Get-Date -Date $from -Format G)"
      $kiki=  $results | Where-Object { $_.FailureDate -as [datetime] -ge $from -and ( ( [string]::IsNullOrEmpty( $username ) -and $null -ne $_.PSObject.Properties[ 'User.UserName' ] ) -or ( $null -ne $_.PSObject.Properties[ 'User.UserName' ] -and $_.'UserName' -eq $username ) ) } | Select-Object -Property 'User.UserName' , @{n='Date';e={$_.FailureDate -as [datetime]}} , ConnectionFailure , @{n='Delivery Group';e={$_.'DesktopGroup.Name'}} , 'Machine.Name' , 'Connection.ClientAddress' , 'Connection.IsReconnect'  | Format-Table -AutoSize
    $tata=$kiki

        $suni= $results | Where-Object { $_.FailureDate -as [datetime] -ge $from -and ( ( [string]::IsNullOrEmpty( $username ) -and $null -ne $_.PSObject.Properties[ 'User.UserName' ] ) -or ( $null -ne $_.PSObject.Properties[ 'User.UserName' ] -and $_.'UserName' -eq $username ) ) } |Select-Object User.Upn,User.UserName,Machine.Name,DesktopGroup.Name,ConnectionFailure, @{n='Date';e={$_.FailureDate -as [datetime]}} 
        $suni #|ft -AutoSize


        
    }
    else
    {
        Write-Warning "No data returned"
    }
}
else
{
    Write-Warning "No data returned"
}

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...