From 0ba5d9c93fe85489ef277f4d3c9a0677e3628a3f Mon Sep 17 00:00:00 2001 From: Danny de Kooker Date: Fri, 19 Jul 2024 17:17:03 +0200 Subject: [PATCH] initial commit --- Hyper-V/HyperV-VM-Status-Report.ps1 | 376 ++++++++++++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100644 Hyper-V/HyperV-VM-Status-Report.ps1 diff --git a/Hyper-V/HyperV-VM-Status-Report.ps1 b/Hyper-V/HyperV-VM-Status-Report.ps1 new file mode 100644 index 0000000..6bece78 --- /dev/null +++ b/Hyper-V/HyperV-VM-Status-Report.ps1 @@ -0,0 +1,376 @@ +<# +.SYNOPSIS + This PowerShell script generates a comprehensive Hyper-V status report, including VM status, replication health, and metric details, + and sends it to specified recipients via email. + +.DESCRIPTION + This script is designed to provide a detailed report on the status of Hyper-V servers, including the state of virtual machines, replication health, + and performance metrics such as CPU, memory, disk usage, and network traffic. The report is generated in HTML format and sent to specified recipients via email. + The script also includes error handling and logging mechanisms to ensure that any issues are captured and reported. + The report can be customized to include or exclude specific information, and the email recipients can be configured to receive the report on a regular basis. + +.NOTES + This script is intended for use in a test or production environment. Make sure to test the script in a non-production environment before running it in production. + Author: D.de Kooker - info@dcomputers.nl + Version: 1.0 + + DISCLAIMER: Use scripts at your own risk, if there is anything I can help you with I will try but I do not take responsibility for the way that anyone else uses my scripts. + Sharing is caring. Share your knowledge with the world so that everybody can learn from it. + +.LINK + The latest version can Always be found on my GIT page on the link below: + https://git.dcomputers.nl/Dcomputers/PowershellScripts +#> + +#region script parameters + param ( + [parameter()] + [String[]]$HyperVServers = $ENV:COMPUTERNAME + ) +#endregion + +#region Global script settings and variables + #General + $Version = "v1.0" + $logfilelocation = "$($MyInvocation.MyCommand.Path | Split-Path -Parent)\Logs" + $logfilename = "$(Get-Date -Format yyyyMMddHHmmss).log" + $summaryfilename = "$(Get-Date -Format yyyyMMddHHmmss)-Summary.txt" + + #Email report settings + $STR_SMTPServer = "" + $STR_SMTPServerPort = "" + $STR_SMTPUsername = "" + $STR_SMTPPassword = "" + $STR_EmailSubject= "Hyper-V Status Report - $(Get-Date -Format "dd-MM-yyyy")" + $STR_SMTPFromaddress = "ICT Servicedesk " + $STR_Receivers = "servicedesk@contoso.com,systemengineer1@contoso.com" #List of commaseperated emailaddresses +#endregion + +#region functions + function SendMailv2 ($To,$Subject,$Body,$Attachments = @()){ + $SMTPClient = New-Object Net.Mail.SmtpClient($STR_SMTPServer, $STR_SMTPServerPort) + # $SMTPClient.EnableSsl = $true + $SMTPClient.Credentials = New-Object System.Net.NetworkCredential($STR_SMTPUsername, $STR_SMTPPassword); + $SMTPMessage = New-Object System.Net.Mail.MailMessage($STR_SMTPFromaddress,$To,$Subject,$Body) + $SMTPMessage.IsBodyHTML = $true + # Add attachments if provided + if ($Attachments.Count -gt 0) { + foreach ($attachment in $Attachments) { + $SMTPMessage.Attachments.Add((New-Object System.Net.Mail.Attachment($attachment))) + } + } + $SMTPClient.Send($SMTPMessage) + } + function Initiate-Log { + # Get current user and session information + $username = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name + $computerName = $env:COMPUTERNAME + $sessionID = $pid + $date = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + + # Write log header + $logHeader = "[$date] Log initiated by $username on $computerName (Session ID: $sessionID)" + Add-Content -Path $logfilelocation\$logfilename -Value "**********************" + Add-Content -Path $logfilelocation\$logfilename -Value "LogFile initiation" + Add-Content -Path $logfilelocation\$logfilename -Value "Start time: $date" + Add-Content -Path $logfilelocation\$logfilename -Value "Username: $username" + Add-Content -Path $logfilelocation\$logfilename -Value "Machine: $computerName" + Add-Content -Path $logfilelocation\$logfilename -Value "Process ID: $sessionID" + Add-Content -Path $logfilelocation\$logfilename -Value "Script Version: $Version" + Add-Content -Path $logfilelocation\$logfilename -Value "Script Source: https://git.dcomputers.nl/Dcomputers/PowershellScripts" + Add-Content -Path $logfilelocation\$logfilename -Value "**********************" + } + function Write-Log { + [CmdletBinding()] + Param ( + [Parameter(Mandatory=$true)] + [string]$Message, + [Parameter(Mandatory=$false)] + [ValidateSet("INFO", "WARNING", "ERROR")] + [string]$Level = "INFO" + ) + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + $logmessage = "[$timestamp] [$Level] $Message" + Add-Content -Path $logfilelocation\$logfilename -Value $logmessage + } + function Write-Summary { + [CmdletBinding()] + Param ( + [Parameter(Mandatory=$true)] + [string]$Message + ) + Add-Content -Path $logfilelocation\$summaryfilename -Value $Message + } +#endregion + +#region prerequisites check + #Create log directory if not present and initiate logfile + if (!(test-path $logfilelocation)) {mkdir $logfilelocation} + Initiate-Log + + #Check if the required Powershell Modules are available + $modules = @("Hyper-V") + foreach ($module in $modules) { + if (!(Get-Module -Name $module -ListAvailable)) { + Write-Host "The $module module is not installed. Please install it and try again." + Write-Log -Message "The $module module is not installed. Please install it and try again." -Level ERROR + exit 1 + } + else { + Import-Module $module + Write-Log -Message "The $module module is loaded." -Level INFO + } + } + + #Check connectivity to the Hyper-V Servers + $STR_HVServers = @() + $STR_HVServersError = @() + foreach ($srv in $HyperVServers){ + if (Test-WSMan -ComputerName $srv -ErrorAction SilentlyContinue){ + $STR_HVServers += $srv + Write-Log -Message "Connection check for $srv successfull" -Level INFO + } + else { + $STR_HVServersError += $srv + Write-Log -Message "Unable to connect to $srv remotely" -Level WARNING + } + } + if (!$STR_HVServers){ + Write-Log -Message "No Hyper-V Servers available" -Level ERROR + Write-Log -Message "Stopping script, cannot continue" -Level ERROR + exit 1 + } +#endregion + +#region script where action is taken + #gather VM information from all Hyper-V servers + $VMinfo = @() + foreach ($hvserver in $STR_HVServers){ + $VMs = Get-VM -ComputerName $hvserver | Sort-Object State,Name + foreach ($vm in $VMs) { + $VMinfo += [PSCustomObject]@{ + HVServer = $hvserver + VMName = $vm.Name + State = $vm.State + Status = $vm.Status + UpTime = $vm.UpTime + } + } + } + + #gather VM replication health from all Hyper-V servers + $VMreplinfo = @() + foreach ($hvserver in $STR_HVServers){ + if (Get-VMReplication -ComputerName $hvserver){ + Write-Log -Message "Found replica's on $hvserver, collecting data for the report" -Level INFO + $VMs = Get-VMReplication -ComputerName $hvserver | Sort-Object Health -Descending + foreach ($vm in $VMs) { + $VMreplinfo += [PSCustomObject]@{ + HVServer = $hvserver + VMName = $vm.Name + ReplicationState = $vm.ReplicationState + ReplicationHealth = $vm.ReplicationHealth + LastReplicationTime = $vm.LastReplicationTime + PrimaryServer = $vm.PrimaryServer + ReplicaServer = $vm.ReplicaServer + ReplicationMode = $vm.ReplicationMode + RelationshipType = $vm.RelationshipType + } + } + } + else { + Write-Log -Message "No replica's found on $hvserver" -Level WARNING + } + } + + #gather VM metric data form all Hyper-V servers + $VMmetricinfo = @() + foreach ($hvserver in $STR_HVServers){ + if (Get-VM -ComputerName $hvserver | Measure-VM -ErrorAction SilentlyContinue){ + Write-Log -Message "Found Metric info on $hvserver, collecting data for the report" -Level INFO + $VMs = Get-VM -ComputerName $hvserver | Where-Object {$_.Name -notlike "*replica*" -and $_.Name -notlike "*maintenance*"} | Measure-VM -ErrorAction SilentlyContinue | Sort-Object VMName + foreach ($vm in $VMs) { + $VMmetricinfo += [PSCustomObject]@{ + HVServer = $hvserver + VMName = $vm.VMName + AvgCPU = $vm.AvgCPU + AvgRAM = ($vm.AvgRAM / 1024) + TotalDisk = ($vm.TotalDisk / 1024) + InboundNWTraffic = (($vm.NetworkMeteredTrafficReport | where-object {($_.Direction -eq "Inbound")} | measure-object TotalTraffic -sum).sum / 1024) + OutboundNWTraffic = (($vm.NetworkMeteredTrafficReport | where-object {($_.Direction -eq "Outbound")} | measure-object TotalTraffic -sum).sum / 1024) + MeteringDuration = $vm.MeteringDuration + } + + #reset metric count + Get-VM -Name $($vm.VMName) -ComputerName $hvserver | Reset-VMResourceMetering + } + } + else { + Write-Log -Message "No Metric info found on $hvserver" -Level WARNING + } + Write-Log -Message "Enable metric logging for possible vm's" -Level INFO + Get-VM -ComputerName $hvserver | Where-Object {$_.Name -notlike "*replica*" -and $_.Name -notlike "*maintenance*"} | Enable-VMResourceMetering + } + +#endregion + +#region send reports and generate summary report + #Generate HTML report based on gathered info + $htmlReport = @" + + + + + + +

