📜 ⬆️ ⬇️

Automate the installation of updates on the client machine with the elimination of erroneous updates

Once a year there is an update breaking the usual work. Windows tries to install it 3 times, and if there was a rollback 3 times, it boots without installation. Users start calling in the morning. If nothing is done, the situation will be repeated the next morning.

This is all inconvenient, so the admin will almost always do the same thing - he will turn off updates on the problem machine. And in a year or two he will start rolling updates and face the same problem.

Since The glitch of updates causes an emergency mode, eats an incredible amount of staff time for absolutely meaningless garbage, and in order to move to a smart infrastructure it was decided to try to automate this process. Along the way, a script was received that can install all updates on a freshly installed machine in automatic mode.

Immediately make a reservation - this script does not know how to repair a system that does not load at all, it can install updates itself, automatically throw in updates causing a cyclic reboot, it can manually add bad updates to the list and signal the administrator about the problems with the installation.

')

How to start it all, quick start


source for the article in one file
0. Enable the execution of scripts on your system. Set-ExecitionPolicy Unrestricted or sign scripts with your certificate.
1. copy the WUErrorreporting script to the disk of the controlled machine and add it to the task scheduler, for example, 12:30 of the day. If anyone does not know how to create tasks on a schedule, click here.
WUErrorreporting.ps1
<# 2016.05.23 Windows Update Error reporter      .           20   . Pak NV #> ############# # Variables # ############# #        [int]$DaysBefore = 5 #         $SaveLog = $false $LogPath = 'c:\WUError.txt' #   HTML [boolean]$SaveReport = $False [string]$ReportPath = 'C:\WUErrorReport.html' #  HTML  [boolean]$SendReport = $true [string]$ReportMail1 = 'admin@test.local' [string]$from = 'bot_abormot@test.local' [string]$SMTPServer = 'mail.test.local' ############################################################################# #      $Events = Get-EventLog -LogName System -EntryType Error -After (Get-Date).AddDays(-$DaysBefore) -InstanceId 20 -ErrorAction SilentlyContinue #          if ($Events.Count -eq 0) { Write-Host '   ,  ' -ForegroundColor Green Exit } #    $Log = @() #          foreach ($Event in $Events) { $regex = $Event.Message -match "KB\d+" $KB = $matches[0] $params = [ordered]@{ 'KB'=$KB #'EntryType'=$Event.EntryType 'Index'=$Event.Index 'MachineName'=$Event.MachineName 'Message'=$Event.Message 'Source'=$Event.Source 'TimeGenerated'=$Event.TimeGenerated 'TimeWritten'=$Event.TimeWritten 'UserName'=$Event.UserName } $obj = New-Object -TypeName PSObject -Property $params $Log += $obj } if ($SaveLog -eq $true) { #      Add-Content -Path $LogPath -Value '' $Log | select -ExpandProperty KB | Add-Content -Path $LogPath } #,    #################################   ##################################### Write-Verbose 'HTML fragment producing' $ClientName = $env:COMPUTERNAME $TotalErrors = $log.Count $frag1 = $Log | ConvertTo-Html -As table -Fragment -PreContent "<h2>Windows Update error report. $ClientName </h2><br><h3>Total $TotalErrors errors.</h3>" | Out-String Write-Verbose 'definiting CSS' $head = @' <style> body { background-color:#ffffff; font-family:Tahoma; font-size:12pt; } td, th { border:1px solid black; border-collapse:collapse; } th { color:white; background-color:black; } table, tr, td, th { padding: 2px; margin: 0px } table { font-family: "Lucida Sans Unicode", "Lucida Grande", Sans-Serif; font-size: 14px; border-radius: 10px; border-spacing: 0; text-align: center; } th { background: #BCEBDD; color: white; text-shadow: 0 1px 1px #2D2020; padding: 10px 20px; } th, td { border-style: solid; border-width: 0 1px 1px 0; border-color: white; } th:first-child, td:first-child { text-align: left; } th:first-child { border-top-left-radius: 10px; } th:last-child { border-top-right-radius: 10px; border-right: none; } td { padding: 10px 20px; background: #F8E391; } tr:last-child td:first-child { border-radius: 0 0 0 10px; } tr:last-child td:last-child { border-radius: 0 0 10px 0; } tr td:last-child { border-right: none; } </style> '@ $Date = Get-Date if ($SendReport -eq $true) { Write-Verbose 'SendEmail' $encoding = [System.Text.Encoding]::UTF8 $body = ConvertTo-HTML -head $head -PostContent $frag1 -PreContent "<h1>Windows Update error report. Client $ClientName. Date:$Date</h1>" | Out-String $params = @{'To'=$ReportMail1 'From'=$from 'Subject'="$ClientName. Windows Update error $Date" 'Body'=$Body 'BodyAsHTML'=$True 'SMTPServer'=$SMTPServer} Send-MailMessage @params -Encoding $encoding } 

