Neon Yellow And Black Simple Modern Motivational Quote LinkedIn Article Cover Image

Simplifying Off-Hours SCCM Package Redistribution with PowerShell Automation



Author: Nawaz kazi

Welcome back to our tech corner! Today, we’re delving into a unique challenge many of us in the systems management field face: ensuring smooth content distribution during off-business hours using the System Center Configuration Manager (SCCM).

But first, a quick note. Ordinarily, I would upload scripts to my GitHub for your easy access. Unfortunately, due to some technical difficulties, I’m unable to do that at the moment. So, I’ll be incorporating the script directly into this blog post. Don’t miss it – it’s worth reading till the end!

Before we embark on this journey, I want to share a little backstory. I came across an intriguing PowerShell script on the web some time ago that aimed to address a similar issue. The exact source slips my memory – I wish I could recall it to give due credit. What follows is a variant of that script, tailored to suit my specific needs and requirements. The idea here is to share this adapted version of the script to help others who might be grappling with similar scenarios in SCCM.

SCCM is an incredibly powerful tool for managing large and complex network environments. One of its valuable features is the ability to create distribution point schedules. These schedules allow us to distribute content during off-peak hours to minimize disruption to normal business operations and efficiently utilize network resources. However, setting up and managing these schedules comes with its own set of intricacies.

Here’s something crucial to note. The options for configuring a distribution point’s schedule are only available when you’re editing the properties of a previously installed distribution point. Also, this tab is available only when you edit the properties for a distribution point that’s remote from the site server.



The schedule allows you to configure restrictions on when Configuration Manager can transfer data to the distribution point. You can decide to restrict data by priority or close the connection entirely for selected time periods. When you select a time period in the grid, you have the following settings for availability:

**Open for all priorities:** Configuration Manager sends data to the distribution point with no restrictions. This setting is the default for all time periods.
**Allow medium and high priority:** Configuration Manager sends only medium-priority and high-priority data to the distribution point.
**Allow high priority only:** Configuration Manager sends only high-priority data to the distribution point.
**Closed:** Configuration Manager doesn’t send any data to the distribution point.

