Tuesday, 14 August 2012

PowerShell - Get SCCM DCM compliance remotely.

Here at the company I contract too we use SCCM DCM to maintain a base list of hotfixes on our SCCM site servers. Whilst installing a new set of additional hotfixes I needed a quick and easy way to remotely view the current DCM compliance state for any given server. Below is a PowerShell module I wrote to return this information easily. I have also gone one step further and added it into an SCCM.psm1 module which I use a profile script to load whenever I open the PS shell so the command is always available for use.


Version 1 Simple Function

function Get-SCCMDCMInfo
{
    param (
        [Parameter(Mandatory=$true, HelpMessage="Computer Name",ValueFromPipeline=$true)] $ComputerName
    )
    $Baselines = Get-WmiObject -ComputerName $ComputerName -Namespace root\ccm\dcm -Class SMS_DesiredConfiguration
    $Baselines | Select @{Name="ComputerName";Expression={$_.__Server}}, @{Name="Baseline Display Name";Expression={$_.Displayname}}, Version, @{Name="Eval Status";Expression={$_.Status}},@{Name="Compliance Status";Expression={$_.Lastcompliancestatus}}, @{Name="Last Evaluation Time";Expression={$_.ConvertToDateTime($_.LastEvalTime)}} | format-table -AutoSize
}

Get-SCCMDCMInfo localhost

Version 2 Advanced Function

I have now spent a little time adding to the basic function I created earlier. Now it will perform a ping check, handle piping and display the DCM baseline GUID when there is no display name value.



function Get-SCCMDCMInfo {

<#
.SYNOPSIS
Returns the current evaluation state and compliance state for a target workstation. 
        If a new baseline has been recieved by a client but not yet been evaluated then
        the 'Baseline Display Name' will return the baseline GUID.  

.PARAMETER  Computer
The computer name to target.

.EXAMPLE
PS C:\> Get-SCCMDCMInfo -ComputerName "Foo"

    .EXAMPLE
PS C:\> Get-SCCMDCMInfo Foo

    .EXAMPLE
PS C:\> Get-SCCMDCMInfo Foo Foo2

#>
[CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, HelpMessage="Computer Name",ValueFromPipeline=$true)] $ComputerName
    )

    Process {

    foreach ($computer in $ComputerName) {

        if(Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet){
     
            try
   {
                $ComplainceHash = [hashtable]@{
                                "0" = 'Non-Compliant'
                                "1" = 'Compliant'
                                "2" = 'Submitted'
                                "3" = 'Detecting'
                                "4" = 'Detecting'
                                "5" = 'Not Evaluated'                  
                        }   
                $EvalHash = [hashtable]@{
                                "0" = 'EVALUATING'
                                "1" = 'Evaluated'
                                "5" = 'Not Evaluated'                                   
                        } 


                $Baselines = Get-WmiObject -ComputerName $Computer -Namespace root\ccm\dcm -Class SMS_DesiredConfiguration
                $Baselines | Select @{Name="ComputerName";Expression={$_.__Server}},
                                    @{Name="Baseline Display Name";Expression={if ($_.Displayname) {$_.DisplayName} else {$_.Name.split("/")[1]}}},
                                    Version,
                                    @{Name="Eval Status";Expression={$EvalHash[$_.Status.tostring()]}},
                                    @{Name="Compliance Status";Expression={$ComplainceHash[$_.Lastcompliancestatus.tostring()]}},
                                    @{Name="Last Evaluation Time";Expression={$_.ConvertToDateTime($_.LastEvalTime)}} | format-table -AutoSize
               }

            catch
        {
      throw
        }
               
      
     }
        else
                {
                     Write-Host "" 
                     Write-Host $($Computer).ToUpper()"is NOT Online." -ForegroundColor DarkYellow
                     Write-Host ""
                }
        }
    }
}
 
Version 3 Advanced Function that outputs a usable object.
 
I have realized the error of my ways and no longer use Write-Host to view results, instead the module is now out putting an actual object that can for example be easily piped to Out-GridView. I have also included better error handling and errors now also get piped out as objects for much better usability.
 
