Microsoft Connected Cache Stats From IIS Logs

Recently we have had some issues with Windows Update network utilization, to tackle this we have enabled Microsoft Connected Cache on some of our System Center Configuration Manager (SCCM) distribution points. This is a new feature available with System Center Current Branch 1906+ technology that provides a dedicated local server.

Microsoft have some great documentation on implementing this here: https://docs.microsoft.com/en-us/configmgr/core/plan-design/hierarchy/microsoft-connected-cache

You can see quite easily from the cache folder created by Connected Cache how much data cached, however it’s not so easy to see:

  • How much data clients are downloading from the cache server.
  • Which network locations are they downloading from, want to know it’s being downloaded from the expected network locations.

I’ve noticed SCCM can be a bit sluggish updating the Delivery Optimization registry key, so there is some concern that users moving between networks. It writes these settings here:

HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\DeliveryOptimization

Note: Useful for debugging, when connected cache is enabled a value named “DOCacheHost” is created here for clients in the relevant boundary group which holds the Connected Cache (Distribution Point) server name.

To tackle this I have have written a PowerShell script that parses IIS log data downloaded from a connected cache server. Clients connecting have the User Agent string “Microsoft-Delivery-Optimization/10.0”. This tells me how much data has been transferred and which subsets and IP address have downloaded content.

logs

Download from https://github.com/Sysfcuk/ConnectedCacheStats or code below:

<#
.SYNOPSIS
Outputs statistics form IIS log files relating to Microsoft Connected Cache
.DESCRIPTION
Outputs statistics form IIS log files relating to Microsoft Connected Cache
.OUTPUTS
Outputs to an PowerShell object containing information on AWS instances.
.PARAMETER NumberOfLogs
Number of ISS logs to process, most recent first.
.PARAMETER IISLogPath
Path to IIS Log Directory
.PARAMETER DOUserAgent
Delivery optimization User Agent
.EXAMPLE
&.\Get-ConnectedCacheIISStats.ps1
.NOTES
Author:       Andy Cattle
Change Log:
03/03/2020: Initial Version
#>

Param (

    [Parameter(Mandatory = $false)]
    [int]$NumberOfLogs = 3,
    [Parameter(Mandatory = $false)]
    [string]$IISLogPath = "C:\inetpub\logs\LogFiles\W3SVC1",
    [Parameter(Mandatory = $false)]
    [string]$DOUserAgent = "Microsoft-Delivery-Optimization/10.0"

)

function Parse-IISLog {

    Param (

        [Parameter(Mandatory = $true, Position = 0,
            ValueFromPipeline = $True,
            ValueFromPipelineByPropertyName = $true)]
        [Alias('FullName')]
        [string[]]$Path

    ) Process {

        foreach ($P in $Path) {

            Write-Verbose "Processing IIS Log File: $p"

            $Fields = (Get-Content $P `
                | Select-Object -First 10 `
                | Where-Object { $_ -match "\#Fields\: " }
            ) -replace "\#Fields\: " -split " "

            Get-Content $P `
            | Where-Object { $_[0] -ne "#" } `
            | ForEach-Object {

                $Entry = @{}

                $i = 0

                $_ -split " " | ForEach-Object {

                    $Entry[$Fields[$i++]] = $_

                }

                [pscustomobject]$Entry

            }
        }
    }
}

$LogsFiles = Get-ChildItem $IISLogPath

$DOData = $LogsFiles | Where-Object { $_.Name -like "u_ex*.log" } `
| Sort-Object Name -Descending | select -First $NumberOfLogs `
| Parse-IISLog -Verbose `
| Where-object { $DOUserAgent -contains $_."cs(User-Agent)" }

$DataByIP = $DOData | Group-Object c-ip | Select-Object @(
    @{
        Name       = "IP Address"
        Expression = { $_."Name" }
    }
    @{
        Name       = "Data GB"
        Expression = {

            $Bytes = $_.Group."sc-bytes" `
            | Measure-Object -Sum `
            | Select-Object -ExpandProperty Sum  `

            "{0:n2}" -f ($bytes / 1GB)

        }
    }
) | Sort-Object "IP Address"

$DataBySubnet = $DataByIP | ForEach-Object {
    [pscustomobject] @{
        "Subnet"  = $_."IP Address" -replace "\d+\.\d+$", "0.0"
        "Data GB" = $_."Data GB"
    }
} | Group-Object Subnet | Select-Object @(
    @{
        Name       = "Subnet"
        Expression = { $_."Name" }
    }
    @{
        Name       = "Data GB"
        Expression = {

            $GBytes = $_.Group."Data GB" `
            | Measure-Object -Sum `
            | Select-Object -ExpandProperty Sum  `

            "{0:n2}" -f $GBytes

        }
    }
)

$TotalData = "{0:n2}" -f (($DOData."sc-bytes" `
        | Measure-Object -Sum `
        | Select-Object -ExpandProperty Sum) / 1GB)

Write-Host "Total Connected Cache Data: $($TotalData)GB" -ForegroundColor Yellow

Write-Host "`nData by Subnet (2 octets):" -ForegroundColor Cyan

$DataBySubnet | Format-Table -AutoSize

Write-Host "Data by IP Address:" -ForegroundColor Cyan

$DataByIP | Format-Table -AutoSize
Written on March 6, 2020
Show Comments