You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WSUS下游服务器删除计算机后未同步至主服务器问题求助

问题描述

使用PowerShell调用Microsoft.UpdateServices.Administration.ComputerTargetScope类的.Delete()方法从WSUS下游服务器删除计算机时,遇到以下问题:

  • 计算机已从下游服务器成功删除,但该删除操作未同步到主WSUS服务器
  • 执行双向同步后,主服务器上仍存在该计算机

附带的脚本如下:

#Définition des paramètres d'invocation du script
[CmdletBinding()]
param
(
    [Parameter(Mandatory = $true, Position = 1)]
    [string]
    $UpdateServer,

    [Parameter(Mandatory = $true, Position = 2)]
    [int]
    $Port,

    [Parameter()]
    [switch]
    $UseSSL,

    [Parameter()]
    [int]
    $ExclusionPeriod = 0
)

#Création du dossier de logs
if (-not (Test-Path -Path "$PSScriptRoot\WsusDeleteComputersLogs"))
{
    New-Item -Path $PSScriptRoot -Name 'WsusDeleteComputersLogs' -ItemType Directory -Force
}

#Création du fichier de logs qui va restranscrire les output du script
$file = "$PSScriptRoot\WsusDeleteComputersLogs\{0:yyyyMMdd_HHmm}_WSUS_Delete_Computers.log" -f (Get-Date) 

#Début de la retranscription pour écrire tous les logs du script dans le fichier ci-dessus
Start-Transcript -Path $file

#Création de variables contenant les chemins des fichiers où seront listées les mises à jours remplacées et rétablies
$outDeletedList = Join-Path -Path "$PSScriptRoot\WsusDeleteComputersLogs" -ChildPath ('{0:yyyyMMdd_HHmm}_DeletedComputers.csv' -f (Get-Date))

#Création de la ligne des titres dans le fichier CSV des mises à jour remplacées et rétablies
Set-Content -Value 'Server; FullDomainName; LastReportStatusTime; In Stock' -Path $outDeletedList

#Importe le module ActiveDirectory pour se connecter à l'AD
Import-Module ActiveDirectory

#Récupération des pcs présents dans l'OU Stock
$OUstock = (Get-ADComputer -SearchBase "OU=Stock,DC=domain,DC=lan" -Filter * -SearchScope Subtree).DNSHostName

#Création d'une variable pour se connecter au serveur WSUS avec ou sans SSL
try
{
    if ($UseSSL)
    {
        Write-Output -InputObject "Connecting to WSUS server $UpdateServer on Port $Port using SSL... "
    }
    else
    {
        Write-Output -InputObject "Connecting to WSUS server $UpdateServer on Port $Port... "
    }
    
    [reflection.assembly]::LoadWithPartialName('Microsoft.UpdateServices.Administration') | Out-Null
    $wsusMaster = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer($UpdateServer, $UseSSL, $Port);
}
catch [System.Exception] 
{
    Write-Output -InputObject 'Failed to connect.'
    Write-Output -InputObject "Error: $($_.Exception.Message)"
    Write-Output -InputObject 'Please make sure that WSUS Admin Console is installed on this machine'
    Write-Output -InputObject ''
    $wsusMaster = $null
}

#Arrête le script si la connexion au serveur a échoué
if ($null -eq $wsusMaster)
{
    return
}

Write-Output -InputObject 'Connected.'

#Création d'une variable qui contiendra tous les serveur en aval
try
{
    #Récupération de la liste des serveur WSUS en aval du Master
    $wsusDownstreamServers = $wsusMaster.GetDownstreamServers()
}
catch [System.Exception]
{
    Write-Output -InputObject 'Failed to get Downstream Servers.'
    Write-Output -InputObject "Error: $($_.Exception.Message)"
    Write-Output -InputObject ''
    return
}

#Initie les filtres pour une liste d'ordinateurs dans une variable
$computerScope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope

#Indique la date et l'heure actuels
Write-Host "Date du jour : " (Get-Date)
#Définit la plage des dates des derniers rapports des ordinateurs dans la variable initiée ci-dessus

Write-Host "Date limite : "((Get-Date).AddDays(-8))

#Création des variables utilisées dans le script
$countAllComputers = 0
$countOutDatedAll = 0
$countInStockAll = 0
$countDeleted = 0

