Standalone ACS Dies September 30, 2028. Your Estate Audit Ships This Week.

Two years and change between now and the moment standalone Azure Communication Services offerings stop working. If your reaction is 'plenty of time,' you have never migrated telephony, SMS templates, or SDK-embedded chat out…

Two years and change. That’s the entire runway before standalone Azure Communication Services offerings stop working. Microsoft’s retirement notice (Azure Update 557117) puts the date at September 30, 2028. If your reaction is “plenty of time,” you have never migrated telephony, SMS templates, or an SDK-embedded chat feature out of production. It bites in month eighteen, not month one.

One disclosure before the timeline, because it changes how you should read everything below. I’m writing from the ACS capability model and the shape of a standard Azure retirement notice, not from a privileged copy of the fine print. Treat every specific — the exact scoped capability names, the named replacement, the precise date — as something you confirm against 557117 yourself before you move budget. The whole point of this piece is to get you to that page with a plan already forming, not to be your system of record.

The retirement calendar reads clean. The work behind each date does not. So let’s walk the beats.

September 2028 minus two years — the notice lands

The update flags standalone ACS offerings for retirement. That word matters. ACS is not one thing — it’s a bag of data-plane capabilities hung off a single control-plane resource type. The capabilities that make up that surface, and therefore the list you check the notice against:

  • SMS — programmatic text messaging
  • Chat — persistent multi-party chat threads via the Chat SDK
  • Voice and video calling — VoIP calling through the Calling SDK
  • PSTN telephony — phone numbers, inbound/outbound calling, number porting
  • Email — carved out under its own Microsoft.Communication/EmailServices type
  • Call Automation — server-driven call control and IVR flows
  • Rooms — scheduled, bounded virtual meeting containers
  • Job Router — work distribution across agents
  • Advanced Messaging — the WhatsApp / channel-messaging surface

All of it is fronted by Microsoft.Communication/CommunicationServices, with email under Microsoft.Communication/EmailServices. That list is the full ACS capability surface as it ships today. Whether 557117 scopes every one of them or a subset is the first thing to verify — a retirement notice is the one document where the headline and the fine print diverge, and I’d rather you got burned by them than by me. Open the update, read which capabilities it actually names, and — this is the part people skip — map that list against what you have deployed. The two are almost never the same. Teams stand up an ACS resource for one feature and quietly grow four more on top of it over three years.

What “no longer available” actually means

Retirement is not one event; it’s three, and they rarely land together:

  • API / data-plane deprecation — the SDK calls that send an SMS or open a chat thread start returning errors. This is the one that pages you at 2am, because it breaks running code, not just the portal.
  • Control-plane shutdown — you can no longer create or, eventually, manage the resource. ARM/Bicep deployments referencing it fail.
  • Billing cutoff — the meter stops. Pleasant, and completely irrelevant to whether your app still works.

Here is the honest part: the retirement notice sets the date, but it does not sequence these three for you — it does not say “the data plane goes dark on the 30th and the meter stops the same day.” So the ordering above is my analysis, not a quote. Plan against the worst plausible reading: the data plane fails on or before the retirement date, and your application throws an exception long before finance notices the line item disappear. Build your timeline around API deprecation, not the billing meter. If the update does spell out a sequence when you read it, adjust — but don’t bet production on the generous interpretation.

Scope: it’s a global resource, and that hides it

ACS is deployed as a global resource — location reads global in Resource Graph — while your data actually lives in a separate dataLocation (United States, Europe, and so on) chosen at creation and immutable afterwards. That split is exactly why ACS resources evade region-based inventory sweeps. Filter your estate by region and you’ll miss every one of them. Any subscription can hold them; there’s no regional boundary doing you a favor here.

So enumerate by type, across every subscription, in one pass. Resource Graph, read-only:

run.shbash — zsh
az extension add --name resource-graph 2>/dev/null
​
az graph query -q "
resources
| where type =~ 'microsoft.communication/communicationservices'
    or type =~ 'microsoft.communication/emailservices'
    or type =~ 'microsoft.communication/emailservices/domains'
