Monitoring Windows Updates

Hello,

I am a student in a work-study program as a system administrator and I am a beginner on PowerShell.

I am working on a WSUS Workgroup deployment project (no GPO) with the help of WSUSClientManager.

I need to query the remote registry to see if new updates are available and then send it by email to the client in HTML format.

I was inspired by a Technet script and modified it to suit my needs.

It works, but when I run it on the WSUS server, either I have a network path problem or access denied.

First of all, is it feasible?
Is it a problem of credentials?

I configured WinRM and authorized the WSUS server as a trusted host.

I don’t know where to go from here.
Thank you for your help.

Without seeing the actual code, and from the description (i.e. “workgroup” deployment, therefore making an assumption that the clients are not in a domain), the access denied messages are probably a credentials problem. Network path problems could be another thing altogether. I assume, again, you’re connecting to multiple servers and getting different results on each.

To test credentials, run this as the user you are running the script with. If it fails (access denied) you could have a credentials or permission problem.

enter-pssession -computerName <computerName>

To test network connectivity:

test-netconnection -computerName <computerName> -port 5985

 

Can you share the code you tried with the error you get

[quote quote=127152]Without seeing the actual code, and from the description (i.e. “workgroup” deployment, therefore making an assumption that the clients are not in a domain), the access denied messages are probably a credentials problem. Network path problems could be another thing altogether. I assume, again, you’re connecting to multiple servers and getting different results on each.

To test credentials, run this as the user you are running the script with. If it fails (access denied) you could have a credentials or permission problem.

<textarea class=“ace_text-input” style=“opacity: 0; height: 17.6px; width: 6.59775px; left: 44px; top: 0px;” spellcheck=“false” wrap=“off”></textarea>

1
enter-pssession -computerName
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
To test network connectivity:

<textarea class=“ace_text-input” style=“opacity: 0; height: 17.6px; width: 6.59775px; left: 44px; top: 0px;” spellcheck=“false” wrap=“off”></textarea>

1
test-netconnection -computerName -port 5985
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
[/quote] Thank you for your help.

Both tests send me an error message.

“Enter-PSSession: The connection to the remote server -10.254.13.29 failed with the following error message: WinRM does not
cannot process the request. The following error occurred with error code 0x8009030e when using
Kerberos authentication: A specified login does not exist. It may already be over.”

For the code :

[pre]

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

Monitoring Windows Updates and Pending Restarts

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

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

List of computers to be monitored

########################################################
$Servers = “10.254.xx.xx”
#$Servers = Get-Content -Path C:\Serveurs.txt

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

List of users who will receive the report

#########################################################
$mailto = “xx”

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

SMTP properties

#########################################################
$emailFrom = “xx”
$smtpServer = “93.121.xx.xx” #SMTP Server.
#$smtpUsername
#$smtpPassword

