Hello Everyone! I´m Stefan Röll, Premier Field Engineer (PFE) at Microsoft Germany for System Center Configuration Manager. One of my customers was challenged with large content distributions and relative slow connections to some satellite locations .
TL;DR
Pull Distribution Points (Pull DPs) can save you a lot of Bandwidth when used together with BranchCache, especially for downloads like Office 365ProPlus Updates, Definitions for Defender and (Boot)Images.
At my customer, I can see about 30% savings compared to Standard DPs. Especially for slow connected Systems, this can be a huge benefit.
The Challenge
My customer is starting to deploy Office 365ProPlus Updates including multiple Languages using SCCM.
We have noticed that this will cause a huge amount of data that needs to be sent to all Distribution Points each month.
When you download a single O365 Update with multiple Languages, you quickly end up with 6-8GB of Data.
This is because you do not download separate Updates for Office - you download the complete setup files for Office.
So how to get all that Data to the Distribution Points?
BranchCache LocalCache
BranchCache LocalCache is one of my favourite features of BranchCache, but unfortunately not well known.
No matter in which mode you are running BranchCache (Local, Distributed or Hosted), when downloading data BranchCache will always ask its LocalCache (aka DataCache) first, before asking peers in the Subnet (Distributed) or the HostedCache Server. In LocalCache mode it will not request data from other sources.
Proof of Concept
First, I wanted to find out if BranchCache LocalCache can help with Office Updates.
To do that I used ddpeval.exe and checked the data source of the package.
This will give you the Data Deduplication savings, which is what you can expect as download savings:
As you can see, we can expect about 34% savings when using Data Deduplication in this example.
As BranchCache is based on the same technology, you can expect similar savings.
For testing, I used the following PS-Script to download two Office Language Files from a Distribution Point.
$Cred = Get-Credential
$Source1 = "http://" + "dp01.sccm.lab/sms_dp_smspkg$/15f39dd2-5662-49b2-a502-9367d5b6fd86/office/data/16.0.11629.20136/stream.x86.en-us.dat"
$Source2 = "http://" + "dp01.sccm.lab/sms_dp_smspkg$/15f39dd2-5662-49b2-a502-9367d5b6fd86/office/data/16.0.11629.20136/stream.x86.de-de.dat"
Start-BitsTransfer -Source $Source1 -Destination "C:\Temp\stream.x86.en-us.dat" -Priority Low -Authentication Ntlm -Credential $Cred
Start-BitsTransfer -Source $Source2 -Destination "C:\Temp\stream.x86.de-de.dat" -Priority Low -Authentication Ntlm -Credential $Cred
In addition, I used the following Script to monitor the download on the Client side:
do
{
$FromServer = "\BranchCache\BITS: Bytes from server"
$FromCache = "\BranchCache\BITS: Bytes from cache"
$Time = Get-Date -Format hh:mm:ss
$TotalMegaBytesfromServer = [math]::round(((Get-Counter $FromServer).CounterSamples.CookedValue / 1024 / 1024),0)
$TotalMegaBytesfromCache = [math]::round(((Get-Counter $FromCache ).CounterSamples.CookedValue / 1024 / 1024),0)
$Ratio = If($TotalMegaBytesfromServer -gt 0){[math]::round(($TotalMegaBytesfromCache*100/($TotalMegaBytesfromServer+$TotalMegaBytesfromCache)),2)}else{0}
Write-Output "MByte from Server: $($TotalMegaBytesfromServer) - MByte from Cache: $($TotalMegaBytesfromCache) - CacheRatio: $Ratio - Time: $Time"
Start-Sleep -Seconds 10
}
until ($false)
Demo Download
The gif above shows how BranchCache LocalCache works. For better understanding, I moved the DataCache to the D: Drive.
What can you see?
- The script and progress to download two Office language Files from a DP ( 1 )
- The BranchCache download statistics ( 2 )
- Although it downloads ( 4 ) to the C: drive, you can see that the D: drive has read/write access ( 3 )
Once the download completes, you can see that we saved 57 MB just by using BranchCache LocalCache - fantastic!
Basic Implementation
Implementation in SCCM is very simple. You just need to enable BranchCache on your source DP and on your PullDP:
Distmgr will enable BranchCache on the DP
With Get-BCStatus you can check the BranchCache Configuration on the DPs.
Advanced Implementation
In the default configuration the DataCache for BranchCache is very small, and savings will be limited.
Therefore I´ve wrote a Baseline to tune the BranchCache settings. You still need to enable BC on your DPs as mentioned in the Basic Implementation above.
The Baseline checks and fixes the following things:
- BranchCache Server Feature is installed
- BranchCache Service is started and set to automatic startup
- Configures BranchCache for LocalCache or DistributedCache
- Moves the BranchChache Caches to the same drive as the ContentLib
- My customer has a separate drive for the ContentLib
- The drive has a lot of free space, so it´s ideal for BranchCache data
- Configures BranchCache limit
- DataCache 50% of the Drive
- Ensures better download savings
- HashCache 50% of the Drive
- Helps if BranchCache enabled Clients are downloading from this DP
- Configures a longer age time (70 days) for data in the cache
- Ensures better download savings over time
You can use the Baseline as template for your environment but be sure to test it out before using it in production.
Discovery Script:
<#
# THIS SAMPLE CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
# WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
# IF THIS CODE AND INFORMATION IS MODIFIED, THE ENTIRE RISK OF USE OR RESULTS IN
# CONNECTION WITH THE USE OF THIS CODE AND INFORMATION REMAINS WITH THE USER.
#>
Import-Module BranchCache
$OK = $true
#Check if BranchCache Server Feature installed
$Feature = Get-WindowsFeature -Name BranchCache
if ($Feature.Installed -ne $true) {$OK = $false}
#Check BranchCache Service Status
$Service = Get-BCStatus
if ($Service.BranchCacheIsEnabled -ne "True") {$OK = $false}
if ($Service.ClientConfiguration.CurrentClientMode -ne "DistributedCache") {$OK = $false} #You need to decide if you want Distributed or local Cache
if ($Service.BranchCacheServiceStatus -ne "Running") {$OK = $false}
if ($Service.BranchCacheServiceStartType -ne "Automatic") {$OK = $false}
#Check BC location is same as SCCMContentLib
$HashCache = Get-BCHashCache -ErrorAction Stop
$DataCache = Get-BCDataCache -ErrorAction Stop
$ContentLibDrive = (Get-ItemProperty -Path "HKLM:\Software\Microsoft\SMS\DP" -Name ContentLibraryPath -ErrorAction SilentlyContinue).ContentLibraryPath
$ContentLibDrive = ($ContentLibDrive).SubString(0, 3)
$HashCacheDrive = ($HashCache.CacheFileDirectoryPath).SubString(0, 3)
$DataCacheDrive = ($DataCache.CacheFileDirectoryPath).SubString(0, 3)
if ($ContentLibDrive -ne $HashCacheDrive) {$OK = $false}
if ($ContentLibDrive -ne $DataCacheDrive) {$OK = $false}
#Check that Cache Sizes are correct
$HashCache = Get-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\PeerDist\CacheMgr\Publication" -Name SizePercent -ErrorAction SilentlyContinue
if ($HashCache.SizePercent -ne 50) {$OK = $false}
$DataCache = Get-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\PeerDist\CacheMgr\RePublication" -Name SizePercent -ErrorAction SilentlyContinue
if ($DataCache.SizePercent -ne 50) {$OK = $false}
#Check BranchCache MaxAge
$CacheAge = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PeerDist\Retrieval" -Name SegmentTTL -ErrorAction SilentlyContinue
if ($CacheAge.SegmentTTL -ne 70) {$OK = $false}
return $OK
Remediation Script:
<#
# THIS SAMPLE CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
# WHETHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
# IF THIS CODE AND INFORMATION IS MODIFIED, THE ENTIRE RISK OF USE OR RESULTS IN
# CONNECTION WITH THE USE OF THIS CODE AND INFORMATION REMAINS WITH THE USER.
#>
Import-Module BranchCache
#Install BranchCache Server Feature if not installed
$Feature = Get-WindowsFeature -Name BranchCache
if ($Feature.Installed -ne $true) {Install-WindowsFeature -Name BranchCache}
#Fix BranchCache Service Status
$Service = Get-BCStatus
if ($Service.BranchCacheIsEnabled -ne "True") {Enable-BCDistributed -Force} #If you don�t want to enable Distributed Mode, use Enable-BCLocal
if ($Service.ClientConfiguration.CurrentClientMode -ne "DistributedCache") {Enable-BCDistributed -Force} #You need to decide if you want Distributed or local Cache
if ($Service.BranchCacheServiceStatus -ne "Running") {Start-Service PeerDistSvc}
if ($Service.BranchCacheServiceStartType -ne "Automatic") {Set-Service PeerDistSvc -StartupType Automatic}
#Fix BC location
$HashCache = Get-BCHashCache -ErrorAction Stop
$DataCache = Get-BCDataCache -ErrorAction Stop
$ContentLibDrive = (Get-ItemProperty -Path "HKLM:\Software\Microsoft\SMS\DP" -Name ContentLibraryPath -ErrorAction Stop).ContentLibraryPath
$ContentLibDrive = ($ContentLibDrive).SubString(0, 3)
$HashCacheDrive = ($HashCache.CacheFileDirectoryPath).SubString(0, 3)
$DataCacheDrive = ($DataCache.CacheFileDirectoryPath).SubString(0, 3)
$HashCacheDest = $ContentLibDrive + "BranchCache\Publication"
$DataCacheDest = $ContentLibDrive + "BranchCache\RePublication"
if ($ContentLibDrive -ne $HashCacheDrive) {
New-Item -ItemType directory -Path $HashCacheDest -Force -ErrorAction Stop
#Clear Cachce and restart service to ensure move works
Clear-BCCache -Force -ErrorAction Stop
Restart-Service PeerDistSvc -Force
Get-BCHashCache | Set-BCCache -MoveTo $HashCacheDest -Force -ErrorAction Stop
}
if ($ContentLibDrive -ne $DataCacheDrive) {
New-Item -ItemType directory -Path $DataCacheDest -Force -ErrorAction Stop
#Clear Cachce and restart service to ensure move works
Clear-BCCache -Force -ErrorAction Stop
Restart-Service PeerDistSvc -Force
Get-BCDataCache | Set-BCCache -MoveTo $DataCacheDest -Force -ErrorAction Stop
}
#FixCache Sizes
$HashCache = Get-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\PeerDist\CacheMgr\Publication" -Name SizePercent -ErrorAction SilentlyContinue
if ($HashCache.SizePercent -ne 50) {Get-BCHashCache | Set-BCCache -Percentage 50 -Force}
$DataCache = Get-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\PeerDist\CacheMgr\RePublication" -Name SizePercent -ErrorAction SilentlyContinue
if ($DataCache.SizePercent -ne 50) {Get-BCDataCache | Set-BCCache -Percentage 50 -Force}
#Fix BranchCache MaxAge
$CacheAge = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PeerDist\Retrieval" -Name SegmentTTL -ErrorAction SilentlyContinue
if ($CacheAge.SegmentTTL -ne 70) {Set-BCDataCacheEntryMaxAge -TimeDays 70 -Force}
Final Test
As a final test I have distributed four office updates with six different languages:
They have a total size of ~15GB and savings should be around 45%:
Mission succeeded! We have saved ~45% of data just by enabling BranchCache :-)
Happy BranchCaching!
Stefan Röll
Premier Field Engineer - Microsoft Germany
Changelog:
V1.0 - 05/31/2019 - initial release
V1.1 - 06/05/2019 - Fixed Bug in Discovery and Remediation Script
The sample scripts are not supported under any Microsoft standard support program or service. The sample scripts are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.