Showing posts with label hardware inventory. Show all posts
Showing posts with label hardware inventory. Show all posts

17 February, 2013

Start Resource Explorer from PowerShell - SCCM

Here is another SCCM snippet. The other day, I put in some custom entries into the SCCM hardware inventory config to be able to report on firmware version of RAID controllers of IBM and HP servers. When you do that, you are either lucky to get everything right first or you need to go down the harder route to troubleshoot why the data is not flowing into the SCCM database.

A good place to look at whether you have the data in SCCM already is SCCM Resource Explorer, where you can see the software and hardware inventory data of one machine:


Resource Explorer - SCCM 2007


















I'm an server ops guy, I've been in operations for too long, so I'm not very patient or I should say I like to be efficient. Opening Resource Explorer for 3-5 hosts is a pain in my view. You need to open the SCCM console, go to Collections, find a collection where your machine is probably included, open the collection (I usually have 10000+ machines in those collections), filter for your machine, wait a bit until Mr. MMC thinks about life and Einstein's relativity theory and then it shows your the machine. Then right click, Start, Resource Explorer. So this is about 90 seconds and 6-8 click (depending on how much you like to use mouse instead of keyboard).

Because this blog is mainly about Powershell, you could figure it by now, that the solution will be a PS script. Let's take it step by step:
First we need to find the SCCM installation bin folder where the resourceexplorer.msc file is located:
$adminui = Get-Itemproperty -path hklm:\SOFTWARE\Microsoft\ConfigMgr\Setup -name "UI Installation Directory" | %{$_."UI Installation Directory"}
$bin = $adminui + "\bin"
$command = "$bin\resourceexplorer.msc"

