My Apps
How each OIDC app in this lab — vCenter,
ArgoCD, Rancher, and
Vault — ends up with a working tile on
myapps.microsoft.com, and the two ways this silently
half-works if a resource is missing. All four use the same underlying pattern in
terraform/azure/entra.tf; this page is the one place documenting it instead of
repeating it four times.
The three things a tile actually needs
An azuread_application + client secret is enough for the app itself to authenticate
users directly (e.g. a user manually navigating to the app and clicking "sign in with
Microsoft"). It is not enough to show up on My Apps or be launchable from there —
that needs all three of the following, confirmed the hard way on 2026-07-25 when both
vCenter and Vault were missing one or more of them:
| Requirement | Resource | What breaks without it |
|---|---|---|
| A service principal exists at all | azuread_service_principal.<app> |
No tile, no assignment possible — an azuread_application alone has no directory presence a user can be assigned to. Confirmed missing entirely for Vault: the app registration and client secret existed, but no azuread_service_principal resource had ever been defined, so there was nothing to show. |
homepage_url set on the application's web block |
azuread_application.<app> |
The tile itself renders, but clicking it fails with a generic Entra-side "App launch failed" error, before the browser ever reaches the app. For preferred_single_sign_on_mode = "oidc" apps, My Apps navigates to homepage_url first as the entry point into the real OIDC flow — with it unset, there's nowhere to send the browser. Confirmed for vCenter: direct navigation to vcsa.devhome.cloud worked the entire time (it never goes through My Apps' launch path), while the tile itself failed outright. |
| An app role assignment for the target group | azuread_app_role_assignment |
The tile still doesn't appear, even with a service principal and homepage_url both correctly set. Tile visibility is driven by an actual assignment existing — independent of whether app_role_assignment_required gates authentication. Confirmed for Vault: adding login_url/preferred_single_sign_on_mode alone via the service principal wasn't enough; the tile only appeared after also assigning app-vault-users the built-in default access role (00000000-0000-0000-0000-000000000000). |
resource "azuread_application" "vault" {
# ...
web {
homepage_url = "https://vault.devhome.cloud:8200"
redirect_uris = [ /* ... */ ]
}
}
resource "azuread_service_principal" "vault" {
client_id = azuread_application.vault.client_id
owners = [data.azuread_client_config.current.object_id]
login_url = "https://vault.devhome.cloud:8200"
preferred_single_sign_on_mode = "oidc"
# app_role_assignment_required intentionally left unset - see Vault SSO
}
resource "azuread_app_role_assignment" "vault_ad_users" {
app_role_id = "00000000-0000-0000-0000-000000000000" # built-in default access role
principal_object_id = data.azuread_group.ad_vault_users.object_id
resource_object_id = azuread_service_principal.vault.object_id
}
homepage_url vs the app's real sign-in URL
homepage_url doesn't need to be a special SSO-aware endpoint — for vCenter it's simply
the bare hostname (https://vcsa.devhome.cloud), the exact same URL that already works
when navigated to directly. The one deliberate choice worth calling out: it's the bare
hostname, not a deep link like /ui/app/home, matching the convention already used
for ArgoCD/Rancher's homepage_url — a bare hostname is more resilient to a future
vCenter upgrade changing its internal SPA path than hardcoding one.
Recipe for adding a new app
azuread_applicationwith awebblock setting bothhomepage_urlandredirect_uris— not just the redirect URI the OIDC flow itself needs.azuread_service_principalreferencing that application'sclient_id, withlogin_url(usually the same value ashomepage_url) andpreferred_single_sign_on_mode = "oidc".azuread_app_role_assignmentgranting the relevant AD-synced group (see On-Prem Active Directory) the built-in default access role (00000000-0000-0000-0000-000000000000) on that service principal — required for the tile to appear, separate from whateverapp_role_assignment_requiredis set to for actual sign-in gating.terraform plan— if step 2 errors withA resource with the ID "..." already exists, a service principal was already created out-of-band at some point (e.g. by an earlier manual portal action). Import it rather than fighting Terraform for ownership:terraform import azuread_service_principal.<name> /servicePrincipals/<object-id>(note the provider needs the full/servicePrincipals/<id>path, not the bare object ID).- Give Entra a minute to propagate, then check the tile from a real
myapps.microsoft.comsession — a hard refresh is usually enough; a full sign-out isn't required.