Пишем скрипт установки отдельных обновлений с WSUS сервера, без использования WUA.

Так я восстанавливал работоспособность Windows Update Agent, на машинах которые перестали отправлять отчеты и устанавливать обновления, но при этом синхронизировались с сервером WSUS.

Теория

Так можно установить обновления из .CAB файлов:

pkgmgr /ip /m:<path><file name>.cab /quiet

Или:

start /wait pkgmgr /ip /m:<path><file name>.cab /quiet

Или:

C:\Windows\System32\DISM.exe /Online /Add-Package /PackagePath:<path><file name>.cab /Quiet /NoRestart

Для поиска url конкретных обновлений можно воспользоваться следующим скриптом:

Скрипт скачивания обновлений с WSUS сервера (должна быть установлена консоль WSUS):
```powershell

<#
.SYNOPSIS
    This script downloads the files for an update from WSUS.
.DESCRIPTION
    WSUS holds all of it's update files in local folder structure which is not intended
    for manual file retreival.  This scripttakes advantage of the WSUS server to locate
    and download the files for one or more updates.  This script will only download the
    latest revision of any update.
.NOTES
    The script must be run on a computer with the Windows Server Update Services manager installed.
    The script must be run in a context of a user who is part of the WSUS Reporters group.
    File Name  :
        Get-WsusUpdateFiles.ps1
    Authors    :
        LandOfTheLostPass (www.reddit.com/u/LandOfTheLostPass)
    Version History:
        2016-09-30 - Inital script creation
.OUTPUTS
    [optional]Output Type: String
.PARAMETER ServerName
    WSUS Server name to get report from.  Defaults to the locally machine's WSUS server
    if it has been configured via policy.
.PARAMETER ServerPort
    WSUS Server port to connect to.  Defaults to port 443
.PARAMETER Filter
    Filter which will be used to search for updates, assumes wilcards on each side
.PARAMETER Path
    Path where the updates will be downloaded to, subfolders automatically created per update
.PARAMETER NoSSL
    Switch to turn off SSL for the WSUS server connection
.PARAMETER DisplayTitlesOnly
    Outputs the titles of updates returned by Filter rather than downloading the files
#>

Param
(
    [Parameter(mandatory=$false)]
    [ValidateScript({Test-Connection -Quiet -Count 2 -ComputerName $_})]
    [string]
    $ServerName = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(
                    "LocalMachine",
                    $env:COMPUTERNAME
                  ).OpenSubKey(
                    "Software\Policies\Microsoft\Windows\WindowsUpdate"
                  ).GetValue(
                    "WUServer"
                  ).Split("/")[-1],
    [Parameter(mandatory=$false)]
    [ValidateRange(0,65535)]
    [Int32]
    $ServerPort = 443,
    [Parameter(position=0, mandatory=$true)]
    [string]$Filter,
    [Parameter(position=1, mandatory=$false)]
    [ValidateScript({ Test-Path $_})]
    [string]$Path = $env:TEMP,
    [switch]$NoSSL,
    [switch]$DisplayTitlesOnly
)


[Reflection.Assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | Out-Null

Write-Verbose "Connecting to WSUS Server $ServerName`:$ServerPort"
try {
    $IServer = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer($ServerName, -not $NoSSL, $ServerPort)
} catch {
    #A login error is non-terminating, so we need to make it terminating
    throw $_
}

Write-Verbose "Searching for updates"
$updateList = $IServer.SearchUpdates($Filter)
$downloader = New-Object System.Net.WebClient
if($updateList.count -gt 0) {
    #Updates can have miltiple revisions, get only the latest
    $updateList = $updateList | ?{ $_.IsLatestRevision }
    if($DisplayTitlesOnly) {
        Write-Output $updateList.Title
    } else {
        Write-Verbose "Found $($updateList.count) updates, getting file info"
        foreach($update in $updateList) {
            $updatePath = Join-Path $Path $update.Title
            New-Item -Path $updatePath -ItemType Directory | Out-Null
            Write-Verbose "Getting file info for update $($update.Title)"
            $fileList = $update.GetInstallableItems().files
            Write-Verbose "Found $($fileList.count) files, starting downloads"
            foreach($file in $fileList) {
                Write-Verbose "Downloading $($file.Name) to $updatePath"
                Write-Verbose $file.FileUri
                $downloader.DownloadFile($file.FileUri, (Join-Path $updatePath $file.Name))
            }
        }
    }
} else {
    Write-Warning "Search returned no results"
}