$results = foreach ($Computer in $Servers)
{
try
{
$service = Get-WmiObject Win32_Service -Filter ‘Name=“wuauserv”’ -ComputerName $Computer -Ea 0
$WUStartMode = $service.StartMode
$WUState = $service.State
$WUStatus = $service.Status
}

try{
if (Test-Connection -ComputerName $Computer -Count 1 -Quiet)
{
#check if the server is the same where this script is running
if($Computer -eq “$env:computername.$env:userdnsdomain”)
{
$UpdateSession = New-Object -ComObject Microsoft.Update.Session
}
else { $UpdateSession = [activator]::CreateInstance([type]::GetTypeFromProgID(“Microsoft.Update.Session”,$Computer)) }
$UpdateSearcher = $UpdateSession.CreateUpdateSearcher()
$SearchResult = $UpdateSearcher.Search(“IsAssigned=1 and IsHidden=0 and IsInstalled=0”)
$Critical = $SearchResult.updates | where { $.MsrcSeverity -eq “Critical” }
$important = $SearchResult.updates | where { $
.MsrcSeverity -eq “Important” }
$other = $SearchResult.updates | where { $_.MsrcSeverity -eq $null }

Get windows updates counters

$totalUpdates = $($SearchResult.updates.count)
$totalCriticalUp = $($Critical.count)
$totalImportantUp = $($Important.count)

if($totalUpdates -gt 0)
{
$updatesToInstall = $true
}
else { $updatesToInstall = $false }
}
else
{

if cannot connected to the server the updates are listed as not defined

$totalUpdates = “nd”
$totalCriticalUp = “nd”
$totalImportantUp = “nd”
}
}
catch
{

if an error occurs the updates are listed as not defined

Write-Warning “$Computer`: $_”
$totalUpdates = “nd”
$totalCriticalUp = “nd”
$totalImportantUp = “nd”
$updatesToInstall = $false
}

Querying WMI for build version

$WMI_OS = Get-WmiObject -Class Win32_OperatingSystem -Property BuildNumber, CSName -ComputerName $Computer -Authentication “Basic” PacketPrivacy -Impersonation Impersonate

Making registry connection to the local/remote computer

$RegCon = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]“LocalMachine”,$Computer)

If Vista/2008 & Above query the CBS Reg Key

If ($WMI_OS.BuildNumber -ge 6001)
{
$RegSubKeysCBS = $RegCon.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing").GetSubKeyNames()
$CBSRebootPend = $RegSubKeysCBS -contains “RebootPending”
}
else{
$CBSRebootPend = $false
}

Query WUAU from the registry

$RegWUAU = $RegCon.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update")
$RegSubKeysWUAU = $RegWUAU.GetSubKeyNames()
$WUAURebootReq = $RegSubKeysWUAU -contains “RebootRequired”

If($CBSRebootPend –OR $WUAURebootReq)
{
$machineNeedsRestart = $true
}
else
{
$machineNeedsRestart = $false
}

Closing registry connection

$RegCon.Close()

if($machineNeedsRestart -or $updatesToInstall -or ($WUStartMode -eq “Manual”) -or ($totalUpdates -eq “nd”))
{
New-Object PSObject -Property @{
Computer = $WMI_OS.CSName
WindowsUpdateStatus = $WUStartMode + “/” + $WUState + “/” + $WUStatus
UpdatesToInstall = $updatesToInstall
TotalOfUpdates = $totalUpdates
TotalOfCriticalUpdates = $totalCriticalUp
TotalOfImportantUpdates = $totalImportantUp
RebootPending = $machineNeedsRestart
}
}
}
Catch
{
Write-Warning “$Computer`: $_”
}
}

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

Formating result

#########################################################
$tableFragment = $results | ConvertTo-HTML -fragment

HTML Format for Output

$HTMLmessage = @"
<font color=““black”” face=““Arial”” size="“3"”>

Rapport des mises à jour en attentes et/ou d'un redémarrage en attente

<p style='font: .8em ""Lucida Grande"", Tahoma, Arial, Helvetica, sans-serif;'>Ce rapport a été généré car il y a des updates en attentes d'installation et/ou d'un reboot en attente sur ce serveur.<br /> Merci de suivre la procédure : http://doc4tme.all4it.local/doku.php/clients/a4i_it/system/majs_4cliwinrdpgwa .</p> <br><br> <style type=""text/css"">body{font: .8em ""Lucida Grande"", Tahoma, Arial, Helvetica, sans-serif;} ol{margin:0;} table{width:80%;} thead{} thead th{font-size:120%;text-align:left;} th{border-bottom:2px solid rgb(79,129,189);border-top:2px solid rgb(79,129,189);padding-bottom:10px;padding-top:10px;} tr{padding:10px 10px 10px 10px;border:none;} #middle{background-color:#900;} </style> <body BGCOLOR=""white""> $tableFragment </body> "@

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

Validation and sending email

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

Regular expression to get what’s inside of

's
$regexsubject = $HTMLmessage
$regex = [regex] '(?im)

If you have data between

's then you need to send the email
if ($regex.IsMatch($regexsubject)) {
$smtp = New-Object Net.Mail.SmtpClient -ArgumentList $smtpServer
#$smtp.credentials = New-Object System.Net.NetworkCredential($smtpUsername, $smtpPassword);
$msg = New-Object Net.Mail.MailMessage
$msg.From = $emailFrom
$msg.To.Add($mailto)
$msg.Subject = “Mises à jour et/ou reboot en attente sur $computer”
$msg.IsBodyHTML = $true
$msg.Body = $HTMLmessage
$smtp.Send($msg)
}[/pre]

