5

We have a very tight outage window and for many of our systems, the servers must be rebooted in the correct order. Because of this, I would like to script our updates.

I have attempted to use this Powershell script found in the Microsoft Script Repository, however, for me it does not work remotely, doesn't always work using Invoke-Command and it starts the installation then returns without waiting for installation to complete. I would like each system rebooted after the installations are complete, this is difficult to script without the installation blocking or status information to block upon. After spending far too much time, attempting to make the CCM_SoftwareUpdate and CCM_SoftwareUpdatesManager WMI classes do what I need, I thought it was time to ask how others might be handling similar situations.

A friend of mine says his company solved this problem by using Shavlik, unfortunately, that is not an option here.

We are using SCCM 2012 and have a mix of 2003, 2008 and 2012 servers.

2
  • Exactly how narrow is your outage window?
    – user62491
    Feb 13, 2014 at 0:22
  • We have advertised that it is two hours, starting at 1 AM, on the third Sunday. One of the systems requires a phone call to a 3rd party to be "put in stand-in". The other systems I patch manually have single points of failure, either because they have yet to reach that level of maturity to have high-availability features or they are running on a single instance of SQL. I would be fine with letting SCCM randomly (within the outage window) update the later, if it can be made to restart them in the correct order. If it could also initiate a database fail-over, all the better. Feb 13, 2014 at 18:50

2 Answers 2

2

Yes. You can do this with powershell similar to what the script you posted tries to do. I came across that script a while back, can't remember if it worked or not but I did get something to work. don't know why his wouldn't work, it does use the same methods, but I was able to do this with .NET and WMI using C#, so I know it can be done with powershell.

private void InstallUpdates()
    {
        ManagementScope sc = new ManagementScope(@"\\.\root\ccm\clientsdk");
        ManagementClass c = new ManagementClass(@"CCM_SoftwareUpdatesManager");
        ManagementObjectSearcher s = new ManagementObjectSearcher("SELECT * FROM CCM_SOFTWAREUPDATE WHERE COMPLIANCESTATE=0 AND EVALUATIONSTATE < 2");
        c.Scope = s.Scope = sc;

        ManagementObjectCollection col = s.Get();
        List<ManagementObject> lUpdates = new List<ManagementObject>();

        //Install each update individually and display progress
        int index = 1;

        //double progress = 5/10;

        //progressBar1.Value = progress;
        //this.Enabled = false;

        foreach (ManagementObject o in col)
        {

            System.Management.ManagementBaseObject[] args = { o };

            object[] methodArgs = { args };

            c.InvokeMethod("InstallUpdates", methodArgs);

            lblCurrentUpdate.Text = "Now Installing Update " + index + " of " + col.Count;

            UInt32 evalState = 0;
            progressBar1.Value = (int)(((index) / (double)col.Count)*100.0);

            //isCompleted = false;
            //backgroundWorker1.RunWorkerAsync(o);

            while (evalState < 7)
            {

                try
                {
                    o.Get();
                    evalState = (UInt32)o.Properties["EvaluationState"].Value;
                }

                catch (Exception ex)
                {
                    break;
                }

            }

            ++index;

        }

        //this.Enabled = true;

        //Restart Workstation
        System.Diagnostics.Process.Start("shutdown.exe", "-r -t 0 -f");

        Application.Exit();

    }

In a nutshell, I used the WMI query "SELECT * FROM CCM_SOFTWAREUPDATE WHERE COMPLIANCESTATE=0 AND EVALUATIONSTATE < 2" and passed each update one by one to the InstallUpdates method, because I wanted to display the progress similar to how Microsoft does it. You must pass an array to the InstallUpdates method, even if you're only passing a single update object. You could pass the entire returned array if you want, and it will queue them up like it always does, installing them one by one.

Also, what about configuring a maintenance window for this collection, and telling sccm not to install updates outside the maintenance window? When one update is finished, if it's past the maintenance window then it will stop installing updates (in theory for me, I've never had the maintenance window luxury).

if you're a gimmiedehcodez kinda person, that code should compile in with .NET 4.0 and

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Management;
using System.Windows.Forms;

More info on the CCMClientSDK here

1

Here is what I put together from various web pages. Using Powershell. Please note that you will have better success using WinRM (winrm quickconfig on the remote servers to enable WinRM) which can use the Invoke-Command cmdlet instead of the Invoke-WmiMethod but this method works for me on Server 2008 and newer. For Server 2003, the Updates deployment evaluation scan works but Powershell will complain about something. Run the commands locally to work around that.

#Start the System Center Configuration Manager Software Updates Deployment Evaluation Scan
$trigger = '{00000000-0000-0000-0000-000000000108}'
$scan = Invoke-WmiMethod -ComputerName $server -Namespace root\ccm -Class sms_client -Name TriggerSchedule $trigger

[System.Management.ManagementObject[]] $CMMissingUpdates = @(GWMI -ComputerName $server -query "SELECT * FROM CCM_SoftwareUpdate WHERE ComplianceState = '0'" -namespace "ROOT\ccm\ClientSDK") #End Get update count.
(GWMI -ComputerName $server -Namespace "root\ccm\clientsdk" -Class "CCM_SoftwareUpdatesManager" -List).InstallUpdates($CMMissingUpdates)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .