Powershell - Сброс SusClientId на проблемных клиентах Windows Update в домене

imageПо мотивам борьбы с проблемой отображения в консоли WSUS некорректно клонированных клиентов, наткнулся на интересную заметку - ShS's Blog - Скрипт для удаленного сброса клиента службы Автоматического обновления. Представленный в этой статье скрипт был взят за основу и несколько переработан. В частности основные переменные были вынесены в отдельный блок, исключено использование временного файла, добавлена обработка исключения возникающего при обращении к 64-битным клиентским ОС, добавлена обработка исключений возникающих при удалении несуществующих ключей реестра, добавлен более детальный вывод хода выполнения скрипта на консоль.

Скрипт работает в двух режимах, в зависимости от значения переменной $ResetSusClientId. Если значение переменной $ResetSusClientId = 0 (по умолчанию), - производится только сравнение данных о компьютерах полученных из AD и WSUS с выводом информации о значении ключа реестра SusClientId по всем проблемным клиентам. Если значение переменной $ResetSusClientId = 1, производится попытка сброса идентификационной информации клиента WU с последующей его перерегистрацией на сервере WSUS.

###############################################################
# Требования: Powershell 2.0, WSUS API (Добавляется в систему при установке консоли WSUS) 
#
Write-Host  'Loading WSUS API...' -ForegroundColor Green
[reflection.assembly]::LoadWithPartialName('Microsoft.UpdateServices.Administration') 
Write-Host ''
#
# Блок переменных 
# $WUSrvName – Имя сервера WSUS
# $WUSrvPort – Порт подключения к серверу WSUS
# $WUSrvHTTPS – Признак использования шифрования при подключении к серверу WSUS ($true или $false)
# $ADSearchOU – ADSI путь к контейнеру с доменными учетными записями пользователей (в формате LDAP://distinguishedName)
# $ADSearchFilter - Фильтр поиска объектов в AD (действующие учетные записи компьютеров)
# $ResetSusClientId – Признак необходимости форсированной смены идентификатора SusClientId у отсутствующих на WSUS клиентов (1 или 0) 
#
$WUSrvName = 'KOM-AD01-SRV-WSUS'
$WUSrvPort = 8530
$WUSrvHTTPS = $false
$ADSearchOU = 'LDAP://OU=Domain Computers,DC=mydom,DC=com'
$ADSearchFilter = '(&(objectCategory=computer)(!userAccountControl:1.2.840.113556.1.4.803:=2))'
$ResetSusClientId = 0
#
# Получаем список всех компьютеров зарегистрированных на WSUS-сервере: 
Write-Host  'Getting WU clients data from Server' $WUSrvName 'on port' $WUSrvPort '...' -ForegroundColor Green
$WSUS = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer($WUSrvName, $WUSrvHTTPS, $WUSrvPort)
$WSUScomps = $WSUS.GetComputerTargets() 
$WSUSCompNames = $WSUScomps | ForEach { $_.FullDomainName.ToUpper() } 
#
# Получаем список учетных записей компьютеров из Active Directory: 
Write-Host  'Getting AD computers from OU' $ADSearchOU '...' -ForegroundColor Green
$ADcomps = (New-Object System.DirectoryServices.DirectorySearcher([ADSI]$ADSearchOU, $ADSearchFilter)).findAll() 
$ADCompNames = $ADcomps | ForEach {$_.GetDirectoryEntry().dNSHostName.ToString().ToUpper()} 
#
# Получаем имена компьютеров, которые есть в Active Directory, но отсутствуют в WSUS:
Write-Host 'Matching...' -ForegroundColor Green
$NoWSUSCompNames = $ADCompNames | Where { $WSUSCompNames -notcontains $_ } | Sort-Object
#
# Блок смены идентификатора SusClientId 
If ($NoWSUSCompNames.Count -eq $null) 
{Write-Host 'Good. No differences.' -ForegroundColor Green}
Else
{
Write-Host 'Found problem computers...' -ForegroundColor DarkRed
Write-Host ''
ForEach ($PC in $NoWSUSCompNames)
{
$Ping = New-Object System.Net.NetworkInformation.Ping
Trap {Write-Host $PC 'can not ping' -ForegroundColor DarkRed; continue}
If ($Ping.send($PC).Status -eq 'Success' ) {
Write-Host $PC 'available' 
#
# Для того чтобы данный метод обращения к удалённому реестру успешно отработал на 64 битных системах скрипт должен запускаться в 64 PS
# Пример проверки битности - http://poshcode.org/2027
$is64 = [bool](gwmi win32_operatingsystem -computer $PC | ?{$_.caption -like '*x64*' -or $_.OSArchitecture -eq '64-bit'})
$isShell32 = [bool]((Get-Process -Id $PID | ?{$_.path -like '*SysWOW64*'}) -or !([IntPtr]::Size -eq 8))
If ($is64 -and $isShell32)
{Write-Warning 'Unable to open registry keys because PC is running an x64 OS. Script must be run from a PowerShell x64 shell' }
Else
{
$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $PC)  
$RegKey= $Reg.OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate',$true)  
$RegKeyValue1 = $RegKey.GetValue('SusClientId')
#Trap {Write-Host $PC 'can not get registry key value' -ForegroundColor DarkRed; continue}
Write-Host 'current SusClientId -' $RegKeyValue1
If ($ResetSusClientId -eq 1) 
{
Write-Host 'attempt to reset SusClientId...'
#
# Останавливаем на удаленном компьютере службу Windows Update
Write-Host 'stop Windows Update service (wuauserv)...'
[System.Reflection.Assembly]::LoadWithPartialName('system.serviceprocess')
$wuauserv=New-Object System.ServiceProcess.ServiceController('wuauserv',$PC)
$Stopped=$true
If ($wuauserv.Status -ne 'Stopped') {
            Try {
                        $wuauserv.Stop()
                        $wuauserv.WaitForStatus('Stopped',(new-timespan -seconds 10))
                        }
            Catch {
                        Write-Warning 'can not stop Windows Update service (wuauserv).'
                        $Stopped=$false
                        }
}
#
# Удаляем идентификационные ключи клиента Windows Update из реестра, предварительно проверив их наличие  
If ($Stopped) {
            Write-Host 'delete Windows Update registry keys...'
            If($RegKeyValue1 -ne $null){$RegKey.DeleteValue('SusClientId')}
            $RegKeyValue2 = $RegKey.GetValue('SusClientIdValidation')
            If($RegKeyValue2 -ne $null){$RegKey.DeleteValue('SusClientIdValidation')}
            $RegKeyValue3 = $RegKey.GetValue('PingID')
            If($RegKeyValue3 -ne $null){$RegKey.DeleteValue('PingID')}
            $RegKeyValue4 = $RegKey.GetValue('AccountDomainSid')
            If($RegKeyValue4 -ne $null){$RegKey.DeleteValue('AccountDomainSid')}
            $Started=$true 
            }
#
# Запускаем на удаленном компьютере службу Windows Update
Write-Host 'start Windows Update service (wuauserv)...'
Try {
            $wuauserv.Start()
            $wuauserv.WaitForStatus('Running',(new-timespan -seconds 15))
            }
Catch {
            Write-Warning 'can not start Windows Update service (wuauserv).'
            $Started=$false
            }
#
# Запускаем процедуру перерегистрации клиента Windows Update на сервере WSUS
If ($Started) {
            Start-Sleep -Seconds 5
            Write-Host 'reset authorization of WU client...'
            $RemoteProcess=([wmiclass]"\$PCrootcimv2:Win32_Process").create('cmd /c wuauclt /resetauthorization /detectnow')
            Write-Host "running return code - $($RemoteProcess.ReturnValue), process ID - $($RemoteProcess.ProcessId)"
            }
}
}
}
Else 
{ 
Write-Host $PC 'not available' -ForegroundColor DarkRed }
Write-Host ''
}                                 
}

