Thursday, June 30, 2016

Basic Maintenance Confguration Baseline

I like to create a configuration baseline, containing several configuration items, to check some basic client health items.  This is not only beneficial at the console but also benefits technicians/administrators at the endpoint when they are trying to troubleshoot issues.

  • Configuration Items:
    • ConfigMgr Client Health - Reboot Pending
      • Checks CCMClientSDK in WMI
        • (Invoke-WmiMethod -Class CCM_ClientUtilities -Name DetermineIfRebootPending -Namespace ROOT\ccm\ClientSDK -Computer $env:COMPUTERNAME).RebootPending equals False 
      • Checks Component Based Servicing in registry
        • HKLM\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending must not exist
      • Checks Pending File Rename Operations in registry
        • HKLM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations string array must be empty
      • Checks Windows Update Reboot Required in registry
        • SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired must not exist
      • Checks if last reboot time is greater than 90 day
        • (New-TimeSpan -Start ((Get-CimInstance -ClassName win32_operatingsystem | select csname, lastbootuptime).lastbootuptime) -End (get-date)).Days less than or equal to 45
    • ConfigMgr Client Health - Windows Update Service (wuauserv)
      • (get-service -Name wuauserv).StartType not equal Disabled
    • ConfigMgr Client Health - Windows Installer Service (msiserver)
      • (get-service -Name msiserver).StartType not equal Disabled

  • Configuration Baseline:
    • ConfigMgr Client Health
      • ConfigMgr Client Health - Reboot Pending
      • ConfigMgr Client Health - Windows Update Service (wuauserv)
      • ConfigMgr Client Health - Windows Installer Service (msiserver)

You can find client health collections here if interested: http://randoltech.blogspot.com/2016/06/client-health-collections.html

Tuesday, June 14, 2016

Client Health Collections

My Client Health Collections

The source that I used to use for client health collections has disappeared from the interwebs so I had to build my own.  I had someone mention that they used to use client health collections but have stopped doing so as more recent SCCM versions do a much better job of self-correcting.  While I agree that SCCM has gotten better at detecting and correcting health problems, it has not eliminated them.  If you don't know where on this spectrum your clients are at then it is far more difficult to troubleshoot problems.  So, I still believe that these are quite useful.  I still find duplicate computer names and duplicate GUIDs.  I still find missing hardware and software inventories as well as failures to heartbeat.  And, of course, I find things that SCCM can't auto-correct like missing clients, disabled services, and pending reboot.  If you trust SCCM to correct all of these and just assume everything is working nicely then you will end up with surprises ranging from deployment failures to surprise reboots.  Better to know ahead of time than to be surprised.

Cheater files are located here.  They include a powershell script to import collections from a .csv file (bonus, you can use it to create any collections you want) and a .csv with the definitions for all of these colllections.  As always, disclaimer, never blindly run anything that you find on the interwebs.  Look them over first to be sure that they will do what you think they should do.

  1. Scope
Healthy Collection
  • Name = "01. Healthy - Scope - Devices in Scope"
  • Comment = "Checks for proper OS.  Discovered items like EMC Celera or UNIX systems are not in scope"
  • Query Limited to = "All Systems"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.OperatingSystemNameandVersion like "%windows%"
Unhealthy Collection
  • Name = "01. Ignored - Scope - Devices Not in Scope"
  • Comment = "These do not have a standard Windows OS on them."
  • Query Limited to = "All Systems"
  • Query Statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.OperatingSystemNameandVersion not like "%Windows%"

  1. Obsolescence
Healthy Collection
  • Name = "02. Healthy - Obsolescence - Device Not Obsolete"
  • Comment = "These devices are discovered in both SCCM and AD"
  • Query Limited to = "01. Healthy - Scope - Devices in Scope"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.OperatingSystemNameandVersion like "%windows%" and SMS_R_System.Obsolete = "0"
Unhealthy Collection
  • Name = "02. Unhealthy - Obsolescence - Device Is Obsolete"
  • Comment = "These devices are no longer in Active Directory.  They probably don't exist on the network anymore."
  • Query Limited to = "01. Healthy - Scope - Devices in Scope"
  • Query Statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.OperatingSystemNameandVersion like "%windows%" and (SMS_R_System.Obsolete != "0" or SMS_R_System.Obsolete is null )

  1. Client
Healthy Collection
  • Name = "03. Healthy - Client - SCCM Client is Installed"
  • Comment = "These devices have an SCCM client installed.  This is the baseline for what SCCM should be able to manage."
  • Query Limited to = "02. Healthy - Obsolescence - Device Not Obsolete"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.Obsolete = "0" and SMS_R_System.Client = "1"
Unhealthy Collection
  • Name = "03. Unhealthy - Client - SCCM Client Not Installed"
  • Comment = "These devices do not have a client installed.  They cannot be managed without a client."
  • Query Limited to = "02. Healthy - Obsolescence - Device Not Obsolete"
  • Query Statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.Obsolete = "0" and SMS_R_System.OperatingSystemNameandVersion like "%windows%" and (SMS_R_System.Client = "0" or SMS_R_System.Client is null )

  1. Activity
Healthy Collection
  • Name = "04. Healthy - Activity - Client Active"
  • Comment = "These clients have provided some status to SCCM within the last 30 days."
  • Query Limited to = "03. Healthy - Client - SCCM Client is Installed"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_CH_ClientSummary on SMS_G_System_CH_ClientSummary.ResourceId = SMS_R_System.ResourceId where SMS_R_System.Obsolete = "0" and SMS_R_System.Client = "1" and SMS_R_System.Active = "1"
Unhealthy Collection
  • Name = "04. Unhealthy - Activity - Client Inactive"
  • Comment = "These clients have not provided any status to SCCM within the last 30 days."
  • Query Limited to = "03. Healthy - Client - SCCM Client is Installed"
  • Query Statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_CH_ClientSummary on SMS_G_System_CH_ClientSummary.ResourceId = SMS_R_System.ResourceId where SMS_R_System.Obsolete = "0" and SMS_R_System.Client = "1" and (SMS_R_System.Active != "1" or SMS_R_System.Active is null ) 

  1. Duplicate Name
Healthy Collection
  • Name = "05. Healthy - Duplicate Name - Name Not Duplicated"
  • Comment = "The device names of these clients are not duplicated on any known systems."
  • Query Limited to = "04. Healthy - Activity - Client Active"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.Obsolete = "0" and SMS_R_System.Client = "1" and SMS_R_System.Active = "1"and SMS_R_System.Name not in (select r.Name from  SMS_R_System as r full join SMS_R_System as s1 on s1.ResourceId = r.ResourceId full join SMS_R_System as s2 on s2.Name = s1.Name where s1.Client = 1 and s1.Obsolete = "0" and s1.Name = s2.Name and s1.ResourceId != s2.ResourceId)
Unhealthy Collection
  • Name = "05. Unhealthy - Duplicate Name - Name Duplicated"
  • Comment = "These device names are duplicated, this can cause problems targeting devices by name."
  • Query Limited to = "All Systems"
  • Query Statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.Obsolete = "0" and SMS_R_System.Client = "1" and SMS_R_System.Active = "1" and SMS_R_System.Name in (select r.Name from  SMS_R_System as r full join SMS_R_System as s1 on s1.ResourceId = r.ResourceId full join SMS_R_System as s2 on s2.Name = s1.Name where s1.Client = 1 and s1.Obsolete = "0" and s1.Name = s2.Name and s1.ResourceId != s2.ResourceId)

  1. Duplicate GUID
Healthy Collection
  • Name = "06. Healthy - Duplicate GUID - GUID Not Duplicated"
  • Comment = "These SCCM GUIDs are not duplicated on any active system."
  • Query Limited to = "05. Healthy - Duplicate Name - Name Not Duplicated"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.Obsolete = "0" and SMS_R_System.Client = "1" and SMS_R_System.Active = "1" and SMS_R_System.Name not in (select r.Name from  SMS_R_System as r full join SMS_R_System as s1 on s1.ResourceId = r.ResourceId full join SMS_R_System as s2 on s2.Name = s1.Name where s1.Client = 1 and s1.Obsolete = "0" and s1.SMSUniqueIdentifier = s2.SMSUniqueIdentifier and s1.ResourceId != s2.ResourceId)
Unhealthy Collection
  • Name = "06. Unhealthy - Duplicate GUID - GUID Duplicated"
  • Comment = "These SCCM GUIDs are duplicated, usually caused by bad imaging practices"
  • Query Limited to = "All Systems"
  • Query Statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.Obsolete = "0" and SMS_R_System.Client = "1" and SMS_R_System.Active = "1" and SMS_R_System.Name in (select r.Name from  SMS_R_System as r full join SMS_R_System as s1 on s1.ResourceId = r.ResourceId full join SMS_R_System as s2 on s2.Name = s1.Name where s1.Client = 1 and s1.Obsolete = "0" and s1.SMSUniqueIdentifier = s2.SMSUniqueIdentifier and s1.ResourceId != s2.ResourceId)

  1. Hardware Inventory
Healthy Collection
  • Name = "07. Healthy - HINV - Hardware Inventoried in last 14 days"
  • Comment = "These devices have had their hardware inventoried recently."
  • Query Limited to = "06. Healthy - Duplicate GUID - GUID Not Duplicated"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_WORKSTATION_STATUS on SMS_G_System_WORKSTATION_STATUS.ResourceId = SMS_R_System.ResourceId where SMS_G_System_WORKSTATION_STATUS.LastHardwareScan >= DateAdd(dd,-14,GetDate())
