Friday, August 30, 2013

Lightweight SCCM and WMI problem scanner

While troubleshooting, I needed a way to scan a bunch of machines looking for common problems with SCCM clients and WMI.  The main goal was to check those 3 area's of WMI so that I could focus on machines that needed WMI repaired.  I know you can do this w/ Client Center and other tools but I like a tool that I can tweak so hopefully this gets someone else off to a good start. 

If you copy it, beware the word wrap on the hardware inventory action ID trigger.  There are a few things specific to my environment, so if you're not using client version 5.00.7804.1202 for example, that'll have to be tweaked.

'---------------------------------------------------------------------------
'                           SCCM 2012 Pinger                               

'                                                                          
' AUTH: David Bennett                                                      
'                                                                          
' THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY   
' KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE      
' IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR    
' PURPOSE.                                                                 
'                                                                          
' Use : cscript PingEm.vbs                                                 
' Desc: Pings a list of systems and does light diagnostics on WMI          
' In  : Expects a list of machines, one per line, named Ping__List.txt     
' Out : Drops two logs                                                     
'       Log_Output.log contains good results                         
'       Log_BadSys.log contains systems with errors (timed out, WMI etc)       
' Note: There are two items that need to be set to your envinronment       
'       They are surronded by <-delimeters->       
'       It's looking for Domain1\UserName                                 
'---------------------------------------------------------------------------


Option Explicit
On Error Resume Next
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8

Dim strInputName  : strInputName = "Ping__List.txt"
Dim FSO   : Set FSO = CreateObject("Scripting.FileSystemObject")
Dim WshShell   : Set WshShell = CreateObject("wscript.shell")
Dim objWMISvc
Dim colItems
Dim objItem
Dim objFile
Dim PingIT


Dim oFile : Set oFile = FSO.CreateTextFile("Ping_Output.log",2)
Dim bFile : Set bFile = FSO.CreateTextFile("Ping_BadSystems.log",2)
Dim iFile : Set iFile = FSO.OpenTextFile(strInputName, 1)


Dim strComputer
Dim strResult
Dim strReply
Dim strUsage
Dim strVersion
Dim strFileDate
Dim strOutput
Dim strOutBad
Dim strOutGood
Dim strInvType
Dim strIP
Dim strOS


Dim boolBad

If NOT FSO.FileExists(strInputName) Then
 strUsage = Msgbox("The file, " & strInputName & " is not available")
 wscript.quit
End If


Do Until iFile.AtEndOfStream
 strComputer = iFile.Readline

 Set PingIT = WshShell.exec("ping -n 2 " & strComputer)
 Do Until PingIT.status = 1 : wscript.sleep 10 : loop
  strResult = pingIT.stdout.readall

  Select Case True
  Case InStr(strResult, "timed out") > 1
   boolBad = 1
   strReply = "Timed Out" & VBTab
   strIP = GetIP(strResult) & VBTab
  Case InStr(strResult, "could not find host") > 1
   boolBad = 1
   strReply = "No DNS Record" & VBTab
   strIP = "N/A" & VBTab
  Case InStr(strResult, "Destination host unreachable") > 1
   boolBad = 1
   strReply = "Unreachable" & VBTab
   strIP = GetIP(strResult) & VBTab
  Case InStr(strResult, "Reply from") > 1
   boolBad = 0
   strReply = "Success" & VBTab
   strIP = GetIP(strResult) & VBTab
  Case Else
   boolBad = 1
   strOutput = strOutput & "UNK Error in Ping: " & err.Number & ", " & err.description & VBTab
  End Select


 If boolBad = 0 Then
  Set objWMISvc=GetObject("winmgmts:{impersonationlevel=impersonate}!\\" & strComputer &"\root\ccm")
  If err.number<>0 Then
   boolBad = 1
   strOutput = strOutPut & "CCM WMI Access Error: " & err.Number & ", " & err.description & VBTab
   err.clear
  Else
   Set colItems = objWMISvc.ExecQuery("Select * from SMS_Client")
   If err.number<>0 Then
    boolBad = 1
    strOutput = strOutPut & "WMI Query Error: " & err.Number & ", " & err.description & VBTab
    err.clear
   Else
    For Each objItem in colItems
     strVersion = objItem.ClientVersion
     strOutput = strOutput & "Version " & strVersion & VBTab
    Next
   End If
   If Left(strVersion, 12) <> "5.00.7804.12" Then
    boolBad = 1
   End If
  End If


  Set objWMISvc=GetObject("winmgmts:{impersonationlevel=impersonate}!\\" & strComputer &"\root\ccm\invagt")
  If err.number<>0 Then
   boolBad = 1
   strOutput = strOutPut & "Invagt WMI Access Error: " & err.Number & ", " & err.description & VBTab
   err.clear
  Else
   Set colItems = objWMISvc.ExecQuery("Select * from InventoryActionStatus Where InventoryActionID = '{00000000-0000-0000-0000-000000000001}'")
   If err.number<>0 Then
    boolBad = 1
    strOutput = strOutPut & "WMI Query Error: " & err.Number & ", " & err.description & VBTab
    err.clear
   Else
    For Each objItem in colItems
     strFileDate = GetDate(objItem.LastCycleStartedDate)
    Next
   End If
   If Right(strFileDate, 4) = "2013" Then
    strOutPut = strOutput & "Invdate: " & strFileDate & VBTab
   Else
    strOutPut = strOutput & "InvDate: Invalid" & VBTab
   End If
  End If

  Set ObjWMISvc = GetObject("winmgmts:{impersonationlevel=impersonate}!\\" & strComputer &"\root\cimv2")
  If err.number<>0 Then
   boolBad = 1
   strOutput = strOutPut & "CIMv2 WMI Access Error: " & err.Number & ", " & err.description & VBTab
   err.clear
  Else
   Set colItems = objWMISvc.ExecQuery("Select * from Win32_OperatingSystem")
   If err.number<>0 Then
    boolBad = 1
    strOutput = strOutPut & "WMI Query Error: " & err.Number & ", " & err.description & VBTab
    err.clear
   Else
    For Each objItem in colItems
     strOS = objItem.Caption
     strOutput = strOutPut & "OS " & strOS & VBTab
    Next
   End If
  End If

 End If

 If boolBad = 1 Then
  strOutBad = strComputer & VBTab & strReply & VBTab & strIP & VBTab & strOutput
  bFile.WriteLine(strOutBad)
  wscript.echo "Bad " & strOutbad
  strOutBad = ""
 Else
  strOutGood = strComputer & VBTab & strReply & VBTab & strIP & VBTab & strOutput
  oFile.WriteLine(strOutGood)
  wscript.echo "Good " & strOutPut
  strOutGood = ""
 End If


 strVersion = ""
 strFileDate = ""
 boolBad = 0
 strOutput = ""
 strFileDate = ""


