SCCM Script: Fix State Messages
Overview
I developed this tool, Fix-DMGSCCMStateMessage.ps1, to assit in troubleshooting SCCM “In Progress” and State Message Communication issues. If the UpdateStore.log shows that a particular windows update component is installed, but it is still in progress in the SCCM console, the State Message is likely not communicating properly to the SQL server. State messaging is a mechanism in SCCM which replicates point in time conditions on the client.
Fix-DGMSCCMStateMessage Tool
The tool will automatically update the State Message locally on the SCCM client by invoking the following two commands:
$SCCMUpdatesStore = New-Object -ComObject Microsoft.CCM.UpdatesStore
$SCCMUpdatesStore.RefreshServerComplianceState()
The tool requires the –csvfile argument, which is the path to a .CSV file containing one column, Hostname, with the computer hostnames listed in the column and can be run as in the example below.
Fix-DGMSCCMStateMessage Log File
The utility will create a log file that is compatible with the CMTrace tool, which includes the thread, time, state and component for each process.
<#
.Synopsis
Update the State Message locally on the SCCM client
.DESCRIPTION
The tool, Fix-DMGSCCMStateMessage.ps1 was written by David Maiolo which will automatically update the State Message locally on the SCCM client by
invoking the following two commands: $SCCMUpdatesStore = New-Object -ComObject Microsoft.CCM.UpdatesStore and $SCCMUpdatesStore.RefreshServerComplianceState()
.EXAMPLE
Fix-DMGSCCMStateMessage -CSVFile state_message_import.csv
.EXAMPLE
Fix-DMGSCCMStateMessage -Hostname WORKSTATION01
#>
function New-DGMLogFile
{
param (
[Parameter(Mandatory=$true)]
$message,
[Parameter(Mandatory=$true)]
$component,
[Parameter(Mandatory=$true)]
$type )
switch ($type)
{
1 { $type = "Info" }
2 { $type = "Warning" }
3 { $type = "Error" }
4 { $type = "Verbose" }
}
if (($type -eq "Verbose") -and ($Global:Verbose))
{
$toLog = "{0} `$$<{1}><{2} {3}>" -f ($type + ":" + $message), ($Global:ScriptName + ":" + $component), (Get-Date -Format "MM-dd-yyyy"), (Get-Date -Format "HH:mm:ss.ffffff"), $pid
$toLog | Out-File -Append -Encoding UTF8 -FilePath ("filesystem::{0}" -f $Global:LogFile)
Write-Host $message
}
elseif ($type -ne "Verbose")
{
$toLog = "{0} `$$<{1}><{2} {3}>" -f ($type + ":" + $message), ($Global:ScriptName + ":" + $component), (Get-Date -Format "MM-dd-yyyy"), (Get-Date -Format "HH:mm:ss.ffffff"), $pid
$toLog | Out-File -Append -Encoding UTF8 -FilePath ("filesystem::{0}" -f $Global:LogFile)
if ($type -eq 'Info') { Write-Host $message }
if ($type -eq 'Warning') { Write-Host $message -ForegroundColor Yellow}
if ($type -eq 'Error') { Write-Host $message -ForegroundColor Red}
}
if (($type -eq 'Warning') -and ($Global:ScriptStatus -ne 'Error')) { $Global:ScriptStatus = $type }
if ($type -eq 'Error') { $Global:ScriptStatus = $type }
if ((Get-Item $Global:LogFile).Length/1KB -gt $Global:MaxLogSizeInKB)
{
$log = $Global:LogFile
Remove-Item ($log.Replace(".log", ".lo_"))
Rename-Item $Global:LogFile ($log.Replace(".log", ".lo_")) -Force
}
}
function GetScriptDirectory
{
$invocation = (Get-Variable MyInvocation -Scope 1).Value
Split-Path $invocation.MyCommand.Path
}
function GetStringBetweenTwoStrings($firstString, $secondString, $importString){
#Get content from file
$file = $importString
#Regex pattern to compare two strings
$pattern = "$firstString(.*?)$secondString"
#Perform the opperation
$result = [regex]::Match($file,$pattern).Groups[1].Value
#Return result
return $result
}
function Fix-DMGSCCMStateMessage
{
[CmdletBinding()]
[Alias()]
[OutputType([int])]
Param
(
# Param1 help description
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
Position=0,
ParameterSetName='Parameter Set 1')]
[ValidateScript({(Test-Path $_)})]
$CSVFile,
# Param2 help description
[Parameter(Mandatory=$true,
ValueFromPipeline=$true,
Position=0,
ParameterSetName='Parameter Set 2')]
[ValidateScript({(Get-ADComputer -Identity $_).objectclass -eq 'computer' })]
[String]$Hostname
)
Begin
{
$path = (get-item -Path .).FullName
if ($CSVFile -ne $null){
Write-Host Importing $CSVFile...
$csv = import-csv "$CSVFile"
}else{
$csv = [PSCustomObject]@{
Hostname = $Hostname}
}
Write-Host =========================================
Write-Host SCCM State Message Update Tool
Write-Host =========================================
Write-Host "dmaiolo"
New-DGMLogFile -message ("Starting Logging for Fix-DMGSCCMStateMessage") -component "Main()" -type 1
}
Process
{
$computers = @();
$csv | foreach-object {
$h = $_.Hostname
if(Test-Connection -ComputerName $h -Count 1 -ErrorAction SilentlyContinue){
Try{
$g = $null
Invoke-Command -ComputerName $h -ScriptBlock { $SCCMUpdatesStore = New-Object -ComObject Microsoft.CCM.UpdatesStore
$SCCMUpdatesStore.RefreshServerComplianceState() } -ErrorAction Stop
New-DGMLogFile -message ("$h`: State Message Updated Succesfully (RefreshServerComplianceState())") -component "Main()" -type 1
Write-Host $h`: Sleeping 5 Seconds...
sleep 5
$resultsfile = "\\$h\c$\Windows\CCM\Logs\UpdatesStore.log"
$logLines = @()
$g = Get-Content $resultsfile -ErrorAction Stop
$lastLine = $g.Length
for($i = $lastLine-1; $i -ge 0; $i--)
{
$g[$i] -match '<\!\[LOG\[(.*?)\]LOG.*
Leave a Reply
Want to join the discussion?Feel free to contribute!