I was recently asked to assist in troubleshooting large WMI repositories, sometimes in upwards of 1GB. This was causing the slow RDP logon issue described in KB2020286. We found that RSOP logging was not enabled on these machines. My first step was to identify which servers had large WMI repositories. I created a SCOM Management Pack (MP) for this which I have attached. The MP also does some basic health checks of WMI using the script below.

Update: I removed the /verifyrepository check from the MP. We found that it can sometimes conflict with WMI on reboots. WMI also does a /verifyrepository on startup. If SCOM also runs this check at the same time then WMI may appear to be hung for several minutes, but it eventually recovers. Connections to WMI can still be made but queries won’t return anything until after both /verifyrepository commands are done.

1 #functions 2 function ConnectToWMINamespace 3 { 4 param ([string]$nameSpace, [ref]$ErrorsFound) 5 $wmiProvider = Get-WmiObject -Namespace $nameSpace -Class '__Namespace' 6 if ($?) 7 { 8 $ret = $false 9 } 10 else 11 { 12 $ErrorsFound.value += "Failed to connect to WMI Namespace {0} with error: {1}`n" -f $nameSpace, $error[0] 13 $ret = $true 14 } 15 return $ret 16 } 17 18 function VerifyRepository 19 { 20 $winmgmt = winmgmt /verifyrepository 21 if ($winmgmt -eq "WMI repository is consistent") {$ret = $false} else {$ret = $true} 22 return $ret 23 } 24 25 function GetFileSize 26 { 27 param ([string]$path) 28 $windir = 'windir' 29 $windir = Get-Item env:$windir 30 $fullpath = $windir.Value + $path 31 $file = Get-Item $fullpath 32 $filesize = $file.Length/1MB 33 return "{0:N0}" -f $filesize 34 } 35 36 function SendToSCOM 37 { 38 param ( 39 [string]$Failure, [string]$ConnectToCimv2, [string]$WMIRepository, 40 [int]$WMIRepositorySize, [string]$WMIRepositorySizeOverThreshold, 41 [int]$WMIRepositorySizeThreshold, [string]$Errors) 42 $api = New-Object -comObject MOM.ScriptAPI 43 $bag = $api.CreatePropertyBag() 44 $bag.AddValue('Failure', $Failure) 45 $bag.AddValue('ConnectToCimv2', $ConnectToCimv2) 46 $bag.AddValue('WMIRepository', $WMIRepository) 47 $bag.AddValue('WMIRepositorySize', $WMIRepositorySize) 48 $bag.AddValue('WMIRepositorySizeOverThreshold', $WMIRepositorySizeOverThreshold) 49 $bag.AddValue('WMIRepositorySizeThreshold', $WMIRepositorySizeThreshold) 50 $bag.AddValue('Errors', $Errors) 51 $bag 52 } 53 54 #Main script 55 [string]$ErrorsFound = "" 56 [bool]$bFailed = $false 57 [bool]$bOverSizeThreshold = $false 58 [int]$wmiRepositorySizeThreshold = '$Config/WMISizeThreshold$' 59 if ((!($wmiRepositorySizeThreshold -is [int])) -or ($wmiRepositorySizeThreshold -eq 0)) {$wmiRepositorySizeThreshold = 500} 60 61 #Run tests 62 $cimv2 = ConnectToWMINamespace -nameSpace("root\cimv2") -ErrorsFound([ref]$ErrorsFound) 63 $wmiRepository = VerifyRepository 64 [int]$wmiRepositorySize = GetFileSize -path("\system32\wbem\repository\objects.data") 65 66 #Review results 67 if ($cimv2) {$bFailed = $cimv2} 68 if ($wmiRepository) {$bFailed = $wmiRepository} 69 if ($wmiRepositorySize -gt $wmiRepositorySizeThreshold) 70 { 71 Write-Host $wmiRepositorySize 72 Write-Host $wmiRepositorySizeThreshold 73 $bFailed = $true 74 $bOverSizeThreshold = $true 75 } 76 77 #Print results 78 Write-Host "Overall Failure: "$bFailed.ToString() 79 Write-Host "cimv2 Failure: "$cimv2.ToString() 80 Write-Host "winmgmt /verifyrepository Failure: "$wmiRepository.ToString() 81 Write-Host "WMI Size: "$wmiRepositorySize"MB" 82 Write-Host "WMI Size Threshold: " $wmiRepositorySizeThreshold 83 Write-Host "Over Size Threshold Failure: "$bOverSizeThreshold.ToString() 84 Write-Host "WMI Errors Reported: "$ErrorsFound 85 86 #Send to SCOM 87 SendToSCOM $bFailed.ToString() $cimv2.ToString() $wmiRepository.ToString() $wmiRepositorySize $bOverSizeThreshold.ToString() $wmiRepositorySizeThreshold $ErrorsFound