Loop

Function GetIP(ByVal reply)
 Dim FindIP
 FindIP = Instr(reply, "[")
 If FindIP = 0 Then
  Exit Function
 End If
 reply = Mid(reply, FindIP+1)
 FindIP = InStr(reply, "]")
 If FindIP = 0 Then
  Exit Function
 End If
 GetIP = Left(reply,FindIP-1)
End Function


Function GetDate(Serial)
 GetDate = mid(Serial,5,2) & "-" & mid(Serial,7,2) & "-" & left(Serial,4)
End Function

Thursday, August 29, 2013

PowerShell "Tail" function

I was trying to write this myself a few weeks ago and was tearing my hair out.  Well, that was mostly because I was trying to write a tail last 10 type function, but I definatly have to play with this little goody from John Murphy (http://www.jbmurphy.com/)

function JBM-AD-GetDHCPLogs
{
 PARAM($ServerName="dhcpServerName")
 $FileName="DhcpSrvLog-$(get-date -format ddd).log"
 $PATH="\\$ServerName\c$\Windows\System32\dhcp\$FileName"
 Get-Content $path –Wait
}

Trigger Hardware Inventory (and more) with WMI, WMIC, and/or Powershell

So I usually use WMIC to kick off a hardware inventory or update eval cycle, etc when I'm troubleshooting.  It's quick and easy when you're PSEXEC'd into a system level command prompt (one of my favorite tools because I don't have to boot users to do a lot of stuff).  For example, to kick off a HW Inventory:

WMIC /namespace:\\root\ccm path sms_client CALL TriggerSchedule "{00000000-0000-0000-0000-000000000001}" /NOINTERACTIVE

Or for remote systems:

WMIC /node:%MACHINE% /namespace:\\root\ccm path sms_client CALL TriggerSchedule "{00000000-0000-0000-0000-000000000001}" /NOINTERACTIVE

I keep a handy notepad file with a bunch of these for easy copy/paste operations.  And once you can do that it's pretty easy to wrap it into a VBScript.  Now however I'm trying to branch out into PowerShell scripting so after a little digging I found this link with the following code that can be nicely wrapped in PowerShell (code slightly modified with the computer variable):

     ref: http://myitforum.com/articles/40/view.asp?id=11731
$strComputer = ""
$SMSCli = [wmiclass] "\\$strComputer\root\ccm:SMS_Client"
$SMSCli.TriggerSchedule("{00000000-0000-0000-0000-000000000001}")
And for quick reference, here are the schedule ID's that I know of:
    ref: http://www.moyerteam.com/2012/11/powershell-trigger-configmgr-client-action-scheduleid/
{00000000-0000-0000-0000-000000000001} Hardware Inventory
{00000000-0000-0000-0000-000000000002} Software Inventory
{00000000-0000-0000-0000-000000000003} Discovery Inventory
{00000000-0000-0000-0000-000000000010} File Collection
{00000000-0000-0000-0000-000000000011} IDMIF Collection
{00000000-0000-0000-0000-000000000012} Client Machine Authentication
{00000000-0000-0000-0000-000000000021} Request Machine Assignments
{00000000-0000-0000-0000-000000000022} Evaluate Machine Policies
{00000000-0000-0000-0000-000000000023} Refresh Default MP Task
{00000000-0000-0000-0000-000000000024} LS (Location Service) Refresh Locations Task
{00000000-0000-0000-0000-000000000025} LS (Location Service) Timeout Refresh Task
{00000000-0000-0000-0000-000000000026} Policy Agent Request Assignment (User)
{00000000-0000-0000-0000-000000000027} Policy Agent Evaluate Assignment (User)
{00000000-0000-0000-0000-000000000031} Software Metering Generating Usage Report
{00000000-0000-0000-0000-000000000032} Source Update Message
{00000000-0000-0000-0000-000000000037} Clearing proxy settings cache
{00000000-0000-0000-0000-000000000040} Machine Policy Agent Cleanup
{00000000-0000-0000-0000-000000000041} User Policy Agent Cleanup
{00000000-0000-0000-0000-000000000042} Policy Agent Validate Machine Policy / Assignment
{00000000-0000-0000-0000-000000000043} Policy Agent Validate User Policy / Assignment
{00000000-0000-0000-0000-000000000051} Retrying/Refreshing certificates in AD on MP
{00000000-0000-0000-0000-000000000061} Peer DP Status reporting
{00000000-0000-0000-0000-000000000062} Peer DP Pending package check schedule
{00000000-0000-0000-0000-000000000063} SUM Updates install schedule
{00000000-0000-0000-0000-000000000071} NAP action
{00000000-0000-0000-0000-000000000101} Hardware Inventory Collection Cycle
{00000000-0000-0000-0000-000000000102} Software Inventory Collection Cycle
{00000000-0000-0000-0000-000000000103} Discovery Data Collection Cycle
{00000000-0000-0000-0000-000000000104} File Collection Cycle
{00000000-0000-0000-0000-000000000105} IDMIF Collection Cycle
{00000000-0000-0000-0000-000000000106} Software Metering Usage Report Cycle
{00000000-0000-0000-0000-000000000107} Windows Installer Source List Update Cycle
{00000000-0000-0000-0000-000000000108} Software Updates Assignments Evaluation Cycle
{00000000-0000-0000-0000-000000000109} Branch Distribution Point Maintenance Task
{00000000-0000-0000-0000-000000000110} DCM policy
{00000000-0000-0000-0000-000000000111} Send Unsent State Message
{00000000-0000-0000-0000-000000000112} State System policy cache cleanout
{00000000-0000-0000-0000-000000000113} Scan by Update Source
{00000000-0000-0000-0000-000000000114} Update Store Policy
{00000000-0000-0000-0000-000000000115} State system policy bulk send high
{00000000-0000-0000-0000-000000000116} State system policy bulk send low
{00000000-0000-0000-0000-000000000120} AMT Status Check Policy
{00000000-0000-0000-0000-000000000121} Application manager policy action
{00000000-0000-0000-0000-000000000122} Application manager user policy action
{00000000-0000-0000-0000-000000000123} Application manager global evaluation action
{00000000-0000-0000-0000-000000000131} Power management start summarizer
{00000000-0000-0000-0000-000000000221} Endpoint deployment reevaluate
{00000000-0000-0000-0000-000000000222} Endpoint AM policy reevaluate
{00000000-0000-0000-0000-000000000223} External event detection

Tuesday, August 27, 2013

VBScript to Unlock Powershell

I know it sounds odd.  But if the execution policy for powershell is set to restricted you can't do an awful lot remotely and most of what I do is remote.  To top that off a lot of times the multitude of remote managment tools don't work when you're troubleshooting an issues.  So there are a couple ways to set execution policy remotely. 

A lot of times I'll just do a psexec to run 'powershell set-executionpolicy unrestricted' but sometimes I've had issues with that erroring out for some reason, where dropping a script to the local machine and kicking it off locally works.  I don't know why.  So here's the script I drop on the box and then I use psexec to run it.

'---------------------------------------------------------------------------
' PowerShell Unlocker                             
' AUTH: David Bennett                                                      
' WEB :
http://sccmbrokeit.blogspot.com/
'                                                                          
' THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY   
' KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE      
' IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR    
' PURPOSE.                                                                 
'                                                                          
' Use : cscript UnlockPowerShell.vbs                                       
' Desc: Just runs "Set-ExecutionPolicy Unrestricted" so that powershell    
' scripts will run. Needs to be ran outside of PSH                  
' The intent of this version is to be dropped on a system and remotely
' executed with PSEXEC                                               
'---------------------------------------------------------------------------

On Error Resume Next
Err.Clear
Dim objShell
set objShell = CreateObject("Wscript.shell")

strPS = "Powershell.exe Set-ExecutionPolicy Unrestricted"
objShell.Run(strPS,0)

Friday, August 23, 2013

Updated Server Inventory Report

Updated the query:


SELECT DISTINCT
             v_R_System.Netbios_Name0 AS Name,v_RA_System_SMSAssignedSites.SMS_Assigned_Sites0 AS SCCM_Site,
             v_GS_WORKSTATION_STATUS.LastHWScan AS SCCM_Scan,
            MAX(REVERSE(LEFT(REVERSE(v_RA_System_SystemOUName.System_OU_Name0), CHARINDEX('/', REVERSE(v_RA_System_SystemOUName.System_OU_Name0)) - 1))) AS OU,
             v_R_System.description0 AS Description, v_GS_COMPUTER_SYSTEM.Manufacturer0 AS Make,
             v_GS_COMPUTER_SYSTEM.Model0 AS Model ,v_GS_PC_BIOS.SerialNumber0 AS Serial,
             v_GS_PROCESSOR.Name0 AS Processor,ISNULL(v_GS_PROCESSOR.NumberOfCores0,1) AS Cores,
             MAX(Right(v_GS_PROCESSOR.DeviceID0,1))+1 AS NumProcs,v_R_System.Operating_System_Name_and0 AS OS,
             v_GS_OPERATING_SYSTEM.CSDVersion0 AS SP,v_GS_X86_PC_MEMORY.TotalPhysicalMemory0 AS TotMemMB,
             v_GS_LOGICAL_DISK.DeviceID0 AS Drive ,v_GS_LOGICAL_DISK.Size0 AS TotalGB, v_GS_LOGICAL_DISK.FreeSpace0 AS FreeGB
FROM   v_R_System LEFT JOIN
              v_RA_System_SystemOUName ON v_R_System.ResourceID = v_RA_System_SystemOUName.ResourceID LEFT JOIN
              v_RA_System_SMSAssignedSites ON v_R_System.ResourceID = v_RA_System_SMSAssignedSites.ResourceID LEFT JOIN
              v_GS_WORKSTATION_STATUS ON v_R_System.ResourceID = v_GS_WORKSTATION_STATUS.ResourceID LEFT JOIN
              v_GS_LOGICAL_DISK ON v_R_System.ResourceID = v_GS_LOGICAL_DISK.ResourceID LEFT JOIN
              v_GS_COMPUTER_SYSTEM ON v_R_System.ResourceID = v_GS_COMPUTER_SYSTEM.ResourceID LEFT JOIN
              v_GS_OPERATING_SYSTEM ON v_R_System.ResourceID = v_GS_OPERATING_SYSTEM.ResourceID LEFT JOIN
              v_GS_PC_BIOS ON v_R_System.ResourceID = v_GS_PC_BIOS.ResourceID LEFT JOIN
              v_GS_X86_PC_MEMORY ON v_R_System.ResourceID = v_GS_X86_PC_MEMORY.ResourceID LEFT JOIN
              v_GS_PROCESSOR ON v_R_System.ResourceID = v_GS_PROCESSOR.ResourceID
Where (NOT (v_GS_LOGICAL_DISK.DriveType0 = '2')) AND (NOT (v_GS_LOGICAL_DISK.DriveType0 = '5'))
GROUP BY v_R_System.description0, v_R_System.Netbios_Name0, v_RA_System_SMSAssignedSites.SMS_Assigned_Sites0,
              v_GS_WORKSTATION_STATUS.LastHWScan,  v_GS_COMPUTER_SYSTEM.Manufacturer0, v_GS_COMPUTER_SYSTEM.Model0,
              v_GS_PC_BIOS.SerialNumber0, v_GS_PROCESSOR.Name0, v_GS_PROCESSOR.NumberOfCores0, v_GS_OPERATING_SYSTEM.CSDVersion0,
              v_GS_X86_PC_MEMORY.TotalPhysicalMemory0, v_GS_LOGICAL_DISK.DeviceID0, v_GS_LOGICAL_DISK.Size0,
              v_GS_LOGICAL_DISK.FreeSpace0, v_R_System.Operating_System_Name_and0
HAVING      (v_R_System.Operating_System_Name_and0 LIKE N'%server%')
ORDER BY Name