function Get-SCCMDCMInfo {
<#
 .SYNOPSIS
  Returns the current evaluation state and compliance state for a target workstation.
        If a new baseline has been recieved by a client but not yet been evaluated then
        the 'Baseline Display Name' will return the baseline GUID. 
 .PARAMETER  Computer
  The computer name to target.
 .EXAMPLE
  PS C:\> Get-SCCMDCMInfo -ComputerName "Foo"
    .EXAMPLE
  PS C:\> Get-SCCMDCMInfo Foo
    .EXAMPLE
  PS C:\> Get-SCCMDCMInfo Foo Foo2
#>
[CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, HelpMessage="Computer Name",ValueFromPipeline=$true)] $ComputerName
    )
    Process {
    $ErrorActionPreference = "SilentlyContinue"
    foreach ($computer in $ComputerName) {  
        if(Test-Connection -Cn $computer -BufferSize 16 -Count 1 -ea 0 -quiet){
    
        $ComplainceHash = [hashtable]@{
                                "0" = 'Non-Compliant'
                                "1" = 'Compliant'
                                "2" = 'Submitted'
                                "3" = 'Detecting'
                                "4" = 'Detecting'
                                "5" = 'Not Evaluated'                 
                        }  
        $EvalHash = [hashtable]@{
                                "0" = 'EVALUATING'
                                "1" = 'Evaluated'
                                "5" = 'Not Evaluated'                                  
                        }
            try
          { 
                $Baselines = Get-WmiObject -ComputerName $Computer -Namespace root\ccm\dcm -Class SMS_DesiredConfiguration -ErrorVariable myerror -ErrorAction Stop
                Write-Debug "Performing WMI query"
                }

            catch
           {
         $object = New-Object –TypeName PSObject
                        $object | Add-Member –MemberType NoteProperty –Name 'Computer Name' -value $($Computer).ToUpper()
                        $object | Add-Member –MemberType NoteProperty –Name 'Baseline Display Name' -value $MyError.ErrorRecord
                        $object | Add-Member –MemberType NoteProperty –Name 'Eval Status' -Value "Error"
                        $object | Add-Member –MemberType NoteProperty –Name 'Compliance Status' -Value "Error"
                        
                        Write-Output $object
                        Write-Debug "Catching the error."
           }
            Foreach ($Baseline in $Baselines){
                $object = New-Object –TypeName PSObject
                    $object | Add-Member –MemberType NoteProperty –Name 'Computer Name' -value $($Computer).ToUpper()
                    $object | Add-Member –MemberType NoteProperty –Name 'Baseline Display Name' -value $(if ($Baseline.Displayname) {$Baseline.DisplayName} else {$Baseline.Name.split("/")[1]})
                    $object | Add-Member –MemberType NoteProperty –Name 'Version' -Value $Baseline.version
                    $object | Add-Member –MemberType NoteProperty –Name 'Eval Status' -Value $EvalHash[$Baseline.Status.tostring()]
                    $object | Add-Member –MemberType NoteProperty –Name 'Compliance Status' -Value $ComplainceHash[$Baseline.Lastcompliancestatus.tostring()]
                    $object | Add-Member –MemberType NoteProperty –Name 'Last Evaluation Time' -Value $Baseline.ConvertToDateTime($Baseline.LastEvalTime)
                   
                    Write-Output $object
                    Write-Debug "Writting whole objects to the pipeline"
                    }
     
     }
        else
                {
                     $object = New-Object –TypeName PSObject
                        $object | Add-Member –MemberType NoteProperty –Name 'Computer Name' -value $($Computer).ToUpper()
                        $object | Add-Member –MemberType NoteProperty –Name 'Eval Status' -Value "Offline"
                        $object | Add-Member –MemberType NoteProperty –Name 'Compliance Status' -Value "Offline"
                        Write-Output $object
                        Write-Debug "Writting an object just to state the machine is offline."
                }
        }
    }
}
 


1 comment:

  1. Ben, Why does the Eval Status say "EVALUATING" for all of the baselines on a client all of the time?

    ReplyDelete