Hyper-V Status Report - $(Get-Date -Format "dd-MM-yyyy - HH:mm")

+ Script version: $Version
+ Checked Servers: $STR_HVServers
+ $(if ($STR_HVServersError){ + "Unable to connect to servers: $STR_HVServersError" + })

+"@ + foreach ($hvserver in $HyperVServers) { + #Generate VM status table + $htmlReport += @" +

Server: $hvserver - VM Status

+ + + + + + + +"@ + foreach ($vm in ($VMinfo | Where-Object {$_.HVServer -eq $hvserver})) { + $htmlReport += @" + + $(switch ($vm){ + {($_.State -eq 'OFF') -and ($_.VMName -notlike "*replica*" -and $_.VMName -notlike "*maintenance*")} {""} + {($_.State -eq 'Saved') -and ($_.VMName -notlike "*replica*" -and $_.VMName -notlike "*maintenance*")} {""} + default {""} + } ) + + + + + +"@ + } + $htmlReport += "
VM NameStateOperational StatusUp Time
$($vm.VMName)$($vm.State)$($vm.Status)$($vm.UpTime)
" + + #Generate VM Replication Health table + $htmlReport += @" +

Server: $hvserver - VM Replication Health

+ + + + + + + + + + + +"@ + foreach ($vm in ($VMreplinfo | Where-Object {$_.HVServer -eq $hvserver})) { + $htmlReport += @" + + $(switch ($vm.ReplicationHealth){ + 'Critical' {""} + 'Warning' {""} + default {""} + } ) + + + + + + + + + +"@ + } + $htmlReport += "
VM NameStateHealthLastReplicationTimePrimaryServerReplicaServerModeRelation
$($vm.VMName)$($vm.ReplicationState)$($vm.ReplicationHealth)$($vm.LastReplicationTime)$($vm.PrimaryServer)$($vm.ReplicaServer)$($vm.ReplicationMode)$($vm.RelationshipType)
" + + #Generate VM Metric details table + $htmlReport += @" +

