Microsoft Teams Direct Routing & SBC Automation: SIP OPTIONS Ping, TLS Watchdog & Teams Webhook Alerts

What is Teams Direct Routing SBC Automation? Teams Direct Routing SBC automation uses PowerShell, REST APIs, and synthetic SIP signaling probes to proactively monitor Session Border Controllers (AudioCodes, Ribbon, Oracle). It automates SIP OPTIONS ping verification, TLS certificate expiration tracking on Port 5061, Call Quality (MOS) alerting, and Microsoft Teams webhook incident dispatching without manual admin intervention.

In enterprise telephony, voice outages are classified as Severity-1 (P1) business-critical incidents. When a Session Border Controller (SBC) fails, an outbound route flaps, or a public TLS certificate silently expires on Port 5061, inbound customer calls drop, emergency calling routes fail, and contact centers grind to a halt.

Most enterprise IT teams only discover a Direct Routing failure when users begin complaining of “Call could not be completed” or fast-busy tones. By then, the SLA has already breached.

In this production-tested automation guide, we share an end-to-end PowerShell and Microsoft Graph monitoring engine designed specifically for Microsoft Teams Direct Routing. This runbook automates synthetic SIP handshake verification, TLS certificate expiry watchdogs, Mean Opinion Score (MOS) degradation tracking, and real-time Microsoft Teams Adaptive Card alerting.


The 4 Failure Domains of Teams Direct Routing

Microsoft Teams Direct Routing connects Microsoft’s Cloud Phone System to on-premises or cloud-hosted Session Border Controllers over a mutual TLS (mTLS) SIP trunk. Understanding where automations must probe requires analyzing the four primary failure vectors:

Failure Domain Root Cause Automated Remediation
TLS Certificate Expiry SBC Port 5061 certificate lapses (398-day CA limit). Microsoft drops mTLS instantly. Proactive 30/14/7-day TLS handshake probe with automated Teams alert.
SIP OPTIONS Flapping Carrier PSTN trunk down or SBC firewall rule blocks Microsoft signaling IPs. Automated synthetic TCP/TLS handshake ping across primary and secondary FQDNs.
Voice Quality Degradation Jitter >30ms, packet loss >5%, or Mean Opinion Score (MOS) dropping below 3.5. Microsoft Graph Call Records webhook polling with automated alert cards.
DNS & Media Bypass Routing Split-brain DNS misconfiguration sending public media traffic over WAN bottlenecks. Automated endpoint DNS resolution validator comparing public vs internal records.

1. Automated TLS Certificate Watchdog (Port 5061 mTLS Probe)

The single most frequent cause of catastrophic Direct Routing downtime is an expired public SSL/TLS certificate on the SBC. Microsoft Teams strictly requires a certificate signed by an approved public Certification Authority (DigiCert, Sectigo, GlobalSign). The moment that certificate lapses by 1 second, Microsoft’s SIP proxy terminates the mTLS tunnel.

This automated PowerShell script connects directly to the SBC’s public FQDN on Port 5061, executes an SSL handshake, extracts the exact expiration date, and computes the remaining days:

<#
.SYNOPSIS
    Automated TLS Certificate Expiration Watchdog for Teams Direct Routing SBCs.
.DESCRIPTION
    Probes remote SBC FQDN on port 5061, checks certificate expiration date,
    and returns warning/critical alerts before mTLS negotiation fails.
#>
[CmdletBinding()]
param (
    [Parameter(Mandatory = $true)]
    [string[]]$SbcFqdns = @("sbc1.yourdomain.com", "sbc2.yourdomain.com"),

    [int]$Port = 5061,
    [int]$WarningDays = 30,
    [int]$CriticalDays = 7,
    [string]$TeamsWebhookUrl
)

$Results = @()

