The Powershell thread

The_Librarian

Another MyBB
Super Moderator
Joined
Nov 20, 2015
Messages
41,546
Reaction score
21,125
Location
Dark room in Adventure. Grues abound.
Let's al make our lives a bit easier here. Sometimes you need a specific powershell script, or you have coded your own (monstrous/beautiful) code and want to post it and have bragging rights.

Well, here we are.

We are using HyperV with replication, and have found that sometimes replication will stop, but in the majority of cases, after a power failure and unexpected server shutdown.

I coded this little script, it will look for VM's that are replicated on Hyper-V VM's and check their replication status, and give feedback accordingly.

Any VM that have a failed replication status will be displayed, and a restart of replication will be attempted.

Suggestions are welcome.

Code:
# Powershell Replication Health Script v2.00
# Written by Ook 02-12-2021
#
# SYNOPSIS 
# This Powershell script will check through all the listed HyperV hosts for virtual machines
# with a replication status of Normal, Warning or Failed. 
#
# Normal VM's will be listed in Green text. 
#
# VM's whose replication was stopped manually, or have a warning flag set,
# will be displayed in Yellow, but will not be restarted. 
#
# VM's with a critical status, where replication have stopped or broken, 
# will be displayed in Red text, and the program will attempt to restart these. 
#
# The program will also restart only the Primary (master) VM's and not
# any Replica VM. 
#
#Clears the screen
Clear-Host -ForegroundColor White -BackgroundColor Black

#Edit the hosts below, change node1 and node2 to the names of your hosts. Do not use IP addresses. 
$Hosts = "Labmaster","Labslave"

# Loops for each host listed above, and display which host it is on the screen
forEach ($server in $hosts)
{
Write-Host "----------------------------------"
Write-Host "Hyper-V Host : ", $server 
Write-Host "----------------------------------"

# Here, we are invoking the Get-VMReplication CMDlet on the specified server,
# and get the information we require. We loop three times, once for Normal, once for Warning, 
# and finally for Critical where replication have stopped. 

Invoke-Command -ComputerName $Server {
$NormalReplicas = Get-VMReplication | Where {$_.Health -eq 'Normal' -and $_.Mode -eq 'Primary'}

ForEach ($VM in $NormalReplicas)
{
$vmname = $VM.Name

Write-Host "Virtual Machine : ", $VMName -ForegroundColor Green -BackgroundColor Black

Write-Host "----------------------------------" 

}

}

Invoke-Command -ComputerName $Server {
$WarningReplicas = Get-VMReplication | Where{$_.Health -eq 'Warning' -and $_.Mode -eq 'Primary'}

ForEach ($VM in $WarningReplicas)
{
$vmname = $VM.Name

Write-Host "Virtual Machine : ", $VMName -ForegroundColor Yellow -BackgroundColor Black

Write-Host "----------------------------------" 

}

}


Invoke-Command -ComputerName $Server {
$FailedReplicas = Get-VMReplication | Where{$_.Health -eq 'Critical' -and $_.Mode -eq 'Primary'}

ForEach ($VM in $FailedReplicas)
{
$vmname = $VM.Name

Write-Host "Virtual Machine : ", $VMName -ForegroundColor Red -BackgroundColor Black
Write-Host "----------------------------------"

# In order to prevent the command from trying to resume replicas and generating an error, we 
# filter only for Primary VM's. 
Resume-VMReplication $VMName -Resynchronize | where{$_.Mode -eq 'Primary'}

}

}

}
 
An upgrade to the above.

Added email functionality, and rewrote it.

Code:
# Powershell Replication Check Script v2.05
# Written by Ook 08-12-2021
#
# SYNOPSIS
# This Powershell script will check through all the listed HyperV hosts for virtual machines
# with a replication status of Normal, Warning or Critical.
#
# Normal VM's will be listed in Green text.
#
# VM's whose replication was stopped manually, or have a warning flag set,
# will be displayed in Yellow, but will not be restarted.
#
# VM's with a critical status, where replication have stopped or broken,
# will be displayed in Red text, and the program will attempt to restart these.
#
# The program will also restart only the Primary (master) VM's and not
# any Replica VM.
#
# An email will be sent upon completion.
#
 
# PREREQUISITES
# 1. A folder C:\Reports must exist for the script to dump its report in
# 2. This script can be run from the PDC to which the HyperV hosts have been joined
# 3. A domainless HyperV replication setup is NOT supported.
 
