Thursday, January 10, 2019

SCCM - Package Distribution: identify, enumerate and resolve 'in progress' status

For direct solution  go to the end of this article

A common problem in SCCM is Package 'in progress' in some Distribution Points

Here some of errors we can find and that never been distributed

"2335" = "Distribution Manager instructed Scheduler and Sender to send package "PRI00123" to child site "SEC".






"2372","The content files for package "PRI00123" have not yet arrived from the source site PRI. Distribution Manager will try again later to distribute the content."






"2380","Start to evaluate package "PRI00123" on distribution point "["Display=\\MYCOMPUTER.mydom.net\"]MSWNET:["SMS_SITE=PRI"]\\MYCOMPUTER.mydom.net\"




"30010","User "MYDOM\myuser" modified a distribution point on \\MYPULLDP.mydom.net at site "SEC - MyCompany - SEC Secondary Site" for a package with package ID PRI00123."
"Informational"








As you can see below in \Monitoring\Overview\Distribution Status\Content Status in this "Software Update Package" example, a lot of targeted are "pending"











Here we can see there is 2315 targeted Distribution Point and 219 pending since at least 5 days


Solution 1
You try to redistribute a package  (manually it is  "\Administration\Overview\Site Configuration\Servers and Site System Roles" then in "Distribution Point" properties then in tab "content". Select package then click on button redistribute )
-> here in my example that failed for these 219 servers (sure I use a script to do it)

Solution 2
You try to "Update Distribution Points" with create a new version for your package (manually for Software Updates it is in \Software Library\Overview\Software Updates\Deployment Packages, right button "Update Distribution Points, same thing for package or any other content)
-> here in my example that failed for these 219 servers

Solution3
You delete Package from your Distribution Point, then after waiting some minutes you "Distribute Content"  on the same DP
-> here in my example that work



Things is I surely have other package for some Distribution Point with same problem.

So I decide to find how to find them. Are you ready ?

 I first try to find in SQL some related table



I found DistributionJobs table could be a good table
but when I try my query on my Secondary I finally see number found does correspond to this problem:

"2357","Distribution Manager instructed Package Transfer manager to send package "PRI00233" to distribution point "["Display=\\MYCOMPUTER.mydom.net\"]MSWNET:["SMS_SITE=PRI"]\\MYCOMPUTER.mydom.net\" ." 
and these Distribution Points found in this query seems just works




Below query to find number of DistributionJobs for a package:
Select count(*)
From [DistributionJobs] as DJ
JOIN [DistributionPoints] as DP
ON DJ.DPID = DP.DPID
WHERE PkgID = 'PRI00233'



















For information to see DP name use for example (here secondary XXH have 2 rows with same message):
Select DP.ServerName, DJ.*
From [DistributionJobs] as DJ
JOIN [DistributionPoints] as DP
ON DJ.DPID = DP.DPID
WHERE PkgID = 'PRI00233'

















Then I found a Microsoft Article Understanding and Troubleshooting Content Distribution in Microsoft Configuration Manager https://support.microsoft.com/en-ae/help/4000401/content-distribution-in-mcm
in Useful query with a view
-- All Package/DPs in InProgress state for more than 3 days
SELECT distinct DPSD.DPName, DPSD.PackageID, SP.Name, DPSD.MessageState, DPSD.LastStatusTime, DPSD.SiteCode
FROM vSMS_DPStatusDetails DPSD
JOIN SMSPackages_All SP ON DPSD.PackageID = SP.PkgID
WHERE DPSD.LastStatusTime > DATEAdd(dd,-3,GETDate()) 
AND MessageState = 2 


and another interesting article with table https://blogs.technet.microsoft.com/umairkhan/2014/10/02/the-case-of-the-unexplained-the-package-gets-distributed-successfully-but-does-not-show-in-the-console/
select * from PkgServers_G where pkgid = 'PRI00233'
select * from ContentDistributionNotification where pkgid = 'PRI00233'
select * from ContentDistribution where pkgid = 'PRI00233'




So as you can see in my example table ContentDistributionNotification is OK

SELECT count(*)
FROM [ContentDistributionNotification]
WHERE PkgID = 'PRI00233'

 (No column name)
2315













Table ContentDistribution is OK too in my example


SELECT count(*)
FROM ContentDistribution
WHERE PkgID = 'PRI00233'

(No column name)
2315















Table PkgServers is OK too in my example

SELECT *
FROM PkgServers_G
WHERE PkgID = 'PRI00233'















Table PkgStatus has not same number BUT perhaps it is normal

SELECT count(*) FROM PkgStatus_G as PKGS
JOIN DistributionPoints as DP ON PKGS.PkgServer = DP.NALPath
WHERE ID = 'PRI00233'

(No column name)
2095--
















GOOD CATCH
Try testing some Distribution Points  I see computer NOT in PkgStatus_G table are those with problems


For example we can see below %17% computer displayed in  PkgStatus_G and not %36%

here %36%  Distribution Point is 'in progress'

 here %17%  Distribution Point is 'Success'























So now I give you SQL Query to find ALL Distribution Points with this problem




-- Find Distribution Point 'in progress' with problems
-- by F.RICHARD 2019-01

-- Declare the variables to store the values returned by FETCH. 
DECLARE @PackageID varchar(50); 
DECLARE @tblResult TABLE (PackageID varchar(50), ServerName varchar(255), SiteCode varchar(10));

DECLARE PackageID_cursor CURSOR FOR 
    SELECT DISTINCT PackageId
    FROM vSMS_DPStatusDetails DPSD
    JOIN SMSPackages_All SP ON DPSD.PackageID = SP.PkgID
    WHERE 1=1
        AND DPSD.LastStatusTime < DATEAdd(dd,-3,GETDate())
        AND MessageState = 2




OPEN PackageID_cursor; 
FETCH NEXT FROM PackageID_cursor INTO @PackageID
WHILE @@FETCH_STATUS = 0 
BEGIN 
        print @PackageID



        -- create temp table after drop
        if(OBJECT_ID('tempdb..#TempPkgStatusServerName') Is Not Null)
        Begin
            Drop Table #TempPkgStatusServerName
        End
        CREATE TABLE #TempPkgStatusServerName
        (
            PkgServer Varchar(255)
            ,ServerName Varchar(255)
            ,PkgID varchar(50)
        );

        -- insert data
        INSERT INTO #TempPkgStatusServerName
        SELECT PkgServer
            ,ServerName =
            CASE
                WHEN CHARINDEX('\\', Pkgserver) > 0
                    THEN SUBSTRING(Pkgserver, CHARINDEX('\\', Pkgserver) + 2, CHARINDEX('"]', Pkgserver) - CHARINDEX('\\', Pkgserver) - 3 )
            ELSE
                Pkgserver
            END
            ,ID
        FROM PkgStatus
        WHERE 1=1
        AND ID = @PackageID

        INSERT @tblResult
                SELECT
                PackageID, DPSD.DPName, DPSD.SiteCode
                FROM vSMS_DPStatusDetails DPSD
                JOIN SMSPackages_All SP ON DPSD.PackageID = SP.PkgID
                WHERE 1=1
                    AND DPSD.LastStatusTime < DATEAdd(dd,-3,GETDate())
                    AND MessageState = 2
                    AND PackageID = @PackageID

                AND DPSD.DPName NOT IN
                (
                SELECT TMPTABLE.ServerName FROM #TempPkgStatusServerName as TMPTABLE
                )

        -- drop temp table
        Drop Table #TempPkgStatusServerName

        -- next data
        FETCH NEXT FROM PackageID_cursor INTO @PackageID
END

CLOSE PackageID_cursor;
DEALLOCATE PackageID_cursor;

SELECT PackageID, ServerName, SiteCode FROM @tblResult






















And as I am really a nice guy I give the same in powershell
below

<#
.Synopsis
   SCCM_Get-Problematic-SMS_PackageStatus.ps1

.EXAMPLE
   .\SCCM_Get-Problematic-SMS_PackageStatus.ps1

.NOTES
  Version:        1.0
  Author:        Franck RICHARD
  Creation Date:    2019-01
  Purpose/Change:

#>
[cmdletbinding()]
Param(
     [String]$CM="mycm.mydomain.net"
    ,[String]$PrimarySiteCode="XXX"
)


# *************************

# For Time checking
$MeasureCommandBegin = Get-Date           


$strCurDir = Split-Path -parent $MyInvocation.MyCommand.Path
$strScriptName = $MyInvocation.MyCommand.Name
$date_for_file = Get-Date -format "yyyy-MM-dd_HHmmss"

$strSaveFile = $strCurDir + "\" + $date_for_file + "_" + $Hostname + "_" + $strScriptName.replace(".ps1",".csv")




# Get all packages
#
Write-Host "Get all Packages"
[HashTable] $hashPackageByPackageID = @{}
$arrSMS_PackageBaseclass = @(Get-WmiObject -Namespace "root\SMS\Site_$PrimarySiteCode" -Class SMS_PackageBaseclass -ComputerName $CM)
Foreach ($SMS_PackageBaseclass in $arrSMS_PackageBaseclass) { 
    if (!$hashPackageByPackageID.ContainsKey($SMS_PackageBaseclass.PackageID) ) {
        $hashPackageByPackageID[$($SMS_PackageBaseclass.PackageID)] = $SMS_PackageBaseclass
    }
}
Write-Host "Nb Package : $($hashPackageByPackageID.count)"



# Date Min = date LastStatusTime
#
$Nbdays = 3
$DateMin =  (Get-date).AddDays(-$Nbdays)



# Get all SMS_DPStatusDetails 'in progress'
#
Write-Host "Get all SMS_DPStatusDetails 'in progress' "
$Query = @"
SELECT * FROM SMS_DPStatusDetails as DPS
WHERE DPS.MessageState='2'
AND DPS.LastStatusTime <= '$DateMin' 
AND DPS.PackageID <> ''
"@
$arrSMS_DPStatusDetails = Get-WmiObject -Namespace "root\SMS\Site_$PrimarySiteCode"  -ComputerName $CM -Query $Query
Write-Host "Nb SMS_DPStatusDetails 'in progress' > $Nbdays days: $($arrSMS_DPStatusDetails.count)"




# Get Distinct PackageID 'in progress'
#
Write-Host "Get Distinct PackageID 'in progress' "
[HashTable] $hashDistinctPackageID = @{}
Foreach ($SMS_DPStatusDetails in $arrSMS_DPStatusDetails) { 
    if ($hashPackageByPackageID.ContainsKey($SMS_DPStatusDetails.PackageID) ) {           
        if (!$hashDistinctPackageID.ContainsKey($SMS_DPStatusDetails.PackageID) ) {
            $hashDistinctPackageID[$($SMS_DPStatusDetails.PackageID)] = 1       
        }
    }
}
Write-Host "Nb Distinct PackageID : $($hashDistinctPackageID.count)"


# For each PackageID  we get
#
[ARRAY] $arrInfos=@()
Foreach ($PackageID in $hashDistinctPackageID.Keys) {
        $SMS_PackageBaseclass = $hashPackageByPackageID[$($PackageID)]
        Write-Host "-> Check PackageID $PackageID 'in progress' ($($SMS_PackageBaseclass.Name))"


        # Get All SMS_PackageStatus for $PackageID
        #
        Write-Host "Get All SMS_PackageStatus for $($PackageID)  "
        [HashTable] $hashPackageIDByPkgServer = @{}
        #$Query = @"
        #SELECT * FROM SMS_PackageStatus WHERE PackageID='$PackageID'
        #"@
        #$Query = @"
        $Query = "SELECT PkgServer FROM SMS_PackageStatus WHERE PackageID='$PackageID'"
        #"@
        $arrSMS_PackageStatus = Get-WmiObject -Namespace "root\SMS\Site_$PrimarySiteCode"  -ComputerName $CM  -Query $Query
        Foreach ($SMS_PackageStatus in $arrSMS_PackageStatus) { 
            $ServerNALPath = $SMS_PackageStatus.PkgServer
            # ex: ["Display=\\MYCOMPUTER.mydom.net\"]MSWNET:["SMS_SITE=XXX"]\\MYCOMPUTER.mydom.net\
            # -> MYCOMPUTER.mydom.net
            $PkgServer = $ServerNALPath.Substring($ServerNALPath.LastIndexOf("]")+3).Trim("\")

            if (!$hashPackageIDByPkgServer.ContainsKey($PkgServer) ) {
                $hashPackageIDByPkgServer[$($PkgServer)] = 1        # $SMS_PackageStatus.PackageID
            }
        }

        # Check if exist in SMS_DPStatusDetails but NOT in SMS_PackageStatus
        #
        Write-Host "Check if exist in SMS_DPStatusDetails but NOT in SMS_PackageStatus for $($PackageID) "
        Foreach ($SMS_DPStatusDetails in $arrSMS_DPStatusDetails) { 
            if ($SMS_DPStatusDetails.PackageID -eq $PackageID) {
                if ($hashPackageByPackageID.ContainsKey($SMS_DPStatusDetails.PackageID) ) {
                    if (!$hashPackageIDByPkgServer.ContainsKey($SMS_DPStatusDetails.DPName) ) {
                        $custObj = New-Object -TypeName psobject
                        $custObj | Add-Member -MemberType NoteProperty -Name PackageID -Value $SMS_DPStatusDetails.PackageID
                        $custObj | Add-Member -MemberType NoteProperty -Name DistributionPoint -Value $SMS_DPStatusDetails.DPName
                        $custObj | Add-Member -MemberType NoteProperty -Name SiteCode -Value $SMS_DPStatusDetails.SiteCode

                        $arrInfos += $custObj
                    }
                }
            }
        }

}



Write-Host "Write file with all package informations"
$arrInfos | Export-Csv -NoType -Path $strSaveFile -Delimiter ";"
#$strContent | Out-File -FilePath  "$strSaveFile


$MeasureCommandEnd = Get-Date
$MeasureCommandTime = $MeasureCommandEnd - $MeasureCommandBegin
Write-host "Script Executing in $($MeasureCommandTime.Day) day(s) $($MeasureCommandTime.Hour) hour(s) $($MeasureCommandTime.Minutes) minute(s) $($MeasureCommandTime.Seconds) second(s) "





[EDIT 2019-01-21]

Please here SCCM_AddPackageToDP.ps1 (https://franckrichard.blogspot.com/2019/01/sccm-powershell-script-to-add-package.html) powershell solution to remove and add content and resolve this problem


.\SCCM_AddPackageToDP.ps1 -CM mycm.mydomain.net -PrimarySiteCode XXX -ForceToDeletePackageBeforeIfExist -ImportFilePath file_generated_by_SCCM_Get-Problematic-SMS_PackageStatus.csv