Correct-ContentDistributionErrors.ps1

  • 6 April 2020
  • Sean Huggans
Description: 

Script utilizes a function by Mike Laughlin to retarget errored packages specifically to DPs they are in an error state on. There is a flag to include in-progress content as well. This can be run on a scheduled task to automate redistribution of failed packages in your environment.

Language: 
PowerShell
Usage Type: 
Standalone
Script Source: 
############################################
# Correct-ContentDistributionErrorsV2.ps1
# Version: 2020.4.6.3
# Author: Sean Huggans
############################################
 
$SiteCode = "FOO"
$SiteServer = "SITESERVER"
$ProviderMachineName = "SITESERVER.FOONET.NET"
$Script:LogRoot = "C:\Temp\Logs\"
$Script:LogName = "Correct-ContentDistributionErrors"
$Script:LogPath = "$($script:LogRoot)$($script:LogName).log"
$Script:CMCMDlets = $false
$Script:ADCMDlets = $false
$RedistributionThreshold = 20
$RedistributeAllInProgress = $false # Set to true to redistribute all in progress content to the DPs the content is in progress to
 
function AddLogLine ($Message) 
{
    if (!(Test-Path -Path "$($Script:LogRoot)")) {
        new-item -ItemType directory -Path "$($Script:LogRoot)" -force -Confirm:$false -ErrorAction SilentlyContinue
    }
    "[ $(get-date -format 'yyyy.MM.dd hh:mm:ss') ] $Message" | out-file $Script:LogPath -Append
}
 
function MaintainLogs {
        # Truncate Log if over 10,000KB
    if ((Get-Item $Script:LogPath).length -gt 10000kb) {
        $ArchiveStamp = "-Archived-$(get-date -Format "yyyyMMddhhmmss")"
        $ArchivedLogName = "$($Script:LogRoot)Archived\$($script:LogName)$ArchiveStamp.log"
        $TryCount = 5
        $Moved = $false
        do {
            try {
                Move-Item -Force $Script:LogPath -Destination $ArchivedLogName -ErrorAction Stop
                AddLogLine "This Log has been truncated.  Messages from the previous truncation can be found at $ArchivedLogName"
                $Moved = $true
            } catch {
                Start-Sleep -Seconds 3
            }
        } until (($TryCount -le 0) -or ($Moved -eq $true))
    }
    #Check for Logs Older than 4 weeks and delete them     
}
 
MaintainLogs
 
# Customizations
$initParams = @{}
 
# Import the ConfigurationManager.psd1 module 
if((Get-Module ConfigurationManager) -eq $null) {
    Import-Module "$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1" @initParams 
}
 
# Connect to the site's drive if it is not already present
if((Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue) -eq $null) {
    New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName @initParams
}
 
# Set the current location to be the site code.
Set-Location "$($SiteCode):\" @initParams
 
 
Function Refresh-SpecificDP {
###################################
# Function by Mike Laughlin
###################################
    param($packageID, $dpName)
    $dpFound = $false
    If ($packageID.Length -ne 8)
    {
        Throw "Invalid package"
    }
    $distPoints = Get-WmiObject -ComputerName $SiteServer -Namespace "root\SMS\Site_$($siteCode)" -Query "Select * From SMS_DistributionPoint WHERE PackageID='$packageID'" -ErrorAction Stop
    ForEach ($dp In $distPoints)
    {
        If ((($dp.ServerNALPath).ToUpper()).Contains($dpName.ToUpper()))
        {
            $dpFound = $true
 
            Try {
                $dp.RefreshNow = $true
                $dp.Put() | Out-Null
                return $true
            }
            Catch [Exception]
            {
                return $false
            }
        }
    }
    If ($dpFound -eq $false)
    {
        return $false
    }
}
 
cd SAN:
 
AddLogLine "===================================="
AddLogLine "====== Script Started Running ======"
AddLogLine "===================================="
 
#We will keep a running list of offline DPs to expedite script execution
$OfflineDPs = New-Object System.Collections.ArrayList
 
AddLogLine "Gathering List of Distribution Points..."
[array]$DistributionPointsData = Get-CMDistributionPoint | sort-object -Property NetworkOSPath
[array]$DistributionPoints = $DistributionPointsData.NetworkOSPath.ToUpper().Replace("\\","")
 