Unhealthy Collection
  • Name = "07. Unhealthy - HINV - Hardware Not Inventoried in last 14 days"
  • Comment = "These devices have not had their hardware inventoried recently.  May indicate stale accounts, communication issues, or client issues."
  • Query Limited to = "06. Healthy - Duplicate GUID - GUID Not Duplicated"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_WORKSTATION_STATUS on SMS_G_System_WORKSTATION_STATUS.ResourceId = SMS_R_System.ResourceId where SMS_G_System_WORKSTATION_STATUS.LastHardwareScan < DateAdd(dd,-14,GetDate())

  1. Software Inventory
Healthy Collection
  • Name = "08. Healthy - SINV - Software Inventoried in last 45 days"
  • Comment = "Software inventory is recent"
  • Query Limited to = "07. Healthy - HINV - Hardware Inventoried in last 14 days"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_LastSoftwareScan on SMS_G_System_LastSoftwareScan.ResourceId = SMS_R_System.ResourceId where SMS_G_System_LastSoftwareScan.LastScanDate >= DateAdd(dd,-45,GetDate())
Unhealthy Collection
  • Name = "08. Unhealthy - SINV - Software Not Inventoried in last 45 days"
  • Comment = "Software has not been inventoried recently."
  • Query Limited to = "07. Healthy - HINV - Hardware Inventoried in last 14 days"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_LastSoftwareScan on SMS_G_System_LastSoftwareScan.ResourceId = SMS_R_System.ResourceId where SMS_G_System_LastSoftwareScan.LastScanDate < DateAdd(dd,-45,GetDate())

  1. Heartbeat
Healthy Collection
  • Name = "09. Healthy - Heartbeat - Heartbeat is recent"
  • Comment = "These clients have responded to heartbeat discovery in the last 23 days"
  • Query Limited to = "08. Healthy - SINV - Software Inventoried in last 45 days"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where AgentName in ("Heartbeat Discovery") and DATEDIFF(day,AgentTime,GetDate())<=23
Unhealthy Collection
  • Name = "09. Unhealthy - Heartbeat - No Heartbeat in 23 days"
  • Comment = "These clients have not responded to heartbeat discovery in over 23 days"
  • Query Limited to = "08. Healthy - SINV - Software Inventoried in last 45 days"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where AgentName in ("Heartbeat Discovery") and DATEDIFF(day,AgentTime,GetDate())>23

  1. SCCM Client Version
Healthy Collection (the exact version needs to be maintained)
  • Name = "10. Healthy - Client Version - Client is current"
  • Comment = "The SCCM client is on a recent version"
  • Query Limited to = "09. Healthy - Heartbeat - Heartbeat is recent"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.Obsolete = "0" and SMS_R_System.Client = "1" and SMS_R_System.Active = "1" and SMS_R_System.ClientVersion >= "5.00.9068.1008"
Unhealthy Collection
  • Name = "10. Unhealthy - Client Version - Client is old"
  • Comment = "The SCCM client is on an old version.  This can cause issues with deployments and can be an indicator of more severe underlying problems."
  • Query Limited to = "09. Healthy - Heartbeat - Heartbeat is recent"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.Obsolete = "0" and SMS_R_System.Client = "1" and SMS_R_System.Active = "1" and SMS_R_System.ClientVersion < "5.00.9068.1008"

  1. WUAU Service
Healthy Collection
  • Name = "11. Healthy - WUAU Service - WUAU enabled"
  • Comment = "WUAU Service is not disabled."
  • Query Limited to = "10. Healthy - Client Version - Client is current"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_SERVICE on SMS_G_System_SERVICE.ResourceID = SMS_R_System.ResourceId where SMS_R_System.Client = "1" and SMS_R_System.Obsolete = "0" and SMS_R_System.Active = "1" and SMS_G_System_SERVICE.Name = "wuauserv" and SMS_G_System_SERVICE.StartMode != "Disabled"
Unhealthy Collection
  • Name = "11. Unhealthy - WUAU Service - WUAU disabled"
  • Comment = "WUAU Service is set to disabled.  Software updates cannot be installed on these devices."
  • Query Limited to = "10. Healthy - Client Version - Client is current"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_SERVICE on SMS_G_System_SERVICE.ResourceID = SMS_R_System.ResourceId where SMS_R_System.Client = "1" and SMS_R_System.Obsolete = "0" and SMS_R_System.Active = "1" and SMS_G_System_SERVICE.Name = "wuauserv" and SMS_G_System_SERVICE.StartMode = "Disabled"

  1. WUAU Version
Healthy Collection
  • Name = "12. Healthy - WUAU Version - WUAU Version Current"
  • Comment = "The WUAU service has been updated within the last six months"
  • Query Limited to = "11. Healthy - WUAU Service - WUAU enabled"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_WINDOWSUPDATEAGENTVERSION on SMS_G_System_WINDOWSUPDATEAGENTVERSION.ResourceID = SMS_R_System.ResourceId where SMS_R_System.Client = "1" and SMS_R_System.Obsolete = "0" and SMS_R_System.Active = "1" and SMS_G_System_WINDOWSUPDATEAGENTVERSION.Version is not null  and SMS_G_System_WINDOWSUPDATEAGENTVERSION.TimeStamp >= DateAdd(dd,-185,GetDate())