Всего комментариев: 9 Комментировать

  1. Айрат /

    Очень нужный скрипт, спасибо. Только нужно доработать один момент.
    как сделать чтобы блок
    "# Получаем список всех компьютеров зарегистрированных на WSUS-сервере:"
    осуществлял поиск по подразделениям внутри $ADSearchOU = 'LDAP://OU=Domain Computers,DC=mydom,DC=com' ?

    1. Алексей Максимов / Автор записи

      То что в скрипте список компьютеров из WSUS сравнивается со списком компьютеров из AD, это совершенно очевидно. А вот что нужно Вам я не понял.

  2. Айрат /

    Разобрался.
    В OU есть много других OU. так понимаю начинает искать по всему дереву ниже той OU, что указана в переменной $ADSearchOU.

  3. Дмитрий /

    Алексей, а что делать если WSUS находится в другом домене.
    Как изменить скрипт, чтобы можно было обратиться к WSUSу другого домена?

    1. Айрат /

      Разобрался как?)

  4. Виктор /

    Добрый вечер подскажите почему на не которых ПК отображается can not ping хотя ПК доступен

    1. Алексей Максимов / Автор записи

      Здравствуйте, Виктор.
      Причины могут быт разные.
      Возможно есть проблемы с доступностью протокола ICMP на удалённой машине, возможно есть проблемы с DNS (обращаетесь по имени, а в DNS это имя отсутствует) и т.д.
      Вообще код скрипта уже довольно старый и может быть оптимизирован, но я специально проверил его на клиенте с Windows 10 и часть, отвечающая за ping, всё ещё работает.

      1. Айрат /

        Я даже не сразу вспомнил про что тут написано, как же давно я был сисадмином. Уведомление ностальгии о начале пути в ИТ)

  5. Виктор /

    Скрипт хоть и старый но очень актуальный еще разобраться почему часть компов нормально пингуются и отрабатываются в скрипте а часть показует что ПК не пингуется и еще одну вещь заметил если в подразделение создана 3 контейнера (папки подразделения) скрипт смотри только верхние контейнеры(папки подразделения). За скрипт большое спасибо!!!!

Добавить комментарий