The consensus reaction to the September 29, 2027 retirement of the Azure Functions v1 hosting model on Azure Container Apps is that you have a ticking production outage on your hands. That reading is correct on the facts and wrong on the priorities. The apps really do stop — no graceful degradation, no read-only limp mode, no support ticket that buys you three more months. But treating this as a twelve-month migration crisis gets the size of the problem exactly backwards, and it makes people spend their runway on the easy part.
Let me steelman the alarmist version first, because it isn’t stupid. Microsoft has drawn a hard line. This is not a “support ends, keep running at your own risk” retirement of the kind we’ve all quietly ignored for years. On September 30, 2027, an affected app stops executing. HTTP triggers stop answering. Queue and timer triggers stop firing. If a Functions v1 app on Container Apps sits in the middle of an order pipeline or a webhook receiver, the pipeline stops and the webhooks pile up unacknowledged upstream. Twelve months sounds like plenty until you subtract change-freeze windows, the quarter where half your team is on a different fire, and the reality that nobody signs off on a payments path migration in a single sprint. So yes: put it on the roadmap, don’t sleepwalk into it. All true.
Here’s where the panic misfires. The thing being retired is a hosting model, not the platform, not the language, not your code. Functions still runs on Container Apps. The migration path for the overwhelming majority of affected apps is a runtime and configuration bump onto the current hosting model on the same compute you’re already using. This is closer to a framework version upgrade than a re-platforming. Compare it to the standalone Functions v1 story — genuinely different service, genuinely different timeline, and a much heavier lift because you’re often dragging .NET Framework code across to the isolated worker model. Don’t confuse the two. The Container Apps retirement is narrow, and narrowness is the whole point.
The real work is finding out whether you’re even exposed
The dangerous scenario isn’t the team that knows it runs Functions v1 on Container Apps and has a migration ticket. It’s the four subscriptions nobody’s looked at since a 2024 proof-of-concept graduated to production without anyone deciding it should. You cannot plan a migration for a resource you can’t see. So the first hour of this project isn’t planning — it’s a tenant-wide inventory.
Azure Resource Graph is the only sane way to do this across every subscription at once. Functions apps that run on Container Apps carry a managedEnvironmentId pointing at their Container Apps environment, which is what separates them from Consumption or App Service Plan hosting. Start there and widen your net deliberately:
That query finds Functions apps hosted on a Container Apps environment. It does not by itself prove which hosting-model generation each one uses — the notice is specific to the v1 model, and the property that distinguishes v1 from the current model isn’t reliably exposed in Graph across every app. So treat this output as your suspect list, not your verdict. For each hit, confirm the runtime version and hosting configuration directly before you touch anything:
Run the Graph query against a management group scope, not a hand-typed list of subscriptions. The subscription you forget is the one running the thing you care about. If you have Resource Graph wired into a workbook or an export, dump the full suspect list to CSV and make someone own each row. “No owner” is itself a finding.
Don’t reflexively bump to v2 — decide where this thing should live
Here’s the contrarian move that actually saves you pain later. The path of least resistance is to migrate every affected app onto the current Functions hosting model on Container Apps and call it done. For plenty of apps that’s the right answer — you’re already invested in Container Apps environments, Dapr, KEDA-style scaling, the whole surrounding setup, and moving hosting models keeps all of that.
But a forced migration is the cheapest chance you’ll get to ask a question nobody asks voluntarily: does this workload belong on Container Apps at all? Three honest options, and the constraint that decides each:
- Stay on Container Apps, current hosting model. Right when the app genuinely uses the Container Apps environment — sidecars, custom networking, co-located services, scale-to-zero you rely on. The migration is a redeploy against the new model with your triggers and bindings intact.
- Move to standalone Functions (Flex Consumption or a plan). Right when the app is a plain event handler that ended up on Container Apps by accident of tooling, not by design. You shed an entire layer of environment management you were never using. This is a bigger change than a hosting-model bump, so scope it as one.
- Re-host on other compute entirely. Right when the “function” has quietly grown into a long-running service that fights the Functions execution model every day. Some of these should have been an AKS deployment or a plain Container App eighteen months ago. Say so now.
The constraint that decides it is almost never technical elegance. It’s blast radius versus effort. A webhook receiver with no downstream state is a low-risk candidate for the bigger move. The app wired into three queues and a database with at-least-once semantics you don’t fully trust — that one gets the smallest possible change, every time.
A migration you can actually roll back
Whatever target you pick, don’t do an in-place mutation of the running app and pray. Deploy the migrated app alongside the original as a separate resource, and use traffic shaping to move load gradually. For HTTP-triggered apps this is straightforward — put both behind a front door or gateway and shift a slice of traffic:
Trigger-driven apps need more care because two apps bound to the same queue will both compete for messages — which is fine if your handlers are genuinely idempotent and a disaster if they aren’t. Test that assumption in a non-production environment with duplicated triggers before you let it near real messages. Your rollback plan is the old app, left deployed and dormant, ready to take 100% of traffic back the moment error rates move the wrong way. Keep it warm until you’ve watched a full business cycle — a month-end, a payroll run, whatever your ugliest periodic load looks like — go through the new app clean.
Pin the whole thing in Bicep so the target hosting is declared, not clicked:
The deadline is real and the outage is real. But the app going dark isn’t the failure mode that should scare you. The one that should is finding out on September 28, 2027 that a subscription you’d forgotten was running the thing that reconciles your billing. Spend the runway on the audit. The migration is the easy part — it’s the part everyone’s staring at precisely because it’s the part they can see.
