
Picture the ticket queue on a Monday in late October. Users who could open Workflows and Lists on Friday now get “Your admin hasn’t made this app available.” Meanwhile a handful of third-party apps you thought were blocked are suddenly installable. Nobody changed a policy. Microsoft did — it migrated your tenant from app permission policies to app-centric management, and the mapping didn’t land where you assumed.
That’s the risk sitting in roadmap item 569434: app-centric management (ACM) reaching the DoD cloud with general availability targeted for October CY2026. Commercial, GCC, and GCC High tenants have been living with this model for a while now. DoD is the last stop, and DoD admins have the least room for a surprise. So let’s get specific about what changes, what breaks, and what to check before the migration flips.
What app-centric management actually is
For years, controlling who could install a Teams app meant juggling two things: app permission policies (allow/block apps, assigned per user or globally) and app setup policies (what gets pinned and pre-installed). Permission policies were the security boundary. They were also clumsy — you managed apps in buckets tied to a policy, then assigned that policy to users. Want one group to have Power BI but not the rest of the tenant? You built and maintained a separate policy for it.
ACM inverts that. Instead of managing apps through policies, you manage availability on the app itself. Every app in the tenant catalog gets one of three states:
- All users can install — the app is open to the whole tenant.
- Specific users and groups can install — availability scoped to Entra ID users and, critically, groups (including security groups and M365 groups).
- No user can install — blocked outright.
On top of the per-app control, there’s a tenant-wide default that decides what happens to new apps published to the Teams store. Set it to “blocked by default” and nothing new becomes installable until you deliberately allow it. Set it to “available” and you’re back to opt-out governance. In a DoD tenant, the answer to that default is not a debate — you lock it down. More on that below.
The honest read: ACM is a genuine improvement. Group-based, per-app targeting is what admins have wanted for years, and it kills the “one policy per exception” sprawl that made permission policies miserable to audit. But the migration is where people get hurt, because “maintain existing availability” is doing a lot of quiet work in that sentence.
The migration is the whole story
Microsoft migrates your existing app permission policies into ACM automatically so that “existing app availability in the tenant is maintained.” That’s the promise. Here’s what it means in practice, and where it gets lossy:
- Your global (org-wide default) permission policy becomes the baseline availability for each app.
- Where you assigned custom permission policies to specific users, those get translated into per-app “specific users and groups” assignments. If you assigned custom policies to hundreds of individual users rather than groups, that translation is verbose and hard to read after the fact.
- The concept of a named policy object goes away for permission purposes. If your documentation, onboarding scripts, or Graph automation reference permission policy names, that automation is now pointing at a model that no longer governs installs.
The failure modes are predictable. A user who was in two custom policies with conflicting app states. An app that was blocked globally but allowed in a policy assigned to a group that itself has stale membership. Nested groups where the effective membership isn’t what the group owner thinks. Any of these produce a post-migration availability that technically matches the old effective policy but isn’t what you’d design from scratch.
So the job before October is inventory and reconciliation: know exactly what’s in your catalog, what state each app is in, and which groups you’re about to hand app-install decisions to.
Inventory your app catalog with Graph, not the deprecated modules
Skip AzureAD and MSOnline entirely — both are retired, and neither ever touched Teams app catalog data cleanly anyway. Use Microsoft Graph. This pulls every app in the tenant catalog with its distribution method (store vs. line-of-business/org uploads) and publishing state, which is your starting map.
# Requires: Microsoft.Graph.Applications / Microsoft.Graph module
# Scope: AppCatalog.Read.All (delegated or app). DoD uses the USGov DoD cloud.
Connect-MgGraph -Scopes "AppCatalog.Read.All" -Environment USGovDoD
# -All handles pagination for you; the catalog can exceed the default page size.
$apps = Get-MgAppCatalogTeamsApp -All -ExpandProperty appDefinitions
$report = foreach ($app in $apps) {
$def = $app.AppDefinitions | Sort-Object -Property CreatedDateTime -Descending | Select-Object -First 1
[PSCustomObject]@{
DisplayName = $app.DisplayName
TeamsAppId = $app.Id
ExternalId = $app.ExternalId
DistributionMethod = $app.DistributionMethod # store | organization | sideloaded
PublishingState = $def.PublishingState # published | submitted | rejected
Version = $def.Version
Bot = [bool]($def.Bots)
}
}
$report | Sort-Object DistributionMethod, DisplayName |
Export-Csv .\TeamsAppCatalog_Inventory.csv -NoTypeInformation
Write-Host "Exported $($report.Count) apps."
The per-app availability assignment surface (the three states) isn’t exposed as a clean v1.0 cmdlet yet, so pull it from the beta admin endpoint with Invoke-MgGraphRequest — still the modern SDK, no legacy modules involved. Treat the beta shape as subject to change and validate against your tenant:
foreach ($app in $apps) {
$uri = "https://graph.microsoft.us/beta/appCatalogs/teamsApps/$($app.Id)"
try {
$detail = Invoke-MgGraphRequest -Method GET -Uri $uri -ErrorAction Stop
# Inspect availability / assignment shape for your tenant before trusting it
$detail | ConvertTo-Json -Depth 5
}
catch {
Write-Warning "Failed for $($app.DisplayName): $($_.Exception.Message)"
}
}
Audit the blast radius of every group you’ll target
ACM’s group targeting is only as safe as the groups behind it. Before you assign “specific users and groups can install” to a group, know how many people are actually in it — including nested membership, which is exactly where DoD tenants tend to have surprises.
Connect-MgGraph -Scopes "GroupMember.Read.All","Group.Read.All" -Environment USGovDoD
# Reviewed input, not a live "grab every group" query.
$targetGroups = Import-Csv .\ReviewedGroups.csv # columns: GroupId, Purpose
foreach ($g in $targetGroups) {
try {
$members = Get-MgGroupTransitiveMember -GroupId $g.GroupId -All -ErrorAction Stop
[PSCustomObject]@{
GroupId = $g.GroupId
Purpose = $g.Purpose
EffectiveUsers = ($members | Where-Object { $_.AdditionalProperties['@odata.type'] -eq '#microsoft.graph.user' }).Count
}
}
catch {
Write-Warning "Group $($g.GroupId): $($_.Exception.Message)"
}
}
Making changes — do it on reviewed input, not a live sweep
Here’s the rule I’d put on the wall: never bulk-change app availability straight off a live query result. Export, review, then act on the reviewed file. The pattern below defaults to a dry run — it tells you what it would do and changes nothing until you consciously pass -Apply. Adapt the write call to the endpoint shape you confirmed above.
function Set-TeamsAppAvailability {
[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory)] [string] $ReviewedCsvPath, # AppId, DesiredState, GroupIds
[switch] $Apply # OFF by default = dry run
)
$plan = Import-Csv $ReviewedCsvPath
foreach ($row in $plan) {
$action = "Set '$($row.AppId)' to '$($row.DesiredState)'"
if ($Apply -and $PSCmdlet.ShouldProcess($row.AppId, $action)) {
# Confirmed write call goes here (Invoke-MgGraphRequest -Method PATCH ...)
Write-Host "APPLIED: $action"
}
else {
Write-Host "DRY RUN: would $action" # nothing changes
}
}
}
# Dry run first — always:
Set-TeamsAppAvailability -ReviewedCsvPath .\ApprovedAppChanges.csv -WhatIf
For most admins, though, the actual clicking happens in the portal. In the Teams admin center, go to Teams apps > Manage apps, open an individual app, and set its Availability to one of the three states with the users/groups picker. The tenant-wide default for new apps lives under Teams apps > Manage apps > Org-wide app settings. That org-wide default is the single most important setting in the whole feature — set new store apps to blocked, and every third-party app that appears becomes a deliberate decision instead of a default-on surprise.
The wider picture DoD admins should be planning for
ACM doesn’t arrive in isolation. A few adjacent shifts hit the same workflow:
- App setup policies stay. Pinning and pre-install are separate from ACM. Don’t assume the whole policy model is gone — availability moved to ACM, but your setup policies still drive the app bar. Keep both in your runbook.
- Copilot agents flow through the Teams store. As agents and Copilot extensions show up as installable apps, your “block new apps by default” stance becomes the gate for AI agents too. That’s a feature. Review before you allow.
- App governance in Defender for Cloud Apps handles the OAuth consent and data-access side that ACM doesn’t. ACM decides installability; app governance watches what the app does with permissions. In a DoD tenant you want both, and they answer different questions.
- Third-party and custom app controls in org-wide settings still gate whether external apps and LOB uploads are permitted at all. ACM sits underneath that — an app has to be permitted by class before per-app availability even matters.
What I’d do before October
In priority order:
- Export your current app permission policy assignments now and turn them into a plain-English map of “who can install what today.” Post-migration, that’s your ground truth for verification.
- Audit the groups you’ll rely on for targeting — transitive membership, ownership, stale accounts. ACM makes groups load-bearing for app security.
- Decide the org-wide default for new apps before the flip, not after. For DoD, block-by-default. Full stop.
- After migration, re-run the inventory and diff it against your pre-migration map. Reconcile every difference before you close the change.
ACM is the model Teams app governance should have shipped with. The trap isn’t the feature — it’s treating an automated migration as a no-op. Verify the mapping, own the default, and the October flip is a non-event instead of a Monday-morning ticket storm.