github.com/cloudfoundry-incubator/stembuild@v0.0.0-20211223202937-5b61d62226c6/modules/BOSH.WindowsUpdates/BOSH.WindowsUpdates.psm1 (about)

     1  <#
     2  .Synopsis
     3      Install Windows Updates
     4  .Description
     5      This cmdlet installs all available Windows Updates in batches
     6  #>
     7  
     8  # Do not place these inside a function - they will not behave as expected
     9  $script:ScriptName = $MyInvocation.MyCommand.ToString()
    10  $script:ScriptPath = $MyInvocation.MyCommand.Path
    11  
    12  function Find-WindowsUpdatesTask {
    13      $task = Get-ScheduledTask -TaskName "InstallWindowsUpdates" -ErrorAction SilentlyContinue
    14      return $task -ne $null
    15  }
    16  
    17  function Register-WindowsUpdatesTask {
    18      $Prin = New-ScheduledTaskPrincipal -GroupId "BUILTIN\Administrators" -RunLevel Highest
    19      $action = New-ScheduledTaskAction -Execute 'Powershell.exe' `
    20      -Argument "-Command `"Install-WindowsUpdates`" "
    21      $trigger =  New-ScheduledTaskTrigger -AtLogon -RandomDelay 00:00:30
    22      Register-ScheduledTask -Principal $Prin -Action $action -Trigger $trigger -TaskName "InstallWindowsUpdates" -Description "InstallWindowsUpdates"
    23  }
    24  
    25  function Unregister-WindowsUpdatesTask {
    26      $task = Find-WindowsUpdatesTask
    27      if ($task)
    28      {
    29          Write-Log "Restart Scheduled Task Exists - Removing It"
    30          Unregister-ScheduledTask -TaskName "InstallWindowsUpdates" -Confirm:$false
    31      }
    32  }
    33  
    34  function Wait-WindowsUpdates {
    35      Param([string]$Password,[string]$User)
    36  
    37      Enable-Autologon -Password $Password -User $User
    38  
    39      Write-Log "Getting WinRM config"
    40      $winrm_config = Get-WinRMConfig
    41      Write-Log "$winrm_config"
    42  
    43      disable-service("WinRM")
    44  
    45      Write-Log "Getting WinRM config"
    46      $winrm_config = Get-WinRMConfig
    47      Write-Log "$winrm_config"
    48  }
    49  
    50  function Install-WindowsUpdates {
    51  
    52      # Set registry key so that we will receive the Jan 2018 patches (KB4056895)
    53      REG ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\QualityCompat /f /v cadca5fe-87d3-4b96-b7fb-a231484277cc /t REG_DWORD /d 0
    54  
    55      # Set registry keys so that KB4056898 will be enabled
    56      # Set registry keys to cover CVE-2018-11091 CVE-2018-12126 CVE-2018-12127 CVE-2018-12130 CVE-2017-5753 CVE-2017-5715 CVE-2017-5754 CVE-2018-3639 CVE-2018-3615 CVE-2018-3620 CVE-2018-3646
    57      reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management" /v FeatureSettingsOverride /t REG_DWORD /d 72 /f
    58      reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management" /v FeatureSettingsOverrideMask /t REG_DWORD /d 3 /f
    59      reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization" /v MinVmVersionForCpuBasedMitigations /t REG_SZ /d "1.0" /f
    60  
    61      if (test-path "C:\provision\patch.msu") {
    62          Write-Log "Already installed out-of-band patch"
    63      } else {
    64          Set-Service -Name wuauserv -StartupType Manual
    65          Start-Service -Name wuauserv
    66  
    67          Invoke-WebRequest -UseBasicParsing -Uri 'http://download.windowsupdate.com/d/msdownload/update/software/secu/2018/01/windows8.1-kb4056898-x64_ad6c91c5ec12608e4ac179b2d15586d244f0d2f3.msu' -Outfile C:\provision\patch.msu
    68          wusa.exe C:\provision\patch.msu /quiet
    69          start-sleep 200
    70      }
    71  
    72      $script:UpdateSession = New-Object -ComObject 'Microsoft.Update.Session'
    73      $script:UpdateSession.ClientApplicationID = 'BOSH.WindowsUpdates'
    74      $script:UpdateSearcher = $script:UpdateSession.CreateUpdateSearcher()
    75      $script:SearchResult = New-Object -ComObject 'Microsoft.Update.UpdateColl'
    76  
    77      $script:Cycles = 0
    78      $script:CycleUpdateCount = 0
    79      $script:MaxUpdatesPerCycle=500
    80      $script:RestartRequired=0
    81      $script:MoreUpdates=0
    82      $script:MaxCycles=5
    83  
    84      Get-UpdateBatch
    85      if ($script:MoreUpdates -eq 1) {
    86          Install-UpdateBatch
    87      } else {
    88          Invoke-RebootOrComplete
    89      }
    90  }
    91  
    92  function Invoke-RebootOrComplete() {
    93      $RegistryEntry = "InstallWindowsUpdates"
    94      switch ($script:RestartRequired) {
    95          0 {
    96              Unregister-WindowsUpdatesTask
    97  
    98              Write-Log "No Restart Required"
    99              Get-UpdateBatch
   100  
   101              if (($script:MoreUpdates -eq 1) -and ($script:Cycles -le $script:MaxCycles)) {
   102                  Install-UpdateBatch
   103              } elseif ($script:Cycles -gt $script:MaxCycles) {
   104                  Write-Log "Exceeded Cycle Count - Stopping"
   105                  Enable-WinRM
   106                  Disable-Autologon
   107              } else {
   108                  Write-Log "Done Installing Windows Updates"
   109                  Enable-WinRM
   110                  Disable-Autologon
   111              }
   112  
   113              Write-Log "Getting WinRM config"
   114              $winrm_config = Get-WinRMConfig
   115              Write-Log "$winrm_config"
   116          }
   117          1 {
   118              $prop = Find-WindowsUpdatesTask
   119              if (-not $prop ) {
   120                  Write-Log "Restart Scheduled Task Does Not Exist - Creating It"
   121                  Register-WindowsUpdatesTask
   122              } else {
   123                  Write-Log "Restart Scheduled Task Exists Already"
   124              }
   125  
   126              Write-Log "Restart Required - Restarting..."
   127              Restart-Computer
   128          }
   129          default {
   130              Write-Log "Unsure If A Restart Is Required"
   131              break
   132          }
   133      }
   134  }
   135  
   136  function Install-UpdateBatch() {
   137      $script:Cycles++
   138      Write-Log "Evaluating Available Updates with limit of $($script:MaxUpdatesPerCycle):"
   139      $UpdatesToDownload = New-Object -ComObject 'Microsoft.Update.UpdateColl'
   140      $script:i = 0;
   141      if ($Host.Version.Major -eq 5) {
   142        $CurrentUpdates = $SearchResult.Updates
   143      } else {
   144        $CurrentUpdates = $SearchResult.Updates | Select-Object
   145      }
   146      while($script:i -lt $SearchResult.Updates.Count -and $script:CycleUpdateCount -lt $script:MaxUpdatesPerCycle) {
   147          $Update = $CurrentUpdates[$script:i]
   148          if (($null -ne $Update) -and (!$Update.IsDownloaded)) {
   149              [bool]$addThisUpdate = $false
   150              if ($Update.InstallationBehavior.CanRequestUserInput) {
   151                  Write-Log "> Skipping: $($Update.Title) because it requires user input"
   152              } else {
   153                  if (!($Update.EulaAccepted)) {
   154                      Write-Log "> Note: $($Update.Title) has a license agreement that must be accepted. Accepting the license."
   155                      $Update.AcceptEula()
   156                      [bool]$addThisUpdate = $true
   157                      $script:CycleUpdateCount++
   158                  } else {
   159                      [bool]$addThisUpdate = $true
   160                      $script:CycleUpdateCount++
   161                  }
   162              }
   163  
   164              if ([bool]$addThisUpdate) {
   165                  Write-Log "Adding: $($Update.Title)"
   166                  $UpdatesToDownload.Add($Update) |Out-Null
   167              }
   168          }
   169          $script:i++
   170      }
   171  
   172      if ($UpdatesToDownload.Count -eq 0) {
   173          Write-Log "No Updates To Download..."
   174      } else {
   175          Write-Log 'Downloading Updates...'
   176          $ok = 0;
   177          while (! $ok) {
   178              try {
   179                  $Downloader = $UpdateSession.CreateUpdateDownloader()
   180                  $Downloader.Updates = $UpdatesToDownload
   181                  $Downloader.Download()
   182                  $ok = 1;
   183              } catch {
   184                  Write-Log $_.Exception | Format-List -force
   185                  Write-Log "Error downloading updates. Retrying in 30s."
   186                  $script:attempts = $script:attempts + 1
   187                  Start-Sleep -s 30
   188              }
   189          }
   190      }
   191  
   192      $UpdatesToInstall = New-Object -ComObject 'Microsoft.Update.UpdateColl'
   193      [bool]$rebootMayBeRequired = $false
   194      Write-Log 'The following updates are downloaded and ready to be installed:'
   195      foreach ($Update in $SearchResult.Updates) {
   196          if (($Update.IsDownloaded)) {
   197              Write-Log "> $($Update.Title)"
   198              $UpdatesToInstall.Add($Update) |Out-Null
   199  
   200              if ($Update.InstallationBehavior.RebootBehavior -gt 0){
   201                  [bool]$rebootMayBeRequired = $true
   202              }
   203          }
   204      }
   205  
   206      if ($UpdatesToInstall.Count -eq 0) {
   207          Write-Log 'No updates available to install...'
   208          $script:MoreUpdates=0
   209          $script:RestartRequired=0
   210          Enable-WinRM
   211  
   212          Write-Log "Getting WinRM config"
   213          $winrm_config = Get-WinRMConfig
   214          Write-Log "$winrm_config"
   215          break
   216      }
   217  
   218      if ($rebootMayBeRequired) {
   219          Write-Log 'These updates may require a reboot'
   220          $script:RestartRequired=1
   221      }
   222  
   223      Write-Log 'Installing updates...'
   224  
   225      $Installer = $script:UpdateSession.CreateUpdateInstaller()
   226      $Installer.Updates = $UpdatesToInstall
   227      $InstallationResult = $Installer.Install()
   228  
   229      Write-Log "Installation Result: $($InstallationResult.ResultCode)"
   230      Write-Log "Reboot Required: $($InstallationResult.RebootRequired)"
   231      Write-Log 'Listing of updates installed and individual installation results:'
   232      if ($InstallationResult.RebootRequired) {
   233          $script:RestartRequired=1
   234      } else {
   235          $script:RestartRequired=0
   236      }
   237  
   238      for($i=0; $i -lt $UpdatesToInstall.Count; $i++) {
   239          New-Object -TypeName PSObject -Property @{
   240              Title = $UpdatesToInstall.Item($i).Title
   241              Result = $InstallationResult.GetUpdateResult($i).ResultCode
   242          }
   243          Write-Log "Item: $UpdatesToInstall.Item($i).Title"
   244          Write-Log "Result: $InstallationResult.GetUpdateResult($i).ResultCode"
   245      }
   246  
   247      Invoke-RebootOrComplete
   248  }
   249  
   250  function Get-UpdateBatch() {
   251      Write-Log "Checking For Windows Updates"
   252      $Username = $env:USERDOMAIN + "\" + $env:USERNAME
   253  
   254      New-EventLog -Source $script:ScriptName -LogName 'Windows Powershell' -ErrorAction SilentlyContinue
   255  
   256      $Message = "Script: " + $script:ScriptPath + "`nScript User: " + $Username + "`nStarted: " + (Get-Date).toString()
   257  
   258      Write-EventLog -LogName 'Windows Powershell' -Source $script:ScriptName -EventID "104" -EntryType "Information" -Message $Message
   259      Write-Log $Message
   260  
   261      $script:UpdateSearcher = $script:UpdateSession.CreateUpdateSearcher()
   262      $script:successful = $FALSE
   263      $script:attempts = 0
   264      $script:maxAttempts = 12
   265      while(-not $script:successful -and $script:attempts -lt $script:maxAttempts) {
   266          try {
   267              $script:SearchResult = $script:UpdateSearcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0")
   268              $script:successful = $TRUE
   269          } catch {
   270              Write-Log $_.Exception | Format-List -force
   271              Write-Log "Search call to UpdateSearcher was unsuccessful. Retrying in 10s."
   272              $script:attempts = $script:attempts + 1
   273              Start-Sleep -s 10
   274          }
   275      }
   276  
   277      if ($SearchResult.Updates.Count -ne 0) {
   278          $Message = "There are " + $SearchResult.Updates.Count + " more updates."
   279          Write-Log $Message
   280          try {
   281              for($i=0; $i -lt $script:SearchResult.Updates.Count; $i++) {
   282                Write-Log $script:SearchResult.Updates.Item($i).Title
   283                Write-Log $script:SearchResult.Updates.Item($i).Description
   284                Write-Log $script:SearchResult.Updates.Item($i).RebootRequired
   285                Write-Log $script:SearchResult.Updates.Item($i).EulaAccepted
   286            }
   287              $script:MoreUpdates=1
   288          } catch {
   289              Write-Log $_.Exception | Format-List -force
   290              Write-Log "Showing SearchResult was unsuccessful. Rebooting."
   291              $script:RestartRequired=1
   292              $script:MoreUpdates=0
   293              Invoke-RebootOrComplete
   294              Write-Log "Show never happen to see this text!"
   295              Restart-Computer
   296          }
   297      } else {
   298          Write-Log 'There are no applicable updates'
   299          $script:RestartRequired=0
   300          $script:MoreUpdates=0
   301      }
   302  }
   303  
   304  function Search-InstalledUpdates() {
   305      $Session = New-Object -ComObject Microsoft.Update.Session
   306      $Searcher = $Session.CreateUpdateSearcher()
   307      $Searcher.Search("IsInstalled=1").Updates | Sort-Object LastDeploymentChangeTime | ForEach-Object { "KB$($_.KBArticleIDs) | $($_.Title)" }
   308  }
   309  
   310  function Test-InstalledUpdates() {
   311      Write-Host "Running Get-HotFix:"
   312      Get-HotFix
   313      $Session = New-Object -ComObject Microsoft.Update.Session
   314      Write-Host "Session: $Session"
   315      $Searcher = $Session.CreateUpdateSearcher()
   316      Write-Host "Searcher: $Searcher"
   317      $UninstalledUpdates = $Searcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0").Updates
   318      if ($UninstalledUpdates.Count -ne 0) {
   319          Write-Log "The following updates are not currently installed:"
   320          foreach ($Update in $UninstalledUpdates) {
   321              Write-Log "> $($Update.Title)"
   322          }
   323          Throw 'There are uninstalled updates'
   324      }
   325  }
   326  
   327  <#
   328  .Synopsis
   329      Disable Automatic Updates
   330  .Description
   331      This cmdlet disables automatic Windows Updates
   332  #>
   333  function Disable-AutomaticUpdates() {
   334      Stop-Service -Name wuauserv
   335      Set-Service -Name wuauserv -StartupType Disabled
   336  
   337      Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update' -Value 1 -Name 'AUOptions'
   338      Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update' -Value 0 -Name 'EnableFeaturedSoftware'
   339      Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update' -Value 0 -Name 'IncludeRecommendedUpdates'
   340  }
   341  
   342  function Enable-CVE-2015-6161() {
   343      #Enable MS15-124 - Internet Explorer ASLR Bypass fix - CVE-2015-6161
   344      reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_ALLOW_USER32_EXCEPTION_HANDLER_HARDENING" /t REG_DWORD /v "iexplore.exe" /d 1 /f
   345      reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_ALLOW_USER32_EXCEPTION_HANDLER_HARDENING" /t REG_DWORD /v "iexplore.exe" /d 1 /f
   346  }
   347  
   348  function Enable-CVE-2017-8529() {
   349      #Enable Microsoft Browser Information Disclosure Vulnerability - CVE-2017-8529
   350      reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_ENABLE_PRINT_INFO_DISCLOSURE_FIX" /v iexplore.exe /t REG_DWORD /d 1 /f
   351      reg add "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_ENABLE_PRINT_INFO_DISCLOSURE_FIX" /v iexplore.exe /t REG_DWORD /d 1 /f
   352  
   353  }
   354  
   355  function Enable-CredSSP() {
   356      #Enable CredSSP  updates - CVE-2018-0886
   357      #Policy set to "mitigated"
   358      reg add "HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters" /v AllowEncryptionOracle /t REG_DWORD /d 1 /f
   359  }
   360  
   361  function Upgrade-PSVersion () {
   362      if (Test-PSVersion) {
   363          Write-Log "Upgrade-PSVersion: No need to upgrade. PSVersion is 5 or above"
   364          return
   365      }
   366  
   367      $existingProtocol = [Net.ServicePointManager]::SecurityProtocol
   368      [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
   369      Write-Log "Upgrade-PSVersion: Downloading."
   370  
   371      $MSUPath = "c:\provision\PS51.msu"
   372      Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/?linkid=839516" -UseBasicParsing -OutFile $MSUPath
   373  
   374      Write-Log "Upgrade-PSVersion: Downloaded. Installing."
   375  
   376      $p = Start-Process -FilePath $MSUPath -ArgumentList '/quiet /norestart /log:"C:\provision\psupgrade.log"' -Wait -PassThru
   377      Write-Log "Upgrade-PSVersion: Installed. Process exit code: $($p.ExitCode)"
   378      [Net.ServicePointManager]::SecurityProtocol = $existingProtocol
   379  }
   380  
   381  function Test-PSVersion {
   382      $version = $PSVersionTable.PSVersion
   383      Write-Log "Powershell is $version"
   384      $version.Major -ge 5
   385  }