[quote quote=127152]Without seeing the actual code, and from the description (i.e. “workgroup” deployment, therefore making an assumption that the clients are not in a domain), the access denied messages are probably a credentials problem. Network path problems could be another thing altogether. I assume, again, you’re connecting to multiple servers and getting different results on each.

To test credentials, run this as the user you are running the script with. If it fails (access denied) you could have a credentials or permission problem.

<textarea class="ace_text-input" style="opacity: 0; height: 17.6px; width: 6.59775px; left: 44px; top: 0px;" spellcheck="false" wrap="off"></textarea>
1
enter-pssession -computerName
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
To test network connectivity:
<textarea class="ace_text-input" style="opacity: 0; height: 17.6px; width: 6.59775px; left: 44px; top: 0px;" spellcheck="false" wrap="off"></textarea>
1
test-netconnection -computerName -port 5985
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
[/quote] I have tested it, but I receive an error message.

“Connection to the remote server -10.254.13.29 failed with
the following error message: WinRM cannot process the request. The error
occurred with error code 0x8009030e when using
Kerberos authentication: A specified login does not exist
not. It may already be over.”

For the code :
[pre]

#########################################################
# #
# Monitoring Windows Updates and Pending Restarts #
# #
#########################################################

#########################################################
# List of computers to be monitored
#########################################################
$Servers = "ip serveur WSUS"

#########################################################
# List of users who will receive the report
#########################################################
$mailto = "mail"

#########################################################
# SMTP properties
#########################################################
$emailFrom = "mailexploit"
$smtpServer = "ip serveur SMTP" #SMTP Server.
#$smtpUsername = "myUsername"
#$smtpPassword = "myPassword"

$results = foreach ($Computer in $Servers) 
{ 
try 
{ 
$service = Get-WmiObject Win32_Service -Filter 'Name="wuauserv"' -ComputerName $Computer -Ea 0
$WUStartMode = $service.StartMode
$WUState = $service.State
$WUStatus = $service.Status

try{
if (Test-Connection -ComputerName $Computer -Count 1 -Quiet)
{ 
#check if the server is the same where this script is running
if($Computer -eq "$env:computername.$env:userdnsdomain")
{
$UpdateSession = New-Object -ComObject Microsoft.Update.Session
}
else { $UpdateSession = [activator]::CreateInstance([type]::GetTypeFromProgID("Microsoft.Update.Session",$Computer)) }
$UpdateSearcher = $UpdateSession.CreateUpdateSearcher()
$SearchResult = $UpdateSearcher.Search("IsAssigned=1 and IsHidden=0 and IsInstalled=0")
$Critical = $SearchResult.updates | where { $_.MsrcSeverity -eq "Critical" }
$important = $SearchResult.updates | where { $_.MsrcSeverity -eq "Important" }
$other = $SearchResult.updates | where { $_.MsrcSeverity -eq $null }
# Get windows updates counters
$totalUpdates = $($SearchResult.updates.count)
$totalCriticalUp = $($Critical.count)
$totalImportantUp = $($Important.count)

if($totalUpdates -gt 0)
{
$updatesToInstall = $true
}
else { $updatesToInstall = $false }
}
else
{
# if cannot connected to the server the updates are listed as not defined
$totalUpdates = "nd"
$totalCriticalUp = "nd"
$totalImportantUp = "nd"
}
}
catch 
{ 
# if an error occurs the updates are listed as not defined
Write-Warning "$Computer`: $_" 
$totalUpdates = "nd"
$totalCriticalUp = "nd"
$totalImportantUp = "nd"
$updatesToInstall = $false
}

# Querying WMI for build version 
$WMI_OS = Get-WmiObject -Class Win32_OperatingSystem -Property BuildNumber, CSName -ComputerName $Computer -Authentication PacketPrivacy -Impersonation Impersonate

# Making registry connection to the local/remote computer 
$RegCon = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]"LocalMachine",$Computer) 

# If Vista/2008 & Above query the CBS Reg Key 
If ($WMI_OS.BuildNumber -ge 6001) 
{ 
$RegSubKeysCBS = $RegCon.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\").GetSubKeyNames() 
$CBSRebootPend = $RegSubKeysCBS -contains "RebootPending" 
}
else{
$CBSRebootPend = $false
}

