232 lines
9.2 KiB
PowerShell
232 lines
9.2 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Find and apply Windows Updates on remote computers
|
|
|
|
.PARAMETER ComputerNames
|
|
One or more fully qualified computer names as a string array
|
|
|
|
.PARAMETER doRestarts
|
|
Restart when completed
|
|
#>
|
|
[CmdletBinding()]
|
|
param (
|
|
[Parameter(Mandatory)]
|
|
[ValidateNotNullOrEmpty()]
|
|
[string[]]$ComputerNames,
|
|
[switch]$doUpdates,
|
|
[switch]$doRestarts
|
|
)
|
|
|
|
<#
|
|
Determine which packages need to be upgraded per server for the individual AWS items
|
|
|
|
$bucketResult = (Invoke-RestMethod -Method GET -Uri https://s3.amazonaws.com/ec2-windows-drivers-downloads)
|
|
$types = @('NVMe','ENA','AWSPV')
|
|
$results = @()
|
|
foreach ($key in $bucketResult.ListBucketResult.Contents.Key) {
|
|
foreach ($type in $types) {
|
|
if ($key.StartsWith($type)) {
|
|
$results += @{ Type = ($key -split '/')[0]; Version = ($key -split '/')[1] }
|
|
}
|
|
}
|
|
}
|
|
$versionTable = @{}
|
|
foreach ($type in $types) {
|
|
$version = $results.Where({$_.Version -match '[0-9]+\.[0-9]+\.[0-9]+'}).Where({$_.Type -eq $type}).Version | Sort-Object -Descending | Select-Object -First 1
|
|
Write-Host $version
|
|
Write-Host $type
|
|
$versionTable.$type = $version
|
|
}
|
|
|
|
$JobNames = @{
|
|
NVMe = 'AWSNVMe'
|
|
ENA = 'AwsEnaNetworkDriver'
|
|
AWSPV = 'AWSPVDriver'
|
|
}
|
|
|
|
|
|
$x = Get-EC2WindowsDriverVersions
|
|
$toUpdate = $x.Where({$_.Version -ne $versionTable[$_.Type]})
|
|
|
|
$instances = (Get-CachedInstances -ProfileName temp-prod).Where({$_.Hostname -match '^tea'})
|
|
|
|
foreach ($instance in $instances) {
|
|
foreach ($update in $toUpdate) {
|
|
if ($instance.Hostname -eq "$($update.ComputerName).fh.local") {
|
|
Write-Host "updating"
|
|
Invoke-AWSConfigureAWSPackage -JobName $JobNames[$update.Type] -InstanceId $instance.InstanceId -Comment 'SRE-17714' -ProfileName 'temp-prod' -Region $instance.Region
|
|
}
|
|
}
|
|
}
|
|
#>
|
|
|
|
Write-Host "Updating chrome on all remote machines"
|
|
$sbChrome = {
|
|
choco upgrade GoogleChrome -y -r
|
|
}
|
|
Invoke-Command -ComputerName $ComputerNames -ScriptBlock $sbChrome
|
|
|
|
#region magic session things for PowerShell
|
|
# The COMObjects can not be created if you don't do this first
|
|
# I should probably have them get removed after the run, but meh
|
|
Write-Host "Ensuring VirtualAccount PSSessionConfiguration exists locally so we can run updates remotely"
|
|
if ($null -eq (Get-PSSessionConfiguration -Name 'VirtualAccount' -ErrorAction SilentlyContinue)) {
|
|
New-PSSessionConfigurationFile -RunAsVirtualAccount -Path .\VirtualAccount.pssc
|
|
# Note this will restart the WinRM service:
|
|
Register-PSSessionConfiguration -Name 'VirtualAccount' -Path .\VirtualAccount.pssc -Force
|
|
}
|
|
|
|
$ensureVirtualAccountSessionManagementExistsScriptBlock = {
|
|
Write-Host "Ensuring VirtualAccount PSSessionConfiguration exists so we can run updates remotely on $($env:COMPUTERNAME)"
|
|
if ($null -eq (Get-PSSessionConfiguration -Name 'VirtualAccount' -ErrorAction SilentlyContinue)) {
|
|
New-PSSessionConfigurationFile -RunAsVirtualAccount -Path .\VirtualAccount.pssc
|
|
# Note this will restart the WinRM service:
|
|
Register-PSSessionConfiguration -Name 'VirtualAccount' -Path .\VirtualAccount.pssc -Force
|
|
}
|
|
}
|
|
Invoke-Command -ComputerName $ComputerNames -ScriptBlock $ensureVirtualAccountSessionManagementExistsScriptBlock
|
|
#endregion magic session things for PowerShell
|
|
|
|
# Take inventory, find out if we need to reboot before we continue
|
|
$rebootRequiredScriptBlock = {
|
|
param (
|
|
[string]$computerName
|
|
)
|
|
|
|
$session = New-PSSession -ComputerName $computerName -ConfigurationName 'VirtualAccount'
|
|
|
|
$serverScript = {
|
|
$UpdateCollection = New-Object -ComObject 'Microsoft.Update.UpdateColl' -Strict
|
|
$Searcher = New-Object -ComObject 'Microsoft.Update.Searcher' -Strict
|
|
$Session = New-Object -ComObject 'Microsoft.Update.Session' -Strict
|
|
$Installer = New-Object -ComObject 'Microsoft.Update.Installer' -Strict
|
|
|
|
$Searcher.Search("") | Out-Null
|
|
$totalHistoryCount = $Searcher.GetTotalHistoryCount()
|
|
|
|
$returnUpdates = @()
|
|
$Updates = @($Searcher.Search("IsHidden=0 and IsInstalled=0").Updates)
|
|
foreach ($update in $Updates) {
|
|
$UpdateCollection.Add($update) | Out-Null
|
|
$returnUpdates += New-Object -Type PSObject -Property @{
|
|
BundledUpdates = $update.BundledUpdates
|
|
Categories = $update.Categories
|
|
CveIDs = $update.CveIDs
|
|
Deadline = $update.Deadline
|
|
DeltaCompressedContentAvailable = $update.DeltaCompressedContentAvailable
|
|
DeltaCompressedContentPreferred = $update.DeltaCompressedContentPreferred
|
|
DeploymentAction = $update.DeploymentAction
|
|
Description = $update.Description
|
|
DownloadPriority = $update.DownloadPriority
|
|
EulaAccepted = $update.EulaAccepted
|
|
EulaText = $update.EulaText
|
|
HandlerID = $update.HandlerID
|
|
Identity = $update.Identity
|
|
InstallationBehavior = $update.InstallationBehavior
|
|
IsBeta = $update.IsBeta
|
|
IsDownloaded = $update.IsDownloaded
|
|
IsHidden = $update.IsHidden
|
|
IsInstalled = $update.IsInstalled
|
|
IsMandatory = $update.IsMandatory
|
|
IsPresent = $update.IsPresent
|
|
IsUninstallable = $update.IsUninstallable
|
|
KBArticleIDs = $update.KBArticleIDs
|
|
Languages = $update.Languages
|
|
LastDeploymentChangeTime = $update.LastDeploymentChangeTime
|
|
MoreInfoUrls = $update.MoreInfoUrls
|
|
MsrcSeverity = $update.MsrcSeverity
|
|
RebootRequired = $update.RebootRequired
|
|
RecommendedCpuSpeed = $update.RecommendedCpuSpeed
|
|
RecommendedHardDiskSpace = $update.RecommendedHardDiskSpace
|
|
RecommendedMemory = $update.RecommendedMemory
|
|
ReleaseNotes = $update.ReleaseNotes
|
|
SecurityBulletinIDs = $update.SecurityBulletinIDs
|
|
SupersededUpdateIDs = $update.SupersededUpdateIDs
|
|
SupportUrl = $update.SupportUrl
|
|
Title = $update.Title
|
|
Type = $update.Type
|
|
}
|
|
}
|
|
|
|
if ($UpdateCollection.Count -gt 0) {
|
|
$Downloader = $Session.CreateUpdateDownloader()
|
|
$Downloader.Updates = $UpdateCollection
|
|
$Downloader.Download() | Out-Null
|
|
}
|
|
|
|
$Installer.AllowSourcePrompts = $true
|
|
$installer.ForceQuiet = $true
|
|
$Installer.Updates = $UpdateCollection
|
|
$isRebootRequired = $installer.RebootRequiredBeforeInstallation
|
|
|
|
return $isRebootRequired, $returnUpdates, $totalHistoryCount
|
|
}
|
|
$isRebootRequired, $returnUpdates, $totalHistoryCount = Invoke-Command -Session $session -ScriptBlock $serverScript
|
|
Exit-PSSession
|
|
$retValue = @{ ComputerName = $computerName; IsRebootRequired = $isRebootRequired; Updates = $returnUpdates; TotalHistoryCount = $totalHistoryCount }
|
|
|
|
return $retValue
|
|
}
|
|
|
|
$doInstallsScriptBlock = {
|
|
param (
|
|
$computerName
|
|
)
|
|
$session = New-PSSession -ComputerName $computerName -ConfigurationName 'VirtualAccount'
|
|
$serverScript = {
|
|
$UpdateCollection = New-Object -ComObject Microsoft.Update.UpdateColl
|
|
$Searcher = New-Object -ComObject Microsoft.Update.Searcher
|
|
$Session = New-Object -ComObject Microsoft.Update.Session
|
|
$Installer = New-Object -ComObject Microsoft.Update.Installer
|
|
|
|
$Updates = @($Searcher.Search("IsHidden=0 and IsInstalled=0").Updates)
|
|
foreach ($update in $Updates) {
|
|
$UpdateCollection.Add($update) | Out-Null
|
|
}
|
|
|
|
if ($UpdateCollection.Count -gt 0) {
|
|
$Downloader = $Session.CreateUpdateDownloader()
|
|
$Downloader.Updates = $UpdateCollection
|
|
$Downloader.Download() | Out-Null
|
|
}
|
|
|
|
$Installer.AllowSourcePrompts = $true
|
|
$installer.ForceQuiet = $true
|
|
$Installer.Updates = $UpdateCollection
|
|
Write-Host "Beginning install"
|
|
$result = $Installer.Install()
|
|
|
|
if ($result.ResultCode -ne 2) {
|
|
Write-Warning "$($env:COMPUTERNAME) ResultCode was not 2, it was $($result.ResultCode)"
|
|
}
|
|
|
|
Write-Host "$($env:COMPUTERNAME) finished installing, time to reboot?"
|
|
}
|
|
Invoke-Command -Session $session -ScriptBlock $serverScript
|
|
Exit-PSSession
|
|
return @{ ComputerName = $computerName; InstallCompleted = $true }
|
|
}
|
|
|
|
$results = Invoke-Parallel -ScriptBlock $rebootRequiredScriptBlock -objects $ComputerNames -ReturnObjects
|
|
Write-Output $results
|
|
|
|
# TODO: Reboot before continuing, if the flag above was true
|
|
if ($results.IsRebootRequired) {
|
|
throw "reboots are required on one or more servers. Please reboot before continuing"
|
|
}
|
|
|
|
if ($doUpdates) {
|
|
$results = Invoke-Parallel -ScriptBlock $doInstallsScriptBlock -objects $ComputerNames -ReturnObjects
|
|
}
|
|
|
|
if ($doRestarts) {
|
|
$computerNames = 'tea31697.fh.local','tea316155.fh.local','tea316208.fh.local','tea316229.fh.local','tea46658.fh.local'
|
|
foreach ($computerName in $computerNames) {
|
|
if ([string]::IsNullOrWhiteSpace($computerName)) {
|
|
Write-Host "Shutting down $computerName"
|
|
shutdown -m $computerName -t 0 -r
|
|
}
|
|
}
|
|
}
|
|
|
|
Write-Host "finished" |