Surprise surprise, my first post is about MECM!

I was wondering where I would start on this blog… why not dig through my many many (MANY) notes on weird things I have found with MECM. Bear with me please on figuring out how these posts should look, I might just start out using this as a dumping ground for the variety of weird issues and fixes I have found over the years.

Let’s start with one of the wonders of Pull distribution points.

This will be a relatively short post however I have found it quite useful.

As a lot of us know, pull distribution points don’t exactly work all that well, however sometimes you have to make do with what you have given the limitations around numbers of standard dp’s you can have as well as aging infrastructure. So without going too deep into how a pull dp works, I will share a little and provide a little script I have found helpful to get content moving.

First, make sure you have a look at some (surprisingly) helpful information from Microsoft: https://learn.microsoft.com/en-us/troubleshoot/mem/configmgr/content-management/troubleshoot-content-distribution also the actual flow of distribution: https://learn.microsoft.com/en-us/troubleshoot/mem/configmgr/content-management/understand-package-actions

Real high level oversight here (I will go in depth later on workings of it but not needed in this post)

Pull dp’s use CCMEXEC with WMI methods and bits to download content from your source dp. The way ccm uses bits is quite frustrating. Basically all bits jobs are created as background jobs. Not only that but there is only one bits job that runs at any time, each job will only be a tiny segment of what ever package you are trying to download.

To be honest, it generally isn’t too bad of an issue for decent WAN links however when you have really slow sites, it is super painful and can often cause other issues such as WMI backlog for DTS (I will touch on this later) which kills data transfers entirely INCLUDING policies!

Anyway, it turns out you can set these bits transfers to run in the foreground which drastically improves the transfer rates and you can have many bits jobs running at a time. Suddenly you are going from kb speeds for remote sites up to mb speeds. (Yes this is still tiny but some slow remote sites only have satellite connectivity or for some companies are across the other side of the world)

Before doing this, please note… this will eat bandwidth! Use with caution!!!!! This has not been recommended by Microsoft.

Oh also, before I post the script, I remember seeing a post somewhere about someone talking with M$ and getting advised that you can create a DWord under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SMS\DP called MaxConcurrentBITSJobs and set it to 300 rather than the default 50 which is meant to help improve the content hang issue that kills ccm, content and policy downloads. WAIT! I Found it! https://sccmpowershell.com/pull-distribution-points-hang-downloading-content

I digress. The bits foreground issue.

There are a few ways you can do this. I have gone down the path of creating a task schedule on the pull dp which runs a powershell script for x hours which checks every x seconds for x bits jobs and moves them to the foreground. Set up the task schedule to run as system and you are as good as gold. Call the Task schedule when you are pushing content through and it pumps content through faster than you could dream with this clunky app.

I personally like it to run after business hours for several hours, check every 15 seconds for bits jobs and change 10 bits jobs to foreground jobs.

I’m not here to hold your hand, if you are a MECM tech, i’m sure you will be able to figure out how to create a scheduled task with the parameters in this script. TEST FIRST IN TEST AND DON’T JUST TRUST MY SCRIPT WILL WORK FOR YOU!!!!!!! I AM A STRANGER ON THE INTERNET! Hopefully this will help someone write their own. It is still in a messy draft state but you get the idea. Keep in mind bandwidth consumption or you will chew your bandwidth!

<#
Set up scheduled task on a distribution point.
Change the switches in the argument with the results you are after.
CheckJobsInSeconds is how often you want the script to check and action the bits jobs.
maxRunTimeInHours is how long you want the script to run.
maxBitsJobs is the maximum number of concurrent jobs you want running. (You should never see over 50 jobs at any time unless you have tweaked settings in the registry) 

Run this in powershell with the required values in switches to set up the scheduled task

$taskname = "bitsWorkaround"
$taskdescription = "Workaround For frustrating bits"
$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument '-file C:\scripts\bitsWorkaround.ps1 -CheckJobsInSeconds 15 -maxRunTimeInHours 8 -maxBitsJobs 10'
Register-ScheduledTask -Action $action -TaskName $taskname -Description $taskdescription -TaskPath "\" -User "System"


You can then go in and run the task or alternatively schedule it to run when ever you want
Alternatively you can script in the scheduled times for the task if you like
This is only a draft of a script but it works at the moment
#>

Param
(
    [Parameter(Mandatory=$True)][Int]$CheckJobsInSeconds,
    [Parameter(Mandatory=$True)][Int]$maxRunTimeInHours,
    [Parameter(Mandatory=$True)][Int]$maxBitsJobs
) # End of Params

