377 lines
15 KiB
PowerShell
377 lines
15 KiB
PowerShell
<#
|
|
.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 <servicedesk@contoso.com>"
|
|
$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 = @"
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="content-type" content="text/html; charset=windows-1252">
|
|
<style>
|
|
body{
|
|
font-family: 'Verdana', sans-serif;
|
|
font-size:9pt;
|
|
}
|
|
h2{
|
|
font-size: 12pt; /* sets the font size to 18 points */
|
|
font-weight: bold; /* sets the font weight to bold */
|
|
margin-bottom: 10px; /* adds a margin of 10 pixels below the heading */
|
|
}
|
|
h3{
|
|
font-size: 10pt; /* sets the font size to 18 points */
|
|
font-weight: bold; /* sets the font weight to bold */
|
|
}
|
|
tbody{
|
|
font-family: 'Verdana', sans-serif;
|
|
font-size:9pt;
|
|
}
|
|
th{
|
|
font-weight: bold; /* sets the font weight to bold */
|
|
padding-right:5px;
|
|
}
|
|
td{
|
|
padding-right:5px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h2>Hyper-V Status Report - $(Get-Date -Format "dd-MM-yyyy - HH:mm")</h2>
|
|
Script version: $Version <br/>
|
|
Checked Servers: $STR_HVServers <br/>
|
|
$(if ($STR_HVServersError){
|
|
"Unable to connect to servers: $STR_HVServersError"
|
|
})</p>
|
|
"@
|
|
foreach ($hvserver in $HyperVServers) {
|
|
#Generate VM status table
|
|
$htmlReport += @"
|
|
<h3>Server: $hvserver - VM Status</h3>
|
|
<table border='1'>
|
|
<tr>
|
|
<th>VM Name</th>
|
|
<th>State</th>
|
|
<th>Operational Status</th>
|
|
<th>Up Time</th>
|
|
</tr>
|
|
"@
|
|
foreach ($vm in ($VMinfo | Where-Object {$_.HVServer -eq $hvserver})) {
|
|
$htmlReport += @"
|
|
|
|
$(switch ($vm){
|
|
{($_.State -eq 'OFF') -and ($_.VMName -notlike "*replica*" -and $_.VMName -notlike "*maintenance*")} {"<tr bgcolor='Red'>"}
|
|
{($_.State -eq 'Saved') -and ($_.VMName -notlike "*replica*" -and $_.VMName -notlike "*maintenance*")} {"<tr bgcolor='Yellow'>"}
|
|
default {"<tr>"}
|
|
} )
|
|
<td>$($vm.VMName)</td>
|
|
<td>$($vm.State)</td>
|
|
<td>$($vm.Status)</td>
|
|
<td>$($vm.UpTime)</td>
|
|
</tr>
|
|
"@
|
|
}
|
|
$htmlReport += "</table>"
|
|
|
|
#Generate VM Replication Health table
|
|
$htmlReport += @"
|
|
<h3>Server: $hvserver - VM Replication Health</h3>
|
|
<table border='1'>
|
|
<tr>
|
|
<th>VM Name</th>
|
|
<th>State</th>
|
|
<th>Health</th>
|
|
<th>LastReplicationTime</th>
|
|
<th>PrimaryServer</th>
|
|
<th>ReplicaServer</th>
|
|
<th>Mode</th>
|
|
<th>Relation</th>
|
|
</tr>
|
|
"@
|
|
foreach ($vm in ($VMreplinfo | Where-Object {$_.HVServer -eq $hvserver})) {
|
|
$htmlReport += @"
|
|
|
|
$(switch ($vm.ReplicationHealth){
|
|
'Critical' {"<tr bgcolor='Red'>"}
|
|
'Warning' {"<tr bgcolor='Yellow'>"}
|
|
default {"<tr>"}
|
|
} )
|
|
<td>$($vm.VMName)</td>
|
|
<td>$($vm.ReplicationState)</td>
|
|
<td>$($vm.ReplicationHealth)</td>
|
|
<td>$($vm.LastReplicationTime)</td>
|
|
<td>$($vm.PrimaryServer)</td>
|
|
<td>$($vm.ReplicaServer)</td>
|
|
<td>$($vm.ReplicationMode)</td>
|
|
<td>$($vm.RelationshipType)</td>
|
|
</tr>
|
|
"@
|
|
}
|
|
$htmlReport += "</table>"
|
|
|
|
#Generate VM Metric details table
|
|
$htmlReport += @"
|
|
<h3>Server: $hvserver - VM Metric details</h3>
|
|
<table border='1'>
|
|
<tr>
|
|
<th>VM Name</th>
|
|
<th>Average CPU Utilization (MHz)</th>
|
|
<th>Average Memory (GB)</th>
|
|
<th>Disk Space Used (GB)</th>
|
|
<th>Inbound Network Traffic (GB)</th>
|
|
<th>Outbound Network Traffic (GB)</th>
|
|
<th>Metering data duration</th>
|
|
</tr>
|
|
"@
|
|
foreach ($vm in ($VMmetricinfo | Where-Object {$_.HVServer -eq $hvserver})) {
|
|
$htmlReport += @"
|
|
|
|
$(switch ($vm.ReplicationHealth){
|
|
'Critical' {"<tr bgcolor='Red'>"}
|
|
'Warning' {"<tr bgcolor='Yellow'>"}
|
|
default {"<tr>"}
|
|
} )
|
|
<td>$($vm.VMName)</td>
|
|
<td>$($vm.AvgCPU)</td>
|
|
<td>$($vm.AvgRAM)</td>
|
|
<td>$($vm.TotalDisk)</td>
|
|
<td>$($vm.InboundNWTraffic)</td>
|
|
<td>$($vm.OutboundNWTraffic)</td>
|
|
<td>$($vm.MeteringDuration)</td>
|
|
</tr>
|
|
"@
|
|
}
|
|
$htmlReport += "</table>"
|
|
}
|
|
$htmlReport += @"
|
|
<p><i>This is an automated report.</i></p>
|
|
</body>
|
|
</html>
|
|
"@
|
|
|
|
#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
|