Real world notes: In-place OS upgrade on Server 2012 R2 ConfigMgr distribution points

In my MEMCM primary site I had several distribution points that were still running Windows Server 2012 R2, so I decided to run an in-place OS upgrade on them to bring them up to Server 2019. After reading the MS Docs, I concluded this is a supported scenario and things would go smoothly. I was quite wrong, however!

The OS upgrade itself went very well. I scripted the process to automate it and deployed it via MEMCM and the servers upgraded in around 1-1.5 hours. However, after the upgrade I found two big issues on each server:

  • The ConfigMgr client was broken – specifically WMI. The SMS Agent host (ccmexec) service was not running and would not start. Digging in the logs, I could see that ccmrepair.exe was running and attempting to fix the client, however it was repeatedly failing with the following error:

MSI: Setup was unable to compile the file DiscoveryStatus.mof. The error code is 80041002

  • The root\SCCMDP namespace was missing from WMI. This essentially breaks the distribution point role as no packages can be updated or distributed to it and content validation will fail

It’s quite possible something on the servers contributed to these issues happening, but they were actually fairly clean boxes – physical HP servers running only the ConfigMgr client, some HP software, antivirus and a few agents such as MMA and Azure agents. When I contacted Microsoft support they suggested to perform a site reset, but when you have many servers to upgrade that isn’t viable. They also wouldn’t update the Docs as they couldn’t reproduce the issues internally even though I’m not the only one to report them.

Anyway, I’m documenting here what I did to fix it and the scripts I used.

To fix the broken ConfigMgr client I did the following:

  1. Compile the mof file %program files%\Microsoft Policy Platform\ExtendedStatus.mof
  2. Stop any ccmrepair.exe or ccmsetup.exe process so they don’t hinder step 3
  3. Run the ‘Configuration Manager Health Evaluation’ scheduled task (ccmeval.exe) a couple of times. This will self-remediate the WMI issues.

To fix the broken DP role:

  1. Compile the mof file ..\SMS_DP$\sms\bin\smsdpprov.mof (this restores the missing WMI namespace)
  2. Query the ConfigMgr database to find the list of packages distributed to the distribution point
  3. Run some PowerShell code to restore the packages as instances in the root\SCCMDP:SMS_PackagesInContLib WMI class
  4. Run the Content Validation scheduled task to revalidate the content and remove any errors in the console

For the latter, I don’t take any credit in my script below as I simply used and expanded something I found here. Note both scripts must be run as administrator and the second script requires read access to the ConfigMgr database.

Repair ConfigMgr client script

# Complile mof file
Write-host "Compiling ExtendedStatus.mof file"
mofcomp "C:\Program Files\Microsoft Policy Platform\ExtendedStatus.mof"

# Stop processes that might hinder ccmeval
If (Get-Process -Name ccmrepair -ErrorAction SilentlyContinue)
{
    Write-host "Found ccmrepair process. Stopping it..."
    Stop-Process -Name ccmrepair -Force
    Start-Sleep -Seconds 5
}
If (Get-Process -Name ccmsetup -ErrorAction SilentlyContinue)
{
    Write-host "Found ccmsetup process. Stopping it..."
    Stop-Process -Name ccmsetup -Force
    Start-Sleep -Seconds 5
}

# Run ccmeval to self-remediate the broken WMI
Write-host "Starting Configuration Manager Health Evaluation to repair the ConfigMgr client"
Start-ScheduledTask -TaskName "Configuration Manager Health Evaluation" -TaskPath "\Microsoft\Configuration Manager"
$P = Get-Process -Name ccmeval
$P.WaitForExit()
Start-Sleep -Seconds 5
Write-host "Starting Configuration Manager Health Evaluation one more time"
Start-ScheduledTask -TaskName "Configuration Manager Health Evaluation" -TaskPath "\Microsoft\Configuration Manager"
$P = Get-Process -Name ccmeval
$P.WaitForExit()

# Open the logs to verify it was successful
Start-Process -FilePath C:\Windows\CCM\CMTrace.exe -ArgumentList "C:\Windows\ccmsetup\Logs\ccmsetup-ccmeval.log"
Start-Process -FilePath C:\Windows\CCM\CMTrace.exe -ArgumentList "C:\Windows\CCM\Logs\CcmEval.log"

Repair DP role script

# MEMCM database params
$script:dataSource = 'MyConfigMgrDatabaseServer' 
$script:database = 'MyConfigMgrDatabase'

# Function to query SQL server...must have db_datareader role for current user context
function Get-SQLData {
    [CmdletBinding()]
    param($Query)
    $connectionString = "Server=$dataSource;Database=$database;Integrated Security=SSPI;"
    $connection = New-Object -TypeName System.Data.SqlClient.SqlConnection
    $connection.ConnectionString = $connectionString
    $connection.Open()
    
    $command = $connection.CreateCommand()
    $command.CommandText = $Query
    $reader = $command.ExecuteReader()
    $table = New-Object -TypeName 'System.Data.DataTable'
    $table.Load($reader)
    
    # Close the connection
    $connection.Close()
    
    return $Table
}

Try
{
    Get-CimClass -Namespace root\SCCMDP -ErrorAction Stop
    Write-host "WMI namespace root\SCCMDP is present"
    Return
}
Catch
{
   If ($_.Exception.NativeErrorCode -eq "InvalidNamespace")
   {
        Write-host "WMI namespace root\SCCMDP is missing" -ForegroundColor Red
        Write-host "Performing remediations..."
   }
   else 
   {
        $_
        Return    
   }
}

# Compile DP mof file
Write-host "Compiling smsdpprov.mof file"
mofcomp "$(Get-SmbShare | where {$_.Name -match "SMS_DP"} | Select -ExpandProperty Path)\sms\bin\smsdpprov.mof"

# Query database for DP package info
$Server = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\SMS\DP" -Name Server | Select -ExpandProperty Server
$Query = @"
declare @ServerName varchar (50) = '$Server'
select
  dp.PackageID
, case
 when p.ShareName <> '' 
  then '\\' + @ServerName + '\' + p.ShareName
 else ''
  end ShareName
, 'Set-WmiInstance -Path ''ROOT\SCCMDP:SMS_PackagesInContLib'' -Arguments @{PackageID="'
  + dp.PackageID + '";PackageShareLocation="' +
 case
  when p.ShareName <> '' 
   then '\\' + @ServerName + '\' + p.ShareName
  else ''
   end + '"}' PowershellCommand
from v_DistributionPoint dp
inner join v_Package p on p.PackageID = dp.PackageID
where dp.ServerNALPath like '[[]"Display=\\' + @ServerName + '%'
"@
Write-host "Querying database for DP package info"
Try
{
    $Results = Get-SQLData -Query $Query  -ErrorAction Stop
    Write-host "Found $($results.rows.count) packages"
}
Catch
{
    $_
    Return
}

# Run the POSH code to add the package back into WMI
Write-host "Restoring WMI instances to ROOT\SCCMDP:SMS_PackagesInContLib"
If ($Results)
{
    Foreach ($result in $results)
    {
        $Scriptblock = [scriptblock]::Create($Result.PowershellCommand)
        Try
        {
            $null = Invoke-Command -ScriptBlock $Scriptblock -ErrorAction Stop
        }
        Catch
        {
            $_
        }
    }
}
Else
{
    Throw "No package info found for this DP"
    Return
}

# Start the content validation task...may take some time
Write-host "Starting Content validation. Check smsdpmon.log for progress"
Start-ScheduledTask -TaskPath "\Microsoft\Configuration Manager" -TaskName "Content Validation"

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.