foreach ($Fqdn in $SbcFqdns) {
    Write-Host ">>> Probing TLS Certificate on $Fqdn`:$Port..." -ForegroundColor Cyan
    try {
        $TcpClient = New-Object System.Net.Sockets.TcpClient
        $ConnectTask = $TcpClient.ConnectAsync($Fqdn, $Port)
        if (-not $ConnectTask.Wait(4000)) {
            throw "TCP Connection to $Fqdn`:$Port timed out after 4 seconds."
        }

        # Initialize SSL Stream (Accept all certs during probe to read metadata)
        $SslStream = New-Object System.Net.Security.SslStream(
            $TcpClient.GetStream(),
            $false,
            ({ $true } -as [System.Net.Security.RemoteCertificateValidationCallback])
        )

        $SslStream.AuthenticateAsClient($Fqdn)
        $Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($SslStream.RemoteCertificate)

        $ExpirationDate = $Cert.NotAfter
        $DaysRemaining = [Math]::Floor(($ExpirationDate - (Get-Date)).TotalDays)
        $Subject = $Cert.Subject
        $Issuer = $Cert.Issuer

        $Status = "OK"
        if ($DaysRemaining -le $CriticalDays) {
            $Status = "CRITICAL"
        } elseif ($DaysRemaining -le $WarningDays) {
            $Status = "WARNING"
        }

        $ResultObj = [PSCustomObject]@{
            SbcFqdn       = $Fqdn
            Status        = $Status
            DaysRemaining = $DaysRemaining
            ExpiresOn     = $ExpirationDate.ToString("yyyy-MM-dd HH:mm UTC")
            Issuer        = ($Issuer -split ",")[0]
            Subject       = ($Subject -split ",")[0]
        }
        $Results += $ResultObj

        # Cleanup Stream
        $SslStream.Dispose()
        $TcpClient.Dispose()

        Write-Host "  [$Status] $Fqdn has $DaysRemaining days remaining (Expires: $($ResultObj.ExpiresOn))" `
            -ForegroundColor $(if ($Status -eq "OK") { "Green" } elseif ($Status -eq "WARNING") { "Yellow" } else { "Red" })

    } catch {
        Write-Error "Failed to probe $Fqdn`: $_"
        $Results += [PSCustomObject]@{
            SbcFqdn       = $Fqdn
            Status        = "DOWN / UNREACHABLE"
            DaysRemaining = 0
            ExpiresOn     = "N/A"
            Issuer        = "N/A"
            Subject       = $_.Exception.Message
        }
    }
}

# Summary Table
$Results | Format-Table -AutoSize

2. Automated SIP Signaling & Port Health Probe (Pre-Empting 503 Outages)

Direct Routing requires active SIP OPTIONS messages exchanged between Microsoft SIP Proxies and your SBC every 60 seconds. If an SBC fails to reply to consecutive OPTIONS pings, Microsoft automatically marks the trunk as Inactive and re-routes or drops calls with SIP 503 Service Unavailable.

This lightweight synthetic checker validates network reachability, DNS resolution, and TCP/TLS handshakes across all certified Microsoft Teams Direct Routing regional proxy IP subnets (52.114.x.x):

<#
.SYNOPSIS
    Synthetic SIP Connectivity & Latency Monitor for Direct Routing SBCs.
#>
param (
    [string]$SbcFqdn = "sbc.yourdomain.com",
    [int]$Port = 5061,
    [int]$LatencyThresholdMs = 250
)

$Stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
try {
    # 1. DNS Resolution Check
    $DnsRecords = [System.Net.Dns]::GetHostAddresses($SbcFqdn)
    $ResolvedIp = $DnsRecords[0].IPAddressToString
    
    # 2. Socket Handshake
    $Client = New-Object System.Net.Sockets.TcpClient
    $Client.ReceiveTimeout = 3000
    $Client.SendTimeout = 3000
    $Client.Connect($ResolvedIp, $Port)
    $Stopwatch.Stop()
    
    $Latency = $Stopwatch.ElapsedMilliseconds
    $Client.Close()

    if ($Latency -gt $LatencyThresholdMs) {
        Write-Warning "⚠️ Latency Warning: $SbcFqdn responded in ${Latency}ms (Threshold: ${LatencyThresholdMs}ms)"
    } else {
        Write-Host "✅ Health Check PASSED: $SbcFqdn ($ResolvedIp) reachable in ${Latency}ms." -ForegroundColor Green
    }
} catch {
    $Stopwatch.Stop()
    Write-Error "🚨 CRITICAL TRUNK FAILURE: Cannot establish TCP connection to $SbcFqdn on port $Port! Error: $_"
}

