Adding custom data to WMI, as a low rights user, and having SMS collect that data

Recently I was working on an issue which involved moving from a custom solution, which used noidmifs to insert data into SMS, to a solution where an end user inserted data directly into WMI and that data was collected by SMS. The requirements for the solution were that it must be done all using scripting.

This seemed simple at first, but after digging further into the issue I ran into a road block. The issue involved the creation of the custom namespace. I could easily script the creation of the custom namespace and deliver this script via SMS, but the permissions on the namespace did not allow an end user to write to it. I could not find a way to set the permissions on this namespace by using only vbscript (this could be done with C++ or C# easily though). I asked a couple of my peers that work with scripting and WMI quite a bit and both Bob Wilton and Wesley McSwain were able to provide me with a method to do this using only vbscript.

The "proof on concept" for this scenario is detailed below.

  1. On a test machine, in your lab, run the following vbscript.

    'CreateCustomNameSpace.vbs

    'Rslaten 03/23/2007

    'This script creates a custom namespace and class

    'In theory, this should be delivered via SMS and run with admin rights

    'Create namespace

    Set oLocator = CreateObject("WbemScripting.sWbemLocator")

    Set oSvc = oLocator.ConnectServer(".", "root")

    Set oNamespace = oSvc.Get("__namespace")

    Set oCustomNameSpace = oNamespace.SpawnInstance_

    oCustomNamespace.name = "Custom"

    oCustomNamespace.Put_()

    'Create class to hold custom data

    wbemCimtypeString = 8

    wbemCimtypeUint32 = 19

    Set oWMI = GetObject("winmgmts:root\Custom")

    Set oClass = oWMI.Get()

    oClass.Path_.Class = "UserData"

    oClass.Properties_.add "EmployeeID", wbemCimTypeUint32

    oClass.Properties_("EmployeeID").Qualifiers_.add "key", true

    oClass.Properties_.add "FirstName", wbemCimtypeString

    oClass.Properties_.add "LastName", wbemCimtypeString

    oClass.Properties_.add "Department", wbemCimtypeString

    oClass.Properties_.add "OfficeNumber", wbemCimTypeString

    oClass.Put_()

  2. Now that the custom namespace and class is created we must configure the WMI permissions manually on this client.  Using Computer Management\WMI Control add the "Partial Write" right to the "Custom" namespace for your end users group, domain users, etc…

  3. Now that we have the permissions configured like we want them we have to capture the permissions so later we can set the same permissions programmatically on all clients. Run the following vbscript on the test machine.

    'GetSD.vbs

    'Gets the permissions from the custom namespace and writes them to c:\sd.txt

    ComputerName = ""

    NameSpace = "root\custom"

    FilePath = "c:\sd.txt"

    Set wmiLocator = CreateObject("WbemScripting.SWbemLocator")

    Set wmiSvc = wmiLocator.ConnectServer(ComputerName, NameSpace,"","")

    wmiSvc.Security_.ImpersonationLevel = 3

    set objSS = wmiSvc.Get("__SystemSecurity=@")

    objSS.GetSD(SDarray)

    SDstring = ""

    For i = 0 To UBound(SDarray)

    SDstring = SDstring & SDarray(i) & ","

    Next

    SDstring = Left(SDstring,Len(SDstring)-1)

    set objFSO = CreateObject("Scripting.FileSystemObject")

    Set objFile = objFso.OpenTextFile(FilePath, 2, True)

    objFile.WriteLine(SDstring)

    objFile.close

  4. Now copy and paste the output located in c:\sd.txt into the CreateCustomNamespace.vbs script that we first used. This should be done in the SDstring variable.

    'CreateCustomNameSpace.vbs

    'Rslaten 03/23/2007

    'This script creates a custom namespace and class

    'In theory, this should be delivered via SMS and run with admin rights

    'Create namespace

    Set oLocator = CreateObject("WbemScripting.sWbemLocator")

    Set oSvc = oLocator.ConnectServer(".", "root")

    Set oNamespace = oSvc.Get("__namespace")

    Set oCustomNameSpace = oNamespace.SpawnInstance_

    oCustomNamespace.name = "Custom"

    oCustomNamespace.Put_()

    'Create class to hold custom data

    wbemCimtypeString = 8

    wbemCimtypeUint32 = 19

    Set oWMI = GetObject("winmgmts:root\Custom")

    Set oClass = oWMI.Get()

    oClass.Path_.Class = "UserData"

    oClass.Properties_.add "EmployeeID", wbemCimTypeUint32

    oClass.Properties_("EmployeeID").Qualifiers_.add "key", true

    oClass.Properties_.add "FirstName", wbemCimtypeString

    oClass.Properties_.add "LastName", wbemCimtypeString

    oClass.Properties_.add "Department", wbemCimtypeString

    oClass.Properties_.add "OfficeNumber", wbemCimTypeString

    oClass.Put_()

    'Must have at least "partial write" permissions to the Custom namespace

    'Set SDstring to output of GetSD.vbs

    SDstring = "!!!!!!!!!Paste the output (long number) here!!!!!!!!!!"

    SDarray = split(SDstring,",")

    Set objSS = oWMI.Get("__SystemSecurity=@")

    objSS.SetSD(SDarray)

  5. Modify your SMS_DEF.MOF to collect instances of the UserData class under the Custom namespace.

    //Custom.mof

    //----------------------

    // Custom Namespace

    //----------------------

    #pragma namespace ("\\\\.\\root\\cimv2\\sms")

    [SMS_Report(TRUE),

    SMS_Group_Name("Custom User Information"),

    SMS_Class_ID("MICROSOFT|CUSTOM_USER_INFO|1.0"),

    Namespace("\\\\\\\\.\\\\root\\\\CUSTOM")]

    class UserData: SMS_Class_Template

    {

    [SMS_Report (TRUE)]

    string Department;

    [SMS_Report (TRUE), key]

    uint32 EmployeeID;

    [SMS_Report (TRUE)]

    string FirstName;

    [SMS_Report (TRUE)]

    string LastName;

    [SMS_Report (TRUE)]

    string OfficeNumber;

    };

  6. Create a test collection with a small group of machines to test this on.

  7. Advertise the custom.mof (or the SMS_DEF.Mof with the applicable additions) to these clients. Since this is a new class this is required in order for the advanced client to be able to collect the custom data. Make sure this has run successfully before continuing.

  8. Advertise the CreateCustomNameSpace.vbs script that you modified earlier to these test clients (Have this run with admin rights and it doesn't require user interaction). Make sure this has run successfully before continuing.

  9. Advertise the following script to your end users requiring user rights, that the user is logged on, and that it can be run manually outside of the schedule.

    'EndUserDataEntry.vbs

    'Employee ID is used as a key value and must be an integer

    iEmployeeID = InputBox("Hello low rights end user. Please enter your employee ID number (must be an integer)", "What is your EmployeeID number?", 1)

    sFirstName = InputBox("Please enter your first name", "What is your first name?", "Bill")

    sLastName = InputBox("Please enter your last name", "What is your last name?", "Lumbergh")

    sDepartment = InputBox("Please enter your department", "What department are you in?", "Management")

    sOfficeNumber = InputBox("Please enter your office number", "What is your office number?", 1)

    'Put in WMI

    Set oWMI = GetObject("winmgmts:root\custom")

    Set oData = oWMI.Get("UserData")

    Set oInstance = oData.SpawnInstance_

    oInstance.EmployeeID = iEmployeeID

    oInstance.FirstName = sFirstName

    oInstance.LastName = sLastName

    oInstance.Department = sDepartment

    oInstance.OfficeNumber = sOfficeNumber

    oInstance.Put_()

  10. Log onto one of your test clients as an end user and run the advertisement. You should see the program appear. Fill in the applicable information.

  11. After running the program view the UserData class (wbemtest) to see if the instance you created is there. After confirming this force hardware inventory and that information should now be available in your database.