Direct Routing is the only Teams PSTN option where you own the trunk. Microsoft hands you a SIP peering surface in Azure; everything from the certificate on the Session Border Controller (SBC) to the regex on a voice route is yours to get right. That control is the reason to choose it, and also why a misordered PSTN usage or an unsigned certificate quietly drops calls in production — no error dialog, just a caller hearing dead air or a fast-busy. This guide builds the full call path in the exact order the platform evaluates it: certified SBC, PSTN gateway, voice routes, PSTN usages, voice routing policy, phone number, tenant dial plan, and emergency calling — then layers on media bypass, local media optimization, branch survivability, and the monitoring you need to prove it works.
Teams Phone is Microsoft’s cloud PBX: presence-integrated calling, voicemail, call queues, auto attendants, and PSTN breakout, all through the Teams client and certified handsets. Direct Routing connects that cloud PBX to the outside telephone network through your SIP trunk, terminated on a Session Border Controller Microsoft has certified. Where Calling Plans and Operator Connect hide the trunk entirely, Direct Routing exposes every layer — precisely what a contact-center integration, an analog paging system, or a decade-long carrier contract needs.
By the end you will be able to pick Direct Routing deliberately (not by default), pair an SBC without the certificate and firewall mistakes that eat the first day, author a voice-routing plan whose usage order you can defend, voice-enable users, wire dynamic E911 so an emergency call reaches the right dispatcher with the right address, decide whether media bypass and local media optimization belong in your topology, deploy a Survivable Branch Appliance for site outages, and read Call Analytics and the Call Quality Dashboard to localize a bad call to a single hop. Everything below assumes the modern MicrosoftTeams PowerShell module. The legacy Skype for Business Online connector is retired; install and connect once:
Install-Module MicrosoftTeams -Scope CurrentUser
Connect-MicrosoftTeams
What problem this solves
You have Teams and you have a phone system. The pain is joining them without giving up the two things that made the phone system valuable: your existing carrier economics and your existing telephony estate. A managed option (Calling Plans, Operator Connect) is a clean answer only when Microsoft or a partner sells numbers in your country, your call patterns are simple PSTN-in/PSTN-out, and you have nothing analog or contact-center-shaped to integrate. The moment any of those is false — Microsoft has no Calling Plan in-country, you signed a three-year SIP trunk contract last year, your warehouse has overhead paging on an analog interface, or your contact center sits behind a Genesys/Five9 SBC — a managed plan cannot reach it and Direct Routing is the only path.
What breaks without a correct Direct Routing build is subtle and expensive because telephony fails quietly. A certificate whose chain Microsoft does not trust means the SBC never appears healthy and no call ever completes, but there is no obvious error — the SBC health dashboard simply shows nothing. A voice route with a .* catch-all placed above a specific route sends every international call down the wrong (or a disabled) trunk. A PSTN usage in the wrong order inside a voice routing policy silently routes internal contact-center queue calls out to the public network and back, breaking a compliance boundary and doubling the cost. A dynamic-location misconfiguration means a 911 call from the third floor reaches a dispatcher with the address of the corporate headquarters two states away — a life-safety failure, and in the US a legal one under Kari’s Law and RAY BAUM’S Act.
Who hits this: enterprises consolidating regional PBXs onto Teams, multinationals where Microsoft’s Calling Plan coverage is patchy, any organization with an incumbent carrier contract or a contact-center platform, and telecom/UC teams migrating away from Skype for Business Enterprise Voice. It is an Advanced topic because success is not “a call connected once” — it is a routing plan you can reason about, an emergency-calling design that survives an audit, media that flows where compliance requires, and a survivability story for the day the WAN link to a branch dies.
Learning objectives
By the end of this article you can:
- Choose deliberately between Calling Plans, Operator Connect, and Direct Routing, and design a hybrid where a single user or the whole tenant uses more than one at once.
- Meet every SBC pairing prerequisite — verified domain, public-CA certificate, EKU, firewall subnets and ports, SIP proxy FQDNs — and pair the SBC with
New-CsOnlinePSTNGateway, understanding what each non-default parameter buys. - Build the routing plane bottom-up: PSTN usages, voice routes (active, backup, catch-all), and voice routing policies, and defend the order of usages as the control plane it is.
- Voice-enable a user with a number, Enterprise Voice, a voice routing policy and a tenant dial plan, including extension dialing and resource accounts.
- Author tenant dial plans with ordered normalization rules that always emit E.164, and explain when translation belongs on the trunk instead of the dial plan.
- Design dynamic E911 with the Location Information Service (LIS), emergency call routing and calling policies, dynamic emergency addresses, and
PidfLoSupportedon the egress gateway. - Decide when to enable media bypass and local media optimization, and explain how they change where RTP flows and what they cost you in troubleshooting complexity.
- Deploy a Survivable Branch Appliance (SBA) and assign a survivability policy so a branch keeps basic calling through a WAN outage.
- Drive Call Analytics and the Call Quality Dashboard (CQD) to monitor voice quality and localize a call problem to a specific hop, and run the SIP-ladder troubleshooting playbook for the common failure modes.
Prerequisites & where this fits
You should hold at least the Teams Administrator role (or Global Administrator) and be comfortable in the MicrosoftTeams PowerShell module — every operation in this article is scriptable and most are only practical in PowerShell. You need one or more Teams Phone licenses (the standalone Teams Phone add-on, or a suite such as E5 that includes it) assigned to the users you will voice-enable, plus a certified SBC (physical, virtual, or a hosting provider’s) and the carrier SIP trunk behind it. Familiarity with SIP (INVITE, 18x/200/BYE, headers like From, P-Asserted-Identity, Diversion), TLS certificates, E.164 number formatting, and .NET regular expressions is assumed — this is not an introduction to telephony.
This sits in the Teams Phone / Voice track and is downstream of tenant readiness: you should already have DNS, licensing and network egress in order. It pairs tightly with Teams Governance: Lifecycle, Naming, Expiration & Sensitivity Labels for the broader Teams-admin surface, and with Exchange Online Mail Flow: Transport Rules & Connectors Deep Dive because Teams voicemail and calendar integration ride on Exchange. Where you gate who may even reach the Teams admin surface, Entra Conditional Access at Scale: Personas & Authentication Context is upstream. For the QoS marking and network segmentation that keep media clean, Micro-Segmentation with NSGs, ASGs & Application-Tier Isolation covers the transport layer these calls traverse.
A quick map of who owns what during a Direct Routing incident, so you call the right person fast:
| Layer | What lives here | Who usually owns it | Failure classes it can cause |
|---|---|---|---|
| Teams client / Entra | Sign-in, policy assignment, presence | Teams / identity admin | No dial pad (not voice-enabled), wrong policy applied |
| Teams admin / routing | Gateways, routes, usages, policies, dial plans | Teams voice admin | Wrong route, dropped/misrouted calls, no PSTN option |
| Microsoft SIP proxy | sip.pstnhub.microsoft.com TLS + SIP |
Microsoft (platform) | 500/503 from the cloud side; health outages |
| SBC | TLS, SIP signaling, media anchoring, transcoding | Telecom / UC team | No OPTIONS (cert/firewall), one-way audio, 488 codec |
| Carrier SIP trunk | PSTN breakout, CLID, number ranges | Carrier / telco vendor | 403/404 from carrier, no inbound, CLID rejected |
| Network / firewall | Subnets, ports, QoS, NAT | Network team | Blocked 5061/media, one-way audio, jitter/loss |
Core concepts
Six mental models make every later decision obvious.
The three PSTN options are three different ownership boundaries, and they compose. Calling Plans put Microsoft on the trunk; Operator Connect puts a partner carrier on it through the admin center; Direct Routing puts you on it via an SBC you pair and routes you author. All three can coexist in one tenant, and even on one user — a user with a Calling Plan license and a voice routing policy routes patterns that match a voice route down the SBC and everything else through the Calling Plan, which always applies as the implicit last route. Choosing Direct Routing is choosing to own the trunk because a managed option cannot reach what you need to reach.
The call path is evaluated in a fixed order, and you build it in reverse. When a user dials, Teams first runs the dialed string through the user’s tenant dial plan (normalization to E.164), then consults the user’s voice routing policy (an ordered list of PSTN usages), walks those usages top-to-bottom, and for the first usage whose voice routes produce a pattern match, picks the lowest-priority route and hands the call to one of that route’s PSTN gateways (SBCs). You author it bottom-up: gateway first (nothing routes without it), then usages (the containers), then routes (the matching), then policies (the ordered assignment), then the number and dial plan on the user.
The SBC is a certified, trusted peer or it is invisible. Microsoft trusts an SBC by two things: a certificate whose full chain roots in the Microsoft Trusted Root Certificate Program with the SBC’s FQDN in the CN or SAN, and a domain for that FQDN verified in your tenant (never *.onmicrosoft.com). Health rides on SIP OPTIONS — the SBC and the Microsoft proxy ping each other; if OPTIONS do not flow (bad chain, firewall, wrong port), the SBC shows unhealthy or blank and no call completes, with no obvious error.
PSTN usages are just named strings whose order is the control plane. A usage carries no logic of its own — it is a label that links voice routes (which reference it) to voice routing policies (which list it in order). Because a voice routing policy is evaluated first match wins, the order in which usages appear in the policy decides which route is even considered. Getting the order wrong is the single most common routing bug, and it fails silently: calls go down a trunk you did not intend.
Media flows through the SBC’s media processor, through Microsoft’s media processors, or directly — and you choose. By default, media (RTP/SRTP) between the Teams client and the SBC traverses Microsoft’s media processors in the cloud. Media bypass removes that hop so RTP flows directly between the Teams client and the SBC. Local media optimization (LMO) goes further for multi-site topologies, keeping media on a downstream SBC local to the user even when signaling anchors on a central proxy SBC. Each is a performance and compliance lever with a real troubleshooting cost.
Emergency calling is two policies plus a location service, and Direct Routing adds one gateway flag. Emergency call routing policy defines which strings are emergency numbers and which PSTN usage (hence trunk) they take; emergency calling policy defines who gets notified and how; the Location Information Service (LIS) maps network identifiers (subnet, WAP, switch/port) to civic addresses so the location is dynamic. For Direct Routing you additionally set -PidfLoSupported $true on the egress gateway so Teams sends the PIDF-LO XML location payload the SBC relays to the emergency provider.
The vocabulary in one table
Before the deep sections, pin down every moving part. The glossary at the end repeats these for lookup; this table is the mental model side by side:
| Concept | One-line definition | Where it lives | Why it matters to routing |
|---|---|---|---|
| SBC / PSTN gateway | Certified device terminating your SIP trunk | On-prem / cloud / hoster | Nothing routes without a paired, healthy gateway |
| PSTN usage | A named string linking routes to policies | Global object (list) | Its order in a policy decides routing |
| Voice route | Regex → gateway(s) mapping | Global object | Matches the called number, picks the trunk |
| Voice routing policy | Ordered list of usages, assigned to users | Per-user policy | First matching usage wins |
| Tenant dial plan | Ordered normalization rules → E.164 | Per-user policy | Turns dialed strings into routable numbers |
| Normalization rule | Pattern/Translation pair | Inside a dial plan | Reshapes a dialed string |
| Online voice routing policy | Same as voice routing policy (Teams term) | Per-user | The policy that selects the SBC path |
| Media bypass | RTP direct: client ↔ SBC | Gateway + policy | Removes the Microsoft media hop |
| Local media optimization | Media stays on a local SBC | Network site config | Keeps media on-net across sites |
| LIS | Network-ID → civic address map | Tenant location DB | Dynamic E911 address discovery |
| Emergency call routing policy | Emergency numbers + their usage | Per-user (DR) | Which string is 911, which trunk |
| SBA | Survivable Branch Appliance | Branch SBC | Basic calling during a WAN outage |
| CQD | Call Quality Dashboard | Teams admin center | Tenant-wide media-quality trend |
| Call Analytics | Per-call, per-leg diagnostics | Teams admin center | One call, hop by hop |
The three PSTN options, and where Direct Routing fits
Before touching an SBC, be deliberate about why you are not using a managed option. All three can coexist in one tenant, even on one user.
| Option | Who runs the trunk | Setup surface | Number source | Best when |
|---|---|---|---|---|
| Calling Plans | Microsoft | Buy a license, assign a number | Microsoft (country-limited) | Microsoft sells numbers in your country and you want zero telco ops |
| Operator Connect | A participating carrier | Carrier provisions through the Teams admin center | The carrier | You want a carrier contract but no SBC to manage |
| Direct Routing | You (or a hosting provider) | Pair a certified SBC, author all routing | Your carrier / existing DIDs | Microsoft has no Calling Plan in-country, you have an existing carrier contract, or you must reach analog / PBX / contact-center gear |
The decision comes down to country coverage and existing telephony assets. A depreciating contact center, analog overhead paging, or an active SIP trunk contract all argue for Direct Routing. Mixing is legitimate: a user with a Calling Plan license and a voice routing policy routes matching patterns down the SBC and everything else through the Microsoft Calling Plan, which always applies as the implicit last route.
The trade-offs that actually decide it, side by side:
| Dimension | Calling Plans | Operator Connect | Direct Routing |
|---|---|---|---|
| Trunk ownership | Microsoft | Carrier | You |
| SBC to manage | None | None | Yes (certified) |
| Country coverage | Where Microsoft sells | Where the operator sells | Anywhere your carrier reaches |
| Existing-contract reuse | No | Only with a participating operator | Yes — any SIP carrier |
| Reach analog / PBX / contact center | No | No | Yes |
| Routing control (dial plans, routes) | Minimal | Minimal | Full |
| Emergency calling | Managed / dynamic | Operator-provided | You wire LIS + PidfLo |
| Media bypass / LMO | N/A | N/A | Available |
| Time to first call | Fastest | Fast | Slowest (build + certify) |
| Ongoing telco ops burden | Lowest | Low | Highest |
| Survivability (SBA) | N/A | N/A | Available |
And a plain decision table — match the constraint to the option:
| If you have… | Then choose… | Because |
|---|---|---|
| Microsoft Calling Plans in every country you operate, simple call patterns | Calling Plans | Zero telco ops; numbers from Microsoft |
| A preference for a carrier contract but no wish to run an SBC | Operator Connect | Carrier owns the trunk, you provision in-portal |
| An in-force SIP trunk contract you can’t exit | Direct Routing | Reuse the trunk and its economics |
| A contact center / IVR / paging behind a SIP or analog interface | Direct Routing | Only DR can reach non-Microsoft gear |
| Countries with no Microsoft or operator coverage | Direct Routing | Your carrier defines coverage |
| A compliance rule that PSTN media stay on your network | Direct Routing + media bypass | RTP goes client ↔ SBC, not via Microsoft |
| A branch that must keep calling through a WAN outage | Direct Routing + SBA | Survivability is DR-only |
Step 1 — SBC prerequisites: domains, certificate, FQDN, firewall
The single most common pairing failure is a domain that was never registered. The SBC’s FQDN must sit under a domain added to your tenant — you cannot use the *.onmicrosoft.com default, and the domain must be verified before pairing.
# The SBC FQDN domain must be one of these (not *.onmicrosoft.com)
Get-CsTenant | Select-Object -ExpandProperty Domains
If the SBC is sbc01.sip.contoso.com, the domain sip.contoso.com must be verified in the tenant and have at least one licensed user under it. Provisioning can take up to 24 hours to propagate. A common subtlety: the base domain (the registrable domain, contoso.com) must be verified; a subdomain FQDN under it (sbc01.sip.contoso.com) is then usable without separately verifying sip.contoso.com, provided contoso.com itself is verified.
The certificate rules are non-negotiable and trip up most first deployments:
- Common Name or SAN must be the SBC FQDN. A wildcard such as
*.contoso.commatchessbc.contoso.combut, per RFC 2818, does not matchsbc.test.contoso.com. - The issuing CA must be in the Microsoft Trusted Root Certificate Program. Self-signed or private-PKI certificates are rejected at the TLS handshake. Use a public CA (DigiCert, Sectigo, GlobalSign, and so on), and install the full chain (leaf + intermediates) on the SBC — a missing intermediate is a frequent “works in a browser, fails with Teams” trap.
- EKU must include Server Authentication (
1.3.6.1.5.5.7.3.1). Most CAs require a private key of at least 2048 bits.
The certificate requirements as a checklist you can hand to whoever requests it:
| Requirement | Value / rule | Why | How it fails if wrong |
|---|---|---|---|
| Subject CN or SAN | The exact SBC FQDN | TLS name match | Handshake name mismatch; no OPTIONS |
| Wildcard scope | *.contoso.com = one label only |
RFC 2818 | sbc.eu.contoso.com not covered → fails |
| Issuing CA | In Microsoft Trusted Root Program | Cloud trusts the leaf | Untrusted chain; TLS rejected |
| Chain completeness | Leaf + all intermediates on the SBC | Path building | Missing intermediate; intermittent failures |
| EKU | Server Authentication present | TLS server role | Rejected at handshake |
| Key length | ≥ 2048-bit RSA | Modern crypto floor | CA won’t issue / cloud rejects |
| Validity / renewal | Track expiry; renew ahead | Expired cert = dead trunk | Silent trunk-wide outage on expiry |
The SBC terminates TLS to three Microsoft SIP proxy FQDNs, tried in order: sip.pstnhub.microsoft.com (global, always first), sip2.pstnhub.microsoft.com (secondary), and sip3.pstnhub.microsoft.com (tertiary). Your firewall must permit all three, and inbound SIP can originate from any IP in the subnets below, not only the resolved ones.
The firewall facts to pin down before the network team asks:
| Purpose | Direction | Protocol / port | Source / destination | Notes |
|---|---|---|---|---|
| SIP signaling to Microsoft | SBC → cloud | TCP/TLS 5061 | Dest: SIP proxy FQDNs | Global FQDN tried first |
| SIP signaling from Microsoft | cloud → SBC | TCP/TLS, src 1024–65535 | Src: 52.112.0.0/14, 52.120.0.0/14 |
Any IP in the subnet, not just resolved |
| Media (no bypass) | both | UDP/SRTP | Media-processor ranges 3478–3481, 49152–53247 |
Skipped entirely with media bypass |
| Media (bypass) | both | UDP/SRTP | Direct client ↔ SBC | Client-network paths must allow it |
| SIP OPTIONS (health) | both | over 5061/TLS | between SBC and proxy | Drives the SBC health dashboard |
GCC High and DoD use different single FQDNs and subnets, and there the SBC must listen on 5061 specifically. Commercial cloud signaling and media both use subnets
52.112.0.0/14and52.120.0.0/14. SIP/TLS from the SBC targets destination port 5061; the SIP proxy replies from source ports 1024–65535. Without media bypass, media (UDP/SRTP) uses media-processor ports3478–3481and49152–53247.
Step 2 — Pair the SBC: create the PSTN gateway
Pairing is a single cmdlet, but the defaults matter. New-CsOnlinePSTNGateway creates the gateway disabled unless you pass -Enabled $true, and the parameter is SipSignalingPort (the old double-l spelling SipSignallingPort was renamed).
New-CsOnlinePSTNGateway `
-Fqdn sbc01.sip.contoso.com `
-SipSignalingPort 5061 `
-Enabled $true `
-SendSipOptions $true `
-MaxConcurrentSessions 500 `
-ForwardCallHistory $true `
-ForwardPai $true `
-FailoverTimeSeconds 10 `
-MediaBypass $false `
-Description "Primary SBC - DC1 carrier trunk"
What each non-default choice buys you:
SendSipOptions $true(the default, stated explicitly): the SBC must answer SIP OPTIONS pings or Microsoft excludes it from health/alerting and you fly blind in the SBC health dashboard.MaxConcurrentSessions 500: at 90% of this number the tenant is alerted. Set it to licensed trunk capacity — it arms monitoring, it does not enforce a cap.ForwardPai $true: forwards P-Asserted-Identity so the carrier can bill/CLID even when the caller presents as Anonymous.ForwardCallHistory $true: sendsHistory-InfoandReferred-Byso the carrier sees the diversion on forwarded/transferred calls.FailoverTimeSeconds 10: if the SBC does not respond in this window, the call tries the next trunk. Raise it only on slow networks; too high and a dead SBC holds calls hostage.
The full New-CsOnlinePSTNGateway parameter reference — the ones you will actually touch:
| Parameter | What it controls | Default | When to change | Gotcha |
|---|---|---|---|---|
-Fqdn |
The SBC’s fully qualified name | — (required) | Always | Must match the cert CN/SAN and a verified domain |
-SipSignalingPort |
TCP port for SIP/TLS | 5061 typical | Rare | Note single-l spelling; must match the SBC listener |
-Enabled |
Whether the gateway takes calls | $false |
Always set $true to go live |
Forgetting this is the classic “paired but no calls” bug |
-SendSipOptions |
Exchange SIP OPTIONS for health | $true |
Leave on | Off = no health data, no alerting |
-MaxConcurrentSessions |
Alerting threshold for concurrent calls | unset | Set to trunk capacity | Alerts at 90%; does not enforce a cap |
-MediaBypass |
Allow direct client↔SBC media | $false |
Enable per topology | SBC must also support it and share media subnet |
-ForwardPai |
Send P-Asserted-Identity | $false |
Carrier needs identity/billing | Needed for correct CLID on anonymous |
-ForwardCallHistory |
Send History-Info / Referred-By | $false |
Forward/transfer scenarios | Carrier sees the diversion chain |
-FailoverTimeSeconds |
Wait before trying next trunk | 10 | Slow networks only | Too high delays failover |
-FailoverResponseCodes |
SIP codes that trigger failover | 408, 503, 504 | Add carrier-specific codes | Wrong codes = no failover on real faults |
-PidfLoSupported |
Send PIDF-LO location XML | $false |
Emergency-egress gateway | Required for dynamic E911 over DR |
-MediaRelayRoutingLocationOverride |
Force media-relay region | unset | Fix suboptimal media path | Only with a real geography reason |
-GatewaySiteId |
Bind gateway to a network site | unset | Local media optimization | Ties into -BypassMode |
-BypassMode |
Always / OnlyForLocalUsers |
Always |
LMO topologies | Controls when bypass applies per site |
To drain an SBC for maintenance, set it disabled — existing calls survive, new calls route elsewhere:
Set-CsOnlinePSTNGateway -Identity sbc01.sip.contoso.com -Enabled $false
Step 3 — Validate the pairing and SIP health
Confirm the gateway is present and enabled before building anything on top of it. The output should show your FQDN with Enabled : True.
Get-CsOnlinePSTNGateway | Format-List Identity, Enabled, SipSignalingPort, MediaBypass, MaxConcurrentSessions, PidfLoSupported
Health monitoring rides on SIP OPTIONS. With SendSipOptions $true, the SBC health dashboard in the Teams admin center (Voice → Direct Routing) shows TLS connectivity, SIP OPTIONS status, and concurrent-call counts per gateway. If a freshly paired SBC shows no OPTIONS traffic, the cause is almost always the certificate chain, a firewall blocking 5061 inbound, or the SBC’s TLS context not trusting Microsoft’s root CAs. The dashboard lags real time by a few hours.
The health signals and what each state means:
| Signal | Healthy value | Unhealthy cause | Where to confirm |
|---|---|---|---|
| TLS connectivity | Connected | Bad chain, expired cert, wrong port | SBC health dashboard; SBC TLS logs |
| SIP OPTIONS | Active (200 OK both ways) | Firewall on 5061, OPTIONS disabled | Dashboard; SendSipOptions on gateway |
| Concurrent calls | Below MaxConcurrentSessions |
Trunk saturated | Dashboard call-count tile |
Gateway Enabled |
True | Created without -Enabled $true |
Get-CsOnlinePSTNGateway |
Step 4 — PSTN usages, voice routes, and voice routing policies
The layering is precise. PSTN usages are named containers; voice routes match a dialed-number regex and point at gateways; voice routing policies are an ordered list of usages assigned to users. Build bottom-up.
The three objects, and how they relate:
| Object | Cmdlet family | Holds | References | Assigned to |
|---|---|---|---|---|
| PSTN usage | *-CsOnlinePstnUsage |
A list of name strings | Nothing | — (referenced by routes and policies) |
| Voice route | *-CsOnlineVoiceRoute |
NumberPattern, gateways, usages, priority |
Usages + gateways | — |
| Voice routing policy | *-CsOnlineVoiceRoutingPolicy |
An ordered list of usages | Usages | Users (Grant-) |
4a. Create PSTN usages
Usages are strings on the global object. Order inside a policy is what matters later, so name them by scope.
Set-CsOnlinePstnUsage -Identity Global -Usage @{Add="US and Canada"}
Set-CsOnlinePstnUsage -Identity Global -Usage @{Add="International"}
Set-CsOnlinePstnUsage -Identity Global -Usage @{Add="USE911"}
# Confirm (the list can be truncated; expand it):
(Get-CsOnlinePstnUsage).Usage
4b. Create voice routes
A route ties a NumberPattern (a .NET regex against the called number in E.164) to a OnlinePstnGatewayList. Lower Priority wins; SBCs within a route are tried in random order. Build an active route, a backup at lower priority, and a catch-all.
# Active route for Seattle area codes -> primary SBCs
New-CsOnlineVoiceRoute -Identity "Redmond 1" `
-NumberPattern "^\+1(425|206)(\d{7})$" `
-OnlinePstnGatewayList sbc01.sip.contoso.com, sbc02.sip.contoso.com `
-Priority 1 -OnlinePstnUsages "US and Canada"
# Backup route, same pattern, secondary SBCs
New-CsOnlineVoiceRoute -Identity "Redmond 2" `
-NumberPattern "^\+1(425|206)(\d{7})$" `
-OnlinePstnGatewayList sbc03.sip.contoso.com, sbc04.sip.contoso.com `
-Priority 2 -OnlinePstnUsages "US and Canada"
# Catch-all for the rest of US/Canada
New-CsOnlineVoiceRoute -Identity "Other +1" `
-NumberPattern "^\+1(\d{10})$" `
-OnlinePstnGatewayList sbc01.sip.contoso.com, sbc02.sip.contoso.com `
-Priority 3 -OnlinePstnUsages "US and Canada"
# Everything else (international), its own usage
New-CsOnlineVoiceRoute -Identity "International" `
-NumberPattern ".*" `
-OnlinePstnGatewayList sbc01.sip.contoso.com, sbc02.sip.contoso.com `
-Priority 4 -OnlinePstnUsages "International"
The voice-route fields and how they behave:
| Field | Meaning | Selection rule | Common mistake |
|---|---|---|---|
NumberPattern |
.NET regex on the E.164 called number | Must match to be eligible | Unanchored / greedy .* swallowing specifics |
Priority |
Order among routes in a usage | Lowest number wins | Backup given a lower priority than active |
OnlinePstnGatewayList |
SBCs this route uses | Tried in random order within the route | Assuming a first-listed gateway is preferred |
OnlinePstnUsages |
Usages this route belongs to | Links route → policy | Route not attached to any used usage |
Two failure modes to internalize. An invalid regex in NumberPattern silently fails to match — test patterns against real numbers first. And the extension is stripped before matching: +14255550100;ext=123 matches as +14255550100.
4c. Create and order voice routing policies
The policy is an ordered list of usages. Usages are evaluated top to bottom and the first match wins — later usages are never consulted. Put the most specific usage first.
# US-only users: one usage
New-CsOnlineVoiceRoutingPolicy "US Only" -OnlinePstnUsages "US and Canada"
# Unrestricted users: US/Canada FIRST so Redmond special-handling applies,
# then International as the fallthrough.
New-CsOnlineVoiceRoutingPolicy "No Restrictions" -OnlinePstnUsages "US and Canada", "International"
Never edit the global (Org-wide default) voice routing policy unless you intend every voice-enabled user — including Calling Plan and Operator Connect users — to inherit it. Doing so can silently divert their PSTN calls down your SBC. Always assign custom policies to the users who should use Direct Routing.
To reorder usages in an existing policy, replace the whole ordered set:
Set-CsOnlineVoiceRoutingPolicy -Identity "No Restrictions" `
-OnlinePstnUsages @{Replace="US and Canada","International"}
How the whole selection resolves at call time, step by step:
| Step | What Teams evaluates | Input | Output |
|---|---|---|---|
| 1 | Tenant dial plan normalization | Dialed string | E.164 number |
| 2 | User’s voice routing policy | E.164 + policy | Ordered usage list |
| 3 | First usage whose routes match | Usage’s routes vs number | Candidate route set |
| 4 | Lowest-Priority matching route |
Route priorities | The chosen route |
| 5 | Random gateway in the route | Route’s gateway list | The egress SBC |
| 6 | Fall through if no gateway healthy | Failover codes/time | Next route / usage |
Step 5 — Assign number, Enterprise Voice, and the voice routing policy
A user needs three things to place a Direct Routing call: a number, Enterprise Voice enabled, and a voice routing policy. Set-CsPhoneNumberAssignment -PhoneNumberType DirectRouting does the first two at once — assigning a number automatically flips EnterpriseVoiceEnabled to true.
# Assign a Direct Routing DID; EnterpriseVoiceEnabled becomes True automatically
Set-CsPhoneNumberAssignment `
-Identity user1@contoso.com `
-PhoneNumber "+14255550123" `
-PhoneNumberType DirectRouting
# Assign the voice routing policy
Grant-CsOnlineVoiceRoutingPolicy -Identity user1@contoso.com -PolicyName "US Only"
If you need to enable Enterprise Voice without (yet) assigning a number — for example a resource account — use the attribute parameter set, which is mutually exclusive with -PhoneNumber:
Set-CsPhoneNumberAssignment -Identity cafe-aa@contoso.com -EnterpriseVoiceEnabled $true
Direct Routing numbers support extensions natively, which is how you map a single trunk DID to a PBX-style range:
Set-CsPhoneNumberAssignment -Identity user2@contoso.com `
-PhoneNumber "+14255550100;ext=1234" -PhoneNumberType DirectRouting
The PhoneNumberType values and what each implies:
PhoneNumberType |
Trunk | EnterpriseVoiceEnabled side-effect |
Notes |
|---|---|---|---|
DirectRouting |
Your SBC | Set to $true on number assignment |
The subject of this article |
CallingPlan |
Microsoft | Set to $true |
Number must be acquired from Microsoft first |
OperatorConnect |
Partner carrier | Set to $true |
Number provisioned by the operator |
| (attribute set, no number) | — | Set explicitly via -EnterpriseVoiceEnabled |
For resource accounts / staged enablement |
Step 6 — Tenant dial plans: normalization rules
A dial plan converts what a human dials (a 5-digit extension, a 7-digit local number, a 9 for an outside line) into E.164 so a voice route can match it. Normalization rules are ordered Pattern/Translation pairs; Teams walks them top-down and stops at the first match. Build them in memory, then attach to a dial plan.
# 5-digit internal extension dialing -> full E.164
$ext = New-CsVoiceNormalizationRule -Parent Global `
-Description "5-digit internal extension" `
-Pattern '^(\d{5})$' -Translation '+1425555$1' `
-Name "Internal5" -IsInternalExtension $true -InMemory
# Local 7-digit dialing in the 425 area -> E.164
$local = New-CsVoiceNormalizationRule -Parent Global `
-Description "Local 7-digit" `
-Pattern '^(\d{7})$' -Translation '+1425$1' `
-Name "Local7" -InMemory
# National: a leading 9 then 1+10 digits -> strip the 9, add +
$national = New-CsVoiceNormalizationRule -Parent Global `
-Description "Outside line + national" `
-Pattern '^9(1\d{10})$' -Translation '+$1' `
-Name "Outside9National" -InMemory
New-CsTenantDialPlan -Identity "Seattle-HQ" `
-Description "Seattle HQ dial plan" `
-SimpleName "Seattle-HQ" `
-NormalizationRules @($ext, $local, $national)
Grant-CsTenantDialPlan -Identity user1@contoso.com -PolicyName "Seattle-HQ"
The dial-plan mechanics you have to keep straight:
| Concept | Rule | Consequence if violated |
|---|---|---|
| Effective dial plan | Tenant dial plan merges with the service-country dial plan | Unexpected extra rules apply from the base |
| Rule order | Top-down, first match stops | A broad rule above a specific one swallows it |
| Output format | Always emit a leading + (E.164) |
Non-+ output triggers a second normalization pass |
| Extension | ;ext= preserved through routing, stripped for match |
Route regex must match the bare E.164 |
| Internal flag | -IsInternalExtension marks intra-org dialing |
Mislabeling affects reporting/handling |
Two habits. Always normalize to a leading +. A rule that emits a number without + triggers a second normalization pass against tenant/regional rules — double normalization with surprising results. If the carrier needs + removed on the wire, do it at the trunk with OutboundPstnNumberTranslationRules on the gateway, not in the dial plan. Order restrictive rules above permissive ones — a .* rule before ^(\d{5})$ would swallow extensions.
Test the effective plan against real dialed strings before you let a user near it:
Test-CsEffectiveTenantDialPlan -DialedNumber 42555 -Identity user1@contoso.com
Test-CsEffectiveTenantDialPlan -DialedNumber 5551234 -Identity user1@contoso.com
Test-CsEffectiveTenantDialPlan -DialedNumber 914255550123 -Identity user1@contoso.com
Step 7 — Media bypass and local media optimization
By default, media between the Teams client and the SBC traverses Microsoft’s media processors in the cloud — signaling and media both go up to Microsoft and back down. Media bypass removes the media hop: RTP/SRTP flows directly between the Teams client and the SBC while signaling still goes through the Microsoft SIP proxy. Local media optimization extends this to multi-site topologies: signaling anchors on a central proxy SBC, but media is kept on a downstream SBC local to the user’s network site.
Enable media bypass on the gateway and in the media-bypass configuration (both the SBC and the platform must agree):
# Turn media bypass on for the gateway
Set-CsOnlinePSTNGateway -Identity sbc01.sip.contoso.com -MediaBypass $true
# Enable tenant media-bypass configuration
Set-CsTeamsMediaConnectivityPolicy -Identity Global -DirectConnection Enabled
Media bypass has real prerequisites — it is not a free toggle:
| Prerequisite | Requirement | Why |
|---|---|---|
| SBC support | The SBC vendor/firmware must support Teams media bypass | Direct SRTP negotiation with the client |
| Same media path | Client and SBC must be able to reach each other’s media IPs | RTP goes direct; no cloud relay to bridge |
| ICE / relay | Media candidates negotiated via ICE; a relay is a fallback | NAT traversal for the direct path |
| Public IP visibility | SBC media reachable from client networks (or via relay) | Otherwise the direct path fails and one-way audio results |
| Gateway flag | -MediaBypass $true on the PSTN gateway |
Platform must know to attempt bypass |
When each media mode is right:
| Mode | Media path | Use when | Cost |
|---|---|---|---|
| Default (no bypass) | Client ↔ Microsoft media processor ↔ SBC | Simple topology; SBC not on the client network path | Extra hop, added latency, cloud egress |
| Media bypass | Client ↔ SBC (direct SRTP) | SBC reachable from client networks; latency/compliance matters | Direct path must be reachable; harder to trace |
| Local media optimization | Media stays on a local downstream SBC; signaling central | Many sites behind one central proxy SBC | Most complex; needs network sites + topology |
Local media optimization requires network topology to be defined — network sites, subnets, and the gateway-to-site binding — plus -BypassMode and -GatewaySiteId on the gateways:
# Bind a downstream SBC to a branch site and keep media local
Set-CsOnlinePSTNGateway -Identity sbc-branch01.sip.contoso.com `
-GatewaySiteId "Branch-London" -BypassMode "OnlyForLocalUsers"
BypassMode = OnlyForLocalUsers keeps bypass to users at the same site as the gateway; Always attempts bypass regardless of site. LMO’s payoff is a media path that never leaves the branch even when the signaling SBC is central — critical for latency-sensitive contact-center audio and for keeping media on-net for compliance.
Step 8 — Emergency calling and E911 routing
Emergency calling earns its operational scrutiny. Two policy types do different jobs: emergency call routing decides which numbers are emergency numbers and which trunk they take; emergency calling decides who gets notified. For dynamic location, you wire the Location Information Service (LIS) to network identifiers. In the US, Kari’s Law (direct 911 dialing, no prefix; on-site notification) and RAY BAUM’S Act (a dispatchable location — building, floor, room — must reach the dispatcher) make this a compliance requirement, not a nicety.
Create an emergency number object and bind it to a routing policy. The dial mask lets users dial a test string (933) that the system treats as 911. The OnlinePSTNUsage must already exist and point at the route to your emergency-capable trunk.
# Emergency number: dial 911 (or 933 as a test mask) out the USE911 usage
$en = New-CsTeamsEmergencyNumber -EmergencyDialString "911" `
-EmergencyDialMask "933" -OnlinePSTNUsage "USE911"
New-CsTeamsEmergencyCallRoutingPolicy -Identity "US-E911" `
-EmergencyNumbers @{add=$en} `
-AllowEnhancedEmergencyServices:$true `
-Description "US dynamic E911"
Grant-CsTeamsEmergencyCallRoutingPolicy -Identity user1@contoso.com -PolicyName "US-E911"
The emergency calling policy (distinct from routing) controls notification — who is told when someone dials 911, and whether external routing to a security desk happens:
New-CsTeamsEmergencyCallingPolicy -Identity "HQ-Notify" `
-NotificationMode NotificationOnly `
-NotificationGroup "security@contoso.com" `
-Description "Notify HQ security on any 911"
Grant-CsTeamsEmergencyCallingPolicy -Identity user1@contoso.com -PolicyName "HQ-Notify"
The two emergency policies compared — they are not interchangeable:
| Aspect | Emergency call routing policy | Emergency calling policy |
|---|---|---|
| Cmdlet | *-CsTeamsEmergencyCallRoutingPolicy |
*-CsTeamsEmergencyCallingPolicy |
| Decides | Which strings are emergency + their PSTN usage | Who is notified and how |
| Key fields | EmergencyNumbers, AllowEnhancedEmergencyServices |
NotificationMode, NotificationGroup, ExternalLocationLookupMode |
| DR-specific | Points 911 at an emergency-capable usage/trunk | Notification, conferencing in security |
| Applies to | Direct Routing (routing side) | All PSTN options |
For dynamic (location-aware) E911, the Teams client reports its network position and LIS returns a civic address. Associate emergency locations with network identifiers — subnet is the most common, but LIS supports several:
# Map an internal subnet to a previously created emergency location
$loc = Get-CsOnlineLisLocation -City Seattle
Set-CsOnlineLisSubnet -Subnet 10.20.30.0 -LocationId $loc.LocationId `
-Description "Seattle HQ Floor 3 - 10.20.30.0/24"
The LIS network identifiers you can map, from most to least common:
| Identifier | Cmdlet | Granularity | Use when |
|---|---|---|---|
| Subnet | Set-CsOnlineLisSubnet |
Per IP subnet | The default; matches wired/wireless subnets |
| Wireless access point (WAP) | Set-CsOnlineLisWirelessAccessPoint |
Per WAP BSSID | Floor/zone precision on Wi-Fi |
| Switch (chassis) | Set-CsOnlineLisSwitch |
Per switch | A whole switch = a location |
| Switch port | Set-CsOnlineLisPort |
Per port | Desk-level precision on wired |
Two Direct-Routing-specific requirements. Set -PidfLoSupported $true on the emergency-egress gateway so Teams sends the PIDF-LO XML location payload to the SBC, which relays it to the emergency service provider:
Set-CsOnlinePSTNGateway -Identity sbc01.sip.contoso.com -PidfLoSupported $true
Second, for users who never move you can attach a static location to a DID by passing -LocationId to Set-CsPhoneNumberAssignment. Dynamic LIS lookups override the static value when a network match exists.
The dynamic-E911 pieces that must all line up, or the call reaches the wrong dispatcher:
| Piece | Cmdlet / setting | If missing… |
|---|---|---|
| Emergency addresses & locations | New-CsOnlineLisCivicAddress / ...LisLocation |
No civic address to send |
| Network-ID → location map | Set-CsOnlineLisSubnet etc. |
Location stays static / wrong |
| Emergency call routing policy | New-CsTeamsEmergencyCallRoutingPolicy |
911 not recognized / no trunk |
| Emergency calling policy | New-CsTeamsEmergencyCallingPolicy |
No on-site notification |
| Gateway PIDF-LO | -PidfLoSupported $true |
Location XML never reaches the SBC/provider |
| Emergency provider / ELIN | Carrier / provider config | Address not honored on the PSTN side |
| Test with the dial mask | EmergencyDialMask "933" |
You never validate before an emergency |
Step 9 — Survivability: the Survivable Branch Appliance (SBA)
Teams Phone is cloud-dependent: if a branch loses its WAN link to Microsoft, users at that branch lose calling — unless you deploy a Survivable Branch Appliance. The SBA is a supported application that runs on a certified SBC at the branch; when the connection to the Teams cloud is down, users’ Teams clients register to the local SBA and can still make and receive PSTN calls over the local trunk, place internal calls to others at the same branch, and access basic voicemail-to-PSTN behavior. When connectivity returns, clients re-register to the cloud automatically.
You register the SBA and assign a survivability policy pointing users at it:
# Assign the branch SBA via a Teams survivable branch appliance policy
New-CsTeamsSurvivableBranchAppliancePolicy -Identity "London-SBA" `
-SurvivableBranchAppliance @{add="sba-london.sip.contoso.com"}
Grant-CsTeamsSurvivableBranchAppliancePolicy -Identity user1@contoso.com `
-PolicyName "London-SBA"
What survives a WAN outage with an SBA, and what does not:
| Capability during outage | With SBA | Without SBA |
|---|---|---|
| Outbound PSTN from the branch | Yes (local trunk) | No |
| Inbound PSTN to branch users | Yes | No |
| Calls between users at the same branch | Yes | No |
| Calls to users at other sites / cloud | No (WAN down) | No |
| Presence, chat, meetings | No (cloud features) | No |
| Auto-reconnect on WAN restore | Automatic | N/A |
The SBA is a resilience layer, not a full PBX — meetings, chat, presence and cross-site calling all still require the cloud. Its job is to keep the branch dialing 911 and taking customer calls through an ISP outage.
Step 10 — Monitoring: Call Analytics and the Call Quality Dashboard
Two tools answer two different questions. Call Analytics (Teams admin center → Users → a user → Call history) is per-call: it shows one call leg by leg — client, network, media path, egress SBC, codec, jitter, packet loss, round-trip time — and is your first stop for “this one call was bad.” The Call Quality Dashboard (CQD) is the tenant-wide aggregate: it trends media quality across all calls so you can spot a single SBC site degrading over a week, a subnet with chronic packet loss, or a Wi-Fi segment that hurts calls. Call Analytics is the microscope; CQD is the map.
The two tools compared:
| Aspect | Call Analytics | Call Quality Dashboard (CQD) |
|---|---|---|
| Scope | One call / one user | Tenant-wide aggregate |
| Question it answers | “Why was this call bad?” | “Which site/subnet/SBC trends bad?” |
| Latency to data | Near-real-time (minutes) | Hours (aggregated) |
| Best for | Reactive, single-incident triage | Proactive quality management |
| Key data | Per-leg jitter, loss, RTT, codec, path | Poor-call %, by building/subnet/endpoint |
| Setup | On by default | Upload a building/subnet map for full value |
The media-quality thresholds Teams uses to classify a call as “poor” — know these so a metric reading is actionable:
| Metric | Good target | “Poor” threshold (indicative) | Typical cause when exceeded |
|---|---|---|---|
| Packet loss | < 1% | > 10% (or ~5% average) | Congestion, Wi-Fi, bad uplink |
| Jitter | < 30 ms | > 30 ms average | Buffering, variable path, QoS missing |
| Round-trip time | < 100 ms | > 500 ms | Distance, no media bypass, routing detours |
| Ratio concealed samples | low | elevated | Loss/jitter forcing audio concealment |
CQD’s value multiplies when you upload a building/subnet map so poor calls attribute to a named site rather than a raw subnet. Feed CQD your subnet-to-building CSV and the reports become “the London 3rd-floor Wi-Fi has 8% poor calls” instead of “10.20.30.0/24 has 8% poor calls.”
Architecture at a glance
Picture the call as it flows, left to right, because the platform evaluates it in exactly this order. A user dials a string — say 9-1-425-555-0123. It first meets the user’s tenant dial plan, whose ordered normalization rules strip the 9 and produce the E.164 number +14255550123. That number is handed to the user’s online voice routing policy, an ordered list of PSTN usages; Teams walks the list top-to-bottom and stops at the first usage whose voice routes contain a matching NumberPattern. Within that usage it picks the lowest-Priority matching route, and within the route it selects one of the listed PSTN gateways (SBCs) at random. Signaling then travels over TLS on port 5061 from Microsoft’s SIP proxy (sip.pstnhub.microsoft.com, tried first) to the chosen SBC, which relays the SIP INVITE onto the carrier trunk and out to the PSTN.
Media takes one of three routes depending on your configuration — the part worth visualizing separately from signaling. In the default case, RTP/SRTP goes from the client up to a Microsoft media processor and back down to the SBC; signaling and media both transit the cloud. With media bypass, signaling still goes through the Microsoft proxy but media flows directly between the client and the SBC. With local media optimization, a central proxy SBC anchors signaling while a downstream SBC local to the branch keeps media on-net. Overlaid on this path are two life-safety/resilience elements: an emergency call (911, matched by the emergency call routing policy) is steered to an emergency-capable usage and trunk with a PIDF-LO payload derived from LIS matching; and if the branch WAN fails, the client re-registers to a local Survivable Branch Appliance so basic PSTN calling continues. Two instruments watch it all: Call Analytics for a single call’s leg-by-leg detail, and the Call Quality Dashboard for the tenant-wide trend. The model to hold: dialing, routing and signaling always follow the fixed chain — dial plan → usage → route → gateway → carrier — while media is the flexible part you place with bypass and LMO, and emergency and survivability are cross-cutting overlays on the same trunk.
Real-world scenario
A multinational with 9,000 seats consolidated six regional PBXs onto Teams Phone. North America moved to Calling Plans cleanly, but their EMEA contact center sat behind a Genesys platform reachable only over an existing SIP trunk, and the carrier contract had three years left. They chose Direct Routing for EMEA and hit a sharp constraint: contact-center agents must reach internal Genesys queues and the external PSTN, but compliance required that all PSTN calls keep the trunk inside the corporate network — no traffic could traverse the public internet to a Microsoft media processor and back.
Two design decisions solved it. First, they kept Calling Plan and Direct Routing on the same agent users: a voice routing policy whose most-specific usage matched the internal Genesys number ranges routed those calls down the SBC (staying on-net), while everything else fell through to the Calling Plan. Because the SBC pattern was evaluated first and the Calling Plan applies as the implicit last route, no extra “international” route was needed for agents. Second, to keep PSTN media on-net, they enabled media bypass so RTP flowed directly between the Teams client and the SBC rather than through Microsoft’s media processors — and for the London and Frankfurt branches, they layered local media optimization so media stayed on the branch’s downstream SBC even though signaling anchored on a central proxy SBC in the primary data center.
The ordering of usages was the make-or-break detail. Putting the Genesys usage first guaranteed on-net handling for queue calls:
# Genesys internal ranges FIRST -> on-net via SBC; PSTN falls through to Calling Plan
New-CsOnlineVoiceRoute -Identity "Genesys-Queues" `
-NumberPattern "^\+44(20)(7\d{6})$" `
-OnlinePstnGatewayList sbc-emea01.sip.contoso.com `
-Priority 1 -OnlinePstnUsages "EMEA-Internal"
New-CsOnlineVoiceRoutingPolicy "EMEA-Agents" -OnlinePstnUsages "EMEA-Internal"
Then the incident that proved the design. Three weeks after go-live, the Frankfurt site reported that outbound calls to German mobiles (+49 15x…) were failing with a fast-busy about 20% of the time, always at peak. The on-call reflex — “the SBC is down” — was wrong: the SBC health dashboard was green. Call Analytics on a failed call showed the SIP ladder reaching the SBC and the SBC returning a 503 Service Unavailable back toward Microsoft. The 503 was the carrier trunk saturating: the German mobile route pointed at a single SBC whose MaxConcurrentSessions had been set to 150 to match a trunk that the carrier had, unknown to the Teams team, downgraded to 100 channels. Under peak the trunk hit its real ceiling, the carrier 503’d, and because the backup route was authored at a higher Priority number but pointed at the same saturated SBC, failover did nothing.
The fix was two-part. Immediately: correct MaxConcurrentSessions to the real 100 so alerting fired at 90 channels before users felt it, and re-point the backup route (Priority 2) at a genuinely independent SBC on a second carrier trunk so failover had somewhere to go. Structurally: split the German mobile pattern into its own PSTN usage so its capacity was reasoned about separately, and add a CQD building-map entry for Frankfurt so the next capacity problem showed as a site-level trend a week early. The runbook line: “A green SBC and a failing call means read the SIP ladder — the 503 is the trunk, not the box. And a backup route that points at the same SBC is not a backup.” With mixed connectivity, voice routing policy order — and route priority — is the control plane; a transposed usage or a mis-numbered priority breaks the compliance boundary or the failover, silently.
Advantages and disadvantages
Direct Routing’s whole value is control, and control cuts both ways. Weigh it honestly:
| Advantages (why you choose it) | Disadvantages (why it costs you) |
|---|---|
| You own the trunk — reuse an existing carrier contract and its economics | You operate the SBC, certificates, firewall and routing — real telco ops |
| Reaches anything: analog paging, PBX, contact center, any SIP carrier | Every layer is yours to get right; failures are quiet, not loud |
| Works in countries with no Calling Plan / Operator Connect coverage | Setup is the slowest of the three options (build + certify + validate) |
| Full routing control: dial plans, normalization, per-pattern routing | Misordered usages / mis-numbered priorities misroute calls silently |
| Media bypass and LMO keep media on-net for latency and compliance | Media bypass complicates troubleshooting (fewer central logs) |
| Survivability via SBA keeps branches calling through a WAN outage | SBA is extra hardware/config and only covers basic calling |
| Coexists with Calling Plans/Operator Connect on the same user | Certificate expiry silently kills the whole trunk if unmonitored |
Direct Routing is right when a managed option cannot reach what you need — a contact center, an incumbent carrier contract, an uncovered country, or a compliance rule about media locality. It is the wrong default when Microsoft or an operator sells numbers where you operate and your call patterns are simple: then Calling Plans or Operator Connect give you 90% of the value with a fraction of the operational surface. The disadvantages are all manageable — but only if you know they exist and monitor for them, which is the point of this article.
Hands-on lab
You cannot pair a real SBC without hardware and a public certificate, so this lab builds and validates the entire routing plane against your tenant using PowerShell — the part that most often hides bugs — then tears it down cleanly. It creates a lab PSTN gateway object (an FQDN under a domain you control), the usages/routes/policies on top of it, and a dial plan, and validates each with the built-in test cmdlets. Run in a PowerShell session connected with Connect-MicrosoftTeams as a Teams Administrator.
Step 1 — Connect and confirm your domains. The gateway FQDN must sit under a verified tenant domain.
Connect-MicrosoftTeams
(Get-CsTenant).Domains
Expected: a list of your verified domains. Pick one (e.g. sip.contoso.com) for the lab FQDN.
Step 2 — Create a lab PSTN gateway (kept disabled). Creating it disabled means it never takes real calls — safe for a lab.
New-CsOnlinePSTNGateway -Fqdn lab-sbc.sip.contoso.com `
-SipSignalingPort 5061 -Enabled $false `
-MaxConcurrentSessions 50 -Description "LAB - do not enable"
Get-CsOnlinePSTNGateway -Identity lab-sbc.sip.contoso.com |
Format-List Identity, Enabled, SipSignalingPort, MaxConcurrentSessions
Expected: the gateway listed with Enabled : False.
Step 3 — Create PSTN usages.
Set-CsOnlinePstnUsage -Identity Global -Usage @{Add="LAB-USCA"}
Set-CsOnlinePstnUsage -Identity Global -Usage @{Add="LAB-Intl"}
(Get-CsOnlinePstnUsage).Usage | Where-Object { $_ -like "LAB-*" }
Expected: LAB-USCA and LAB-Intl appear in the list.
Step 4 — Create voice routes (active + catch-all + international).
New-CsOnlineVoiceRoute -Identity "LAB-Redmond" `
-NumberPattern "^\+1(425|206)(\d{7})$" `
-OnlinePstnGatewayList lab-sbc.sip.contoso.com `
-Priority 1 -OnlinePstnUsages "LAB-USCA"
New-CsOnlineVoiceRoute -Identity "LAB-OtherUS" `
-NumberPattern "^\+1(\d{10})$" `
-OnlinePstnGatewayList lab-sbc.sip.contoso.com `
-Priority 2 -OnlinePstnUsages "LAB-USCA"
New-CsOnlineVoiceRoute -Identity "LAB-Intl" `
-NumberPattern ".*" `
-OnlinePstnGatewayList lab-sbc.sip.contoso.com `
-Priority 3 -OnlinePstnUsages "LAB-Intl"
Get-CsOnlineVoiceRoute | Where-Object Identity -like "LAB-*" |
Sort-Object Priority | Format-Table Identity, Priority, NumberPattern, OnlinePstnUsages
Expected: three routes, priorities 1–3, in order.
Step 5 — Create the voice routing policy (usage order matters).
New-CsOnlineVoiceRoutingPolicy "LAB-NoRestrictions" `
-OnlinePstnUsages "LAB-USCA", "LAB-Intl"
Get-CsOnlineVoiceRoutingPolicy -Identity "LAB-NoRestrictions" |
Format-List Identity, OnlinePstnUsages
Expected: the policy with LAB-USCA listed before LAB-Intl.
Step 6 — Create and test a dial plan.
$labExt = New-CsVoiceNormalizationRule -Parent Global -Name "LABInternal5" `
-Pattern '^(\d{5})$' -Translation '+1425555$1' -IsInternalExtension $true -InMemory
$labNat = New-CsVoiceNormalizationRule -Parent Global -Name "LABOutside9" `
-Pattern '^9(1\d{10})$' -Translation '+$1' -InMemory
New-CsTenantDialPlan -Identity "LAB-DialPlan" -SimpleName "LAB-DialPlan" `
-NormalizationRules @($labExt, $labNat)
# Validate normalization against the tenant dial plan (no user needed for the tenant scope)
Test-CsEffectiveTenantDialPlan -DialedNumber 55123 -TenantDialPlan "LAB-DialPlan"
Test-CsEffectiveTenantDialPlan -DialedNumber 914255550123 -TenantDialPlan "LAB-DialPlan"
Expected: 55123 normalizes to +1425555512... shape (per your translation) and 914255550123 normalizes to +14255550123, both matched by the expected rule.
Step 7 — (Optional) assign to a test user and verify end-to-end config. Only if you have a spare, licensed, non-production test user.
$u = "labtest@contoso.com"
Grant-CsOnlineVoiceRoutingPolicy -Identity $u -PolicyName "LAB-NoRestrictions"
Grant-CsTenantDialPlan -Identity $u -PolicyName "LAB-DialPlan"
Get-CsOnlineUser $u |
Format-List UserPrincipalName, EnterpriseVoiceEnabled, OnlineVoiceRoutingPolicy, TenantDialPlan
Expected: the user shows OnlineVoiceRoutingPolicy : LAB-NoRestrictions and TenantDialPlan : LAB-DialPlan.
Validation checklist. You built the full routing plane — gateway, usages, routes, policy, dial plan — and validated normalization with Test-CsEffectiveTenantDialPlan without placing a real call or enabling a live trunk. The lab steps mapped to what each proves:
| Step | What you did | What it proves |
|---|---|---|
| 2 | Create a disabled gateway | Pairing object exists; safe (no calls) |
| 3 | Add PSTN usages | The containers routes/policies reference |
| 4 | Add prioritized routes | Regex matching and priority ordering work |
| 5 | Create the ordered policy | Usage order is captured as authored |
| 6 | Create + test the dial plan | Normalization produces the E.164 you expect |
| 7 | Assign to a test user | The user picks up the policy and dial plan |
Teardown (remove every lab object).
# Unassign from the test user first if you did Step 7
Grant-CsOnlineVoiceRoutingPolicy -Identity $u -PolicyName $null
Grant-CsTenantDialPlan -Identity $u -PolicyName $null
Remove-CsTenantDialPlan -Identity "LAB-DialPlan"
Remove-CsOnlineVoiceRoutingPolicy -Identity "LAB-NoRestrictions"
Get-CsOnlineVoiceRoute | Where-Object Identity -like "LAB-*" |
ForEach-Object { Remove-CsOnlineVoiceRoute -Identity $_.Identity }
Set-CsOnlinePstnUsage -Identity Global -Usage @{Remove="LAB-USCA"}
Set-CsOnlinePstnUsage -Identity Global -Usage @{Remove="LAB-Intl"}
Remove-CsOnlinePSTNGateway -Identity lab-sbc.sip.contoso.com
Cost note. This lab creates only configuration objects in your tenant — there is no per-hour Azure charge and no PSTN usage. The only prerequisite cost is the Teams Phone license already assigned to any test user in Step 7.
Common mistakes & troubleshooting
Telephony fails quietly, so the playbook is about reading the right signal. First as a scannable table you keep open mid-incident, then the detail on the entries that bite hardest.
| # | Symptom | Root cause | Confirm (exact cmd / portal path) | Fix |
|---|---|---|---|---|
| 1 | SBC never healthy; no call completes | Cert chain not trusted / missing intermediate / wrong CN | SBC health dashboard (blank); SBC TLS logs; verify chain | Full public-CA chain, CN/SAN = FQDN, CA in MS Trusted Root |
| 2 | Gateway paired but no calls route | Gateway created without -Enabled $true |
Get-CsOnlinePSTNGateway | fl Enabled → False |
Set-CsOnlinePSTNGateway -Enabled $true |
| 3 | No SIP OPTIONS on the dashboard | Firewall blocking 5061 inbound, or SendSipOptions $false |
Dashboard OPTIONS state; firewall rules; gateway flag | Open TCP 5061 from 52.112/14+52.120/14; SendSipOptions $true |
| 4 | Outbound call fails, fast-busy | No matching voice route / bad NumberPattern regex |
Get-CsOnlineVoiceRoute; test the regex on the E.164 |
Fix/anchor the pattern; add a catch-all route |
| 5 | Calls take the wrong trunk | Usage order in the policy (first match wins) | Get-CsOnlineVoiceRoutingPolicy | fl OnlinePstnUsages |
Reorder usages most-specific-first |
| 6 | Failover doesn’t happen when SBC saturates | Backup route higher Priority number but same SBC, or wrong failover codes |
Get-CsOnlineVoiceRoute | sort Priority; gateway FailoverResponseCodes |
Point backup at an independent SBC; add carrier 503 to failover codes |
| 7 | Call connects, but one-way / no audio | Media ports / NAT on the SBC; bypass path unreachable | Call Analytics per-leg media; SBC media logs | Open media ranges; fix NAT/SBC public media IP; verify bypass reachability |
| 8 | Call rejected with 488 Not Acceptable Here | Codec mismatch between Teams and SBC/carrier | SIP ladder in Call Analytics / SBC logs | Enable a Teams-supported codec (e.g. G.722, OPUS) on the SBC |
| 9 | User has no dial pad | Not voice-enabled / no routing policy / no license | Get-CsOnlineUser | fl EnterpriseVoiceEnabled, OnlineVoiceRoutingPolicy |
Assign number (sets EV true) + voice routing policy + license |
| 10 | 911 reaches the wrong dispatcher | LIS not mapped / no dynamic location / no PIDF-LO | Get-CsOnlineLisSubnet; gateway PidfLoSupported |
Map subnet→location; -PidfLoSupported $true; test via 933 mask |
| 11 | Inbound calls fail; outbound fine | Carrier not sending to the right SBC / number not assigned | Carrier logs; Get-CsPhoneNumberAssignment |
Fix carrier routing to SBC; assign the DID to the user |
| 12 | Trunk dead tenant-wide overnight | Certificate expired | SBC cert validity; dashboard TLS down | Renew cert; add expiry monitoring/alerting |
| 13 | Some calls one-way after enabling media bypass | SBC media IP not reachable from client subnet | Call Analytics media path; bypass reachability test | Make SBC media reachable (or relay); or disable bypass for that site |
| 14 | Emergency test (933) does nothing | Emergency call routing policy not assigned / wrong usage | Get-CsTeamsEmergencyCallRoutingPolicy; user assignment |
Assign policy; point emergency number at an emergency-capable usage |
The expanded form for the entries that cause the most wasted hours:
1. SBC never healthy; no call ever completes. Root cause: the certificate chain is not trusted by Microsoft — a private/self-signed cert, a missing intermediate, an expired leaf, or a CN/SAN that doesn’t match the FQDN. Confirm: the SBC health dashboard (Voice → Direct Routing) shows the SBC blank or TLS-down; the SBC’s own TLS logs show the handshake failing; verify the leaf’s full chain roots in the Microsoft Trusted Root Certificate Program. Fix: install the full public-CA chain (leaf + all intermediates) on the SBC, with CN or SAN equal to the exact FQDN and EKU Server Authentication; re-test OPTIONS.
2. Gateway paired but nothing routes.
Root cause: New-CsOnlinePSTNGateway creates the gateway disabled by default and someone forgot -Enabled $true.
Confirm: Get-CsOnlinePSTNGateway | Format-List Identity, Enabled shows Enabled : False.
Fix: Set-CsOnlinePSTNGateway -Identity <fqdn> -Enabled $true.
4. Outbound call fails with fast-busy; SBC is healthy.
Root cause: no voice route matches the E.164 number — an over-specific or mis-anchored NumberPattern, or no catch-all — so the call has nowhere to go.
Confirm: Get-CsOnlineVoiceRoute | Sort-Object Priority | Format-Table Identity, Priority, NumberPattern, OnlinePstnUsages; test the regex against the real E.164 string.
Fix: correct/anchor the pattern (test it first), and ensure a lower-priority catch-all exists for the intended scope.
5. Calls take the wrong trunk.
Root cause: usage order in the voice routing policy — evaluation is first-match-wins, so a broad usage placed above a specific one wins.
Confirm: Get-CsOnlineVoiceRoutingPolicy -Identity <name> | Format-List OnlinePstnUsages and read the order.
Fix: Set-CsOnlineVoiceRoutingPolicy -OnlinePstnUsages @{Replace=<most-specific-first order>}.
7. Call connects but audio is one-way or absent.
Root cause: media (RTP/SRTP) is blocked or NAT’d — the media-processor ranges are closed (non-bypass), or with bypass the SBC’s media IP isn’t reachable from the client subnet.
Confirm: Call Analytics for the call shows the media leg with loss/no packets on one direction; the SBC media logs corroborate.
Fix: open the media ranges (3478–3481, 49152–53247) for non-bypass, fix SBC NAT/public media IP, or verify bypass reachability; disable bypass for the affected site as a fallback.
8. Call rejected with SIP 488. Root cause: the SBC/carrier and Teams share no offered codec — a 488 Not Acceptable Here is a codec negotiation failure. Confirm: the SIP ladder in Call Analytics or SBC logs shows the INVITE answered with 488. Fix: enable a Teams-supported codec on the SBC (Teams uses SILK/OPUS toward the client and commonly G.711/G.722 toward the trunk); align the SBC’s codec list.
10. A 911 call reaches the wrong dispatcher.
Root cause: dynamic location isn’t working — the caller’s subnet/WAP/switch isn’t mapped in LIS, or the egress gateway lacks -PidfLoSupported $true, so a static or default location is used.
Confirm: Get-CsOnlineLisSubnet for the caller’s subnet; Get-CsOnlinePSTNGateway | fl PidfLoSupported.
Fix: map the network identifier to a valid civic location, set -PidfLoSupported $true on the emergency-egress gateway, and validate with the 933 dial mask before relying on it.
The SIP response codes you will actually see on this path, and what each usually means here:
| SIP code | Meaning | Typical cause on Direct Routing | Where it comes from |
|---|---|---|---|
| 401 / 407 | Unauthorized / Proxy auth | Trunk registration/auth to the carrier | Carrier |
| 403 | Forbidden | Carrier rejects the number/CLID | Carrier |
| 404 | Not Found | Number not provisioned on the trunk | Carrier |
| 408 | Request Timeout | SBC unreachable / no answer in time | Network / SBC |
| 480 | Temporarily Unavailable | User offline / no survivable route | Teams / SBA |
| 486 | Busy Here | Called party busy | PSTN |
| 488 | Not Acceptable Here | Codec mismatch | SBC / carrier |
| 500 | Server Internal Error | SBC or cloud-side fault | SBC / Microsoft |
| 503 | Service Unavailable | Trunk saturated / SBC overloaded | SBC / carrier |
| 504 | Server Time-out | Downstream (carrier) timeout | Carrier |
Best practices
- Pair with a full public-CA chain and monitor its expiry. CN/SAN = the exact SBC FQDN, EKU Server Authentication, all intermediates installed, CA in the Microsoft Trusted Root Program. An expired cert is a silent tenant-wide outage — alert on it 30+ days out.
- Always create gateways with
-Enabled $trueand-SendSipOptions $true. Disabled gateways route nothing; without OPTIONS you have no health signal. - Set
MaxConcurrentSessionsto the real trunk capacity. It arms the 90% alert; a wrong value means you learn about saturation from users, not monitoring. - Author routes most-specific-first, and give backups a genuinely independent SBC. A backup route pointing at the same (saturated) SBC is not a backup; and remember lower
Prioritynumbers win. - Never edit the Org-wide default voice routing policy. Assign custom policies to the users who should use Direct Routing so you don’t divert Calling Plan / Operator Connect users by accident.
- Order usages in policies deliberately — order is the control plane. First match wins; a transposed usage misroutes calls silently. Review the order in code, not just in the portal.
- Normalize every dial-plan rule to a leading
+. Non-+output triggers a second normalization pass with surprising results; do trunk-side digit manipulation withOutboundPstnNumberTranslationRules, not the dial plan. - Test dial plans with
Test-CsEffectiveTenantDialPlanbefore assigning. Prove the E.164 output for extensions, local, national and outside-line dialing. - Wire dynamic E911 fully and test it with the dial mask. Map LIS network IDs, set
-PidfLoSupported $trueon the emergency gateway, and validate with933— a life-safety path is not “probably working.” - Deploy an SBA at branches that must survive a WAN outage. It keeps basic PSTN calling (including 911) alive when the cloud is unreachable.
- Decide media placement on purpose. Enable media bypass / LMO where latency or media-locality compliance demands it, and accept the added troubleshooting complexity; leave it off where the SBC isn’t on the client media path.
- Manage all voice configuration as versioned PowerShell. Gateways, usages, routes, policies and dial plans as reviewed scripts make the routing plane auditable and reproducible.
Security notes
- Trust is anchored on the certificate and the verified domain. Only a public-CA cert whose chain roots in the Microsoft Trusted Root Program, with CN/SAN matching the SBC FQDN under a domain you verified, is accepted. Treat the SBC’s private key like any TLS server key — protect it, rotate before expiry, and never move it to an untrusted host.
- Least privilege for administration. Grant the Teams Administrator role (or the finer Teams Communications roles) rather than Global Administrator to whoever manages voice; voice routing changes can redirect PSTN traffic and must be controlled and audited.
- Lock down the SBC’s signaling and media surface. Permit SIP only from Microsoft’s published subnets (
52.112.0.0/14,52.120.0.0/14) on 5061, and restrict media to the required ranges; an SBC exposed broadly to the internet is a toll-fraud and DoS target. - Guard against toll fraud with route scoping. Restrictive voice routing policies (e.g.
US Only) limit which destinations a compromised account can dial; don’t hand every user an unrestricted international policy by default. - Keep P-Asserted-Identity and history forwarding intentional.
ForwardPai/ForwardCallHistoryexpose identity and diversion data to the carrier — enable them for legitimate CLID/billing needs, and understand they leave your trust boundary. - Protect the emergency path from misconfiguration. A wrong LIS mapping is a life-safety failure; treat emergency-calling changes as high-risk, require review, and re-test with the dial mask after any change.
- Segregate and QoS-mark voice traffic. Placing media on a controlled network segment (see the micro-segmentation article) both protects it and keeps quality predictable; DSCP marking should be applied where the network honors it.
Cost & sizing
Direct Routing shifts cost from per-user Microsoft calling to your carrier and your own infrastructure — the trade you make for control.
- SBC infrastructure is the up-front and ongoing capital/operating cost: a physical or virtual certified SBC (redundant pair for production), its maintenance, and the certificate. A public-CA certificate is a small annual cost (roughly ₹5,000–20,000/year depending on CA and type) but its expiry is a catastrophic-if-missed item — budget the monitoring, not just the cert.
- Carrier SIP trunk is priced by channels (concurrent calls) and usage (per-minute or bundled). Size channels to peak concurrency, not user count — a 9,000-seat org rarely needs 9,000 channels; busy-hour concurrency (often a low single-digit percentage of seats) plus headroom drives the number, and it’s exactly what
MaxConcurrentSessionsshould reflect. - Teams Phone licensing is per user (the Teams Phone add-on or a suite like E5). Resource accounts for call queues/auto attendants use Teams Phone Resource Account licenses (often free virtual-user licenses up to a tenant allocation).
- Media bypass can reduce cost by removing the Microsoft media hop (less cloud egress) and improving quality; LMO and SBA add SBC footprint at branches.
- Network costs are indirect but real: QoS-capable WAN and adequate internet egress for signaling and (non-bypass) media.
A rough monthly picture for a 500-seat Direct Routing deployment:
| Cost driver | What you pay for | Rough monthly (INR) | Sizing lever |
|---|---|---|---|
| Certified SBC (redundant pair) | Appliance/VM + support (amortized) | ₹15,000–40,000 | One pair per data center; scale by concurrency |
| Public-CA certificate | Annual cert (amortized) | ₹500–1,700 | Watch expiry; wildcard vs SAN |
| Carrier SIP trunk | Channels + minutes | ₹20,000–80,000 | Channels = busy-hour concurrency + headroom |
| Teams Phone licenses | Per voice-enabled user | Per your EA/CSP | Only license users who need PSTN |
| Resource-account licenses | Call queues / auto attendants | Often ₹0 (allocation) | Use the free virtual-user pool |
| Branch SBA (per site) | SBC at the branch | ₹10,000–25,000/site | Only sites needing survivability |
| Network / QoS | WAN + egress capacity | Existing budget | Bypass reduces media egress |
The sizing discipline that matters most: channels, not seats. MaxConcurrentSessions on each gateway should equal the carrier’s real channel count so the 90% alert is meaningful; over-provisioning channels wastes carrier spend, under-provisioning produces exactly the 503-under-peak failure in the scenario above.
Interview & exam questions
1. When do you choose Direct Routing over Calling Plans or Operator Connect? When a managed option cannot reach what you need: Microsoft has no Calling Plan in your country, you have an in-force carrier/SIP-trunk contract, or you must integrate analog/PBX/contact-center gear. Direct Routing gives full routing control at the cost of operating the SBC and trunk yourself; the three options can also coexist on one tenant or user.
2. Walk the full outbound call-selection order for a Direct Routing user. The dialed string is normalized to E.164 by the user’s tenant dial plan; the E.164 number is evaluated against the user’s voice routing policy (ordered PSTN usages); Teams stops at the first usage whose voice routes contain a matching NumberPattern; within that usage it picks the lowest-Priority matching route; within the route it selects a PSTN gateway at random; signaling goes over TLS 5061 via the Microsoft SIP proxy to the SBC and onto the carrier.
3. What are the certificate requirements for pairing an SBC, and what breaks if they’re wrong? The cert’s CN or SAN must equal the SBC FQDN, the issuing CA must be in the Microsoft Trusted Root Certificate Program, the full chain (leaf + intermediates) must be installed, EKU must include Server Authentication, and the FQDN’s domain must be verified in the tenant. A wrong or self-signed cert, a missing intermediate, or a wildcard that doesn’t cover the label fails the TLS handshake — the SBC never shows healthy and no call completes, with no obvious error.
4. Why is PSTN-usage order the “control plane,” and what’s the classic bug? A voice routing policy is an ordered list of usages evaluated first-match-wins; the order decides which route is even considered. The classic bug is placing a broad usage above a specific one (or a .* route inside an early usage), which silently routes calls down the wrong trunk — e.g. sending internal contact-center queue calls out to the PSTN and back.
5. A user reports a fast-busy on outbound calls but the SBC health dashboard is green. Where do you look? Read the SIP ladder in Call Analytics: a green SBC with a failing call usually means a downstream response — a 503 (trunk saturated) or 403/404 (carrier rejecting the number/CLID). Check MaxConcurrentSessions versus real trunk capacity and whether the backup route points at an independent SBC; a green box does not mean a healthy trunk.
6. What’s the difference between an emergency call routing policy and an emergency calling policy? The routing policy (*-CsTeamsEmergencyCallRoutingPolicy) defines which strings are emergency numbers and which PSTN usage/trunk they take — the Direct-Routing-specific side. The calling policy (*-CsTeamsEmergencyCallingPolicy) defines notification: who is alerted (e.g. security), how, and external-lookup behavior. Both, plus LIS and PidfLoSupported, are needed for compliant dynamic E911.
7. How does dynamic E911 determine the caller’s location on Direct Routing, and what’s the DR-specific requirement? The Teams client reports its network position (subnet, WAP, switch/port); the Location Information Service maps that identifier to a civic address; the address travels as a PIDF-LO XML payload. Direct Routing additionally requires -PidfLoSupported $true on the emergency-egress gateway so the SBC receives and relays that location to the emergency provider.
8. What does media bypass change, and what must be true for it to work? It removes the Microsoft media processor from the path so RTP/SRTP flows directly between the Teams client and the SBC (signaling still transits the Microsoft proxy). It requires an SBC that supports Teams media bypass, media reachability between the client networks and the SBC (via ICE/relay for NAT), and -MediaBypass $true on the gateway; if the direct path isn’t reachable you get one-way audio.
9. What survives a branch WAN outage with a Survivable Branch Appliance, and what doesn’t? With an SBA, users at the branch keep outbound and inbound PSTN calls over the local trunk and calls between users at the same branch (including 911). They lose cross-site/cloud calls, presence, chat and meetings — those need the cloud. Clients auto-reconnect to the cloud when the WAN recovers.
10. Call Analytics versus the Call Quality Dashboard — when do you use each? Call Analytics is per-call/per-user: use it reactively to diagnose why one call was bad, leg by leg (jitter, loss, RTT, codec, media path, egress SBC). CQD is the tenant-wide aggregate: use it proactively to spot a site/subnet/SBC trending poor over time; upload a building/subnet map so poor calls attribute to named locations.
11. A brand-new voice-enabled user has no dial pad. What do you check? That the user is licensed for Teams Phone, has a number assigned (which sets EnterpriseVoiceEnabled = True), and has a voice routing policy assigned — Get-CsOnlineUser | fl EnterpriseVoiceEnabled, OnlineVoiceRoutingPolicy, LineUri. All three are required; missing any one removes calling.
12. Why must every dial-plan normalization rule emit a leading +? Output without a + isn’t recognized as complete E.164 and triggers a second normalization pass against tenant/regional rules, producing surprising results. Keep the dial plan producing E.164 and push any carrier-required digit stripping to the trunk via OutboundPstnNumberTranslationRules on the gateway.
These map primarily to the Microsoft 365 Certified: Teams Voice Engineer Expert (MS-720) exam — plan and configure Teams Phone, Direct Routing, dial plans and voice routing, emergency calling, and monitoring — and touch MS-700 (Teams Administrator Associate) for the tenant-admin surface.
| Question theme | Primary cert | Objective area |
|---|---|---|
| PSTN option choice; DR vs Calling Plan vs OC | MS-720 | Plan Teams Phone |
| SBC pairing, certificates, gateways | MS-720 | Configure Direct Routing |
| Dial plans, normalization, voice routing | MS-720 | Configure voice routing |
| Dynamic E911, LIS, emergency policies | MS-720 | Configure emergency calling |
| Media bypass, LMO, survivability (SBA) | MS-720 | Optimize/secure network for voice |
| Call Analytics, CQD, quality management | MS-720 | Monitor and troubleshoot |
| Tenant admin, roles, policy assignment | MS-700 | Manage Teams and policies |
Quick check
- In what order does Teams evaluate a Direct Routing outbound call — from the dialed string to the egress SBC?
- A newly paired SBC never appears healthy and no call completes, but there’s no obvious error. Name the two most likely causes and where you confirm them.
- True or false: within a single voice route, the SBCs in
OnlinePstnGatewayListare tried in the order listed. - Your contact-center queue calls are going out to the PSTN and back instead of staying on-net. What single configuration detail is almost certainly wrong?
- For dynamic E911 over Direct Routing, name the one gateway parameter that is specific to Direct Routing and what it does.
Answers
- Tenant dial plan normalizes the dialed string to E.164 → the voice routing policy (ordered PSTN usages) is walked top-to-bottom, first matching usage wins → within that usage the lowest-
Prioritymatching voice route is chosen → within that route a PSTN gateway is selected at random → signaling goes over TLS 5061 via the Microsoft SIP proxy to the SBC and onto the carrier. - Most likely (a) the certificate chain isn’t trusted — self-signed/private CA, missing intermediate, or CN/SAN not matching the FQDN (confirm on the SBC health dashboard and the SBC’s TLS logs); or (b) a firewall blocking TCP 5061 inbound from Microsoft’s subnets so SIP OPTIONS don’t flow (confirm on the dashboard OPTIONS state and firewall rules).
- False. SBCs within a single route are tried in random order; use route priority (lowest number wins) to express preference between trunks.
- The order of PSTN usages in the voice routing policy. Evaluation is first-match-wins, so the specific “internal/queue” usage must be listed before the broad PSTN usage; a transposed order sends queue calls out to the public network.
-PidfLoSupported $trueon the emergency-egress gateway — it makes Teams send the PIDF-LO XML location payload to the SBC, which relays the caller’s civic location to the emergency service provider.
Glossary
- Direct Routing — the Teams Phone option where you connect the cloud PBX to the PSTN through your own certified SBC and SIP trunk, authoring all routing yourself.
- Calling Plans — Microsoft-operated PSTN calling: buy a license, get a Microsoft number; zero telco operations.
- Operator Connect — PSTN calling through a participating carrier provisioned in the Teams admin center; the carrier owns the trunk, you manage no SBC.
- Session Border Controller (SBC) — the certified device (physical, virtual, or hosted) that terminates your SIP trunk, anchors/transcodes media, and peers with Microsoft over TLS.
- PSTN gateway — the Teams-side object (
*-CsOnlinePSTNGateway) representing a paired SBC and its call-handling settings. - PSTN usage — a named string that links voice routes (which reference it) to voice routing policies (which list it in order); carries no logic itself, but its order in a policy decides routing.
- Voice route — a mapping from a
NumberPattern(E.164 regex on the called number) to one or more PSTN gateways, with aPriority; the lowest priority number wins. - Voice routing policy (online voice routing policy) — an ordered list of PSTN usages assigned to a user; evaluated first-match-wins.
- Tenant dial plan — a set of ordered normalization rules assigned to users that converts dialed strings to E.164; merges with the service-country dial plan.
- Normalization rule — a
Pattern/Translationpair inside a dial plan; Teams applies them top-down and stops at the first match, and each should output a leading+. - Enterprise Voice — the per-user flag (
EnterpriseVoiceEnabled) that turns on PSTN calling; set automatically when a number is assigned. - Media bypass — configuration that lets RTP/SRTP media flow directly between the Teams client and the SBC, skipping Microsoft’s media processors (signaling still goes via the proxy).
- Local media optimization (LMO) — keeps media on a downstream SBC local to the user’s network site while signaling anchors on a central proxy SBC.
- Location Information Service (LIS) — the tenant service mapping network identifiers (subnet, WAP, switch, port) to civic addresses for dynamic emergency calling.
- PIDF-LO — the XML presence/location payload carrying a caller’s civic location; on Direct Routing the egress gateway must have
PidfLoSupported = $trueto relay it. - Emergency call routing policy — defines which strings are emergency numbers and which PSTN usage/trunk they take (Direct-Routing-specific).
- Emergency calling policy — defines who is notified (and how) when a user dials an emergency number; applies across PSTN options.
- Survivable Branch Appliance (SBA) — an application on a branch SBC that keeps basic PSTN and intra-branch calling working when the branch loses connectivity to the Teams cloud.
- Call Analytics — per-call, per-leg diagnostics in the Teams admin center for a single user’s call history.
- Call Quality Dashboard (CQD) — the tenant-wide aggregate of media quality, best used with an uploaded building/subnet map to attribute poor calls to named sites.
- SIP OPTIONS — the keep-alive/health ping exchanged between the SBC and Microsoft’s SIP proxy; its state drives the SBC health dashboard.
Next steps
You can now build, voice-enable, secure, and monitor a Teams Phone Direct Routing deployment end to end. Build outward:
- Related: Teams Governance: Lifecycle, Naming, Expiration & Sensitivity Labels — the broader Teams-admin controls that surround your voice deployment.
- Related: Exchange Online Mail Flow: Transport Rules & Connectors Deep Dive — Teams voicemail and calendar integration ride on Exchange Online.
- Related: Email Authentication: SPF, DKIM & DMARC in Exchange Online — harden the messaging side of the same tenant.
- Related: Entra Conditional Access at Scale: Personas & Authentication Context — gate who can administer and use these services.
- Related: Micro-Segmentation with NSGs, ASGs & Application-Tier Isolation — the network segmentation and QoS discipline that keeps voice media clean.