Obviously a fully fledged strip will contain a test-path check against the file, error handling if we couldn't find the msc file...etc. but I don't want to bore you with this.
The next step is to go through each host in the list and open Resource Explorer for them.
foreach($srv in $hostlist){
   $inputhost = "`'" + $srv + "`'"
   $cmd = $command + " -s -sms:ResExplrQuery=`"SELECT ResourceID FROM SMS_R_SYSTEM WHERE NAME = $inputhost`" -sms:connection=\\$sccmsrv\root\sms\site_cen"
   $tmpVal = [diagnostics.process]::start("mmc", $cmd)
}

There are two tricks in this code, the first was to find out how to specify the necessary parameters for the msc file, it's basically a WQL query:
SELECT ResourceID FROM SMS_R_SYSTEM WHERE NAME = $inputhost

You also need to escape the quotes for the WQL query string, that's why the `" characters are there.

The second trick is to put the server name into single quotes:
$inputhost = "`'" + $srv + "`'"

The full script:
 param ([string] $hosts = "")
  
 $adminui = Get-Itemproperty -path hklm:\SOFTWARE\Microsoft\ConfigMgr\Setup -name "UI Installation Directory" | %{$_."UI Installation Directory"}
 $bin = $adminui + "\bin"
 $command = "$bin\resourceexplorer.msc"
 $sccmsrv = $env:COMPUTERNAME
  
 #### Collate the host list.
 $hostlist = @($Input)
 if ($hosts) {
      if($hosts -imatch " "){
           $hostsArr = @($hosts.split(" "))
           $hostlist += $hostsArr
      }
      else{
           $hostlist += $hosts
      }
 }
  
 foreach($srv in $hostlist){
      $inputhost = "`'" + $srv + "`'"
      $cmd = $command + " -s -sms:ResExplrQuery=`"SELECT ResourceID FROM SMS_R_SYSTEM WHERE NAME = $inputhost`" -sms:connection=\\$sccmsrv\root\sms\site_cen"
      $tmpval = [diagnostics.process]::start("mmc", $cmd)
 }  


Let me know if there's any suggestion, comment...etc. May The Force be with you all.
t


13 January, 2013

Invoke Inventory - SCCM

Continuing one of the SCCM related posts from last year where I showed a way to invoke Machine Policy Retrieval & Evaluation on SCCM clients remotely. Here is a quick script to invoke some other actions as well, namely:



  • Hardware Inventory Cycle (Delta)
  • Hardware Inventory Cycle (Full)
  • Software Inventory Cycle (Delta)
  • Software Inventory Cycle (Full)
  • Discovery Data Collection Cycle (Delta)
  • Discovery Data Collection Cycle (Full)
  • File Collection Cycle (Delta)
  • File Collection Cycle (Full)
  • Software Updates Deployment Evaluation Cycle
  • Software Updates Scan Cycle



An interesting piece of the code is this:
$wmiQuery = "\\$srv\root\ccm\invagt:InventoryActionStatus.InvetoryActionID=$fscheduleID"
$checkdelete = ([wmi]$wmiQuery).Delete()

These two lines take care of the cleanup of an existing WMI instance which tells the SCCM client to perform a delta inventory instead of a full one. While you can invoke client actions on SCCM client's GUI, you can only kick off delta Software and Hardware inventory (although the GUI doesn't say that). So this piece of code give you the opportunity to force a full Hardware or Software inventory remotely.
Why is this important? Because there are cases when you try to troubleshoot why HW or SW inventory data is not flowing into the SCCM database and the best way to do that is enforcing the client to perform a full inventory and look at the logs (like the InventoryAgent.log on the client or the DataTransferService.log on the Management Point...etc.)
Some explanation for the code:

$SMSCli = [wmiclass] \\$srv\root\ccm:SMS_Client
This is for binding the SCCM Client's WMI class, if it's done, then we can invoke the TriggerSchedule method to start an action.

switch ($action) {
...
}
In this section, depending on what action is selected with the -action switch, we set up 2 variables, one is the identifier of the WMI instance which identifies the particular action. The other one is just for displaying the friendly name of the action for the user.

$hostlist = @($Input)
if($($hostlist.length) -gt 0){
   foreach ($srv in $hostlist) {
      if(!$srv){
         executeSCCMAction $srv $action $scheduleID
      }
   }
}
Get the list of hosts from the pipe into an array, go through the array and call the executeSCCMAction  function to create and/or delete the necessary WMI instance.

Clipboard friendly code:
 param ( [string] $action = "") 

function executeSCCMAction ([string]$srv, [string]$action, $fscheduleID){

   #Binding SMS_Client wmi class remotely.... 
   $SMSCli = [wmiclass] "\\$srv\root\ccm:SMS_Client"

   if($SMSCli){
      if($action -imatch "full"){
         #Clearing HW or SW inventory delta flag...
         $wmiQuery = "\\$srv\root\ccm\invagt:InventoryActionStatus.InventoryActionID=$fscheduleID"
         $checkdelete = ([wmi]$wmiQuery).Delete()
      }   
      #Invoking $action ...
      Write-Host "$srv, Invoking action $script:actionName"
      $check = $SMSCli.TriggerSchedule($fscheduleID)
   }
   else{
      # could not get SCCM WMI Class
      Write-Host "$srv, could not get SCCM WMI Class"
   }
}

switch ($action) {
   "hw" {
      $scheduleID = "{00000000-0000-0000-0000-000000000001}"
      $script:actionName = "Hardware Inventory Cycle (Delta)"
   }
   "hwfull" {
      $scheduleID = "{00000000-0000-0000-0000-000000000001}"
      $script:actionName = "Hardware Inventory Cycle (Full)"
   }
   "sw" {
      $scheduleID = "{00000000-0000-0000-0000-000000000002}"
      $script:actionName = "Software Inventory Cycle (Delta)"
   }
   "swfull" {
      $scheduleID = "{00000000-0000-0000-0000-000000000002}"
      $script:actionName = "Software Inventory Cycle (Full)"
   }
   "datadisc" {
      $scheduleID = "{00000000-0000-0000-0000-000000000003}"
      $script:actionName = "Discovery Data Collection Cycle (Delta)"
   }
   "datadiscfull" {
      $scheduleID = "{00000000-0000-0000-0000-000000000003}"
      $script:actionName = "Discovery Data Collection Cycle (Full)"
   }
   "filecollect" {
      $scheduleID = "{00000000-0000-0000-0000-000000000010}"
      $script:actionName = "File Collection Cycle (Delta)"
   }
   "filecollectfull" {
      $scheduleID = "{00000000-0000-0000-0000-000000000010}"
      $script:actionName = "File Collection Cycle (Full)"
   } 
   "swupdatedeploy" {
      $scheduleID = "{00000000-0000-0000-0000-000000000108}"
      $script:actionName = "Software Updates Deployment Evaluation Cycle"
   }
   "swupdatescan" {
      $scheduleID = "{00000000-0000-0000-0000-000000000113}"
      $script:actionName = "Software Updates Scan Cycle"
   }
   default {
      Write-Host -ForegroundColor 'red' "No valid Action is specified. Exiting..."
      exit
   }
}

#getting hostlist from pipe i.e.: PS C:\> gc list.txt | script.ps1
$hostlist = @($Input)

if($($hostlist.length) -gt 0){
   foreach ($srv in $hostlist) {
      if($srv){
         executeSCCMAction $srv $action $scheduleID 
      }
   }
}
else{
   # No hostname or hostlist is specified
}
Let me know your thoughts.
May The Force...
t