# Query WUAU from the registry 
$RegWUAU = $RegCon.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\") 
$RegSubKeysWUAU = $RegWUAU.GetSubKeyNames() 
$WUAURebootReq = $RegSubKeysWUAU -contains "RebootRequired" 

If($CBSRebootPend –OR $WUAURebootReq)
{
$machineNeedsRestart = $true
}
else
{
$machineNeedsRestart = $false
}

# Closing registry connection 
$RegCon.Close() 

if($machineNeedsRestart -or $updatesToInstall -or ($WUStartMode -eq "Manual") -or ($totalUpdates -eq "nd"))
{
New-Object PSObject -Property @{
Computer = $WMI_OS.CSName 
WindowsUpdateStatus = $WUStartMode + "/" + $WUState + "/" + $WUStatus 
UpdatesToInstall = $updatesToInstall 
TotalOfUpdates = $totalUpdates 
TotalOfCriticalUpdates = $totalCriticalUp 
TotalOfImportantUpdates = $totalImportantUp
RebootPending = $machineNeedsRestart
}
}
}
Catch 
{ 
Write-Warning "$Computer`: $_" 
}
}

#########################################################
# Formating result
#########################################################
$tableFragment = $results | ConvertTo-HTML -fragment

# HTML Format for Output 
$HTMLmessage = @"
<font color=""black"" face=""Arial"" size=""3"">

Rapport des majs en attentes et/ou d'un redemarrage en attente

<p style='font: .8em ""Lucida Grande"", Tahoma, Arial, Helvetica, sans-serif;'>Ce rapport a été généré car il y a des updates en attentes d'installation et/ou d'un reboot en attente sur ce serveur. Merci de suivre la procédure : http://doc4tme.all4it.local/doku.php/clients/a4i_it/system/majs_4cliwinrdpgwa .</p> <br><br> <style type=""text/css"">body{font: .8em ""Lucida Grande"", Tahoma, Arial, Helvetica, sans-serif;} ol{margin:0;} table{width:80%;} thead{} thead th{font-size:120%;text-align:left;} th{border-bottom:2px solid rgb(79,129,189);border-top:2px solid rgb(79,129,189);padding-bottom:10px;padding-top:10px;} tr{padding:10px 10px 10px 10px;border:none;} #middle{background-color:#900;} </style> <body BGCOLOR=""white""> $tableFragment </body> "@ ######################################################### # Validation and sending email ######################################################### # Regular expression to get what's inside of 's $regexsubject = $HTMLmessage $regex = [regex] '(?im)' # If you have data between 's then you need to send the email if ($regex.IsMatch($regexsubject)) { $smtp = New-Object Net.Mail.SmtpClient -ArgumentList $smtpServer #$smtp.credentials = New-Object System.Net.NetworkCredential($smtpUsername, $smtpPassword); $msg = New-Object Net.Mail.MailMessage $msg.From = $emailFrom $msg.To.Add($mailto) $msg.Subject = "Update et/ou reboot en attente sur $computer" $msg.IsBodyHTML = $true $msg.Body = $HTMLmessage $smtp.Send($msg) }[/pre]

Getting remote PowerShell to work on a non domain joined machine is a bit tricky. Luckly other people have had this issue as well. Take a look at this article as it think it help guide you in the right direction.

https://4sysops.com/archives/enable-powershell-remoting-on-a-standalone-workgroup-computer/

Keep us update!

I will read this article, thank you !

I still haven’t found a solution.

Does WinRM have to be configured with the HTTPS protocol for it to work in WORKGROUP?

Can someone help me clean up the existing code?

I guess there are some things that are useless.

You stated you read the article, but what you did not say, is if you configured things as the article pointed out.
Without PSRemoting properly enabled, you cannot do this at all regardless of what they script is doing.

If you did this…

test-netconnection -computerName  -port 5985

… and got this…

"Connection to the remote server -10.254.13.29 failed with
 the following error message: WinRM cannot process the request. The error
 occurred with error code 0x8009030e when using
 Kerberos authentication: A specified login does not exist
 not. It may already be over."