# Handle in progress content if the option is enabled
if ($RedistributeAllInProgress -eq $true) {
    $LoopCount = 0
    AddLogLine "Warning!  Redistribute In-Progress Content option is enabled!  Building a list of all in progress content..."
    [array]$InProgressContentObjects = Get-CMDistributionStatus | Where-Object {$_.NumberInProgress -gt 0}  | sort-object -Property SoftwareName
    AddLogLine "Total Count of Objects with In-Progress Content Distributions: $($InProgressContentObjects.Count)"
    foreach ($InProgressContentObject in $InProgressContentObjects) {
        $LoopCount +=1
        AddLogLine "Building a list of DPs with content In-Progress for Package $($LoopCount)/$($InProgressContentObjects.count), ID: $($InProgressContentObject.PackageID) ($($InProgressContentObject.SoftwareName))"
        $DistributionPointsWithInProgressStatusForThisPackage = New-Object System.Collections.ArrayList
        #Check Each DP for the status of this package
        foreach ($DistributionPoint in $DistributionPoints) {
            $InProgressContentObjectStatusonDistributionPoint = Get-WmiObject –NameSpace Root\SMS\Site_$SiteCode –Class SMS_DistributionDPStatus -ComputerName $SiteServerFilter "PackageID='$($InProgressContentObject.PackageID)' And Name='$($DistributionPoint)'"
            switch ($InProgressContentObjectStatusonDistributionPoint.MessageState) {
                1 {
                    #Compliant
                }
                2 {
                    #In Progress
                    $DistributionPointsWithInProgressStatusForThisPackage.Add($DistributionPoint) | Out-Null
                }
                3 {
                    #Unknown
                }
                4 {
                    #Error
                }
            }
        }
        AddLogLine "Checking the status of each DP in the 'InProgress State' content list, and attempting to retrigger content redistribution if it is online..."
        foreach ($DistributionPointWithInProgressStatusForThisPackage in $DistributionPointsWithInProgressStatusForThisPackage) {
            #Content in InProgress State on this DP, If we already don't know, Check to see if the DP is online
            if (($DistributionPointWithInProgressStatusForThisPackage -like "*CMG*") -or (($OfflineDPs -notcontains $DistributionPointWithInProgressStatusForThisPackage) -and (Test-Connection -Cn $DistributionPointWithInProgressStatusForThisPackage -BufferSize 16 -Count 1 -ea 0 -quiet))) {
                AddLogLine "- $($DistributionPointWithInProgressStatusForThisPackage) - Online.  Triggering Content Redistribution to this host for package: $($InProgressContentObject.PackageID)..."
                if (Refresh-SpecificDP $InProgressContentObject.PackageID $DistributionPointWithInProgressStatusForThisPackage) {
                    AddLogLine "- - Success"
                } else {
                    AddLogLine "- - Failure"
                }
            } else {
                $OfflineDPs.Add($DistributionPointWithInProgressStatusForThisPackage) | Out-Null
                AddLogLine "- $($DistributionPointWithInProgressStatusForThisPackage) - Offline.  No Actions can be taken at this time."
            }
        }
    }
} else {
    AddLogLine "Redistribute In-Progress Content option is not enabled (this is usually the desired setting), skipping."
}
 
AddLogLine "Building a list of all content with failues..."
[array]$FailedContentObjects = Get-CMDistributionStatus | Where-Object {$_.NumberErrors -gt 0}  | sort-object -Property SoftwareName
AddLogLine "Total Count of Objects with Failed Content Distributions: $($FailedContentObjects.Count)"
$LoopCount = 0
foreach ($FailedContentObject in $FailedContentObjects) {
    $LoopCount +=1
    AddLogLine "Building a list of DPs with content failures for Package $($LoopCount)/$($FailedContentObjects.count), ID: $($FailedContentObject.PackageID) ($($FailedContentObject.SoftwareName))"
    $DistributionPointsWithErrorStatusForThisPackage = New-Object System.Collections.ArrayList
    #Check Each DP for the status of this package
    foreach ($DistributionPoint in $DistributionPoints) {
        $FailedContentObjectStatusonDistributionPoint = Get-WmiObject –NameSpace Root\SMS\Site_$SiteCode –Class SMS_DistributionDPStatus -ComputerName $SiteServerFilter "PackageID='$($FailedContentObject.PackageID)' And Name='$($DistributionPoint)'"
        switch ($FailedContentObjectStatusonDistributionPoint.MessageState) {
            1 {
                #Compliant
            }
            2 {
                #In Progress
            }
            3 {
                #Unknown
            }
            4 {
                #Error
                $DistributionPointsWithErrorStatusForThisPackage.Add($DistributionPoint) | Out-Null
            }
        }
    }
    AddLogLine "Checking the status of each DP in the 'Failed State' content list, and attempting to retrigger content redistribution if it is online..."
    foreach ($DistributionPointWithErrorStatusForThisPackage in $DistributionPointsWithErrorStatusForThisPackage) {
        #Content in Error State on this DP, If we already don't know, Check to see if the DP is online
        if (($DistributionPointWithErrorStatusForThisPackage -like "*CMG*") -or (($OfflineDPs -notcontains $DistributionPointWithErrorStatusForThisPackage) -and (Test-Connection -Cn $DistributionPointWithErrorStatusForThisPackage -BufferSize 16 -Count 1 -ea 0 -quiet))) {
            AddLogLine "- $($DistributionPointWithErrorStatusForThisPackage) - Online.  Triggering Content Redistribution to this host for package: $($FailedContentObject.PackageID)..."
            if (Refresh-SpecificDP $FailedContentObject.PackageID $DistributionPointWithErrorStatusForThisPackage) {
                AddLogLine "- - Success"
            } else {
                AddLogLine "- - Failure"
            }
        } else {
            $OfflineDPs.Add($DistributionPointWithErrorStatusForThisPackage) | Out-Null
            AddLogLine "- $($DistributionPointWithErrorStatusForThisPackage) - Offline.  No Actions can be taken at this time."
        }
    }
}
 
AddLogLine "===================================="
AddLogLine "====== Script Finished Running ====="
AddLogLine "===================================="
Note: that all applications posted here are posted for use, both commercial and non-commercial, free of charge, and as such are provided as-is, without warranty of any kind whatsoever. visuaFUSION, FMSCUG or any other program listed here's author are not responsible for any damages or shortcomings that result from usage of any of these applications.
Error | visuaFUSION Systems Solutions Blog

Error message

  • Warning: Cannot modify header information - headers already sent by (output started at /mnt/home/visuafus/public_html/bahusa.net/includes/common.inc:2861) in drupal_send_headers() (line 1551 of /mnt/home/visuafus/public_html/bahusa.net/includes/bootstrap.inc).
  • Error: Call to undefined function mail() in DefaultMailSystem->mail() (line 79 of /mnt/home/visuafus/public_html/bahusa.net/modules/system/system.mail.inc).

Error

The website encountered an unexpected error. Please try again later.