System Center 2012 Configuration Manager 中的软件更新内容清理

我们在 System Center 2012 Configuration Manager 中添加了一项功能,来从分发点自动删除与过期的更新相关的软件更新内容。该流程可删除您不再需要的任何内容,进而有助于您管理分发点的驱动器空间。这可对端点保护定义更新进行定期发布、部署和过期的操作,因而对于此类更新尤为有用。在本篇博文中,我将向您准确介绍该流程的工作原理,由于我们开箱即用的流程并不会管理源内容,因此我还将在本文中为您介绍一个用于清除与已过时的更新相关的源位置的脚本。

过期的更新与部署

管理过期更新相关内容的流程中的第一步就是将过期的更新移出任何部署的更新组。首先,由于我们不希望删除与您的部署相关的任何内容,因此我们始终不会删除与活动部署相关的任何过期的更新。如果更新从您的部署中消失,而且您并不知道其中的原因,那么这必将让您感到很紧张,因此我们并不会这样做。然而,令过期的更新变得可删除的步骤很简单,而且其应该包含于每月所进行的流程中。将过期的更新从所有部署中删除只需数次点击即可实现,请执行以下步骤。请注意:使用自动部署规则来传递定义更新时,您应在每次运行规则的时候重新使用相同的更新组,系统将在每次规则运行时从更新组中自动删除过期的更新。

  1. 导航到软件库下的所有软件更新节点,并搜索所有过期的更新。

     

  2. 为需搜索的过期的更新添加值,保留默认值 [Yes](是),并单击 [search](搜索)。

  3. 搜索结果将包含您所有过期的更新,因此您只需在列表视图中选中所有更新 (CTRL+A),单击右键,并选择 [Edit Membership](编辑成员)。

     

  4. 然后您将看到所有更新组的列表,其中列表视图中选定的任何更新均是成员。您只需取消选中选定的复选框。

  5. 单击 [OK](确定),以删除选定的更新组中所有过期的更新,系统将通过下一步中所概述的流程删除这些更新。

删除过期的更新的流程

接下来,删除过期的更新及其相关的内容共含四个阶段,分别是:过期操作、清除、删除,以及源清理(一个执行脚本的操作)。在较高的级别中,已经过期,且不属于活动部署的更新将在其过期 7 天后被删除。系统中并无特定或可配置的维护任务来进行这一操作,这是一个跨多个 ConfigMgr 组件的自动化流程。让我们一起来详细看看这一流程,从而让您理解其工作原理。

  1. 当系统运行完整软件更新点同步时,更新将过期。完整软件更新点同步是一个计划的同步,而非通过控制台启动的增量同步。 只有完整/计划的同步将使更新过期。

  2. 完整同步将使更新过期的原因如下:

    1. 发布服务器使更新过期。
    2. 发布服务器取代了更新(且发生于您针对软件更新点进行配置的 [keep superseded updates](保留被取代的更新)时间以外)。
    3. WSUS 拒绝或丢失更新。 
  3. 您可在 wsyncmgr.log 日志文件中查看过期活动。请在日志中查看类似于以下的行:

    Removed 106 unreferenced updates SMS_WSUS_SYNC_MANAGER 3/30/2012 12:06:15 AM

  4. 过期的更新仍将显示于控制台中,但是这些更新已被标记为 [expired](过期),并包含以下图标来显示其状态:他们将在过期后于控制台中保留 7 天,下一次处理将开始清理操作。

  5. 7 天后,与活动的部署无关的过期的更新将被清除。这意味着用户无法再在 UI 中看到这些更新。再次提示,您可使用 wsyncmgr.log 来查看这些更新的删除过程:

    Deleting old expired updates...SMS_WSUS_SYNC_MANAGER 3/30/2012 12:06:16 AM
    Deleted 80 expired updates SMS_WSUS_SYNC_MANAGER 3/30/2012 12:06:20 AM
    Deleted 80 expired updates total SMS_WSUS_SYNC_MANAGER 3/30/2012 12:06:20 AM

  6. 接下来,系统将通过向 objmgr.box 发送 .CIN 通知文件,通知对象复制管理器进行此项删除。

    Found notification file C:\Program Files\Microsoft Configuration Manager\inboxes\objmgr.box\16824032.CIN SMS_OBJECT_REPLICATION_MANAGER 3/30/2012 12:06:11 AM
    Completed processing CIN notification files SMS_OBJECT_REPLICATION_MANAGER 3/30/2012 12:07:31 AM
    Successfully deleted SoftwareUpdate 3da7abb4-d643-422c-9e6d-a5f838fb71a1 SMS_OBJECT_REPLICATION_MANAGER 3/30/2012 12:07:32 AM

  7. 最后,分发管理器将每小时检查一次,并删除与被 objreplmgr 删除的更新 CI 相关的任何分发点内容。

  8. 由于针对过期的更新的元数据和分发内容已被删除,因此我们需要继续清理源目录,您可使用本篇博文末尾所介绍的脚本来进行这一操作。

  9. 只需使用以下提供的内容来创建一个 VBS,并在顶级站点服务器(CAS 或独立主站点)中运行该 VBS 即可。由于格式的问题,我们在 [CR] 所显示的位置中插入了换行符。请删除这些换行符,然后再执行该脚本。有关该脚本的一些注意事项:

    1. 请确保您有(从您运行脚本的站点服务器)访问内容源的权限。
    2. 脚本将为不再拥有相关的 CI 的任何更新删除源内容。
    3. 源目录清理并不一定会映射到源目录中的日期 - 时间戳。由于某些二进制文件将与其他二进制文件链接,因此数据库中可能存在一个 CI 引用。换句话说,如果您清理了端点保护定义,而源目录仍然包含 7 天以上的文件和文件夹,那么这些文件中将仍然包含活动的 CI,且系统需要这些文件(无需手动删除它们)。
    4. 最后,脚本将查找引用 CI 所不需要的源内容,并将删除该源内容。

