• WSUSでmovecontent完了後、sqlserverがCPU 100%に貼りついて何もできなくなった話

    結論

    WSUSサーバのディスク容量が逼迫してきたので、HDDを増設しContentを移動したが、 (この環境では)movecontentしないでアンインストール& 再インストール時にWsusContentのpathを変更した方がよかった。

    経緯

    HDD増設後NTFSフォーマットして、新ドライブ下フォルダにWsusContentを移動

    > cd "C:\Program Files\Update Services\Tools" 
    > wsusutil.exe movecontent F:\WSUS F:\move.log
    

    wsustil.exeが完了、コマンドプロンプトに戻ってきた。 上記move.logの内容は下記のとおりで正常に終わったぽい。

    2015-05-08T11:58:50 Successfully stopped WsusService.
    2015-05-08T11:58:50 Beginning content file location change to F:\WSUS
    2015-05-08T13:11:16 Successfully copied content files.
    2015-05-08T13:11:16 Successfully copied application files.
    2015-05-08T13:11:30 Successfully changed WUS configuration.
    2015-05-08T13:11:31 Successfully changed IIS virtual directory path.
    2015-05-08T13:11:31 Successfully removed existing local content network shares.
    2015-05-08T13:11:31 Successfully created local content network shares.
    2015-05-08T13:11:31 Successfully changed registry value for content store directory.
    2015-05-08T13:11:31 Successfully changed content file location.
    2015-05-08T13:11:33 Successfully started WsusService.
    2015-05-08T13:11:33 Content integrity check and repair...
    2015-05-08T13:11:37 Initiated content integrity check and repair.
    

    で、WSUS管理コンソールを開いたら応答がなく、 タスクマネージャーで確認したら、sqlserver(Windows Internal Databaseを使用)が CPUを食い尽くしていた。(4コア機なので実際は25%)

    WSUSのログ"C:\Program Files\Update Services\LogFiles\SoftwareDistribution.log"を確認すると、SQLserverへの接続がタイムアウトしたというエラーが出ている。 上記move.logの最後にあるように、wsusutil.exeの実行は終了しても その後wsusservice実行の中で整合性チェック&repairが呼出されるらしい。

    この処理に要する時間は、更新の数に依存するらしいが目安は不明。 以下のURLによれば「in some cases several days」もあり得るという。

    https://social.technet.microsoft.com/Forums/en-US/99df985b-f88b-471b-9b66-fd92203de323/wsus-not-working-after-movecontent?forum=winserverwsus

    開発の都合で、多数の製品を同期しており更新の数は多かった。 (管理コンソールが開かなくなったので確認できていない。 WsusContent下のファイル数でいえば3万ぐらいだったと思う)

    結局、1週間たってもCPU貼りつき状態のままだったのでアンインストール&再インストールした。 ネットワーク環境の問題もあるので一般化できないが、メタデータ同期+コンテンツDLしなおしで3日必要だった。

    環境

    • Windows Server 2008 R2 SP1
      • Windows Internal Database
      • WSUS 3.0 SP2
  • PowerShellから WSUSの更新を自動承認する

    同期される更新全部に対し、受け入れるか否かを検証・判断するのは専属の要員をアサイン出来ないなら無理だな、と思う。
    そういうわけで、一定の更新クラス(セキュリティ問題の修正プログラム、修正プログラム集、重要な更新)に対して自動承認を行っている。

    これらの自動承認とは別に「ルート証明書の更新プログラム」を自動承認したい。

    WSUS APIを参照すると、IUpdateのApproveメソッドで承認できるらしい。
    http://msdn.microsoft.com/en-us/library/aa354567(VS.85).aspx

    ということで↓のスクリプトをPowerShellで書いてみた。
    • 「すべてのコンピュータ」に対して
    • 更新の名前に「ルート証明書の更新プログラム」を含む
    • インストール承認する
    すべてのコンピュータ、を指すIComputerTargetGroupを得る
    $all_computers_group = $update_server.GetComputerTargetGroup([Microsoft.UpdateServices.Administration.ComputerTargetGroupId]::AllComputers)
    $target_computer_group = new-object Microsoft.UpdateServices.Administration.ComputerTargetGroupCollection
    $target_computer_group.Add($all_computers_group) | out-null
    

    更新に対する検索条件。Title以外にも適用されるらしいので、これだと余計なhitがあるかも。。
    $update_scope = new-object Microsoft.UpdateServices.Administration.UpdateScope
    $update_scope.TextIncludes = "ルート証明書の更新プログラム"

    IUpdateを列挙。拒否済み、最新リビジョンでないものはスキップ
    foreach($update in $update_server.GetUpdates($update_scope) | sort ArrivalDate)
    {
    if($update.IsDeclined -or -not $update.IsLatestRevision)
    {
    # 既に拒否済みのもの
    # 最新リビジョンでないもの
    # は相手にしない
    continue
    }

    適用したいコンピュータグループの数だけ承認メソッド呼び出し
      foreach($target_group in $target_computer_group)
    {
    $update.Approve(
    [Microsoft.UpdateServices.Administration.UpdateApprovalAction]::Install,
    $target_group) | out-null
    }



    いつもどおり無保証。どのような結果についても責任を負いかねます。利用は自己責任で。

    trap [Exception] {
    $t = $error[0].ToString().Trim() + "`n" + $error[0].InvocationInfo.PositionMessage.Trim()
    [Diagnostics.EventLog]::WriteEntry("approve_update", $t, "Error", 1)
    }

    [reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | out-null
    $update_server = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer()
    $update_server.PreferredCulture ="ja"

    $all_computers_group = $update_server.GetComputerTargetGroup([Microsoft.UpdateServices.Administration.ComputerTargetGroupId]::AllComputers)
    $unassigned_computers_group = $update_server.GetComputerTargetGroup([Microsoft.UpdateServices.Administration.ComputerTargetGroupId]::UnassignedComputers)

    $target_computer_group = new-object Microsoft.UpdateServices.Administration.ComputerTargetGroupCollection
    $target_computer_group.Add($all_computers_group) | out-null

    # 更新に対する条件
    $update_scope = new-object Microsoft.UpdateServices.Administration.UpdateScope
    $update_scope.TextIncludes = "ルート証明書の更新プログラム"


    foreach($update in $update_server.GetUpdates($update_scope) | sort ArrivalDate)
    {
    if($update.IsDeclined -or -not $update.IsLatestRevision)
    {
    # 既に拒否済みのもの
    # 最新リビジョンでないもの
    # は相手にしない
    continue
    }
    $rev_id = $update.Id
    write-host ($update.ArrivalDate.ToLocalTime(), $rev_id.UpdateId, $rev_id.RevisionNumber, $update.Title)
    foreach($app in $update.GetUpdateApprovals() | sort CreationDate)
    {
    $target_group = $app.GetComputerTargetGroup()
    write-host ("`t", $app.CreationDate.ToLocalTime(), $app.Action.ToString(), $target_group.Name)
    }

    foreach($target_group in $target_computer_group)
    {
    $update.Approve(
    [Microsoft.UpdateServices.Administration.UpdateApprovalAction]::Install,
    $target_group) | out-null
    }
    }

    Windows Server 2008
    WSUS 3.0 SP1
    PowerShell 1.0

  • PowerShellから WSUS APIを使う

    前提条件とやりたいこと
    • WSUSでWindows PCの更新管理をしている。
    • ここの運用では、ほとんどのコンピュータはADドメインに参加しており、コンピュータアカウントの管理者が設定してある。
    • 一定の間隔で更新未適用コンピュータの一覧を作成し自動的にメール通知するようにしたい。
      • 更新クラスが「SP、セキュリティ問題の修正プログラム、修正プログラム集、重要な更新」のいずれかで、これらの更新をインストール完了していないコンピュータの一覧。
      • メールの内容を自由にカスタマイズしたい。
    • また、当該コンピュータの管理者の氏名を一覧に含めることで連絡リストを兼ねたい。
      • WSUSで得たコンピュータ名をキーにActive Directoryに問い合わせ、管理者名を付記する。
      • ここの環境ではWSUSサーバとDCは同じサーバで運用。
    • メールの送付に関する設定および送付先はWSUSの通知設定と同じ設定を用いる
      • 「状態レポート」の通知先に送る。(更新レポートの通知先でなく)

    WSUSの機能は.NET Frameworkから利用することが出来る。
    http://msdn.microsoft.com/en-us/library/aa354567(VS.85).aspx

    PowerShellは.NETのクラスライブラリを使うことが出来るから、WSUSの操作をスクリプトから行うことが出来る。

    ということで、レポートスクリプト
    無保証です。使用は自己責任で。
    タスクスケジューラで定期的に実行している。ここの環境はw2k8のため管理者としての実行が必要だった。

    .NETのクラスライブラリ(に限らずCOMでもActiveXでも)がスクリプトからさくさく使えるのはなんとも強力。unixのtarやgzipやsha1sumのようなレベルのコマンドがないので手間がかかるな、と感じることはあるけど、書き下ろせなくはない強力さがある。

    アセンブリのロードと各種情報(更新クラスのTitleとか)の日本語化指定
    [reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration") | out-null
    $update_server = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer()
    $update_server.PreferredCulture ="ja"

    IComputerTarget.FullDomainNameからコンピュータアカウントを検索
    $searcher = New-Object DirectoryServices.DirectorySearcher
        $manage_cn = ""
        $computer_account = $computer.FullDomainName.Split(".")[0] + '$'
        $searcher.Filter = "(sAMAccountName=$computer_account)"
        $result = $searcher.FindOne()
        if($result -ne $null)
        {
            $managed_by = $result.Properties["managedby"]
    
            if($managed_by -match "CN=(?<cn>[^,]+)")
            {
                $manage_cn = $matches.cn
            }
        }
    ※$computerはIComputerTarget

    メール作成と送信
    $email_conf = $update_server.GetEmailNotificationConfiguration()
    $mail = New-Object System.Net.Mail.SmtpClient($email_conf.SmtpHostName)
    $mail.Timeout = 30 * 1000

    $msg = New-Object System.Net.Mail.MailMessage
    $msg.Subject = "[WSUS]更新未適用コンピュータ"
    $msg.From = New-Object System.Net.Mail.MailAddress($email_conf.SenderEmailAddress, $email_conf.SenderDisplayName)
    foreach($rcpt in $email_conf.StatusNotificationRecipients)
    {
    $msg.To.Add($rcpt)
    }
    $msg.Body = $body

    $mail.Send($msg)
    ※デフォルトで本文charsetおよびsubjectのMIMEエンコードのcharsetはutf8。
    ※WSUS側で通知設定がされていない場合は想定せず。
    ※ここの環境ではSMTP認証が不要のため無視している。


    環境:
      Windows Server 2008(DC)
      WSUS 3.0 SP1
      PowerShell 1.0