# Clears the screen
cls
 
# Sets the report path and other report variables
$reportpath = "c:\reports\ReplicationsReport $(get-date -f yyy-MM-dd).htm"
 
if((test-path $reportpath) -like $false)
{
new-item $reportpath -type file
}
 
$report = $reportpath
 
Clear-Content $report
 
# Formats report document
 
Add-Content $report "<html>"
Add-Content $report "<head>"
Add-Content $report "<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>"
Add-Content $report '<title>HyperV Status Report</title>'
add-content $report '<STYLE TYPE="text/css">'
add-content $report  "<!--"
add-content $report  "td {"
add-content $report  "font-family: Tahoma;"
add-content $report  "font-size: 11px;"
add-content $report  "border-top: 1px solid #999999;"
add-content $report  "border-right: 1px solid #999999;"
add-content $report  "border-bottom: 1px solid #999999;"
add-content $report  "border-left: 1px solid #999999;"
add-content $report  "padding-top: 0px;"
add-content $report  "padding-right: 0px;"
add-content $report  "padding-bottom: 0px;"
add-content $report  "padding-left: 0px;"
add-content $report  "}"
add-content $report  "body {"
add-content $report  "margin-left: 5px;"
add-content $report  "margin-top: 5px;"
add-content $report  "margin-right: 0px;"
add-content $report  "margin-bottom: 10px;"
add-content $report  ""
add-content $report  "table {"
add-content $report  "border: thin solid #000000;"
add-content $report  "}"
add-content $report  "-->"
add-content $report  "</style>"
Add-Content $report "</head>"
Add-Content $report "<body>"
add-content $report  "<table width='100%'>"
add-content $report  "<tr bgcolor='Lavender'>"
add-content $report  "<td colspan='7' height='25' align='center'>"
add-content $report  "<font face='tahoma' color='#003399' size='4'><strong>HyperV Health Replication Check</strong></font>"
add-content $report  "</td>"
add-content $report  "</tr>"
add-content $report  "</table>"
 
# Here you can enumerate the HyperV hosts. You can add more hosts if you require,
# as long as they all are on the same domain
$Hosts = "PrimaryHost","ReplicaHost","AnotherHost"
 
# Main Loop starts
forEach ($server in $hosts)
{
 
Write-Host "-----------------------------"
Write-Host "HyperV Host : ", $server
Write-Host "-----------------------------"
 
Add-Content $report "<B>------------------------------------------------</B><br>"
Add-Content $report "<B>HyperV Host : $server </B><br>"
Add-Content $report "<B>------------------------------------------------</B><br>"
 
 
# Loop for normal "no worries" VM's start here
$replica = get-vmreplication -ComputerName $server | where {$_.Health -eq 'Normal' -and $_.Mode -eq 'Primary'}
 
forEach ($vm in $replica)
{
$vmname = $VM.Name
 
Write-Host "Virtual Machine : ", $VMName -ForegroundColor Green -BackgroundColor Black
Write-Host "----------------------------------"
 
 
 
Add-Content $report "Virtual Machine : $vmname is OK<br>"
Add-Content $report "------------------------------------------------<br>"
 
} # ends normal loop
 
# List all VM's with WARNING status
$replica = get-vmreplication -ComputerName $server | where {$_.Health -eq 'Warning' -and $_.Mode -eq 'Primary'}
 
forEach ($vm in $replica)
{
$vmname = $VM.Name
 
Write-Host "Virtual Machine Warning : ", $VMName -ForegroundColor Yellow -BackgroundColor Black
Write-Host "----------------------------------"
 
Add-Content $report "Virtual Machine : $vmname is in WARNING status<br>"
Add-Content $report "------------------------------------------------<br>"
 
} # end warning loop
 
 
# List all VM's with CRITICAL status
$replica = get-vmreplication -ComputerName $server | where {$_.Health -eq 'Critical' -and $_.Mode -eq 'Primary'}
 
forEach ($vm in $replica)
{
$vmname = $VM.Name
 
Write-Host "Virtual Machine Warning : ", $VMName -ForegroundColor Red -BackgroundColor Black
Write-Host "----------------------------------"
 
Add-Content $report "Virtual Machine : $vmname is in CRITICAL status<br>"
Add-Content $report "*** CALL SUPPORT ***<br>"
Add-Content $report "------------------------------------------------<br>"
 
Write-Host "Attempting to fix issue automatically..." -ForegroundColor Red -BackgroundColor Black
 
Add-Content $report "Trying to fix issue with Virtual Machine : $vmname<br>"
Add-Content $report "------------------------------------------------<br>"
 
Resume-VMReplication $server $VMName -Resynchronize | where{$_.Mode -eq 'Primary'}
if($?)
{
    Write-Host "Automatic repair succeeded..." -ForegroundColor Green -BackgroundColor Black
 
    Add-Content $report "Issue with Virtual Machine : $vmname has been resolved<br>"
    Add-Content $report "------------------------------------------------<br>"
 
}
else
{
 
    Write-Host "Automatic repair FAILED..." -ForegroundColor Red -BackgroundColor Black
 
    Add-Content $report "Issue with Virtual Machine : $vmname have NOT been fixed<br>"
    Add-Content $report "Call Support Urgently<br>"
    Add-Content $report "------------------------------------------------<br>"
 
 
}
 
} # ends critical loop
 
} # ends main loop
 