2. Set the parameters for notifications of update errors, to do this, change the parameters
$ ReportMail1 = 'admin@test.local' to your email
$ SMTPServer = 'mail.test.local' to your mail server.

3. copy the configuration reset script to the desired machine and run it. It will create a directory with initial settings, if the directory is there will reset them to the initial state.
Rearm_Install-ClientUpdate.ps1
 <#       #> #         $Path = Split-Path ($MyInvocation.MyCommand.Path) -Parent if ((Test-Path -Path "$Path\BadUpdates") -eq $false) { New-Item -Path "$Path\BadUpdates" -ItemType Directory -Force } #    $LastInstalledUpdatesFile = "$Path\BadUpdates\LastInstalledUpdates.txt" #     $BadUpdatesFile = "$Path\BadUpdates\BadUpdates.txt" # FastInstaller $KAFUFile = "$Path\BadUpdates\KAFU.txt" #      $KAFUDeltaFile = "$Path\BadUpdates\KAFUDelta.txt" # Fastinstaller watchdog $KAFUWatchDogFile = "$Path\BadUpdates\KAFUWatchDog.txt" # Fastinstaller update installer watchdog $KAFUWatchDog2File = "$Path\BadUpdates\KAFUWatchDog2.txt" # SlowInstaller $KASUFile = "$Path\BadUpdates\KASU.txt" #   $WorkLogFile = "$Path\BadUpdates\log.txt" #   $KAFile = "$Path\BadUpdates\KA.txt" Set-Content $LastInstalledUpdatesFile -Value '' Set-Content $KAFUFile -Value 0 Set-Content $KAFUDeltaFile -Value 5 Set-Content $KAFUWatchDogFile -Value 0 Set-Content $KAFUWatchDog2File -Value 0 Set-Content $KASUFile -Value 0 Set-Content $KAFile -Value 99 Set-Content $BadUpdatesFile -Value '' 


4. copy the update script to the directory with the previous script (note, below is a large script, 85kb), so as not to copy and paste, download it from here