Foreach($wsusDownstreamserver in $wsusDownstreamServers)
{
    $computerScopeIn = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope
    $computerScopeIn.ToLastReportedStatusTime = (Get-Date).AddDays(-8)

    Write-Host $wsusDownstreamserver.FullDomainName

    try
    {
        $connectDownstreamserver = (Get-WsusServer -Name $wsusDownstreamserver.FullDomainName -PortNumber "8530")
        Write-Host "Connected to : $($wsusDownstreamserver.FullDomainName)"
        $computerList = $connectDownstreamserver.GetComputerTargets()
        $computerListScope = $connectDownstreamserver.GetComputerTargets($computerScopeIn)
    }
    catch [System.Exception] 
    {
        Write-Output -InputObject 'Failed to connect.'
        Write-Output -InputObject "Error: $($_.Exception.Message)"
        Write-Output -InputObject 'Please make sure that WSUS Admin Console is installed on this machine'
        Write-Output -InputObject ''
        continue
    }

    
    Write-Host "Groupe $($wsusDownstreamserver.FullDomainName) : "
    Write-Host ""
    Foreach($computer in $computerList)
    {
        $countAllComputers++
        if($OUstock -contains $computer.FullDomainName)
        {
            $countStockAll++
            Write-host "L'ordinateur $($computer.FullDomainName) est dans l'OU Stock"
            "$($wsusDownstreamserver.FullDomainName); $($computer.FullDomainName); $($computer.LastReportedStatusTime.ToLocalTime()); TRUE" | Out-File $outDeletedList -Append
            $computer.Delete()
            $countDeleted++
        }
    }

    Foreach($computerScope in $computerListScope)
    {
        $countOutDatedAll++
        Write-host "L'ordinateur $($computerScope.FullDomainName) n'a pas été synchronisé depuis le $($computerScope.LastReportedStatusTime.ToLocalTime())"
        "$($wsusDownstreamserver.FullDomainName); $($computerScope.FullDomainName); $($computerScope.LastReportedStatusTime.ToLocalTime()); FALSE" | Out-File $outDeletedList -Append
        $computerScope.Delete()
        $countDeleted++
    }
    Write-Host ""
    Write-Host "-----"
    Write-Host ""
    $computerListScope = 0
    $computerListScope = 0
}

#Log de sortie contenant les informations de comptage ci-dessus
Write-Output -InputObject 'Summary:'
Write-Output -InputObject '========'
Write-Output -InputObject "All Computers = $($countAllComputers)"
Write-Output -InputObject "All OutDated Computers = $($countOutDatedAll)"
Write-Output -InputObject "All In Stock OU = $($countStockAll)"
Write-Output -InputObject "All Deleted Computers = $($countDeleted)"
Write-Output -InputObject ""
Write-Output -InputObject "---"
Write-Output -InputObject "End"

Stop-Transcript
解决方案

1. 问题根源

WSUS默认同步机制不会自动同步下游服务器的计算机删除操作到主服务器,主服务器仅同步下游的更新批准、计算机组配置等信息,反向同步不包含计算机删除事件。

2. 具体修复方案

方案一:同步删除主服务器对应计算机

在下游删除计算机后,直接在主服务器中找到对应条目并删除,确保两端数据一致。修改脚本中删除计算机的逻辑:

# 原下游删除代码后添加:
# 在主服务器查找该计算机(可通过FullDomainName匹配)
$masterComputer = $wsusMaster.GetComputerTargets() | Where-Object { 
    $_.FullDomainName -eq $computer.FullDomainName -and 
    $_.ParentServerName -eq $wsusDownstreamserver.FullDomainName
}
if ($masterComputer) {
    $masterComputer.Delete()
    Write-Host "同步从主服务器删除计算机:$($computer.FullDomainName)"
}

注:通过ParentServerName过滤可避免同名计算机被误删。

方案二:切换到主服务器管控删除(仅复制模式下游)

如果下游服务器是复制模式,所有操作应从主服务器发起。调整脚本逻辑:

  1. 在主服务器中筛选需要删除的计算机(对应下游的目标机器)
  2. 删除主服务器上的计算机条目
  3. 主服务器会自动同步删除操作到下游服务器

方案三:强制下游向主服务器同步计算机元数据

删除计算机后,触发下游服务器的同步任务,强制将计算机状态同步到主服务器:

# 删除计算机后添加:
$subscription = $connectDownstreamserver.GetSubscription()
$syncManager = $subscription.GetSynchronizationManager()
# 启动同步
$syncManager.StartSynchronization()
# 等待同步完成(可选)
while ($syncManager.GetSynchronizationProgress().State -ne 'Completed') {
    Start-Sleep -Seconds 10
    Write-Host "等待同步完成..."
}

3. 脚本额外优化点

  • 删除无意义的重复代码:$computerListScope = 0重复执行,可直接移除
  • 替换硬编码端口:从主服务器获取下游服务器的实际端口,避免固定使用8530
  • 增强错误处理:在$computer.Delete()后添加try-catch,确保删除成功再记录日志

内容的提问来源于stack exchange,提问作者Livarkhal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 09:05:57