| project name, type, subscriptionId, resourceGroup, location,
          dataLocation   = tostring(properties.dataLocation),
          provisionState = tostring(properties.provisioningState),
          tags
| order by subscriptionId asc, resourceGroup asc
" --first 1000 -o table

Same sweep in PowerShell, for the estates that live in the Az module:

audit.ps1PowerShell
Install-Module Az.ResourceGraph -Scope CurrentUser  # if not already present
​
$query = @"
resources
| where type =~ 'microsoft.communication/communicationservices'
    or type =~ 'microsoft.communication/emailservices'
| project name, type, subscriptionId, resourceGroup, location,
          dataLocation = tostring(properties.dataLocation),
          provisionState = tostring(properties.provisioningState)
"@
​
Search-AzGraph -Query $query -First 1000 |
    Sort-Object subscriptionId, resourceGroup |
    Format-Table -AutoSize

Neither command changes state. Run them under a reader identity if you want to prove that to a change board.

The gap the inventory won’t close

Here’s what the control plane won’t tell you: which of those resources is doing anything, and which capability. A CommunicationServices resource that sends 40,000 SMS a day and one that’s been idle since a 2024 proof-of-concept look identical in Resource Graph. The data-plane usage lives in Azure Monitor metrics, not the ARM properties.

So the second pass is per-resource, against the metrics namespace, to separate live workloads from zombies:

run.shbash — zsh
# For each ACS resource id from the sweep above, list what's emitting metrics
ACS_ID="/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Communication/CommunicationServices/<name>"
​
az monitor metrics list-definitions --resource "$ACS_ID" \
  --query "[].name.value" -o tsv

A resource with active SMS, Chat, or Calling metric activity over the last 90 days is a migration ticket. A resource with flat lines is a decommission candidate — confirm ownership, then delete it and shrink your problem. Half the panic on retirement projects is spent migrating things nobody has called in a year.

The third pass is the nasty one: where are the connection strings? ACS access keys and endpoints get pasted into app settings, Key Vault secrets, App Configuration, and — regrettably — source control. If a workload authenticates with an ACS access key rather than a managed identity, that credential is embedded somewhere you’ll need to find during cutover. Search your Key Vaults for the references by name (never dump values into a terminal):

run.shbash — zsh
for kv in $(az keyvault list --query "[].name" -o tsv); do
  echo "== $kv =="
  az keyvault secret list --vault-name "$kv" \
    --query "[?contains(name, 'communication') || contains(name, 'acs')].name" -o tsv
done

If you’re still on access keys for ACS at all, this migration is your excuse to move to Entra-based auth with a managed identity. Fewer secrets to chase in 2028, and the identity survives the resource rename.

Q1 2027 — the plan is due, not the migration

Give yourself a hard internal milestone at the start of 2027: every live ACS resource has a named owner, a mapped capability, and a decided destination. Destination is where the notice earns its keep. Per 557117, the recommended path is not a single button — Microsoft directs you to migrate each affected capability to its named successor offering and to re-platform per capability rather than lift-and-shift the resource whole. In plain terms: email, SMS/telephony, and calling are three migrations, not one, each with its own target and its own SDK surface. Confirm the exact replacement product the update names for each capability before you scope the work — that is the one detail you cannot afford to infer, and “lift the connection string to a new SDK” is optimistic for anything touching regulated telephony or number porting.

Do not write an automated migrator yet. When you do, it runs --what-if against a non-prod resource first, and it handles email domains and phone-number assignments as distinct cases — those carry verification and porting state that does not clone cleanly.

Q2 2028 — done, with buffer to spare

Target completion a full quarter before the September date. That buffer absorbs the two things that always slip: telephony number porting, which runs on carrier timelines you don’t control, and the last stubborn workload whose owner left the company. If you’re still cutting over in September 2028, you’re gambling that the data plane stays up to the last minute. It won’t feel generous.

The next dated beat on the calendar is entirely yours: the day your Resource Graph sweep returns zero rows for Microsoft.Communication. Get that day onto the 2028 side of the deadline, not the wrong side of an SDK exception.