Install-ClientUpdate.ps1
 <# 2016.06.14 ver 2            .        .       (FastUpdate)       30%  ,      25%,      5  .    ,             ,                                 ,   ,         1.      2.    "\BadUpdates\KA.txt"   9,  11 3.            -  ,  6   -   powershell.exe -file "   \Install-ClientUpdate.ps1" 4.       5.       $RebootEnabled = $False                    1.      2.    "\BadUpdates\KA.txt"   9,  11 3.            -   (  ),   10   -   powershell.exe -file "   \Install-ClientUpdate.ps1" 4.       5.       $RebootEnabled = $true              :       SoftwareDistribution.                  Plugins.           .           ,          .        "    "      .          .      IsHidden=1            .        "4.      ".    .    win 7       .             : powershell 2.0 Pak Nikolay GEOM, Aqtobe   win 8.1/2012R2; win 7; win 2008R2 #> ############# # Variables # ############# # Allow reboot.        .      #    .       10 ,   .    #    . #$RebootEnabled = $true $RebootEnabled = $False # Fast install          $FastInstallLimit = 45 #         $Path = Split-Path ($MyInvocation.MyCommand.Path) -Parent #    $LastInstalledUpdatesFile = "$Path\BadUpdates\LastInstalledUpdates.txt" #     $BadUpdatesFile = "$Path\BadUpdates\BadUpdates.txt" # FastInstaller $KAFUFile = "$Path\BadUpdates\KAFU.txt" #      $KAFUDeltaFile = "$Path\BadUpdates\KAFUDelta.txt" # Fastinstaller watchdog $KAFUWatchDogFile = "$Path\BadUpdates\KAFUWatchDog.txt" # Fastinstaller update installer watchdog $KAFUWatchDog2File = "$Path\BadUpdates\KAFUWatchDog2.txt" # SlowInstaller $KASUFile = "$Path\BadUpdates\KASU.txt" #   $WorkLogFile = "$Path\BadUpdates\log.txt" #   $KAFile = "$Path\BadUpdates\KA.txt" ########################## Report params ################################### [boolean]$SaveReport = $true [string]$ReportPath = 'C:\Report-InstallUpdates.html' [boolean]$SendReport = $true [string]$From = 'bot_abormot@test.local' [string]$SMTP = 'mail.test.local' [string]$ReportMail1 = 'admin1@test.local' [string]$ReportMail2 = 'admin2@test.local' [string]$ReportMail3 = 'admin3@test.local' function report { Param ( [string]$Text = 'test' ) $Date = Get-Date if ($SaveReport = $true) { $Text | Out-File $ReportPath } if ($SendReport = $true) { $encoding = [System.Text.Encoding]::UTF8 $body = $Text $Subj = "install-updates script report $Date" $params = @{'To'=$ReportMail1 'From'=$From 'Subject'=$Subj 'Body'=$Body 'BodyAsHTML'=$True 'SMTPServer'=$SMTP} Send-MailMessage @params -Encoding $encoding $params = @{'To'=$ReportMail2 'From'=$From 'Subject'=$Subj 'Body'=$Body 'BodyAsHTML'=$True 'SMTPServer'=$SMTP} Send-MailMessage @params -Encoding $encoding $params = @{'To'=$ReportMail3 'From'=$From 'Subject'=$Subj 'Body'=$Body 'BodyAsHTML'=$True 'SMTPServer'=$SMTP} Send-MailMessage @params -Encoding $encoding } } ########################## Report params ################################### function Log { PARAM ( [parameter(Mandatory = $true)] [string]$Message ) $Date = Get-Date -Format "yyyy.MM.dd HH:mm:ss" [string]$Msg = $Date + "`t" + $Message Out-File -FilePath $WorkLogFile -InputObject $Msg -Append # -encoding unicode } function Clear-Log { $Date = Get-Date -Format "yyyy.MM.dd HH:mm:ss" $Msg = $Date + "`t" + '   Clear-Log' Set-Content -Path $WorkLogFile -Value $Msg } function Trim-Log { #    5    if ( (Get-Item -Path $WorkLogFile).Length -gt 5mb ) { Clear-Log } } <#   SoftwareDistribution      ,  ,       WUAUSRV       .       #> function ResetSoftwareDistribution { $WUServices = 'BITS','wuauserv' $windows = 0x24 Log 'call ResetSoftwareDistribution' Log 'stopping WU services' Write-Host " SoftwareDistribution" -ForegroundColor Green Write-Host "-------------------------------" -ForegroundColor Green Write-Host " " -ForegroundColor Green $WUServices | Stop-Service -Verbose Start-Sleep -Seconds 20 #      if ( ((Get-Service 'BITS').Status -eq 'stopped' ) -and ((Get-Service 'wuauserv').Status -eq 'stopped') ) { Log 'services stopped sucsessful' Write-Host '  ' -ForegroundColor Green $Folders = Get-ChildItem ( (New-Object -ComObject Shell.Application).Namespace( $windows ).Self.Path) -Directory | where { $_.Name -like '*SoftwareDistribution*' } Write-Host "   WU" -ForegroundColor Green $Folders Write-Host " ..." $Folders | Remove-Item -Recurse -Force -Verbose -Confirm:$false #   $Folders = Get-ChildItem ( (New-Object -ComObject Shell.Application).Namespace( $windows ).Self.Path) -Directory | where { $_.Name -like '*SoftwareDistribution*' } if ($Folders -eq $null) { Log 'SoftwareDistribution folder deleted successful' Write-Host " SoftwareDistribution  " -ForegroundColor Green } else { Log 'SoftwareDistribution folder deleted NOT successful' Write-Host "SoftwareDistribution   !" -ForegroundColor Red #              Softwaredistrib #Exit } } Log 'starting WU services' Write-Host " " -ForegroundColor Green $WUServices | Start-Service -Verbose } <#            KB      IsHidden  0,    Softwaredistribution    Hidden .         IsHidden = 1             '2976978', '3156418' | install-updates -Verbose #> function install-updates { [CmdletBinding()] PARAM ( [Parameter( Position=0, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true) ] [string[]]$KB ) BEGIN { Log 'call install-updates' $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $Updates = $searcher.Search("IsInstalled=0 and Type='Software' and ISHidden=0") #     if ($Updates.Updates.Count -eq 0) { $NoUpdates = $true } else { $NoUpdates = $false } #   Write-Verbose '---------------------------------------------------------------------' Write-Verbose "install-updates:   NoUpdates = $NoUpdates" if ($NoUpdates -eq $false) { Write-Verbose "   " $temp1 = $Updates.updates | select Title $temp1 | Write-Verbose $Count = $Updates.Updates.Count Write-Verbose "$Count   " Log "$Count   " foreach ( $temp in $temp1 ) { Log " $temp" } } else { Log "install-updates:      " Write-Verbose "install-updates:      " } Write-Verbose '---------------------------------------------------------------------' } PROCESS { Write-Verbose "install-updates:     process $KB" if ($NoUpdates -eq $true) { break } if ($KB -match ' ' ) { break } $HotFix = $Updates.Updates | where { $_.Title -like "*$KB*" } if ($HotFix -eq $null) { Write-Verbose "     " Log "     " break } else { $Title = $HotFix.Title Write-Verbose "    $Title" Log "    $Title" } #   $downloads = New-Object -ComObject Microsoft.Update.UpdateColl $downloads.Add( $HotFix ) #      80070005.        SYSTEM #                  #      Write-Verbose ' ' Log 'download update' $downloader = $session.CreateUpdateDownLoader() $downloader.Updates = $downloads $downloader.Download() if ($HotFix.IsDownloaded) { Log 'download successfull' Write-Verbose ' . (   downloads)' } $installs = New-Object -ComObject Microsoft.Update.UpdateColl if ($HotFix.IsDownloaded) { $installs.Add( $HotFix ) | Out-Null } log 'start instal' #      80070005.        SYSTEM #                  #      $installer = $session.CreateUpdateInstaller() $installer.Updates = $installs $installresult = $installer.Install() $installresult log 'installing successfull' } END { Write-Verbose 'install-updates:   ' log 'install-updates:   ' log '--------------------------------------------------------------' } } <#         20-30     .    .               .            , ..  10-20                .      ,             .       $KAFUWatchDog     0       1/3          1       1/4          2       5         #> #  [int]$KAFU = Get-Content $KAFUFile #      [int]$KAFUDelta = Get-Content $KAFUDeltaFile function Fast-Update { function Write-KAFU { PARAM ( $State ) Set-Content $KAFUFile -Value $State } Log "--- Fast-Update ---------------------------------------------------------" Log "Fast-Update    " switch ($KAFU) { 0 { Log "   0" #        $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $temp = $searcher.Search("IsInstalled=0 and Type='Software'" ) $Count = $temp.Updates.Count #   ,              $KAFUWatchDog = Get-Content $KAFUWatchDogFile log "KAFUWatchDog = $KAFUWatchDog" switch ($KAFUWatchDog) { 0 { #  ,     2      $KAFUDelta = [Math]::Truncate( $Count / 3 ) Log " ,   1/3     $KAFUDelta" } 1 { #     4      $KAFUDelta = [Math]::Truncate( $Count / 4 ) Log " ,   1/4     $KAFUDelta" } 2 { #     -          #   5    $KAFUDelta = 5 log '    -            5' } default { Log '   ,   ' $KAFUWatchDog = 0 $KAFUDelta = [Math]::Truncate( $Count / 3 ) Set-Content $KAFUWatchDogFile -Value $KAFUWatchDog } } #     Set-Content $LastInstalledUpdatesFile -Value '' Set-Content $KAFUDeltaFile -Value $KAFUDelta Set-Content $KAFUWatchDog2File -Value 0 Write-KAFU 1 break } 1 { Log "enter in state 1" $LastInstalled = Get-Content $LastInstalledUpdatesFile $BadUpdates = Get-Content $BadUpdatesFile log '     ' $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $temp = $searcher.Search("IsInstalled=0 and Type='Software'" ) $Updates = $temp.Updates | select title #        [string[]]$HotFixs = '' foreach ($temp in $Updates) { $regex = $temp.Title -match "KB\d+" $KB = $matches[0] $HotFixs += $KB Log "   : $KB" } $Count = $HotFixs.Count Log "    $Count" log ' ' $Upd = @() foreach ($temp in $HotFixs) { if ($temp -ne '') { $Upd += $temp } } $Upd #         if ($BadUpdates.Count -ne 0) { $temp = Compare-Object -ReferenceObject $HotFixs -DifferenceObject $BadUpdates $temp = $temp | where { $_.Sideindicator -like '<=' } | select -ExpandProperty InputObject $HotFixs = $temp $Upd = @() foreach ($temp in $HotFixs) { if ($temp -ne '') { $Upd += $temp } } $HotFixs = $Upd $temp = $HotFixs Log "    :" foreach ( $temp1 in $temp ) { Log "    : $temp1" } $Count = $temp.Count Log ": $Count " } else { Log "   " } if ($LastInstalled.Count -ne 0) { $temp = Compare-Object -ReferenceObject $HotFixs -DifferenceObject $LastInstalled $temp = $temp | where { $_.Sideindicator -like '<=' } | select -ExpandProperty InputObject $HotFixs = $temp Log "     :" $Upd = @() foreach ($temp in $HotFixs) { if ($temp -ne '') { $Upd += $temp } } $HotFixs = $Upd $temp = $HotFixs foreach ( $temp1 in $temp ) { Log "     : $temp1" } $Count = $temp.Count Log ": $Count " } else { Log "     " } #     [int]$KAFUWatchDog2 = Get-Content $KAFUWatchDog2File if ($KAFUWatchDog2 -gt 20) { Log "   ,      1,  : $KAFUWatchDog2" Write-KAFU 2 Set-Content $KAFUWatchDog2File -Value 0 break } if ($HotFixs.Count -eq 0) { #   log ' ,   .    2' Write-KAFU 2 break } else { #     $temp = $HotFixs | select -First $KAFUDelta $Count = $temp.Count if ($Count -eq 0) { log '  .    2' Write-KAFU 2 break } foreach ( $temp1 in $temp ) { Log "   : $temp1" } Log ": $Count " $HotFixs = $temp #   Add-Content -Path $LastInstalledUpdatesFile -Value '' Add-Content -Path $LastInstalledUpdatesFile -Value $HotFixs @(Get-Content $LastInstalledUpdatesFile) -match '\S' | out-file $LastInstalledUpdatesFile LOG "    " #    log '  ' $HotFixs | install-updates } Set-Content $KAFUWatchDog2File -Value ($KAFUWatchDog2 + 1) break } 2 { #           Log '   2.' $KAFUWatchDog = Get-Content $KAFUWatchDogFile $KAFUDelta = Get-Content $KAFUDeltaFile $LastInstalled = Get-Content $LastInstalledUpdatesFile log "KAFUWatchDog = $KAFUWatchDog" switch ($KAFUWatchDog) { 0 { #   .   ?      $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $temp = $searcher.Search("IsInstalled=0 and Type='Software'" ) $Updates = $temp.Updates #       * 3 - 2 $Count1 = $Updates.Count if ($KAFUDelta -eq 0) { $KAFUDelta = 5 } $Count2 = $KAFUDelta * 3 - 2 if ($Count2 -lt $Count1) { #     .     $KAFUWatchDog = 1 Set-Content $KAFUWatchDogFile -Value 1 Write-KAFU 0 Log '    .   = 1.    0' break } else { log '    WatchDog = 0' log '   3' Write-KAFU 3 break } } 1 { #     $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $temp = $searcher.Search("IsInstalled=0 and Type='Software'" ) $Updates = $temp.Updates | select title #       * 3 - 2 $Count1 = $Updates.Count $Count2 = $KAFUDelta * 4 - 2 if ($Count2 -gt $Count1) { #     - 4        25%  #      5 $KAFUWatchDog = 2 Set-Content $KAFUWatchDogFile -Value 2 Write-KAFU 0 Log '    .  = 2.    0' break } else { log '    WatchDog = 1' log '   3' Write-KAFU 3 break } } 2 { #     5    log '    WatchDog = 2' log '   3' Write-KAFU 3 break } default { Log '     0' Set-Content $KAFUWatchDogFile -Value 0 Write-KAFU 0 break } } } 3 { # ,  Write-KAFU 3 break } default { Set-Content $KAFUWatchDogFile -Value 0 Write-KAFU 0 Log "warning!!!   ,   0. KAFU = $KAFU" break } } Log "--- Fast-Update -- -------------------------------------------------------" } <################################################################################################      .       .     ,      .    48      24   2  ,   1 .       3-6   ,      30-40    . ################################################################################################> <#                           #> #  [int]$KASU = Get-Content $KASUFile function Slow-update { function Write-KASU { PARAM ( $State ) Set-Content $KASUFile -Value $State } Log "--- Slow Update ---------------------------------------------------------" Log "Slow Update " switch ($KASU) { 0 { #    log "   0,   " #     Set-Content $LastInstalledUpdatesFile -Value '' Set-Content $KAFUWatchDog2File -Value 0 Set-Content $KAFUWatchDog2File -Value 0 Write-KASU 1 break } 1 { log "   1" $LastInstalled = Get-Content $LastInstalledUpdatesFile $BadUpdates = Get-Content $BadUpdatesFile log '     ' $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $temp = $searcher.Search("IsInstalled=0 and Type='Software'" ) $Updates = $temp.Updates | select title #        [string[]]$HotFixs = '' foreach ($temp in $Updates) { $regex = $temp.Title -match "KB\d+" $KB = $matches[0] $HotFixs += $KB Log "   : $KB" } $Count = $HotFixs.Count Log "    $Count" $Upd = @() foreach ($temp in $HotFixs) { if ($temp -ne '') { $Upd += $temp } } $PotentialBad = $Upd #         if ($BadUpdates.Count -ne 0) { $temp = Compare-Object -ReferenceObject $HotFixs -DifferenceObject $BadUpdates $temp = $temp | where { $_.Sideindicator -like '<=' } | select -ExpandProperty InputObject $HotFixs = $temp Log "    :" $Upd = @() foreach ($temp in $HotFixs) { if ($temp -ne '') { $Upd += $temp } } $HotFixs = $Upd $temp = $HotFixs foreach ( $temp1 in $temp ) { Log "    : $temp1" } $Count = $temp.Count Log ": $Count " } else { Log "   " } $Upd = @() foreach ($temp in $HotFixs) { if ($temp -ne '') { $Upd += $temp } } $HotFixs = $Upd if ($LastInstalled.Count -ne 0) { $temp = Compare-Object -ReferenceObject $HotFixs -DifferenceObject $LastInstalled $temp = $temp | where { $_.Sideindicator -like '<=' } | select -ExpandProperty InputObject $HotFixs = $temp Log "     :" $Upd = @() foreach ($temp in $HotFixs) { if ($temp -ne '') { $Upd += $temp } } $HotFixs = $Upd $temp = $HotFixs foreach ( $temp1 in $temp ) { Log "     : $temp1" } $Count = $temp.Count Log ": $Count " } else { Log "     " } $Upd = @() foreach ($temp in $HotFixs) { if ($temp -ne '') { $Upd += $temp } } $HotFixs = $Upd if ($HotFixs.Count -eq 0) { #   log ' ,   .     ' if ($PotentialBad -ne 0 ) { foreach ( $Bad in $PotentialBad ) { log "   : $Bad" } #   ..   ,         #Add-Content -Path $BadUpdatesFile -Value '' #Add-Content -Path $BadUpdatesFile -Value $LastInstalled } Write-KASU 2 break } else { #     $temp = $HotFixs | select -First 1 $Count = $temp.Count if ( $Count -eq 0 ) { log '   2' Write-KASU 2 break } foreach ( $temp1 in $temp ) { Log "   : $temp1" } Log ": $Count " $HotFixs = $temp #   Add-Content -Path $LastInstalledUpdatesFile -Value '' Add-Content -Path $LastInstalledUpdatesFile -Value $HotFixs @(Get-Content $LastInstalledUpdatesFile) -match '\S' | out-file $LastInstalledUpdatesFile LOG "    " #    log '  ' Write-Host $HotFixs -ForegroundColor Green $HotFixs | install-updates } Set-Content $KAFUWatchDog2File -Value ($KAFUWatchDog2 + 1) break } 2 { #  ,  Write-KASU 2 break } default { #   log " " Write-KASU 0 } } Log "--- Slow-Update -- -------------------------------------------------------" } [int]$KA = Get-Content $KAFile function Write-KA { PARAM ( $State ) Set-Content $KAFile -Value $State } Log ' ' Log " " Log ' ' Log ' ' Log ' ' Log '##############################' Log "### ---   --- ###" Log '##############################' switch ($KA) { 0 { #   log( ' = 0.         ' ) Set-Content $LastInstalledUpdatesFile -Value '' Set-Content $KAFUWatchDogFile -Value 0 Set-Content $KASUFile -Value 0 Set-Content $KAFUFile -Value 0 Write-KA 9 break } 1 { #   Log '  1.    ' [int]$KAFU = Get-Content $KAFUFile if ($KAFU -eq 3) { #    log '  ,    ' Write-KA 2 Set-Content $KASUFile -Value 0 break } else { log '  ' ResetSoftwareDistribution Fast-Update Trim-Log break } } 2 { #  log ' 2,  ' [int]$KASU = Get-Content $KASUFile if ($KASU -eq 2) { #    #                 Trim-Log ResetSoftwareDistribution log( ' = 2.    ' ) $BadUpdates = Get-Content $BadUpdatesFile $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $temp = $searcher.Search("IsInstalled=0 and Type='Software'" ) $Updates = $temp.Updates | select title #        [string[]]$HotFixs = '' foreach ($temp in $Updates) { $regex = $temp.Title -match "KB\d+" $KB = $matches[0] $HotFixs += $KB Log "   : $KB" } $Count = $HotFixs.Count Log "    $Count" $PotentialBad = $HotFixs #    if ($BadUpdates.Count -ne 0) { $temp = Compare-Object -ReferenceObject $HotFixs -DifferenceObject $BadUpdates $temp = $temp | where { $_.Sideindicator -like '<=' } | select -ExpandProperty InputObject $HotFixs = $temp Log "    :" foreach ( $temp1 in $temp ) { Log "    : $temp1" } $Count = $temp.Count Log ": $Count " } else { Log "   " } $Updates = @() foreach ($temp in $HotFixs) { if ($temp -ne '') { $Updates += $temp } } if ($Updates.Count -ne 0) { #    log '        ' Add-Content -Path $BadUpdatesFile -Value '' Add-Content -Path $BadUpdatesFile -Value $Updates $Date = Get-Date $CompName = $env:COMPUTERNAME $body = "<H1>     $CompName,<br><br>   </H1><h3>" foreach ($upd in $updates) { $body += "<br> $upd<br>" } $body += "<br>         .   ,        " report -text $body } Write-KA 9 break } else { log '        ' ResetSoftwareDistribution Trim-Log Slow-update break } } 9 { #     log( ' = 9.     ' ) Trim-Log ResetSoftwareDistribution $BadUpdates = Get-Content $BadUpdatesFile log '    ' $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $temp = $searcher.Search("IsInstalled=0 and Type='Software'" ) $Updates = $temp.Updates | select title #        [string[]]$HotFixs = '' foreach ($temp in $Updates) { $regex = $temp.Title -match "KB\d+" $KB = $matches[0] $HotFixs += $KB Log "   : $KB" } $Count = $HotFixs.Count Log "    $Count" $PotentialBad = $HotFixs #    if ($BadUpdates.Count -ne 0) { $temp = Compare-Object -ReferenceObject $HotFixs -DifferenceObject $BadUpdates $temp = $temp | where { $_.Sideindicator -like '<=' } | select -ExpandProperty InputObject $HotFixs = $temp Log "    :" $Upd = @() foreach ($temp in $HotFixs) { if ($temp -ne '') { $Upd += $temp } } $HotFixs = $Upd $temp = $HotFixs foreach ( $temp1 in $temp ) { Log "    : $temp1" } $Count = $temp.Count Log ": $Count " } else { Log "   " } log ' ' $Updates = @() foreach ($temp in $HotFixs) { if ($temp -ne '') { $Updates += $temp } } $Count = $Updates.Count Log "  $Count  " #     $FastInstallLimit    if ($Updates.Count -gt $FastInstallLimit) { #    log "updates more than $FastInstallLimit. Go to FastInstall" Write-KA 1 break } if ($Updates.Count -gt 0) { log "Go to Slow Install" Write-KA 2 Set-Content $KASUFile -Value 0 break } if ($Updates.Count -eq 0) { log '    ' Write-KA 9 break } } default { #   log( '   .    0' ) Write-KA 0 } } #ResetSoftwareDistribution #Fast-Update #Trim-Log #Slow-update Log '##############################' Log "### ---   --- ###" Log '##############################' if ( $RebootEnabled -eq $true ) { Restart-Computer -Force Log ' >>>' } <# schtasks /run /tn "\My_Tasks\PSWindowsUpdate" [Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("cp866") #> 


