Monitoring Windows Updates

Welcome Forums General PowerShell Q&A Monitoring Windows Updates

This topic contains 11 replies, has 5 voices, and was last updated by

 
Participant
4 days, 5 hours ago.

  • Author
    Posts
  • #127113

    Participant
    Points: 37
    Rank: Member

    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.

    https://gallery.technet.microsoft.com/scriptcenter/Windows-Updates-and-684c355c

    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.

  • #127152

    Participant
    Points: 62
    Team Member
    Rank: Member

    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 

    To test network connectivity:

    test-netconnection -computerName  -port 5985

     

  • #127154

    Participant
    Points: 815
    Helping Hand
    Rank: Major Contributor

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

  • #127179

    Participant
    Points: 37
    Rank: Member

    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.

    1
    enter-pssession computerName
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    To test network connectivity:

    1
    test-netconnection computerName port 5985
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    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 :

    
    #########################################################
    # #
    # 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 = @"
    
    

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

    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 .



    $tableFragment "@ ######################################################### # 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) }
  • #127338

    Participant
    Points: 37
    Rank: Member

    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.

    1
    enter-pssession computerName
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    To test network connectivity:

    1
    test-netconnection computerName port 5985
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    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 :

    
    
    #########################################################
    # #
    # 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 = @"
    
    

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

    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 .



    $tableFragment "@ ######################################################### # 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) }
  • #127457

    Keymaster
    Points: 74
    Team Member
    Rank: Member

    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!

  • #127737

    Participant
    Points: 37
    Rank: Member

    I will read this article, thank you !

  • #127816

    Participant
    Points: 37
    Rank: Member

    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.

  • #127911

    Participant
    Points: 312
    Helping Hand
    Rank: Contributor

    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.

  • #128206

    Participant
    Points: 37
    Rank: Member

    The cmdlets "Test-NetConnection" return

    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>

    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.

  • #128620

    Participant
    Points: 37
    Rank: Member

    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)

    
    
    $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
    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

  • #128622

    Participant
    Points: 37
    Rank: Member

    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

You must be logged in to reply to this topic.