Powershell – Get SCCM Client info from WMI

Last week I was busy with a deployment and this week the question came up “does every server have a SCCM client installed”. My first reaction was “yes” but after a close look at our SCCM sites I wasn’t sure. The problem I had was that there are multiple AD domains linked to multiple SCCM sites, so if one site showed ‘Client=No’ that didn’t mean the server didn’t have a client. It only meant that the server didn’t have a client in that particular site. So only looking at SCCM wouldn’t give me the answer.

What if I would use AD as a source for my search? Dump every computer object into a list and run through that list to obtain information about the client. The challenge with that approach was that AD contains computer objects that are not really computer objects… For instance, if you build a Failover Cluster the cluster itself creates a computer object and also the cluster resources use computer objects. So using AD as a starting point was of the table.

Another possible source was vCenter. As vCenter contains all the (virtual) servers I could use that as a source. A small problem was that in the DTA (Development, Test, and Acceptance) domains a lot of servers were turned off. So I had to use a filter to get the correct list of servers. And I had to filter on the OS of the VM…

The script I finally put together looked like this:

<#
.SYNOPSIS
    Get SCCM client information from WMI(based on vSphere VM's)
.DESCRIPTION
    Use this script if you need to retrieve SCCM client information
.PARAMETER vCenterServer
    Name of the vCenter Server
.EXAMPLE
    .\Get-CMClientInfo.ps1 -vCenterServer 'vcserver.domain.local'
    Retrieve SCCM client info from all VM's under the vCenter Server vcserver.domain.local
.NOTES
    Script name: Get-CMClientInfo.ps1
    Author:      Jeroen Buren
    DateCreated: 06-06-2017
#>

[CmdletBinding(SupportsShouldProcess=$true)]

param(
    [parameter(Mandatory=$true,HelpMessage="Name of the vCenter Server")]
    [ValidateScript({Test-Connection -ComputerName $_ -Count 1 -Quiet})]
    [string]$vCenterServer
)

#----------------------------------------------------------
# STATIC VARIABLES
#----------------------------------------------------------
$ProgressCount = 0
$Results = @()

#----------------------------------------------------------
# FUNCTIONS
#----------------------------------------------------------

function Get-VMFolderPath {
    Begin {}
    Process {
        foreach ($vm in $Input) {
            $DataCenter = $vm | Get-Datacenter
            $DataCenterName = $DataCenter.Name
            $VMname = $vm.Name
            $VMParentName = $vm.Folder
            if ($VMParentName.Name -eq "vm") {
                $FolderStructure = "{0}\{1}" -f $DataCenterName, $VMname
                $FolderStructure
                Continue
            }
            else {
                $FolderStructure = "{0}\{1}" -f $VMParentName.Name, $VMname
                $VMParentID = Get-Folder -Id $VMParentName.ParentId
                do {
                    $ParentFolderName = $VMParentID.Name
                    if ($ParentFolderName -eq "vm") {
                        $FolderStructure = "$DataCenterName\$FolderStructure"
                        $FolderStructure
                        break
                    }
                    $FolderStructure = "$ParentFolderName\$FolderStructure"
                    $VMParentID = Get-Folder -Id $VMParentID.ParentId
                }
                until ($VMParentName.ParentId -eq $DataCenter.Id)
            }
        }
   }
   End {}
}


#----------------------------------------------------------
# SCRIPTBODY
#----------------------------------------------------------

# Connect to vCenter Server
Connect-VIServer -Server $vCenterServer | Out-Null

# Select the VMs..
$VMs = Get-VM | Where {($_.PowerState -eq 'PoweredOn') -and ($_.Guest -match 'Microsoft Windows Server')} | Sort-Object -Property Name
# ..and count them
$ComputerCount = $VMs.Count

Foreach ($VM in $VMs) {
    $ProgressCount++
    $Computer = $VM.Name
    $Folder = $VM | Get-VMFolderPath
    Write-Progress -Activity "Getting SCCM client information..." -Id 1 -Status "$($ProgressCount) / $($ComputerCount)" -CurrentOperation "$Computer" -PercentComplete (($ProgressCount / $ComputerCount) * 100)
    #If (Test-Connection -ComputerName $Computer -Count 1 -Quiet) {
        Try {
            $ClientVersion = $(Get-WMIObject -Query "SELECT ClientVersion FROM SMS_Client" -Namespace "root/ccm" -Computername $Computer -ErrorAction Stop | Select ClientVersion).ClientVersion
            $SiteCode = $($([WMIClass]"\\$Computer\root\ccm:SMS_Client").GetAssignedSite() | Select sSiteCode).sSiteCode
            $Object = [pscustomobject]@{
                Computername = $Computer
                Folder = $Folder
                SiteCode = $SiteCode
                ClientVersion = $ClientVersion
            }
            $Results += $Object
        }
        Catch {
            $Object = [pscustomobject]@{
                Computername = $Computer
                Folder = $Folder
                SiteCode = "Unknown"
                ClientVersion = "Unknown"
            }
            $Results += $Object
        }
    #}
}

$Results #| Export-Csv -Path <path to CSV file> -NoTypeInformation

# Disconnect from the vCenter Server
Disconnect-VIServer -Server $vCenterServer -Confirm:$false

SCCM – Windows Update settings and errors

The last two weeks I was very busy deploying a specific update to several hundred servers. Besides the deployment I also spent some time answering questions about the process because some collegues didn’t understand why it took longer than expected.

Because I didn’t find the answers all in one place I decided to write this post. For myself, as a placeholder for recurring issues and for my collegues so they could understand the process a bit better.

Client Settings

When it comes to Software Update there are a few client settings that are important.

  • Computer Agent – Disable deadline randomization
    When you set this to Yes the client starts with the installation as soon as the deadline is reached.
    When you set this to No there will be a random wait time of up to two hours.
  • Computer Restart – Display notification/Display dialog box
    This specifies the restart behaviour. Your maintenance windows should be longer then those two periods combined.
  • Software Updates – Software update scan schedule
    This specifies at which interval the client checks for new updates.

More information can be found here.

Error Codes

During the deployment I experienced several errors. Some related to SCCM but others a bit more general.

  • 0x87D00692 – GPO conflict
    In some domains there were old WSUS GPO’s that conflicted with the WSUS settings that are generated by the SCCM client.
    Go to HKLM\Software\Policies\Microsoft\Windows\WindowsUpdate and check your settings. There is a lot of information out there how you should configure your GPO’s.
  • 0x87D00669 – Unable to download content
    This could also be related to the 0x87D00692 error. Check your content distribution.
  • 0x87D0070C – Timeout
    When this error occurs the server took more then the default 10 minutes to install the update. Increase the maximum run time on the related update.
  • 0x87000070 – Not enough disk space
    This is more a general error. But when I had this error the server didn’t have enough free space on the system disk.