总结

Configuration Manager 2012 清理过期的内容和相关内容的功能是一个有助于使您的控制台、数据库、分发点,以及(包含脚本的)源目录尽可能保持干净的内置机制。希望您能够更好地理解进行这一流程所需的手动操作和自动化操作,进而共同管理过期的更新和内容。

 option explicit
 dim CRLF: CRLF=chr(13) & chr(10)
  
 dim WSH: set WSH = CreateObject("WScript.Shell")
 dim FSO: set FSO = CreateObject("Scripting.FileSystemObject")
  
 dim WMI: set WMI = GetObject("winmgmts:/root/SMS")
 dim Prov: set Prov = nothing
 dim I, f
  
 for each I in WMI.ExecQuery("select * from SMS_ProviderLocation where ProviderForLocalSite=TRUE")
     set Prov = I
 next    
 if Prov is nothing then err.Raise 438,"No provider found for the local site"
  
 dim SiteCode: SiteCode = Prov.SiteCode
 dim SiteServer: SiteServer = Prov.Machine
  
 set WMI = GetObject("winmgmts:" & Prov.NamespacePath)
  
 dim Pkg
 for each Pkg in WMI.ExecQuery("select * from SMS_SoftwareUpdatesPackage")
  Log "Processing package " & Pkg.PackageID & " - " & Pkg.Name
  ' remove orphaned package-to-content relations (not associated with CIs)

for each i in WMI.ExecQuery("select pc.* from SMS_PackageToContent pc left join [CR]
SMS_CIToContent cc on cc.ContentID=pc.ContentID where pc.PackageID=""" & Pkg.PackageID & """ and [CR]
cc.ContentID is null")

      i.Delete_
  next
  
  Log "Package source path: " & Pkg.PkgSourcePath
  dim srcFolder: set srcFolder = FSO.GetFolder(Pkg.PkgSourcePath)
     dim foldersToDelete: set foldersToDelete = CreateObject("Scripting.Dictionary")
  foldersToDelete.CompareMode = vbTextCompare
  
  ' collect subfolders currently in pkg source
     for each i in srcFolder.SubFolders
      if i.Attributes=16 and i.Name<>"." and i.Name<>".." then 
             'Log "Existing folder " & i.Name
             foldersToDelete.Add i.Name, nothing
         end if
  next
  ' exclude subfolders associated with active content

for each i in WMI.ExecQuery("select pc.* from SMS_PackageToContent pc where [CR]
pc.PackageID=""" & Pkg.PackageID & """")

         'Log "Excluding active folder " & i.ContentSubFolder

if foldersToDelete.Exists(i.ContentSubFolder) then [CR]
foldersToDelete.Remove(i.ContentSubFolder)

  next
  
  f = vbFalse
  ' delete remaining folders
  for each i in srcFolder.SubFolders
         if foldersToDelete.EXists(i.NAme) then
             Log "Deleting orphaned subfolder " & i.name
             i.Delete
             f = vbTrue
         end if 
  next
  if f = vbTrue then
         Log "Refreshing package " & pkg.PackageID
         pkg.RefreshPKgSource
  end if
 next
 Log "cleanup completed"
  
 '================== logging support ==============
 dim logMode
 dim logWindow
  
 sub Log(msg)
  if IsEmpty(logWindow) then call LogInit
  select case logMode
      case "console":   WScript.StdOut.WriteLine msg

case "window": if IsObject(logWindow) then [CR]
logWindow.document.all.logLines.innerHtml = logWindow.document.all.logLines.innerHTML & msg & [CR]
"<br/>"

  end select
 end sub
  
 sub LogInit
  if not WScript.Interactive then
      logMode = "none"
  elseif lcase(right(WScript.FullName, 11))="cscript.exe" then
      logMode = "console"
  else

set logWindow = WScript.CreateObject("InternetExplorer.Application", [CR]
"Log_")

      with logWindow
          .Navigate("about:blank")
          .Document.Title = WScript.ScriptName
          .AddressBar = false
          .ToolBar = false
          .StatusBar = false
          .Resizable = true
          .Visible = 1
          do while .Busy: WScript.Sleep 100: loop

.document.body.innerHTML = "<div id=""logLines"" [CR]
style=""font:10pt sans-serif;text-align:left;"" />"

      end with
      logMode = "window"
  end if
 end sub
  
 sub LogTerm
  if IsObject(logWindow) then logWindow.Quit: set logWindow = nothing
 end sub
  

' provide a way of terminating the script in windowed mode - closing the log window will [CR]
terminate the script

 sub Log_onQuit
  WScript.Quit
 end sub

 

--Jason Githens

本篇博文按照“原样”提供,且不提供任何担保和授予任何权利。