Function Write-Log ($LogText) {
    Add-Content $LogFile "$(Get-Date -format G),$LogText"
    Write-Output "$Date - $LogText"
} # End Of Function Write-Log

# Define a global array to store BITS jobs outside the function
$result = @()
function CheckAndSetBITSJobPriority {
    Write-Log -LogText "Checking bits queue"
    $allBitsJobs = Get-BitsTransfer -allusers
    foreach($job in $jobs){
        $global:result += $job.jobid
    }
    $actionableJobs = $allBitsJobs | Where-Object { ($_.JobState -eq 'Queued' -or $_.JobState -eq 'Transferring') -and $_.priority -eq 'normal' }
    $errorJobs = $allBitsJobs | Where-Object { ($_.JobState -like '*Error*') }
    $transferringJobs = $allBitsJobs | Where-Object { ($_.JobState -eq 'Transferring') }

    Write-Log -LogText "Jobs in queued state: $($queuedJobs.count)"
    Write-Log -LogText "Actionable jobs: $($actionableJobs.count)"
    Write-Log -LogText "Jobs in transferring state: $($transferringJobs.count)"

    if ($errorJobs.count -gt 0) {
        Write-Log -LogText "Jobs in error state: $($errorJobs.count)"
    }
    if($actionableJobs.count -gt 0){
        if ($transferringJobs.count -lt $global:maxBitsJobs) {
            $jobsToStart = $global:maxBitsJobs - $transferringJobs.count
            Write-Log -LogText "Updating priority of $jobsToStart Jobs"
            $actionableJobs | Select-Object -First $jobsToStart | ForEach-Object {
                $_ | Set-BitsTransfer -Priority Foreground
            }
        }
    } else {
        Write-Log -LogText "No BITS jobs to update"
    }
} # End Of Function CheckAndSetBITSJobPriority

$CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$LogFile = "C:\scripts\bitsWorkaround.log"
$endTime = (Get-Date).AddHours($maxRunTimeInHours)

Write-Log -LogText "####################################################"
Write-Log -LogText "Script started"
Write-Log -LogText "Script end time: $endTime"
Write-Log -LogText "Script running as $currentUser"
Write-Log -LogText "Check for jobs every $CheckJobsInSeconds second(s)"
Write-Log -LogText "Run time specified: $maxRunTimeInHours hour(s)"
Write-Log -LogText "Maximum Concurrent bits jobs: $maxBitsJobs"


Write-Log -LogText "####################################################"


if ($(get-bitstransfer -allusers).count -gt 100) {
    Write-Log -LogText "Clearing BITS Cache - $((get-bitstransfer -allusers).count)"
    get-bitstransfer -allusers | remove-bitstransfer
    ReStart-Service -Name BITS
    Restart-Service -Name ccmexec 
}


while ((Get-Date) -lt $endTime) {
    CheckAndSetBITSJobPriority
    $currentTime = (Get-Date)
    $remainingMinutes = ($endTime - $currentTime).TotalMinutes
    Write-Log -LogText "Script runtime minutes remaining: $remainingMinutes"
    Write-Log -LogText ""
    Start-Sleep -Seconds 30
}

$uniqueJobs = ($result | Group-Object -Property JobID | ForEach-Object { $_.Group[0] }).count
Write-Log -LogText "####################################################"
Write-Log -LogText "Script finished"
write-Log -Logtext "Bits jobs encountered: $uniqueJobs"
Write-Log -LogText "####################################################"

Any way, you hopefully get the idea. There is one alternative method you can use which I have used as well, I yoinked this from a colleague who I will be thanking and also showing this post to later (Thanks dude!). You can set up a script within MECM and run it on a device collection which you could have what ever pull dp’s you want in the collection which to be honest, way better than my idea but hey, mine looks cooler 😉

#Create a once off Scheduled Task to run in background to set bits priority to foreground for X hours or until system reboot
# The /du section is in hours, /du 0048:00  is 48 hours
$time = (get-date).Addminutes(1).ToString('HH:mm:ss')
schtasks /create /RU SYSTEM /f /tn "BITS Foreground" /tr "powershell.exe -command For(;;){Start-Sleep -seconds 15 ; Get-BitsTransfer | Set-BitsTransfer -Priority Foreground}" /sc once /st $time  /du 0048:00 /rl HIGHEST
schtasks /run /tn "BITS Foreground"

You may also like...

Leave a Reply

Discover more from Trav Finds Things

Subscribe now to keep reading and get access to the full archive.

Continue reading