---
cuppy_hub: azure
cuppy_path: guides/managed-identity/use-managed-identity.md
cuppy_section: docs
cuppy_doc: ZM1W-BQ8M
cuppy_doc_uuid: 5cc5e82c-859e-4c7e-a5ac-29a223b1548d
cuppy_permalink: https://cuppy.io/azure/d/ZM1W-BQ8M
cuppy_version: 1
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/azure/docs/guides/managed-identity/use-managed-identity
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: 7c9967bfee1d8fc160336577f3105413299c2a84c145f4fb8bfd9254efda37f9
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# Give an app a managed identity and use it from TypeScript

**A managed identity is a service principal that Azure creates and rotates for you; your code asks the host for a token, and the target service checks a role assignment.** Three steps: enable the identity, assign the role on the target, select that identity in code.

## 1. System-assigned or user-assigned

| | System-assigned | User-assigned |
|---|---|---|
| Lifecycle | Deleted with its resource | Independent; delete it yourself |
| Sharing | One resource only | Many resources |
| Role assignments survive redeploying the app | No, if the app is deleted and recreated (new principal ID) | Yes |
| Microsoft's recommendation | — | Recommended for Microsoft services |

Use user-assigned when several apps share access, when infrastructure-as-code recreates resources, or when the role must exist before the app does.

## 2. Enable it

```
# user-assigned: create once, attach anywhere
az identity create --name id-orders --resource-group rg-app
az webapp identity assign --name app-orders --resource-group rg-app --identities <identity-resource-id>
az functionapp identity assign --name fn-orders --resource-group rg-app --identities <identity-resource-id>
az containerapp identity assign --name ca-orders --resource-group rg-app --user-assigned <identity-resource-id>
az vm identity assign --name vm-orders --resource-group rg-app --identities <identity-resource-id>

# system-assigned: omit --identities (Container Apps: --system-assigned)
az webapp identity assign --name app-orders --resource-group rg-app
```

The output gives two IDs. **`principalId`** (object ID) is what role assignments use. **`clientId`** is what your code passes to pick the identity.

## 3. Grant the role on the target

Control-plane roles such as Contributor or Owner don't open the data: reading secrets, blobs or messages needs the service's **data** role.

```
az role assignment create \
  --assignee-object-id <principalId> --assignee-principal-type ServicePrincipal \
  --role "Key Vault Secrets User" \
  --scope $(az keyvault show --name kv-orders --query id -o tsv)
```

Allow a few minutes before the first call. Each service family in this hub names its data roles.

## 4. Use it in code

```ts
import { ManagedIdentityCredential } from "@azure/identity";
import { SecretClient } from "@azure/keyvault-secrets";

// A user-assigned identity must be named by its client ID (or resource ID).
// A system-assigned identity: new ManagedIdentityCredential() with no arguments.
const credential = new ManagedIdentityCredential({ clientId: process.env.APP_IDENTITY_CLIENT_ID ?? "" });

const secrets = new SecretClient("https://kv-orders.vault.azure.net", credential);
const dbPassword = await secrets.getSecret("db-password");
console.log(dbPassword.properties.version);
```

If you use `DefaultAzureCredential` instead, pass `managedIdentityClientId`. Otherwise it falls back to the `AZURE_CLIENT_ID` environment variable, or to the system-assigned identity (`reference/azure-identity/credential-chain.md`). A resource with several user-assigned identities and no client ID chosen can't tell which one you mean.

**Under the hood:** App Service, Functions and Container Apps expose `IDENTITY_ENDPOINT` and `IDENTITY_HEADER` to the process (send the header as `X-IDENTITY-HEADER`). VMs use IMDS at `http://169.254.169.254/metadata/identity/oauth2/token` with `Metadata: true`. The SDK handles both, so call them yourself only from languages without an SDK, and never through a proxy.

## Traps

- **Entra group and app-role changes reach a managed identity slowly.** Groups and app roles are claims in the token, the platform caches managed identity tokens for about 24 hours per resource, and the refresh can't be forced. So adding the identity to a group that holds the role can take hours to work. Microsoft recommends assigning permissions directly to a user-assigned identity instead (an Azure RBAC assignment made directly to the identity takes minutes, like any other).
- **A VM moved while running** fails to start after its next stop, because its identity reference is stale (`reference/managed-identity/errors.md`).
- **A subscription moved to another Microsoft Entra tenant** breaks every managed identity in it. Re-create user-assigned identities, re-enable system-assigned ones, and re-assign roles.
- **Federated credentials on a user-assigned identity** let GitHub Actions or Kubernetes use it too (`guides/entra/github-actions-oidc.md`).

## Next

Failures: `reference/managed-identity/errors.md`. Picking roles and scopes: `reference/rbac/role-assignments-and-scopes.md`.


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 107 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/azure/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