```
Практика
Скрипт скачивания обновлений с WSUS сервера и их установка:
```powershell

<#
get-wua-updates
Автор: Павел Сатин <pslater.ru@gmail.com>
02.06.2017
Description: Скачивание и установка необходимых обновлений для восстановления работоспособности WUA
#>


$wsus_server = "222.222.222.222"
$wsus_port = "8530"
$localPath = "C:\Windows\Temp"


$kb3020369_x86_cab = "http://" + $wsus_server + ":" + $wsus_port + "/Content/CD/140CEF1C4D88922EE14FFF51B33C7668046E6FCD.cab"
$kb3020369_x64_cab = "http://" + $wsus_server + ":" + $wsus_port + "/Content/CF/FED6964A3A01D85E6D3F672322EAF1A4B1E79FCF.cab"

$kb3050265_x86_cab = "http://" + $wsus_server + ":" + $wsus_port + "/Content/4A/A384E041C518C24056076A3D339AD40F2DF42A4A.cab"

#$kb3102810_x86_cab = "http://" + $wsus_server + ":" + $wsus_port + "/Content/3E/C2111E9DB485851DB68F71CB184FC2D251EFA33E.cab"
#$kb3102810_x64_cab = "http://" + $wsus_server + ":" + $wsus_port + "/Content/F5/A6D50440882D381D3AF7474421025B76E134B7F5.cab"

$kb3172605_x86_cab = "http://" + $wsus_server + ":" + $wsus_port + "/Content/29/3C52B0AC05719CC3753ADB4D36261E97572FA029.cab"
$kb3172605_x64_cab = "http://" + $wsus_server + ":" + $wsus_port + "/Content/FB/5EFE2E384C1699F7D31EEA6BA1C9BC444E1C37FB.cab"


$downloader = New-Object System.Net.WebClient


if ([IntPtr]::Size -eq 4) {
    $architecture = "x86"

    $kb3020369_x86_cab_file = (Join-Path $localPath "kb3020369_x86.cab")
    $kb3050265_x86_cab_file = (Join-Path $localPath "kb3050265_x86.cab")
    $kb3172605_x86_cab_file = (Join-Path $localPath "kb3172605_x86.cab")


    $result = $downloader.DownloadFile($kb3020369_x86_cab, $kb3020369_x86_cab_file)
    $result = $downloader.DownloadFile($kb3050265_x86_cab, $kb3050265_x86_cab_file)
    $result = $downloader.DownloadFile($kb3172605_x86_cab, $kb3172605_x86_cab_file)

    $result = Start-Process "c:\Windows\System32\pkgmgr.exe" "/ip /m:$kb3020369_x86_cab_file /quiet /l:$localPath\kb3020369_x86_cab_file.log /norestart" -NoNewWindow -Wait
    $result

    $result = Start-Process "c:\Windows\System32\pkgmgr.exe" "/ip /m:$kb3050265_x86_cab_file /quiet /l:$localPath\kb3050265_x86_cab_file.log /norestart" -NoNewWindow -Wait
    $result

    $result = Start-Process "c:\Windows\System32\pkgmgr.exe" "/ip /m:$kb3172605_x86_cab_file /quiet /l:$localPath\kb3172605_x86_cab_file.log /norestart" -NoNewWindow -Wait
    $result


} else {
    $architecture = "x86_64"

    $kb3020369_x64_cab_file = (Join-Path $localPath "kb3020369_x64.cab")
    $kb3172605_x64_cab_file = (Join-Path $localPath "kb3172605_x64.cab")

    $result = $downloader.DownloadFile($kb3020369_x64_cab, $kb3020369_x64_cab_file)
    $result = $downloader.DownloadFile($kb3172605_x64_cab, $kb3172605_x64_cab_file)

    $result = Start-Process "c:\Windows\System32\pkgmgr.exe" "/ip /m:$kb3020369_x64_cab_file /quiet /l:$localPath\kb3020369_x64_cab_file.log /norestart" -NoNewWindow -Wait
    $result

    $result = Start-Process "c:\Windows\System32\pkgmgr.exe" "/ip /m:$kb3172605_x64_cab_file /quiet /l:$localPath\kb3172605_x64_cab_file.log /norestart" -NoNewWindow -Wait
    $result


}



#C:\Windows\System32\DISM.exe /Online /Add-Package /PackagePath:c:\Windows\Temp\kb3172605_x86.cab /Quiet /NoRestart


```

Ссылки