Managed Identity¶
This recipe enables system-assigned managed identity, grants RBAC roles, and uses DefaultAzureCredential to access Azure Storage and Cosmos DB without secrets.
Architecture¶
flowchart LR
FUNC[Function App] --> MSI[System-assigned Managed Identity]
MSI --> ENTRA[Microsoft Entra ID]
ENTRA --> STORAGE[Azure Storage Data Plane]
ENTRA --> COSMOS[Azure Cosmos DB Data Plane] Prerequisites¶
Use extension bundle v4 in host.json:
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}
Create baseline resources:
az storage account create \
--name $STORAGE_NAME \
--resource-group $RG \
--location $LOCATION \
--sku Standard_LRS
az cosmosdb create \
--name <cosmos-account-name> \
--resource-group $RG \
--kind GlobalDocumentDB
Enable identity and capture principal id:
Assign RBAC roles:
az role assignment create \
--assignee <principal-id> \
--role "Storage Blob Data Contributor" \
--scope $(az storage account show --name $STORAGE_NAME --resource-group $RG --query id --output tsv)
az cosmosdb sql role assignment create \
--account-name <cosmos-account-name> \
--resource-group $RG \
--scope "/" \
--principal-id <principal-id> \
--role-definition-name "Cosmos DB Built-in Data Contributor"
Install SDK packages:
Working Node.js v4 Code¶
const { app } = require("@azure/functions");
const { DefaultAzureCredential } = require("@azure/identity");
const { BlobServiceClient } = require("@azure/storage-blob");
const { CosmosClient } = require("@azure/cosmos");
const credential = new DefaultAzureCredential();
const blobServiceClient = new BlobServiceClient(
`https://${process.env.STORAGE_ACCOUNT_NAME}.blob.core.windows.net`,
credential
);
const cosmosClient = new CosmosClient({
endpoint: process.env.COSMOS_ENDPOINT,
aadCredentials: credential
});
app.http("identityProbe", {
methods: ["GET"],
route: "identity/probe",
authLevel: "function",
handler: async (_request, context) => {
const containerClient = blobServiceClient.getContainerClient("incoming");
const exists = await containerClient.exists();
const { database } = await cosmosClient.databases.createIfNotExists({
id: "appdb"
});
context.log("Managed identity calls succeeded", {
storageContainerExists: exists,
databaseId: database.id
});
return {
status: 200,
jsonBody: {
storageContainerExists: exists,
cosmosDatabaseId: database.id
}
};
}
});
Implementation Notes¶
DefaultAzureCredentialuses managed identity automatically in Azure and developer credentials locally.- Keep endpoint-style settings (
COSMOS_ENDPOINT,STORAGE_ACCOUNT_NAME) and avoid storing keys. - Assign least-privilege data-plane roles instead of broad management-plane roles.
- Initialize SDK clients once per process to reduce cold-start overhead.