Unhealthy Collection
  • Name = "12. Unhealthy - WUAU Version - WUAU Version Old"
  • Comment = "WUAU service has not been updated in over six months.  This means that they may need a servicing stack update.  If the servicing stack is too far behind they will not be able to apply updates."
  • Query Limited to = "11. Healthy - WUAU Service - WUAU enabled"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_WINDOWSUPDATEAGENTVERSION on SMS_G_System_WINDOWSUPDATEAGENTVERSION.ResourceID = SMS_R_System.ResourceId where SMS_R_System.Client = "1" and SMS_R_System.Obsolete = "0" and SMS_R_System.Active = "1" and SMS_G_System_WINDOWSUPDATEAGENTVERSION.Version is not null  and SMS_G_System_WINDOWSUPDATEAGENTVERSION.TimeStamp < DateAdd(dd,-185,GetDate())

  1. MSIExec Service
Healthy Collection
  • Name = "13. Healthy - Windows Installer - Windows Installer enabled"
  • Comment = "Windows Installer service is not disabled"
  • Query Limited to = "10. Healthy - Client Version - Client is current"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_SERVICE on SMS_G_System_SERVICE.ResourceID = SMS_R_System.ResourceId where SMS_R_System.Client = "1" and SMS_R_System.Obsolete = "0" and SMS_R_System.Active = "1" and SMS_G_System_SERVICE.Name = "msiserver" and SMS_G_System_SERVICE.StartMode != "Disabled"
Unhealthy Collection
  • Name = "13. Unhealthy - Windows Installer - Windows Installer disabled"
  • Comment = "Windows Installer Service is disabled.  This means that anything that uses MSIExec.exe will be unable to run (like any .msi, .msu or .msp file)."
  • Query Limited to = "10. Healthy - Client Version - Client is current"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System inner join SMS_G_System_SERVICE on SMS_G_System_SERVICE.ResourceID = SMS_R_System.ResourceId where SMS_R_System.Client = "1" and SMS_R_System.Obsolete = "0" and SMS_R_System.Active = "1" and SMS_G_System_SERVICE.Name = "msiserver" and SMS_G_System_SERVICE.StartMode = "Disabled"

  1. Pending Reboot
Healthy Collection
  • Name = "14. Healthy - Pending Reboot - Compliant"
  • Comment = "These clients are compliant with the pending reboot CI.    Whenever the CI's version changes it will receive a new CIID and that new CIID must be updated in this collection's query."
  • Query Limited to = "10. Healthy - Client Version - Client is current"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System join sms_combineddeviceresources on sms_combineddeviceresources.resourceid = sms_r_system.resourceid where sms_combineddeviceresources.clientstate = 0
Unhealthy Collection
  • Name = "14. Unhealthy - Pending Reboot - Not Compliant or Error"
  • Comment = "These clients are non-compliant, errored, or not detected for the pending reboot CI.  Whenever the CI's version changes it will receive a new CIID and that new CIID must be updated in this collection's query."

  • Query Limited to = "10. Healthy - Client Version - Client is current"
  • Query statement
    • select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System join sms_combineddeviceresources on sms_combineddeviceresources.resourceid = sms_r_system.resourceid where sms_combineddeviceresources.clientstate != 0

Tuesday, March 22, 2016

Determine if a computer is connected via Wired or Wireless network (or both)

This is not the first time this has come up for me and it is not a trivial thing to determine.  So here's the PS script to do it. Please give me a thumbs up, a comment, or a link back to this blog if you use it.