Remember, the schedule is based on the time zone of the sending site, not the distribution point. You can find more detailed information about setting up SCCM distribution point schedules in the [official Microsoft documentation](

While this scheduling functionality is beneficial, it’s not without its quirks. Sometimes, packages get stuck in the queue, retry, and then end up in a failed state. Once failed, these stubborn packages won’t redistribute themselves until we manually intervene and re-trigger the distribution process. This can be a time-consuming task, especially when dealing with a large number of packages and distribution points.

Now, wouldn’t it be fantastic if we could automate this entire process? Imagine a script running behind the scenes, doing all the hard work for us once business hours end. That’s exactly what we’ll explore today. We’re going to discuss a robust PowerShell script that identifies failed packages and redistributes them automatically during off-hours.

Our PowerShell script leverages the WMI (Windows Management Instrumentation) classes to interact with SCCM. We utilize the `Get-WmiObject` cmdlet to query and filter for failed packages on the distribution points.

The script first pulls all the failed packages and classifies them based on their state and type. This classification happens inside the `switch` sections, where numeric codes transform into human-readable states and package types. Our script cleverly extracts the server name and package size by splitting and converting raw data into more valuable, understandable insights.

Once the script has processed all the necessary information, it starts automatic redistribution. We’ve eliminated manual selection, and the script automatically deals with every failed package it finds. This simplification makes the process quicker, more efficient, and less prone to mistakes.

At this point, you might be thinking, “This sounds great, but how do I keep track of what’s being redistributed?” That’s where the cherry on top

comes in: the script now sends an HTML email containing all the information packages that were in a failed state and are now re-triggered to be re-distributed.

In this way, we’re keeping track of all the redistribution actions while freeing up our time to focus on more pressing tasks. With a bit of setup, this script can run post-business hours, automatically addressing any failed packages, and ensuring smoother, more efficient operations in your SCCM environment.

The script is as follows :

Please make sure to replace "your-smtp-server" with your actual SMTP server address, and also replace both instances of "" with your actual email address.

This script assumes that your SMTP server doesn’t require authentication. If it does, you’ll have to add the credentials in the script as well. Also, it is set to work with the current scope of your infrastructure. If there are more complex conditions or a different scope, you may have to adjust the script accordingly.

$SiteCode = "COD"
$smtpServer = "Your-Smtp-Server"
$from = ""
$to = ""
$subject = "Failed Packages Redistributed"
$body = ""

$failures = Get-WmiObject -Namespace root\sms\site_$SiteCode -Query "SELECT * FROM SMS_PackageStatusDistPointsSummarizer WHERE State > 1" |
    Select ServerNALPath,PackageType,State,PackageID,SummaryDate |
        ForEach-Object {
            $PKG = Get-WmiObject -NameSpace root\sms\site_$SiteCode -Class SMS_Packagebaseclass -Filter "PackageID = '$($_.PackageID)'" | Select Name,PackageSize
            $server = $_.ServerNALPath.Split("\\")[2]
            $size = $PKG.PackageSize / 1KB
            $State =  switch ($_.State)
                    1 {"Install_Pending"}
                    2 {"Install_Retrying"}
                    3 {"Install_Failed"}
                    4 {"Removal_Pending"}
                    5 {"Removal_Retrying"}
                    6 {"Removal_Failed"}
                    7 {"Instal_Start_Pending"}
                    8 {"Content Validation Failed"}
            $Type = switch ($_.PackageType)
                    0 {"Standard Package"}
                    3 {"Driver Package"}
                    4 {"Task Sequence Package"}
                    5 {"Software Update Package"}
                    6 {"Device Setting Package"}
                    7 {"Virtual App Package"}
                    8 {"Application Package"}
                    257 {"OS Image Package"}
                    258 {"Boot Image Package"}
                    259 {"OS Install Package"}
            $SummaryDate = [System.Management.ManagementDateTimeconverter]::ToDateTime($_.SummaryDate)
            $package = New-Object psobject -Property @{
                Name = $PKG.Name
                'PackageSize (MB)' = $size
                PackageType = $Type
                PackageID = $_.PackageID
                State = $State
                StateCode = $_.State
                DistributionPoint = $server
                SummaryDate = $SummaryDate
            $body += "<tr><td>$($package.Name)</td><td>$($package.'PackageSize (MB)')</td><td>$($package.PackageType)</td><td>$($package.PackageID)</td><td>$($package.DistributionPoint)</td><td>$($package.SummaryDate)</td></tr>"
        } |
            Select Name,'PackageSize (MB)',PackageType,PackageID,State,StateCode,DistributionPoint,SummaryDate | Sort LastCopied -Descending |
                 ForEach-Object {
                    Get-WmiObject -Namespace root\sms\site_$SiteCode -Query "SELECT * FROM SMS_DistributionPoint WHERE PackageID='$($_.PackageID)' and ServerNALPath like '%$($_.DistributionPoint)%'" |
                        ForEach-Object {
                            $_.RefreshNow = $true

# Create HTML Body for the email
$htmlBody = "<html><body><table border='1'><tr><th>Name</th><th>PackageSize (MB)</th><th>PackageType</th><th>PackageID</th><th>DistributionPoint</th><th>SummaryDate</th></tr>$body</table></body></html>"

# Send an email
$mailmessage = New-Object
$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$mailmessage.from = ($from)
$mailmessage.Subject = $subject
$mailmessage.Body = $htmlBody
$mailmessage.IsBodyHtml = $true

Email screenshot:2

Isn’t it wonderful how a bit of scripting can simplify complex tasks and improve the efficiency of our tech operations? This is just one example of how automation can work its magic. Stay tuned for more insights and practical tips to streamline your IT workflows!

Remember, if you run into any issues or have any questions, drop them in the comments section below, and let’s learn together. Happy scripting!

2 thoughts on “Simplifying Off-Hours SCCM Package Redistribution with PowerShell Automation”

Leave a Comment

Your email address will not be published. Required fields are marked *