5. SYSTEM 1 . — SYSTEM . ( )


6


, .

3 . \BadUpdates\log.txt . , , .

45 ( ) . 1\3 , 3 , 1/4, 5. , 6 , 90 (90 ).

:
1. WUErrorreporting 20, .
2. SoftwareDistribution
3. . :
4. 45 «»
  • , , 1/3 , 1/4 , 5
  • ,
  • BadUpdates\LastInstalledUpdates.txt

5. 45
6.
  • , .

7.

SoftwareDistribution SoftwareDistribution\Plugins , .
:
  • $RebootEnabled = $true ,
  • 10
  • , , .
  • 9 ( ) $RebootEnabled = $false




WUErrorreporting


1. Set your variables.
The most significant variable is $ DaysBefore - for how many days the log will be scanned, by default 5 days. If there was an error installing the update, you will receive an administrative notification for 5 days in a row about the same error. In principle, it is normal to set 1 or 2 days, it does not make sense, you can skip the report if someone left to work on the weekend.

The $ SaveLog variable is responsible for saving the current run log to disk. It is useful, you can come or connect to the client machine and look at the work which update was not installed.

The last 4 variables control who to send the report to. You need to leave $ SendReport = $ true , email $ReportMail1 = 'admin@test.local' $SMTPServer = 'mail.test.local' . 156

 ############# # Variables # ############# #        [int]$DaysBefore = 5 #         $SaveLog = $false $LogPath = 'c:\WUError.txt' #   HTML [boolean]$SaveReport = $False [string]$ReportPath = 'C:\WUErrorReport.html' #  HTML  [boolean]$SendReport = $true [string]$ReportMail1 = 'admin@test.local' $from = 'bot_abormot@test.local' [string]$SMTPServer = 'mail.test.local' 



