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
+
+
+ | VM Name |
+ State |
+ Operational Status |
+ Up Time |
+
+"@
+ 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 {"
"}
+ } )
+ | $($vm.VMName) |
+ $($vm.State) |
+ $($vm.Status) |
+ $($vm.UpTime) |
+
+"@
+ }
+ $htmlReport += "
"
+
+ #Generate VM Replication Health table
+ $htmlReport += @"
+ Server: $hvserver - VM Replication Health
+
+
+ | VM Name |
+ State |
+ Health |
+ LastReplicationTime |
+ PrimaryServer |
+ ReplicaServer |
+ Mode |
+ Relation |
+
+"@
+ foreach ($vm in ($VMreplinfo | Where-Object {$_.HVServer -eq $hvserver})) {
+ $htmlReport += @"
+
+ $(switch ($vm.ReplicationHealth){
+ 'Critical' {""}
+ 'Warning' {"
"}
+ default {"
"}
+ } )
+ | $($vm.VMName) |
+ $($vm.ReplicationState) |
+ $($vm.ReplicationHealth) |
+ $($vm.LastReplicationTime) |
+ $($vm.PrimaryServer) |
+ $($vm.ReplicaServer) |
+ $($vm.ReplicationMode) |
+ $($vm.RelationshipType) |
+
+"@
+ }
+ $htmlReport += "
"
+
+ #Generate VM Metric details table
+ $htmlReport += @"
+ Server: $hvserver - VM Metric details
+
+
+ | VM Name |
+ Average CPU Utilization (MHz) |
+ Average Memory (GB) |
+ Disk Space Used (GB) |
+ Inbound Network Traffic (GB) |
+ Outbound Network Traffic (GB) |
+ Metering data duration |
+
+"@
+ foreach ($vm in ($VMmetricinfo | Where-Object {$_.HVServer -eq $hvserver})) {
+ $htmlReport += @"
+
+ $(switch ($vm.ReplicationHealth){
+ 'Critical' {""}
+ 'Warning' {"
"}
+ default {"
"}
+ } )
+ | $($vm.VMName) |
+ $($vm.AvgCPU) |
+ $($vm.AvgRAM) |
+ $($vm.TotalDisk) |
+ $($vm.InboundNWTraffic) |
+ $($vm.OutboundNWTraffic) |
+ $($vm.MeteringDuration) |
+
+"@
+ }
+ $htmlReport += "
"
+ }
+ $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