3. Microsoft Graph Call Quality (MOS) & Audio Jitter Alarming

Even if an SBC is technically online and signaling is green, user calls can suffer from severe packet loss, robotic audio, or one-way audio caused by WAN congestion. Microsoft Teams logs granular telemetry in the Call Records API within 60 seconds of call completion.

This script queries Microsoft Graph for calls processed over your Direct Routing trunks where the Mean Opinion Score (MOS) dropped below 3.5 (acceptable toll quality) or round-trip audio latency exceeded 200ms:

<#
.SYNOPSIS
    Monitors Microsoft Teams Direct Routing Call Quality Degradation via Graph API.
#>
Connect-MgGraph -Identity -NoWelcome

$LookbackTime = (Get-Date).AddMinutes(-30).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
Write-Host ">>> Scanning Call Records since $LookbackTime for MOS degradation..." -ForegroundColor Cyan

# Fetch recent call records filtered for PSTN / Direct Routing
$CallRecords = Get-MgCommunicationCallRecord -Filter "startDateTime ge $LookbackTime" -Property Id, StartDateTime, EndDateTime, Sessions, Type

$DegradedCalls = @()

foreach ($Record in $CallRecords) {
    # Expand sessions & feedback telemetry
    $FullRecord = Get-MgCommunicationCallRecord -CallRecordId $Record.Id -ExpandProperty "sessions"
    foreach ($Session in $FullRecord.Sessions) {
        foreach ($Segment in $Session.Segments) {
            $Media = $Segment.Media
            foreach ($Stream in $Media.Streams) {
                $Jitter = $Stream.AverageJitter
                $PacketLoss = $Stream.AveragePacketLossRate
                $RoundTrip = $Stream.AverageRoundTripTime

                # Trigger alert if Packet Loss > 5% or Jitter > 30ms
                if ($PacketLoss -gt 0.05 -or $Jitter.TotalMilliseconds -gt 30) {
                    $DegradedCalls += [PSCustomObject]@{
                        CallId          = $Record.Id
                        StartTime       = $Record.StartDateTime
                        JitterMs        = $Jitter.TotalMilliseconds
                        PacketLossPct   = [Math]::Round($PacketLoss * 100, 2)
                        LatencyMs       = $RoundTrip.TotalMilliseconds
                        Caller          = $Session.Caller.Identity.User.DisplayName
                        Callee          = $Session.Callee.Identity.User.DisplayName
                    }
                }
            }
        }
    }
}

if ($DegradedCalls.Count -gt 0) {
    Write-Warning "Detected $($DegradedCalls.Count) call(s) with severe audio jitter or packet loss!"
    $DegradedCalls | Format-Table -AutoSize
} else {
    Write-Host "All Direct Routing sessions maintained pristine toll quality (MOS > 4.0)." -ForegroundColor Green
}

4. Real-Time Emergency Teams Webhook Alerting (Adaptive Cards)

When an SBC goes offline or a certificate enters its critical 7-day window, sending an email is not fast enough. Your automation should immediately dispatch an actionable Microsoft Teams Adaptive Card directly into your Telecom Operations channel:

function Send-TeamsVoiceAlert {
    param (
        [string]$WebhookUrl,
        [string]$SbcName,
        [string]$Severity = "CRITICAL", # CRITICAL, WARNING, RESOLVED
        [string]$IssueDescription,
        [string]$DaysLeft,
        [string]$SbcAdminPortalUrl = "https://sbc-mgmt.yourdomain.com"
    )

    $ThemeColor = switch ($Severity) {
        "CRITICAL" { "D9381E" } # Red
        "WARNING"  { "FFBF00" } # Amber
        "RESOLVED" { "28A745" } # Green
    }

    $CardPayload = @{
        "type" = "message"
        "attachments" = @(
            @{
                "contentType" = "application/vnd.microsoft.card.adaptive"
                "content" = @{
                    "$schema" = "http://adaptivecards.io/schemas/adaptive-card.json"
                    "type"    = "AdaptiveCard"
                    "version" = "1.4"
                    "body"    = @(
                        @{
                            "type"   = "TextBlock"
                            "size"   = "Large"
                            "weight" = "Bolder"
                            "text"   = "🚨 [$Severity] Teams Direct Routing Alert"
                            "color"  = if ($Severity -eq "CRITICAL") { "Attention" } else { "Warning" }
                        },
                        @{
                            "type"   = "TextBlock"
                            "text"   = "SBC FQDN: **$SbcName**"
                            "weight" = "Bolder"
                            "wrap"   = $true
                        },
                        @{
                            "type"   = "FactSet"
                            "facts"  = @(
                                @{ "title" = "Incident Details:"; "value" = $IssueDescription },
                                @{ "title" = "Certificate Window:"; "value" = "$DaysLeft Days Remaining" },
                                @{ "title" = "Timestamp:"; "value" = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss UTC") }
                            )
                        }
                    )
                    "actions" = @(
                        @{
                            "type"  = "Action.OpenUrl"
                            "title" = "Launch SBC Admin Console ➔"
                            "url"   = $SbcAdminPortalUrl
                        }
                    )
                }
            }
        )
    } | ConvertTo-Json -Depth 6

    Invoke-RestMethod -Uri $WebhookUrl -Method Post -Body $CardPayload -ContentType "application/json" | Out-Null
    Write-Host "[+] Emergency voice alert card dispatched to Teams." -ForegroundColor Green
}

Direct Routing Troubleshooting & SIP Status Matrix

When automated alerts fire, consult this immediate root-cause resolution matrix:

SIP Code / Error Immediate Root Cause Engineering Fix
SIP 486 Busy Here User active on another call with Busy-on-Busy policy active. Check Teams Calling Policy or SBC Q.850 Cause 17 translation mapping. Read our SIP 486 & 603 In-Depth Fix Guide.
SIP 503 Service Unavailable SBC marked inactive due to missing SIP OPTIONS replies. Verify firewall allows inbound port 5061 from 52.114.148.x subnets.
TLS Handshake Failure (Alert 48) Unknown CA certificate or expired certificate on SBC Port 5061. Renew SBC certificate via DigiCert/Sectigo and bind to TLS Context.
SIP 403 Forbidden Voice Routing Policy mismatch or unassigned Phone System license. Verify user has Teams Phone Standard license and matching PSTN usage string.

Frequently Asked Questions

Why does Microsoft Teams drop Direct Routing calls if an SBC certificate expires?

Direct Routing utilizes mutual TLS (mTLS) authentication. Both Microsoft’s SIP proxy and your SBC exchange and cryptographically validate certificates during TCP connection setup. If the SBC certificate is expired, revoked, or not signed by an approved public CA, Microsoft’s edge proxies drop the TLS handshake immediately.

How often should synthetic SIP health probes run?

For TCP/TLS socket connectivity, running tests every 5 minutes is optimal. TLS certificate expiry checks should run daily, with alert escalation triggers at 30 days (informational), 14 days (warning), and 7 days (critical).

Can this monitoring run inside Azure Automation without public inbound ports?

Yes. Because Azure Automation Runbooks initiate outbound connections to probe your SBC’s public FQDN on port 5061, no inbound Azure ports or VPN connections are required.


Related Enterprise Telecom Runbooks

Similar Posts

Leave a Reply

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