There are quite a few lines commented out, they can be uncommented to give you more information about the variables within the script (or you can just delete them if you don't care).  I left them there mostly for myself in case I find a use case that I need to know more about the adapters.
#Get-NetConnectionType.ps1
#by Mark Randol
#randoltech.blogspot.net
cls
#PhysicalAdapter cannot be only criteria because, for no apparent reason, some virtual adapters set this to true.  Especially VPN adapters.
#AdapterType eliminates anything that is not TCP/IP
#NetConnectionStatus of 2 = connected
$AdapterTypes = (Get-WmiObject -Namespace "root/WMI" -Query "SELECT * FROM MSNdis_PhysicalMediumType")
$Adapters = (Get-WmiObject -class win32_networkadapter | Where-Object {$_.PhysicalAdapter -eq "True" -and $_.AdapterType -eq "Ethernet 802.3" -and $_.NetConnectionStatus -eq "2"})
$NetworkConnected = $false
$WiredConnected = $false
$WirelessConnected = $false

#Spit out a header line with how many adapters are connected
$AdaptersCount = ($Adapters | measure).count
if ($AdaptersCount -lt 1)
    {
        $HeaderString = "There are no adapters connected"
    }
if ($AdaptersCount -eq 1)
    {
        $HeaderString = "There is $AdaptersCount adapter connected"
    }
if ($AdaptersCount -gt 1)
    {
        #Two or more connected adapters means one of two things:
        #  a. Both your wired and wireless NICs are currently connected.
        #  b. You have multiple NICs (generally a server like a Virtual Server Host may have multiple NICs on multiple networks simultaneously).
        $HeaderString = "There are $AdaptersCount adapters connected"
    }

#Loop through all of the adapters and for each one check its type to see if it is wired or wireless
#Only return those that are connected and are TCP/IP wired or wireless adapters
foreach ($Adapter in $Adapters)
{
    foreach ($AdapterType in $AdapterTypes)
    {
        if ($AdapterType.InstanceName -eq $Adapter.Name)
        {
            if ($AdapterType.NdisPhysicalMediumType -eq 0)
            {
                #$AdapterType | format-list -Property [a-z]*
                <#write-host ("-------------------------------------------")
                $AdapterSettings = (Get-WmiObject -Class win32_NetworkAdapterSetting | where-object {$_.Element -eq $Adapter.Path})
                $AdapterOutput = $Adapter.ProductName
                $AdapterOutput = $AdapterOutput + ", "
                $AdapterOutput = $AdapterOutput + $Adapter.NetConnectionID
                $AdapterOutput = $AdapterOutput + ", Wired"
                write-host $AdapterOutput
                write-host ("-------------------------------------------")#>
                $NetworkConnected = $true
                $WiredConnected = $true
            }
            if ($AdapterType.NdisPhysicalMediumType -eq 9)
            {
                #$AdapterType | format-list -Property [a-z]*
                <#write-host ("-------------------------------------------")
                $AdapterSettings = (Get-WmiObject -Class win32_NetworkAdapterSetting | where-object {$_.Element -eq $Adapter.Path})
                $AdapterOutput = $Adapter.ProductName
                $AdapterOutput = $AdapterOutput + ", "
                $AdapterOutput = $AdapterOutput + $Adapter.NetConnectionID
                $AdapterOutput = $AdapterOutput + ", Wireless"
                write-host $AdapterOutput
                write-host ("-------------------------------------------")#>
                $NetworkConnected = $true
                $WirelessConnected = $true
            }
        }
    }
}
write-host ("Network connected = $NetworkConnected - $HeaderString")
write-host ("Connected via a wire = $WiredConnected")
write-host ("Connected wirelessly = $WirelessConnected")

Thursday, March 17, 2016

Determining which users are using offline files

One of my colleagues came to me today seeking advice on how to check which users are using offline files.  I honestly don't know why he needs to know this but, according to him, it is something that quite a lot of people on the Internet need to know.

Here's what we came up with...

He provided a registry area:
HKEY_CURRENT_USER\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\NetCache\SyncItemLog

Apparently this registry area is something that he found and it is not well known.  What he had discovered is that if a user has chosen to use offline files there will be sub-keys generated here.  If the user has not turned on offline files then there will not be any sub-keys.  The sub-key names are UNC paths to offline file locations.

So, this is where he came to me and said "How do we gather up this information on all of our users?"

What I came up with is two-fold.  First I created the PowerShell script below to gather the information into a log file.  The problem, however, is that the script must run under the user context of whichever user you want to check.  You can't run it as yourself or as system because it would not be able to read items from the other user's HKEY_CURRENT_USER hive.

The solution to the problem of running it as the local user is simply change the $OutputLog variable to point to a central share to which all of the users can write and then create a package with the script.  Have the package execute once per user per machine, under the user credentials, and only if a user is logged in.

Hopefully others find this useful.  If so, please +1, leave a commment, and/or link back to this blog.

Here's the script:

#Test-OfflineFileUse.ps1
#by Mark Randol
#randoltech.blogspot.net
new-psdrive -Name O -PSProvider FileSystem -Root "\\server\share"
$OutputLog = "O:\OfflineFilesCheck.csv"
Clear-Host
Set-Location -Path "Registry::HKEY_CURRENT_USER\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\NetCache"
$SyncItemLogRegKey = (Get-ChildItem -Path . -Name)
IF ($SyncItemLogRegKey -like 'SyncItemLog')
{
    $SyncKey = (Get-ChildItem -path .\$SyncItemLogRegKey -Name)
    foreach ($SubKey in $SyncKey)
    {
        if ($SubKey -ne $null)
        {
            $LogOutputString = $env:USERDOMAIN + '\' + $env:USERNAME + ',' + $env:COMPUTERNAME + ',' + $SubKey
            Out-File -FilePath $OutputLog -Append -InputObject $LogOutputString
        }
    }
}
ELSE
{
    $SyncKey = "SyncItemLog registry key does not exist"
    $LogOutputString = $env:USERDOMAIN + '\' + $env:USERNAME + ',' + $env:COMPUTERNAME + ',' + $SyncKey
    Out-File -FilePath $OutputLog -Append -InputObject $LogOutputString
}

Wednesday, March 9, 2016

Powershell - Ping test a list of servers

It amazes me that if you search "Powershell Ping List Servers" it doesn't return something like this.  So, here you go...

# Script by Mark Randol
# randoltech.blogspot.com

# edit the input and output file paths/names at the end of script

# the first line of input file MUST BE ComputerName
# then the list of whatever computers you want to test after that

function Test-ListOfServers
{
    [CmdletBinding(DefaultParameterSetName='Parameter Set 1',
                  SupportsShouldProcess=$true,
                  PositionalBinding=$false,
                  HelpUri = 'http://www.microsoft.com/',
                  ConfirmImpact='Medium')]
    [OutputType([String])]
    Param
    (
        # Computer Name
        [Parameter(ValueFromPipeline=$true,
                  ValueFromPipelineByPropertyName=$true,
                  ValueFromRemainingArguments=$false,
                  Position=0)]
        [Alias("computer","comp")]
        [String]
        $ComputerName
    )

    Begin
    {
    }
    Process
    {
        if (test-connection $ComputerName -Count 1 -Quiet)
            {
            $PingResult="$ComputerName,responds to ping"
            }
        else
            {
            $PingResult="$ComputerName,does not respond to ping"
            }
        out-file -Append -Encoding "Default" -FilePath $OutputFile -InputObject $PingResult
    }
    End
    {
    }
}

$InputFile="H:\WindowsPowerShell\PingThese.csv"
$OutputFile="H:\WindowsPowerShell\PingResults.csv"
Import-Csv $InputFile | Test-ListOfServers

Tuesday, March 8, 2016

Use SCCM to Centrally Manage Java Configuration

Managing java settings, from site exceptions lists to trusted certs to which TLS versions to use, has been a major pain in the butt for techs and administrators for a long time.  Using SCCM's Compliance Settings (formerly Desired Configuration Management) we can make fairly quick work of this.

I am going to give you is two scripts.  The first one checks the four files against your central store to be sure that what is on the client machine matches the central store (discovery script).  The second script copies the files from your central store to the client machine (remediation script).  The contents of the four Java settings/configuration files are up to you.

Steps to set up your configuration item:
  • Build a configuration item with a compliance rule of type "Script" and data type "String".
  • Copy the scripts into the Discovery Script and Remediation Script areas of your CI.  Both scripts are powershell.
  • Put your Java files in a central place that all of your machine accounts have access to read (users don't need access but the machines do).
  • Modify the Discovery Script and Remediation Script in your CI and change the CentralFileLocation variable to point to the location of your files.
  • Add a compliance rule that evaluates as compliant if the value returned by the script = "True All Java files pass all checks."
  • Check the check-box "Run the specified remediation script when this setting is noncompliant"
  • Add the CI to a baseline and deploy the baseline to a collection.  Be sure that in the deployment you have selected "Remediate noncompliant rules when supported".

Discovery Script:

<#
.Synopsis
   Check the files in %windir%\Sun\Java\Deployment against centrally managed files.
.DESCRIPTION
   This script is meant to be used in conjunction with the Copy_JavaSettings.ps1 script to
   manage Java JRE settings from a central location.  This script checks the files on the
   local workstation.  If the files do not match the central repository then Copy_JavaSettings.ps1
   can be used to write the correct files.
   It is suggested that the two files be used together in an SCCM configuration item for checking
   and automated remediation.
   You will need to set $CentralFileLocation to the location of your centrally managed files.
   That should be the only change that you need to make to the script.  It is suggested that
   you use a location in your sysvol as shown in the example.
   Success condition will return "True All Java files pass all checks."
   Failure conditions will return "False " followed by the reason for failure.
#>

function Check-LocalJavaFolder
{
    [OutputType([Boolean],[String])]
    $Compliant=$false
    $JavaFolderExist=Test-Path $JavaFolder
    #write-host ("Checking Java Folder Existence")
    if ($JavaFolderExist -eq $true)
    {
        $Compliant=$true
        $Problem="Java Folder Exists in $JavaFolder"
    }
    else
    {
        $Compliant=$false
        $Problem='Folder %windir%\Sun\Java\Deployment does not exist.'
    }
    Return $Compliant, $Problem
}

function Check-JavaSettingsFileExist
{
    [OutputType([Boolean],[String])]
    $Compliant=$false
    $DeploymentConfigFileExist=Test-Path $DeploymentConfigFile
    $DeploymentPropertiesFileExist=Test-Path $DeploymentPropertiesFile
    $ExceptionSitesFileExist=Test-Path $ExceptionSitesFile
    $TrustedCertsFileExist=Test-Path $TrustedCertsFile
    if ($DeploymentConfigFileExist -ne $true)
    {
        $Compliant=$false
        $Problem="$DeploymentConfigFile does not exist."
        Return $Compliant, $Problem
    }
    if ($DeploymentPropertiesFileExist -ne $true)
    {
        $Compliant=$false
        $Problem="$DeploymentPropertiesFile does not exist."
        Return $Compliant, $Problem
    }
    if ($ExceptionSitesFileExist -ne $true)
    {
        $Compliant=$false
        $Problem="$ExceptionSitesFile does not exist."
        Return $Compliant, $Problem
    }
    if ($TrustedCertsFileExist -ne $true)
    {
        $Compliant=$false
        $Problem="$TrustedCertsFile does not exist."
        Return $Compliant, $Problem
    }
    $Compliant=$true
    $Problem='All managed files exist.'
    Return $Compliant, $Problem
}

function Check-JavaSettingsFileSizes
{
    [OutputType([Boolean],[String])]
    $Compliant=$false
    $DeploymentConfigFileSize=(Get-Item $CentralDeploymentConfigFile).length
    $DeploymentConfigLocalFileSize=(Get-Item $DeploymentConfigFile).Length
    if ($DeploymentConfigLocalFileSize -ne $DeploymentConfigFileSize)
    {
        $Compliant=$false
        $Problem="$DeploymentConfigFile file size is not correct."
        Return $Compliant, $Problem
    }
    $DeploymentPropertiesFileSize=(Get-Item $CentralDeploymentPropertiesFile).length
    $DeploymentPropertiesLocalFileSize=(Get-Item $DeploymentPropertiesFile).Length
    if ($DeploymentPropertiesLocalFileSize -ne $DeploymentPropertiesFileSize)
    {
        $Compliant=$false
        $Problem="$DeploymentPropertiesFile file size is not correct."
        Return $Compliant, $Problem
    }
    $ExceptionSitesFileSize=(Get-Item $CentralExceptionSitesFile).length
    $ExceptionSitesLocalFileSize=(Get-Item $ExceptionSitesFile).Length
    if ($ExceptionSitesLocalFileSize -ne $ExceptionSitesFileSize)
    {
        $Compliant=$false
        $Problem="$ExceptionSitesFile file size is not correct."
        Return $Compliant, $Problem
    }
    $TrustedCertsFileSize=(Get-Item $CentralTrustedCertsFile).length
    $TrustedCertsLocalFileSize=(Get-Item $TrustedCertsFile).Length
    if ($TrustedCertsLocalFileSize -ne $TrustedCertsFileSize)
    {
        $Compliant=$false
        $Problem="$TrustedCertsFile file size is not correct."
        Return $Compliant, $Problem
    }
    $Compliant=$true
    $Problem='All managed files are correct size.'
    Return $Compliant, $Problem
}

function Check-JavaSettingsFileDates
{
    [OutputType([Boolean],[String])]
    $Compliant=$false
    $DeploymentConfigFileDate=(Get-Item $CentralDeploymentConfigFile).length
    $DeploymentConfigLocalFileDate=(Get-Item $DeploymentConfigFile).Length
    if ($DeploymentConfigLocalFileDate -ne $DeploymentConfigFileDate)
    {
        $Compliant=$false
        $Problem="$DeploymentConfigFile file date is not correct."
        Return $Compliant, $Problem
    }
    $DeploymentPropertiesFileDate=(Get-Item $CentralDeploymentPropertiesFile).length
    $DeploymentPropertiesLocalFileDate=(Get-Item $DeploymentPropertiesFile).Length
    if ($DeploymentPropertiesLocalFileDate -ne $DeploymentPropertiesFileDate)
    {
        $Compliant=$false
        $Problem="$DeploymentPropertiesFile file date is not correct."
        Return $Compliant, $Problem
    }
    $ExceptionSitesFileDate=(Get-Item $CentralExceptionSitesFile).length
    $ExceptionSitesLocalFileDate=(Get-Item $ExceptionSitesFile).Length
    if ($ExceptionSitesLocalFileDate -ne $ExceptionSitesFileDate)
    {
        $Compliant=$false
        $Problem="$ExceptionSitesFile file date is not correct."
        Return $Compliant, $Problem
    }
    $TrustedCertsFileDate=(Get-Item $CentralTrustedCertsFile).length
    $TrustedCertsLocalFileDate=(Get-Item $TrustedCertsFile).Length
    if ($TrustedCertsLocalFileDate -ne $TrustedCertsFileDate)
    {
        $Compliant=$false
        $Problem="$TrustedCertsFile file date is not correct."
        Return $Compliant, $Problem
    }
    $Compliant=$true
    $Problem='All managed files are correct dates.'
    Return $Compliant, $Problem
}


$CentralFileLocation="\\domain.com\sysvol\domain.com\Java"
$WindowsFolder=$env:windir
$JavaFolder="$WindowsFolder\Sun\Java\Deployment"
$DeploymentConfigFile=$JavaFolder+"\deployment.config"
$DeploymentPropertiesFile=$JavaFolder+"\deployment.properties"
$ExceptionSitesFile=$JavaFolder+"\exception.sites"
$TrustedCertsFile=$JavaFolder+"\trusted.certs"
$CentralDeploymentConfigFile=$CentralFileLocation+"\deployment.config"
$CentralDeploymentPropertiesFile=$CentralFileLocation+"\deployment.properties"
$CentralExceptionSitesFile=$CentralFileLocation+"\exception.sites"
$CentralTrustedCertsFile=$CentralFileLocation+"\trusted.certs"
$Compliant=$false
$Problem='Compliant'

clear-host
$Compliance=(Check-LocalJavaFolder)
If ($Compliance -match $true)
    {
        $Compliance=(Check-JavaSettingsFileExist)
    }
If ($Compliance -match $true)
    {
        $Compliance=(Check-JavaSettingsFileSizes)
    }
If ($Compliance -match $true)
    {
        $Compliance=(Check-JavaSettingsFileDates)
    }
If ($Compliance -match $true)
    {
        $Compliance=($true, "All Java files pass all checks.")
        write-host $Compliance
    }
    else
    {
        write-host $Compliance
    }



Remediation Script:


<#
.Synopsis
    Copies files used for centralized management of Java JRE from central location to local workstation.
.DESCRIPTION
   This script will copy the centrally managed Java config files from a
   central repository (definied by $CentralFileLocation) to %windir%\Sun\Java\Deployment
   You will need to set $CentralFileLocation to the location of your centrally managed files.
   That should be the only change that you need to make to the script.  It is suggested that
   you use a location in your sysvol as shown in the example.
#>

$CentralFileLocation="\\domain.com\sysvol\domain.com\Java"
$WindowsFolder=$env:windir
$JavaFolder="$WindowsFolder\Sun\Java\Deployment"
$DeploymentConfigFile=$JavaFolder+"\deployment.config"
$DeploymentPropertiesFile=$JavaFolder+"\deployment.properties"
$ExceptionSitesFile=$JavaFolder+"\exception.sites"
$TrustedCertsFile=$JavaFolder+"\trusted.certs"
$CentralDeploymentConfigFile=$CentralFileLocation+"\deployment.config"
$CentralDeploymentPropertiesFile=$CentralFileLocation+"\deployment.properties"
$CentralExceptionSitesFile=$CentralFileLocation+"\exception.sites"
$CentralTrustedCertsFile=$CentralFileLocation+"\trusted.certs"

#Check first part of path - if it doesn't exist then create it
  $JavaFolder="$WindowsFolder\Sun"
  $JavaFolderExist=Test-Path $JavaFolder
  if ($JavaFolderExist -eq $false) {New-Item $JavaFolder -type directory}
#Check second part of path - if it doesn't exist then create it
  $JavaFolder="$WindowsFolder\Sun\Java"
  $JavaFolderExist=Test-Path $JavaFolder
  if ($JavaFolderExist -eq $false) {New-Item $JavaFolder -type directory}
#Check last part of path - if it doesn't exist then create it
  $JavaFolder="$WindowsFolder\Sun\Java\Deployment"
  $JavaFolderExist=Test-Path $JavaFolder
  if ($JavaFolderExist -eq $false)     {New-Item $JavaFolder -type directory}

Clear-Host

#clear any contents that might already be there
Get-ChildItem -Path $JavaFolder -Include *.* -File -Recurse | foreach { $_.Delete()}

#Drop in our configuration files
Copy-Item -path $CentralDeploymentConfigFile -destination $DeploymentConfigFile
Copy-Item -path $CentralDeploymentPropertiesFile -destination $DeploymentPropertiesFile
Copy-Item -path $CentralExceptionSitesFile -destination $ExceptionSitesFile
Copy-Item -path $CentralTrustedCertsFile -destination $TrustedCertsFile

Tuesday, March 1, 2016

Loading the SCOM PowerShell Module

Loading the SCOM PowerShell Module

This is a little script that I got from cchamp over on Technet that will allow you to load up the SCOM module into your local PowerShell without having to load the entire SCOM console.

-----------------------------------------------------------
-----------------------------------------------------------

$OMCmdletsTest = (Get-Module|% {$_.Name}) -Join ' '
If (!$OMCmdletsTest.Contains('OperationsManager')) {
    $ModuleFound = $false
    $SetupKeys = @('HKLM:\Software\Microsoft\Microsoft Operations Manager\3.0\Setup',       
        'HKLM:\SOFTWARE\Microsoft\System Center Operations Manager\12\Setup')
    foreach($setupKey in $SetupKeys) {
        If ((Test-Path $setupKey) -and ($ModuleFound -eq $false)) {
            $setupKey = Get-Item -Path $setupKey           
            $installDirectory = $setupKey.GetValue('InstallDirectory')
            $psmPath = $installdirectory + '\Powershell\OperationsManager\OperationsManager.psm1'
            If (Test-Path $psmPath) {
                $ModuleFound = $true
            }
        }
    }
    If ($ModuleFound) {
        Import-Module $psmPath
    } else {
        Import-Module OperationsManager
    }
}

-----------------------------------------------------------
-----------------------------------------------------------

Thanks cchamp.