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.
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_()
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…
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
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)
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;
};
Create a test collection with a small group of machines to test this on.
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.
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.
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_()
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.
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.
Comments
Anonymous
August 21, 2007
Hi, the GetSD.vbs doesen´t works in my test lab. I get an VBscript runtime error: Type mismatch: 'Ubound' plz fix thx a lot!Anonymous
August 22, 2007
There is no error checking in most of my scripts (they are just meant to be examples) but it looks like this command is failing: objSS.GetSD(SDarray) If SDarray doesn't contain anything, or isn't any array, then you'd likely get a similiar error. You should be able to use wbemtest to see what the script is trying to get and isn't getting...Anonymous
December 10, 2008
Hi, You mentioned above that you could do all of this via C/C++ instead of script. Can you give me a hint on how I can do that? Thanx!