Once we identified the machines with WMI repositories over 300MB we worked to identify what in the repository was taking up all the space. I researched various ways to determine this including recursively going through each namespace and class in WMI and counting them but since many providers populate instances that aren’t actually stored in the repository I figured this wouldn’t be an accurate method of determining what is taking up space in WMI. I ended up going with the method below instead.

  1. Copy the objects.data (WMI repository) file from several of the machines identified to your desktop. The file is located in the %windir%\system32\wbem\repository folder.
  2. Download strings from sysinternals
  3. Run the following command: strings objects.data > strings.txt
  4. Run the following PowerShell script after modifying the two variables at the top of the script.
1 $fileIn = 'c:\temp\strings.txt' 2 $fileOut = 'c:\temp\output.csv' 3 4 function PopulateHash ([string]$s, [hashtable]$h) 5 { 6 if (!$h.ContainsKey($s)) 7 { 8 $h.Add($s, 1) 9 } 10 else 11 { 12 $i = $h.Get_Item($s) 13 $i++ 14 $h.Set_Item($s, $i) 15 } 16 } 17 $strings = @{} 18 19 Write-Host "Counting strings" 20 21 foreach ($line in [System.IO.File]::ReadLines($fileIn)) 22 { 23 PopulateHash $line $strings 24 } 25 26 $strings.GetEnumerator() | Sort-Object Value -Descending | Export-Csv $fileOut 27

We found that SCCM data seemed to be using up the most space. Knowing that I figured that now would be a good time to recursively go through the root\ccm namespace (used by SCCM) to determine which class has the most instances. The script below shows you how many instances are under root\ccm (and any namespaces under this) and lists out each class along with the top namespaces.

1 function CountWMIObjects 2 { 3 param ([string]$nameSpace) 4 Write-Host "." -NoNewline 5 if ($nameSpace -like "root\ccm*") 6 { 7 $objects = Get-WmiObject -Namespace $nameSpace -List 8 foreach ($object in $objects) 9 { 10 $instances = Get-WmiObject -Namespace $nameSpace -Class $object.Name -ErrorAction SilentlyContinue 11 $instanceLine = "{0}({1})" -f $object.Name, $instances.count 12 $entryName = "{0}:{1}" -f $nameSpace, $object.Name 13 $global:AllInstances.Add($entryName, $instances.Count) 14 $count += $instances.Count 15 } 16 $namespaceLine = "{0} ({1} classes, {2} instances)" -f $nameSpace, $objects.count, $count 17 } 18 else 19 { 20 $objects = @(0) 21 } 22 23 $global:TotalObjects += $objects.Count 24 $subs = Get-WmiObject -Namespace $nameSpace __NAMESPACE | Sort-Object 25 26 if($subs -ne $null) 27 {$subs | ForEach-Object { CountWMIObjects "$nameSpace\$($_.Name)"}} 28 } 29 30 function Get-ScriptDirectory 31 { 32 $Invocation = (Get-Variable MyInvocation -Scope 1).Value 33 Split-Path $Invocation.MyCommand.Path 34 } 35 36 #Main script 37 [int]$global:TotalObjects = 0 38 [hashtable]$global:AllInstances = @{} 39 $OutputFile = "SCCMClasses.csv" 40 41 #Run test 42 CountWMIObjects "root" 43 44 #export to csv 45 $path = Join-Path (Get-ScriptDirectory) $OutputFile 46 $global:AllInstances.GetEnumerator() | sort value -Descending | Export-Csv $path -NoTypeInformation 47 Write-Host "" 48 Write-Host "SCCM Instances Found: "$global:TotalObjects 49 Write-Host "Output File: $path" 50

Eventually we found that one of the causes was SCCM Software Metering data, particularly on terminal servers, due to the amount of people logging on and launching applications. I continue troubleshooting WMI repository bloat in my Dumping WMI Instance Property Values for all Classes in a Namespace post.