Write-Host "Run finished..."
 
Add-Content $report "Run finished..."
 
# Email support if there is an issue
forEach ($server in $hosts)
{
$result = get-vmreplication -ComputerName $server | where {$_.Health -eq 'Critical' -and $_.Mode -eq 'Primary'}
if ($result.health -eq 'Critical')
{
# email the file!
 
$smtphost = "my.smtp.host"
$from = "[email protected]"
$to = "[email protected]"
$timeout = "60"
$subject = "HyperV Replication Urgent Issue"
$body = Get-Content $report
$smtp= New-Object System.Net.Mail.SmtpClient $smtphost
$msg = New-Object System.Net.Mail.MailMessage $from, $to, $subject, $body
$msg.isBodyhtml = $true
$smtp.send($msg)
break
#break added to prevent mass duplicate mailing
 
}
 
Now for WSUS

Create three folders on your C: drive

C:\Reports
C:\Scripts
C:\sqlscrips

Drop this into c:\scripts\wsuscleanup.ps1

Code:
#Run this script with administrative privileges

$reportpath = "C:\reports\wsuscleanup $(get-date -f yyy-MM-dd).htm"

if((test-path $reportpath) -like $false)
{
new-item $reportpath -type file
}

$report = $reportpath

Clear-Content $report

# Formats report document
Add-Content $report "<html>"
Add-Content $report "<head>"
Add-Content $report "<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>"
Add-Content $report '<title>WSUS cleanup script</title>'
add-content $report '<STYLE TYPE="text/css">'
add-content $report  "<!--"
add-content $report  "td {"
add-content $report  "font-family: Tahoma;"
add-content $report  "font-size: 11px;"
add-content $report  "border-top: 1px solid #999999;"
add-content $report  "border-right: 1px solid #999999;"
add-content $report  "border-bottom: 1px solid #999999;"
add-content $report  "border-left: 1px solid #999999;"
add-content $report  "padding-top: 0px;"
add-content $report  "padding-right: 0px;"
add-content $report  "padding-bottom: 0px;"
add-content $report  "padding-left: 0px;"
add-content $report  "}"
add-content $report  "body {"
add-content $report  "margin-left: 5px;"
add-content $report  "margin-top: 5px;"
add-content $report  "margin-right: 0px;"
add-content $report  "margin-bottom: 10px;"
add-content $report  ""
# add-content $report  "table {"
# add-content $report  "border: thin solid #000000;"
# add-content $report  "}"
add-content $report  "-->"
add-content $report  "</style>"
Add-Content $report "</head>"
Add-Content $report "<body>"
add-content $report  "<table width='100%'>"
add-content $report  "<tr bgcolor='LightBlue'>"
add-content $report  "<td colspan='7' height='25' align='center'>"
add-content $report  "<font face='tahoma' color='#003399' size='4'><strong>WSUS cleanup script</strong></font>"
add-content $report  "</td>"
add-content $report  "</tr>"
add-content $report  "</table>"
 
$action = Get-WsusServer "esawsus01" -Port 8530 | Invoke-WsusServerCleanup -CleanupObsoleteUpdates
if($?)
{
    add-content $report  "<table width='25%'>"
    Add-Content $report  "<tr bgcolor='LightGreen'>"
    add-content $report  "<td colspan='7' height='25' align='centre'>"
    add-content $report  "<font face='tahoma' color='#003399' size='2'><strong>$action</strong></font>"
    add-content $report  "</td>"
    add-content $report  "</tr>"
    add-content $report  "</table>"
}
else
{
    add-content $report  "<table width='25%'>"
    Add-Content $report  "<tr bgcolor='Red'>"
    add-content $report  "<td colspan='7' height='25' align='centre'>"
    add-content $report  "<font face='tahoma' color='#003399' size='2'><strong>Obsolete updates cleanup failed</strong></font>"
    add-content $report  "</td>"
    add-content $report  "</tr>"
    add-content $report  "</table>"
}
 
$action = Get-WsusServer "esawsus01" -Port 8530 | Invoke-WsusServerCleanup -CleanupObsoleteComputers
if($?)
{
    add-content $report  "<table width='25%'>"
    Add-Content $report  "<tr bgcolor='LightGreen'>"
    add-content $report  "<td colspan='7' height='25' align='centre'>"
    add-content $report  "<font face='tahoma' color='#003399' size='2'><strong>$action</strong></font>"
    add-content $report  "</td>"
    add-content $report  "</tr>"
    add-content $report  "</table>"
}
else
{
    add-content $report  "<table width='25%'>"
    Add-Content $report  "<tr bgcolor='Red'>"
    add-content $report  "<td colspan='7' height='25' align='centre'>"
    add-content $report  "<font face='tahoma' color='#003399' size='2'><strong>Obsolete computers cleanup failed</strong></font>"
    add-content $report  "</td>"
    add-content $report  "</tr>"
    add-content $report  "</table>"
}
 
$action = Get-WsusServer "esawsus01" -Port 8530 | Invoke-WsusServerCleanup -CleanupUnneededContentFiles
if($?)
{
    add-content $report  "<table width='25%'>"
    Add-Content $report  "<tr bgcolor='LightGreen'>"
    add-content $report  "<td colspan='7' height='25' align='centre'>"
    add-content $report  "<font face='tahoma' color='#003399' size='2'><strong>$action</strong></font>"
    add-content $report  "</td>"
    add-content $report  "</tr>"
    add-content $report  "</table>"
}
else
{
    add-content $report  "<table width='25%'>"
    Add-Content $report  "<tr bgcolor='Red'>"
    add-content $report  "<td colspan='7' height='25' align='centre'>"
    add-content $report  "<font face='tahoma' color='#003399' size='2'><strong>Obsolete contents cleanup failed</strong></font>"
    add-content $report  "</td>"
    add-content $report  "</tr>"
    add-content $report  "</table>"
}
 
$action = Get-WsusServer "esawsus01" -Port 8530 | Invoke-WsusServerCleanup -DeclineSupersededUpdates
if($?)
{
    add-content $report  "<table width='25%'>"
    Add-Content $report  "<tr bgcolor='LightGreen'>"
    add-content $report  "<td colspan='7' height='25' align='centre'>"
    add-content $report  "<font face='tahoma' color='#003399' size='2'><strong>$action</strong></font>"
    add-content $report  "</td>"
    add-content $report  "</tr>"
    add-content $report  "</table>"
}
else
{
    add-content $report  "<table width='25%'>"
    Add-Content $report  "<tr bgcolor='Red'>"
    add-content $report  "<td colspan='7' height='25' align='centre'>"
    add-content $report  "<font face='tahoma' color='#003399' size='2'><strong>Obsolete updates cleanup failed</strong></font>"
    add-content $report  "</td>"
    add-content $report  "</tr>"
    add-content $report  "</table>"
}
 
$action = Get-WsusServer "esawsus01" -Port 8530 | Invoke-WsusServerCleanup -DeclineExpiredUpdates
if($?)
{
    add-content $report  "<table width='25%'>"
    Add-Content $report  "<tr bgcolor='LightGreen'>"
    add-content $report  "<td colspan='7' height='25' align='centre'>"
    add-content $report  "<font face='tahoma' color='#003399' size='2'><strong>$action</strong></font>"
    add-content $report  "</td>"
    add-content $report  "</tr>"
    add-content $report  "</table>"
}
else
{
    add-content $report  "<table width='25%'>"
    Add-Content $report  "<tr bgcolor='Red'>"
    add-content $report  "<td colspan='7' height='25' align='centre'>"
    add-content $report  "<font face='tahoma' color='#003399' size='2'><strong>Expired updates cleanup failed</strong></font>"
    add-content $report  "</td>"
    add-content $report  "</tr>"
    add-content $report  "</table>"
}
 
$action = Get-WsusServer "esawsus01" -Port 8530 | Invoke-WsusServerCleanup -CompressUpdates
if($?)
{
    add-content $report  "<table width='25%'>"
    Add-Content $report  "<tr bgcolor='LightGreen'>"
    add-content $report  "<td colspan='7' height='25' align='centre'>"
    add-content $report  "<font face='tahoma' color='#003399' size='2'><strong>$action</strong></font>"
    add-content $report  "</td>"
    add-content $report  "</tr>"
    add-content $report  "</table>"
}
else
{
    add-content $report  "<table width='25%'>"
    Add-Content $report  "<tr bgcolor='Red'>"
    add-content $report  "<td colspan='7' height='25' align='centre'>"
    add-content $report  "<font face='tahoma' color='#003399' size='2'><strong>Compress updates cleanup failed</strong></font>"
    add-content $report  "</td>"
    add-content $report  "</tr>"
    add-content $report  "</table>"
}
 
# cleans up number of synchronizations
# you will need to adjust the ServerInstance if you use MSSQL server instead of Windows Internal Database.
# This script assumes WID is used as database.
$ScriptPath = "c:\sqlscripts"
    Invoke-Sqlcmd -ServerInstance "\\.\pipe\microsoft##WID\tsql\query" -InputFile "$scriptPath\delsync.sql" -verbose -OutputSqlErrors $true
    add-content $report  "<table width='25%'>"
    Add-Content $report  "<tr bgcolor='LightGreen'>"
    add-content $report  "<td colspan='7' height='25' align='centre'>"
    add-content $report  "<font face='tahoma' color='#003399' size='2'><strong>Synchronizations count reset</strong></font>"
    add-content $report  "</td>"
    add-content $report  "</tr>"
    add-content $report  "</table>"
 
# emails the report
# you can use any smtp method you'd prefer, just get the wee beastie to email $report.
# or you can SFTP $report to a server of your choice
# or you can open a web browser instead
# totally your choice
 
$smtphost = "your.smtp.host"
$from = "[email protected]"
$to = "[email protected]"
$timeout = "60"
$subject = "WSUS Cleanup"
$body = Get-Content $report
$smtp= New-Object System.Net.Mail.SmtpClient $smtphost
$msg = New-Object System.Net.Mail.MailMessage $from, $to, $subject, $body
$msg.isBodyhtml = $true
$smtp.send($msg)

Drop this into c:\sqlscrips\delsync.sql

Code:
USE SUSDB;
GO
DELETE FROM tbEventInstance WHERE EventNamespaceID = '2' AND EVENTID IN ('381', '382', '384', '386', '387', '389')

Run the main script manually, and check for any errors. If no errors, then you can automate this task with task scheduler.

NOTE : If you have issues cleaning out a specific WSUS record, and it bombs out, the recommendation is to uninstall WSUS completely, and do a fresh install, and then to run this script on a regular (daily/weekly) basis.

PROTIP : I have found that if you do synchronization on a new WSUS server, do not choose a plethora of products and classifications. Rather, choose one product and classification (eg Windows - all Windows 10 stuff only), let WSUS complete synchronization and update, set up a rule for that specific product, and when that's finished, select another product (eg Server 2016 - all Server 2016 classifications).
This way you will not have an unhappy WSUS server flaking out due to the amount of updates it have to process.
 
Last edited:
PS : if the n00b programming style bothers you, rewrite them if you want. I did it specifically to assist other n00bs with their powershell scripting so that they can get to understand the concepts before getting to grips with something more complex.
 
Right, stuck with this one, if anybody can help? :

Write-Host 'Report for VM',$vmname

Get-VM $vmname | Select-Object -Property Name, Generation

Get-VM $vmname | Select-Object VMId | Get-VHD | select vhdtype,path,@{label='Size(Gb)';expression={$_.size/1gb -as [int]}}

Basically what happens is only the first Get-VM command get executed, not the second one.

You can swap them around, only the first Get-VM command is executed.

I'm like wtf is going on here... it's not supposed to do that.
 
Top
Sign up to the MyBroadband newsletter
X