PSRemoting is not enabled at all and or the target host firewall is not properly configured to allow the connection.

There are several ways to remotely connect to a target …

WinRS —

https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/winrs

WMI Class direclty or using Invoke-WmiMethod —

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/invoke-wmimethod?view=powershell-5.1

PSExec.exe (a tool from Microsoft SysInternals site) —

https://docs.microsoft.com/en-us/sysinternals/downloads/psexec

PSRemoting – using that workgroup link already provided.

Work Remotely with Windows PowerShell without using Remoting or WinRM https://technet.microsoft.com/en-us/library/ff699046.aspx

If you want to do this over HTTPS (though it does not have to be) then this…

https://blogs.technet.microsoft.com/uktechnet/2016/02/11/configuring-winrm-over-https-to-enable-powershell-remoting

… but each has prerequisites, even for just WMI and if you have not done the prerequisites, then all else is a no go.

The reason for the firewall comment is when using WMI, WMI(DCOM) is not firewall friendly, where CIM is. Which is why MS is pushing all to use CIM and why PowerShell v6 is CIM only.

So, again, make sure, you have the source and target host properly configured, before stressing yourself out over code that is not working, because it may be false negative, because the environment is not allowing it to work as designed.

[quote quote=127911][/quote]
The cmdlets “Test-NetConnection” return

[pre]PS C:\Users\Administrateur> test-netconnection -computerName 10.254.13.52 -port 5985
AVERTISSEMENT : Ping to 10.254.13.52 failed – Status: TimedOut

ComputerName : 10.254.13.52
RemoteAddress : 10.254.13.52
RemotePort : 5985
InterfaceAlias : LAN
SourceAddress : 10.254.3.43
PingSucceeded : False
PingReplyDetails (RTT) : 0 ms
TcpTestSucceeded : True

PS C:\Users\Administrateur>[/pre]

PSRemoting is enabled on the client and server as specified in the first post.

Thank you for the links.

I understand, but i followed a tutorial to the letter so normally i shouldn’t have missed any steps.

Context:

  • Clients under Windows 2008 R2
  • Server WSUS sous Windows server 2012 R2
Investigation completed:
  • transition from PowerShell to version 5.1
  • opening from port 5985 on the PFSense firewall
To do:
  • checking virtual network adapters in vCenter to make sure they are on "Private".
We look forward to hearing from you.

Hi,

I have some news, I can connect to the remote server using “Enter-PSSession” and “Invoke-Command” cmdlets, so I think that at the level of the prerequisites it is good:

  • switch to PowerShell 5.1
  • open ports 5985
  • checking vSwitches on vCenter
  • check if the services are activated (remote registry…)
  • implementation of CredSSP in workgroup for double-jumping (client and server delegation)
  • creation of a persistent session on the remote server

I tried this as a piece of code, but I get an error message. (access denied)

[pre]$cred = Get-Credential
$s = New-PSSession -ComputerName 10.254.3.43
Invoke-Command -FilePath C:\Users\llefevre\Desktop\Projets\Refonte WSUS\Powershell\Script1_Update_notif.ps1`
-ComputerName 10.254.3.43 -Credential $cred[/pre]
And I'm reading Don Jones' "Secref of powershell remoting" in the hope that i can find something.
Do you anything else ?

Best Regards,
llefevre

Hi,

I have some news, I can connect to the remote server using “Enter-PSSession” and “Invoke-Command” cmdlets, so I think that at the level of the prerequisites it is good:

  • switch to PowerShell 5.1
  • open ports 5985
  • checking vSwitches on vCenter
  • check if the services are activated (remote registry…)
  • implementation of CredSSP in workgroup for double-jumping (client and server delegation)
  • creation of a persistent session on the remote server

I tried this as a piece of code, but I get an error message. (access denied)

And I'm reading Don Jones' "Secref of powershell remoting" in the hope that i can find something.
Do you anything else ?

Best Regards,
llefevre

$cred = Get-Credential
$s = New-PSSession -ComputerName 10.254.3.43
Invoke-Command -FilePath C:\Users\llefevre\Desktop\Projets\Refonte WSUS\Powershell\Script1_Update_notif.ps1
-ComputerName 10.254.3.43 -Credential $cred