Server: $hvserver - VM Metric details

+ + + + + + + + + + +"@ + foreach ($vm in ($VMmetricinfo | Where-Object {$_.HVServer -eq $hvserver})) { + $htmlReport += @" + + $(switch ($vm.ReplicationHealth){ + 'Critical' {""} + 'Warning' {""} + default {""} + } ) + + + + + + + + +"@ + } + $htmlReport += "
VM NameAverage CPU Utilization (MHz)Average Memory (GB)Disk Space Used (GB)Inbound Network Traffic (GB)Outbound Network Traffic (GB)Metering data duration
$($vm.VMName)$($vm.AvgCPU)$($vm.AvgRAM)$($vm.TotalDisk)$($vm.InboundNWTraffic)$($vm.OutboundNWTraffic)$($vm.MeteringDuration)
" + } + $htmlReport += @" +

This is an automated report.

+ + +"@ + + #Send email with the generated report + $reportstatus = "OK" + switch ($htmlReport) { + { $_ -like "*bgcolor='Red'*" } { $reportstatus = "ERROR"; break } + { $_ -like "*bgcolor='Yellow'*" } { $reportstatus = "WARNING"; break } + } + if ($reportstatus -ne "OK") { + $subject = "$($reportstatus): $STR_EmailSubject" + } + else { + $subject = "$STR_EmailSubject" + } + SendMailv2 -To $STR_Receivers -Subject $subject -Body $htmlReport +#endregion