2.

ID20 System
 $Events = Get-EventLog -LogName System -EntryType Error -After (Get-Date).AddDays(-$DaysBefore) -InstanceId 20 -ErrorAction SilentlyContinue 




 #    $Log = @() #          foreach ($Event in $Events) { $regex = $Event.Message -match "KB\d+" $KB = $matches[0] $params = [ordered]@{ 'KB'=$KB #'EntryType'=$Event.EntryType 'Index'=$Event.Index 'MachineName'=$Event.MachineName 'Message'=$Event.Message 'Source'=$Event.Source 'TimeGenerated'=$Event.TimeGenerated 'TimeWritten'=$Event.TimeWritten 'UserName'=$Event.UserName } $obj = New-Object -TypeName PSObject -Property $params $Log += $obj } 

«»
  $encoding = [System.Text.Encoding]::UTF8 $body = ConvertTo-HTML -head $head -PostContent $frag1 -PreContent "<h1>Windows Update error report. Client $ClientName. Date:$Date</h1>" | Out-String $params = @{'To'=$ReportMail1 'From'=$from 'Subject'="$ClientName. Windows Update error $Date" 'Body'=$Body 'BodyAsHTML'=$True 'SMTPServer'=$SMTPServer} Send-MailMessage @params -Encoding $encoding 

