Update: Code has been re-written from the previous post to make full use of WMI as the means of execution, and scripting dictionary to simplify reporting and controls
Alright, so I’ve got a bit of a problem.
A large volume of data_loader kick backs, and resynch requests. The problem is, for one reason or another, the clients inventory data is being held up. Sure, plenty of machines are only missing records for maybe 3, 4, or 5 days. However I’ve started to see machines that aren’t getting a record in for about 15 days or more.
This shall not stand! Mind you I’m not going to fix these one by one either. So what now?
Script!
So I begin to dive into the CCM namespace looking for a method to force a full inventory. Which brings me to the InvAgt class. Great! But wait, I don’t see any method for just forcing an inventory.
This is no longer the object used in the script, but leaving here for reference:
I’ll save you the rest of the story and just get to what I found. According to Technet, the best way to “script” the solution is to delete the InventoryActionID from the InventoryActionStatus in WMI and use the CPApplet COM automation class to invoke the rescan.
They were also nice enough to provide their own code so it was made increasingly more simple to build the following script as I already had the core functionality objects defined for me. Now, I needed to weaponize code for deployment…. Perhaps weaponize isn’t the best term, but I needed it to deploy and do some work via SCCM.
So I decided to allow 4 arguments and 2 switches that would allow me to perform:
- Hardware Inventory (hinv)
- Software Inventory (sinv)
- Discovery Data (ddr)
- File Collection (file)
I also wanted to be able to fire this off in 2 methods, one to clear the old inventory instance ID information and force a full inventory or just force an inventory. I also wanted to be able to run this in a verbose mode to check and see if all actions were being completed as requested. So I added the following switches:
- /full
- /debug
So the end result is the ability to build collections based off of machines who haven’t reported to one or more inventory types for X number of days and have the script deployed to them auto-magically with the corresponding inventory type.
I also set the script so someone could use it ad-hoc on a machine by double-clicking. The default behavior is to NOT delete the IDs but to initialize all 4 of the inventory calls. I also provided CONST declarations at the top so anyone could add or remove what they want in there (they will however need to modify the arrays arrinvtypes and arrkeys to match as well as the select case statement to add additional arguments).
For people that don’t give a hoot about SCCM (there are plenty of you, shockingly enough) feel free to examine the way the loops and conditionals are being used in this script. They coorelate back to the fundamentals I have covered previously in my stick to the script series.
- Stick to the script…
- Stick to the script #!/BIN/BASH…
- Stick to the script CreateObject(“Wscript.Shell”)…
- Stick to the script PS C:> PowerShell…
Finally, the code…..
'||============================||
'||Author Daniel Belcher ||
'||Full HInv ||
'||Date 9/22/2011 ||
'||Updated 11/7/12 ||
'||============================||
'==================================================================================
'Usage: For any prefered deployment method
'This script can:
' 1) Initiate Soft, Hard, Discover, File Collection on a client PC
' 2) Clear agent inv cache and perform full collection
'
' cscript.exe inventory.vbs
'
'It's possible to target single inventory types or cherry pick them as needed.
'Accepted arguements are:
'1) hinv (performs a hardware inventory)
'2) sinv (performs a software inventory)
'3) ddr (performs a discovery data)
'4) file (performs a file collection)
'
'Accepted switches are:
'1) /full (blows away all inv cache for declared inventories and initiates request)
'2) /debug (runs the script verbose to check things are running)
'Using /full or /debug it's best to target an inventory type, but not required.
'
'All arguements and switches are case insensitive.
'
'|Objects, Variables, and Constants ***********************************************
'==================================================================================
'Constant strings for cleanup and initialization subroutines.
'==================================================================================
Option Explicit
Dim oWShell,oWmiLoc,oDict,Args,sArgs,nArgs
Dim Item,x,Full,DEBUGMSG
Set Args = Wscript.Arguments
Set NArgs = Wscript.Arguments.Named
Set oWShell = CreateObject("WScript.Shell")
Set oWmiLoc = CreateObject("WbemScripting.SWbemLocator")
Set oDict = CreateObject("Scripting.Dictionary")
'Add additional INV types here with IDs and update Case & dictionary key item pairs
Const sHinv = "Hardware Inventory Collection Cycle"
CONST Hinv = "{00000000-0000-0000-0000-000000000001}":oDict.Add sHinv, Hinv
Const sSinv = "Software Inventory Collection Cycle"
CONST Sinv = "{00000000-0000-0000-0000-000000000002}":oDict.Add sSinv, Sinv
Const sDDR = "Discovery Data Collection Cycle"
CONST DDR = "{00000000-0000-0000-0000-000000000003}":oDict.Add sDDR, DDR
Const sFile = "Standard File Collection Cycle"
Const File = "{00000000-0000-0000-0000-000000000010}":oDict.Add sFile, File
'|Main Run ***********************************************************************
'=================================================================================
'Checking for Named arguments
If NArgs.Exists("Full") Then
Full = TRUE
Else
Full = FALSE
End If
if NArgs.Exists("debug") Then
DEBUGMSG = TRUE
Else
DEBUGMSG = FALSE
End If
'Check for Unnamed arguments
If Args.Unnamed.Count > 0 Then
If DEBUGMSG then wscript.echo "Unnamed arguments Found..."
'Looping arguments looking for declared Inv types
For Each item In Args
Select Case lcase(item)
Case "hinv"
If Full Then
DelInvActionID sHinv
wscript.sleep 1500
End If
RunInvAction sHinv
Case "sinv"
If Full Then
DelInvActionID sSinv
wscript.sleep 1500
End If
RunInvAction sSinv
Case "ddr"
If Full Then
DelInvActionID sDDR
wscript.sleep 1500
End If
RunInvAction sDDR
Case "file"
If Full Then
DelInvActionID sFile
wscript.sleep 1500
End If
RunInvAction sFile
End Select
Next
else
'If no Unnamed arguments then loop through appropriate arrays
'based on /full or not
If DEBUGMSG Then wscript.echo "No Unnamed Arguments found, " _
&"checking for full switch..."
If Full Then
If DEBUGMSG Then wscript.echo "/Full switch used, " _
&"clearing caches..."
For Each item in oDict.Keys
DelInvActionID item
wscript.sleep 1500
Next
End If
If DEBUGMSG Then wscript.echo "Initiating HINV, SINV, DDR, " _
&"and File Collection now..."
For Each item in oDict.Keys
RunInvAction item
wscript.sleep 1500
Next
End If
Report
Wscript.Quit(0)
'SubRoutines *********************************************************************
'=================================================================================
'Sub to delete the InventoryActionID as requested
Sub DelInvActionID(guid)
On Error Resume Next
Set oInvAgt = oWmiLoc.ConnectServer(,"root\ccm\invagt")
oInvAgt.Delete "InventoryActionStatus.InventoryActionID='"& _
oDict.Item(guid)&"'"
If DEBUGMSG Then wscript.echo "Clearing "& _
oDict.Item(guid)&" from InventoryActionStatus"
End Sub
'*********************************************************************************
'Sub to initialize Inventory Cycle requested
Sub RunInvAction(name)
On Error Resume Next
Dim smsclient:Set smsclient=GetObject("winmgmts://./root/ccm:sms_client")
smsclient.triggerschedule(oDict.Item(name))
If DEBUGMSG Then wscript.echo "Running "&name
End Sub
'**********************************************************************************
'Sub to report script completion if /debug
Sub Report
If DEBUGMSG Then wscript.echo "Complete" 'Set for popup on script complete
End Sub
'End ******************************************************************************
'==================================================================================
2 Comments
Great write up, man. Kuddos!
danke sir