我正在尝试编写一个 powershell 脚本来查询多个服务器并检查它们是否有任何待处理的更新。
基本上我使用“MSCatalog”模块来查询更新,如下所示:
$Search = (Get-Date -Format "yyyy-MM")
$Updates = (Get-MSCatalogUpdate -Search $Search -AllPages ) | Where-Object { ($_.Title -like "*Server*") -OR ($_.Products -like "*Server*") }
我最感兴趣的是新的每月更新,所以我只使用获取日期来查询这个月的更新。当然,我会根据标题或产品列表中的服务器进行过滤。
现在我的服务器全线了。我们有一些旧服务器 2008、2012、2012R2、2016 和 2019。
我正在尝试找出一种检查并查看是否缺少任何更新的好方法。 要获取当前服务器更新,我运行以下命令:
$ServerKBs = (((Get-HotFix -ComputerName $($FilteredServerResult.ServerName)) | Select-Object HotFixID,InstalledOn) | Where-Object {($_.InstalledOn -like "$ServerUpdateSearch" )}).HotFixID
我坚持的部分是我试图只比较与服务器相关的更新。例如,我不关心 2016 或 2012r2 服务器的 2019 更新。我只希望显示特定于该服务器的那些。现在我知道我可以按列名过滤它并将它与服务器上的操作系统进行比较以确保它匹配。例如,要获取操作系统的数字版本,我可以使用这个:
$ServerOSString = (($FilteredServerResult.OS) -replace '\D+(\d+)\D+','$1')
输出将是例如 2016 而不是“Windows Server 2016 Standard” 然后我可以做这样的事情:
if(($Update.Products -match $ServerOSString) -eq $True )
{
#Do Stuff
}
这是我坚持的部分,因为让我们说这一切都是一致的,现在我将服务器上安装的知识库与我从 Microsoft Windows 目录中查询的内容进行比较......可能有一些更新匹配,而其他更新可能匹配不匹配。我只想关注那些有与他的服务器不匹配的未决更新的那些。我怎样才能去做这样的事情?
我正在使用 poshrs-job 在多台服务器上运行它,所以它更快,所以我想尽可能高效地完成它,并希望输出一个简单的服务器列表,这些服务器仍然需要用 KB &\or 打补丁标题。
这是我正在尝试做的一个例子:
CLS
#Main Variables
#The Search will always be the year-month when you run the script
$Search = (Get-Date -Format "yyyy-MM")
#Formatting is different one servers than on Windows update catalog
$ServerUpdateSearch = (Get-Date -Format "*MM*yyyy*")
#------------- MSCatalog (For Querying Windows Catalog)
if((Get-Module -ListAvailable -Name "MSCatalog") -or (Get-Module -Name "MSCatalog"))
{
Import-Module MSCatalog
}
else
{
Install-Module -Name MSCatalog -Scope CurrentUser -Force -Confirm:$False
Import-Module MSCatalog
}
#------------- PoshRSJob (multitasking)
if((Get-Module -ListAvailable -Name "PoshRSJob") -or (Get-Module -Name "PoshRSJob"))
{
Import-Module PoshRSJob
}
else
{
Install-Module -Name PoshRSJob -Scope CurrentUser -Force -Confirm:$False
Import-Module PoshRSJob
}
#------------- ImportExcel Module
if((Get-Module -ListAvailable -Name "ImportExcel") -or (Get-Module -Name "ImportExcel"))
{
Import-Module ImportExcel
}
else
{
#Install NuGet (Prerequisite) first
Install-PackageProvider -Name NuGet -Scope CurrentUser -Force -Confirm:$False
Install-Module -Name ImportExcel -Scope CurrentUser -Force -Confirm:$False
Import-Module ImportExcel
}
#Clear screen again
CLS
#----------------------------------------------------------------------------------------------------------------
#Start Timestamp
$Start = Get-Date
#Global Variables
$Path = (Split-Path $script:MyInvocation.MyCommand.Path)
$ErrorFile = (Split-Path $script:MyInvocation.MyCommand.Path) + "\ERROR.csv"
#------------------------------------ Setup Excel Variables
#The file we will be reading from
$ExcelFile = (Get-ChildItem -Path "$Path\*.xlsx").FullName
#Worksheet we are working on (by default this is the 1st tab)
$worksheet = (((New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList (New-Object -TypeName System.IO.FileStream -ArgumentList $ExcelFile,'Open','Read','ReadWrite')).Workbook).Worksheets[1]).Name
$ExcelServers = Import-Excel -Path $ExcelFile -WorkSheetname $worksheet -StartRow 1
#------------------------------------ Populate our variable with data from spreadsheet
$ExcelServersList = foreach($ExcelServer in $ExcelServers) {
$ExcelServer | Select-Object @{Name="ServerName";Expression={$_.Child}}, "Primary", @{Name="PatchWindow";Expression={$_."Patch Window"}}, @{Name="TestServer";Expression={$_."Test Server"}}, "DMZ", @{Name="OS";Expression={$_."Operating System"}}
}
#------------------------------------ Remove Duplicate entries
$SortedExcelServersList = ($ExcelServersList | Sort-Object -Property ServerName -Unique)
#------------------------------------ Seperate Servers from DMZ Servers
$FilteredServers = ForEach($SortedExcelServerList in $SortedExcelServersList) {
if($($SortedExcelServerList.DMZ) -eq $true)
{
$SortedExcelServerList.ServerName = [System.String]::Concat("$($SortedExcelServerList.ServerName)",".DMZ.com")
}
$SortedExcelServerList
}
#------------------------------------ Grab all servers from AD so we can use to compare against our list - also trimany whitespaces from output
$Servers = (dsquery * -filter "(&(objectClass=Computer)(objectCategory=Computer)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(operatingSystem=*Server*))" -limit 0 -attr Name | sort).trim()
#------------------------------------ Compare our list to servers in AD and filter out appliances
$FilteredServersResult = $Null
$FilteredServersResult = ForEach ($Item in $FilteredServers)
{
If (($item.servername -in $Servers) -or ($item.DMZ -eq $True))
{
$Item
}
}
#---------------------------- Perform our search. In this case all Monthly Updates and filter it to only show updates for Servers
$Updates = (Get-MSCatalogUpdate -Search $Search -AllPages ) | Where-Object { ($_.Title -like "*Server*") -OR ($_.Products -like "*Server*") }
#------------------------------------ Multithreading Magic
$FilteredServersResult | Start-RSJob -Throttle 50 -Batch "Test" -ScriptBlock {
Param($Server)
#Ping servers to make sure they're responsive
if($NULL -ne (Get-CimInstance -ClassName Win32_PingStatus -Filter "Address='$($Server.servername)' AND Timeout=100").ResponseTime)
{
Try
{
$ServerKBs = (((Get-HotFix -ComputerName $($Server.servername)) | Select-Object HotFixID,InstalledOn) | Where-Object {($_.InstalledOn -like "$ServerUpdateSearch" )}).HotFixID
foreach($Update in $Updates)
{
If (($Server.OS -like "*2016*") -And $Update.Products -match "*2016*")
{
#Check if there are any missing Updates
#($Server | Add-Member -NotePropertyMembers @{"Missing Updates" = $Update} -PassThru)
}
If (($Server.OS -like "*2019*") -And $Update.Products -match "*2019*")
{
#Check if there are any missing Updates
#($Server | Add-Member -NotePropertyMembers @{"Missing Updates" = $Update} -PassThru)
}
}
}
Catch
{
($Server | Add-Member -NotePropertyMembers @{"Error" = [string]$Error} -PassThru) | Export-Csv -Path $using:ErrorFile -NoTypeInformation -Force -Append
}
#Get list of Updates for servers
}
} | Wait-RSJob -ShowProgress | Receive-RSJob | Export-Csv -Path "$Path\Results.csv" -NoTypeInformation -Force
$End = (Get-Date)
$End - $Start