CSS outlook, HTML Word, . Outlook .
CSS
 $head = @' <style> body { background-color:#ffffff; font-family:Tahoma; font-size:12pt; } td, th { border:1px solid black; border-collapse:collapse; } th { color:white; background-color:black; } table, tr, td, th { padding: 2px; margin: 0px } table { font-family: "Lucida Sans Unicode", "Lucida Grande", Sans-Serif; font-size: 14px; border-radius: 10px; border-spacing: 0; text-align: center; } th { background: #BCEBDD; color: white; text-shadow: 0 1px 1px #2D2020; padding: 10px 20px; } th, td { border-style: solid; border-width: 0 1px 1px 0; border-color: white; } th:first-child, td:first-child { text-align: left; } th:first-child { border-top-left-radius: 10px; } th:last-child { border-top-right-radius: 10px; border-right: none; } td { padding: 10px 20px; background: #F8E391; } tr:last-child td:first-child { border-radius: 0 0 0 10px; } tr:last-child td:last-child { border-radius: 0 0 10px 0; } tr td:last-child { border-right: none; } </style> '@ 



Install-ClientUpdate.ps1


3 , .

:
$FastInstallLimit , .
$RebootEnabled . . $true , 10 .


 ########################## Report params ################################### [boolean]$SaveReport = $true [string]$ReportPath = 'C:\Report-InstallUpdates.html' [boolean]$SendReport = $true [string]$From = 'bot_abormot@test.local' [string]$SMTP = 'mail.test.local' [string]$ReportMail1 = 'admin1@test.local' [string]$ReportMail2 = 'admin2@test.local' [string]$ReportMail3 = 'admin3@test.local' 

.
 $session = New-Object -ComObject Microsoft.Update.Session $searcher = $session.CreateUpdateSearcher() $temp = $searcher.Search("IsInstalled=0 and Type='Software'" ) $Count = $temp.Updates.Count $Count '' 


: , .. .
, , « » .

win 8.1; 2012R2; win 7 x64

2016.08.03:
\BadUpdates\BadUpdates.txt KB121212

Source: https://habr.com/ru/post/306866/


All Articles