Video Screencast Help
Give us your opinion and win with Symantec! Please help us by taking this survey and tell us about your satisfaction level using Symantec Connect. One lucky winner will receive 500 Connect points!* Take the survey.

Patch Management Automation 7.1: A Look at the Project and Code

Created: 13 Nov 2012 | 4 comments
Language Translations
Ludovic Ferre's picture
+2 2 Votes
Login to vote

Due to popular demand on the download tool [1] (okay, one person asked - but that's enough for me ;-) I am releasing and documenting the source code (Visual Studio 2005 solution and project) here.

We will review here the assembly references needed to build the project, the code itself and the SQL (as this could be changed to target only bulletins with vulnerable computers).

So, let's dive into the project and assembly references: first of all we are using the PatchManagement WorkFlow API to stage and create the bulletin policies. This API is implement in "<install_path>\Patch Management\Core\Web\bin\Altiris.PatchManagementCore.Web.dll".

Here is a screen shot of some of the classes and methods exposed by this DLL (with the one that interest us highlighted):

Then we need to bring in a few assemblies from the GAC (which can be tricky in itself, but that's another subject on its own), namely:

  • Altiris.NS.dll
  • Altiris.NS.Common.dll
  • Altiris.NS.Database.dll
  • Altiris.Resource

And here comes a review of the code itself (from program.cs, unabridged but broken down into sections of interest):

First we do the traditional declaration of types namspaces that we will use, so we don't have to provide full path to classes to use them.

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;

using Altiris.NS;
using Altiris.Resource;
using Altiris.NS.ItemManagement;
using Altiris.NS.ContextManagement;
using Altiris.NS.Security;
using Altiris.Common;
using Altiris.PatchManagementCore.Web;

Then comes the name space declaration, class declaration and global variables:

namespace ZeroDayPatch
    class Program
        static string targetGuid = "";
        static string severity = "critical";
        static bool dryrun = false;
        static bool test = false;

This tool is command line based, so the program starts in the function Main():

        static void Main(string[] args)

Which starts processing by checking the command line argument and setting global variables accordingly:

            Console.Write("ZeroDayPatch starting");
            foreach (string arg in args)
                if (arg.ToLower().StartsWith("/targetguid="))
                    targetGuid = arg.Substring("/targetguid=".Length);
                    Console.Write(", Custom target guid = {0}", targetGuid);
                if (arg == "/dryrun")
                    dryrun = true;
                    Console.Write(", tryrun = true");
                if (arg == "/test")
                    test = true;
                    Console.Write(", test = true");
                if (arg.ToLower().StartsWith("/severity="))
                    severity = arg.Substring("/severity=".Length);
                    Console.Write(", severity = {0}", severity);


Now we're all set, so we can start the real work: get a list of bulletins we want to check and process them:

            int i = 0;
                GuidCollection bulletins = new GuidCollection();

                bulletins = GetSoftwareBulletins();

                PatchWorkflowSvc wfsvc = new PatchWorkflowSvc();

                string name = "";

                if (dryrun)
                    Console.WriteLine("\n######## THIS IS A DRY RUN ########");
                foreach (Guid bulletin in bulletins)
                    name = Item.GetItem(bulletin).Name;
                    Console.WriteLine("Processing bulletin {0} ({1}) now.", name, bulletin);
                    if (wfsvc.IsStaged(bulletin.ToString()))
                        Console.WriteLine("\tThis bulletin is already staged.");
                        Console.WriteLine("\t... bulletin will be stagged now.");
                        if (!dryrun)
                            wfsvc.EnsureStaged(bulletin.ToString(), true);
                        Console.WriteLine("\tBulletin is now stagged.");
                    Console.WriteLine("\tChecking if we need to create a new policy now.");

                    if (targetGuid == "" || targetGuid.Length == 0)
                        targetGuid = "f74f662b-f586-457a-9a2e-804cc8f347e5";

                    string policyGuid = "";
                    policyGuid = wfsvc.ResolveToPolicies(bulletin.ToString());

                    // Console.WriteLine("Existing policy guids: " + policyGuid);
                    if (policyGuid == "" || policyGuid.Length == 0)
                        Console.WriteLine("\t... create a policy for the bulletin now.");
                        if (!dryrun)
                            wfsvc.CreateUpdatePolicy(name, bulletin.ToString(), targetGuid, true);
                        Console.WriteLine("\tSoftware update policy created!");
                        Console.WriteLine("\tA policy already exists for this bulletin.");
                    if (i == 10  && test)
                        break; // Limit the staging to 10 bulletin whilst testing
            catch (Exception e)

            Console.WriteLine("\n{0} software update policy creation tasks were started.", i.ToString());
            Console.WriteLine("ZeroDayPatch execution completed now. See you soon...");

/*          Console.Write("Press enter to terminate...");
*/      }

Here is the method where we get the list of bulletins: you can use a procedure or SQL query of your own to get bulletins that are for example applicable to the environement:

        public static GuidCollection GetSoftwareBulletins()
            GuidCollection gcResources = new GuidCollection();

            string strSelect = @"exec spPMCoreReport_AllSoftwareBulletins";

            using (DatabaseContext context = DatabaseContext.GetContext())
                SqlCommand cmdAllResources = context.CreateCommand() as SqlCommand;
                cmdAllResources.CommandText = strSelect;

                using (SqlDataReader sqlRdr = cmdAllResources.ExecuteReader())
                    while (sqlRdr.Read())
                        Guid bulletinGuid = sqlRdr.GetGuid(0); // Position 0 = _ResourceGuid
                        String sev = sqlRdr.GetString(2); //Position 2 = Severity

                        // if (sev.ToUpper() != "UNCLASSIFIED" && sev.ToUpper() != "LOW" && sev.ToUpper() != "MODERATE")
                        if (sev.ToUpper() == severity.ToUpper())
                            //Console.WriteLine("Acquired guid {0} with severity {1}...", bulletinGuid.ToString(), sev);
            Console.WriteLine("{0} bulletins match the {1} severity and will be checked for policies.", gcResources.Count, severity);
            return gcResources;


This is it. Any comments and feedback is more than welcome as usual :D.


Comments 4 CommentsJump to latest comment

Rich Nahra's picture

Thank you for posting this. Very useful information.  I'm wondering why you imported from the GAC instead of using the native webservices.  I'm assuming the webservices lack a method or two?  If not, using the webservices would ensure (being optimistic here)  the program would still run after an upgrade.  Or maybe you found the XML serialization takes to long..  I'm curious.  



Login to vote
Ludovic Ferre's picture

There are a few reason why I need to access GAC assemblies.

For Altiris.NS I need it to resolve the Bulletin names in this section:

name = Item.GetItem(bulletin).Name;

For Altiris.Database I need it to setup the context (and get the needed DB access):


Overall it's upgrade safe because the database and item method I use have been in the product since the migration to .Net in NS 6.0. And of course it's way faster and easier than accessing web-services (specially when you are on the SMP server itself.

PS: On a sad note, the ASDK has not proven itself to be upgrade safe from 6.0 to 7.0 and 7.1. I will rebase my current build to use plain 7.1 SP2 assemblies, and that should cover a safe 7.1 support (to include the various rollups, and maintenance packs).

Ludovic FERRÉ
Principal Remote Product Specialist

Login to vote
eck8tor's picture

Hi Ludovic,

I'm realy interest about this but I don't find some reference (dll) and I don't understand the GAC ! Could you please help me to understand and where I can found any references.

Thank you so much for your help.



Login to vote
Brian Nelson's picture


I ran into the same issue. The following link helped me figure out about the GAC. I used the registry method to expose GAC in explorer.

I was then able to locate all the needed DLL files in this directory on my notification server:


Hope that helps!


Login to vote