Sign in during Public Preview to get the Team plan free, plus an early-adopter discount when we launch. Sign in
This guide shows you how to add Federated Identity Credentials to a user-assigned Managed Identity and to an app registration against Locally. A Federated Identity Credential lets a workload outside Azure - like a GitHub Actions workflow - sign in as that identity using a token from its own identity provider, rather than a secret.
az) installed.Managed Identities, app registrations and their Federated Identity Credentials are all built into Locally, so there are no plugins to install.
Firstly, we need to launch Locally which we can do from a terminal by running:
$
locally build
Once Locally has started, the Locally Dashboard will open automatically:
Next we can create the Resource Group to hold the Managed Identity:
$
locally run az group create -n sample-federation -l berlin
There's two things to note here:
locally run.locally run when you do.We'll start with a user-assigned Managed Identity for the workload to sign in as:
$
locally run az identity create -g sample-federation -n sample-workload-identity -l berlin
{
"note": "some fields skipped for brevity",
"clientId": "8d9fa14d-f903-4372-bac2-eeca3d246f60",
"id": "/subscriptions/307d8f52-9719-460e-9f85-aa408e28ee55/resourceGroups/sample-federation/providers/Microsoft.ManagedIdentity/userAssignedIdentities/sample-workload-identity",
"location": "berlin",
"name": "sample-workload-identity",
"principalId": "deffb84e-a9ac-4efa-94e1-37e8e2eef1c6",
"resourceGroup": "sample-federation",
"tenantId": "59e01520-e049-4dfa-8a7d-1a847fc07763",
"type": "Microsoft.ManagedIdentity/userAssignedIdentities"
}
A Federated Identity Credential says which tokens the identity trusts. Here we're trusting workflows that run on the main branch of a GitHub repository:
$
locally run az identity federated-credential create -g sample-federation --identity-name sample-workload-identity -n github-main --issuer https://token.actions.githubusercontent.com --subject repo:contoso/sample-app:ref:refs/heads/main --audiences api://AzureADTokenExchange
{
"note": "some fields skipped for brevity",
"audiences": [
"api://AzureADTokenExchange"
],
"id": "/subscriptions/307d8f52-9719-460e-9f85-aa408e28ee55/resourceGroups/sample-federation/providers/Microsoft.ManagedIdentity/userAssignedIdentities/sample-workload-identity/federatedIdentityCredentials/github-main",
"issuer": "https://token.actions.githubusercontent.com",
"name": "github-main",
"resourceGroup": "sample-federation",
"subject": "repo:contoso/sample-app:ref:refs/heads/main",
"type": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials"
}
A token is only accepted when all three of these match it:
issuer - the identity provider that issued the token, here GitHub Actions.subject - the workload the token was issued to, here the main branch of contoso/sample-app. This has to match exactly, including its case.audiences - who the token was issued for. api://AzureADTokenExchange is the value Azure recommends.We can list the identity's Federated Identity Credentials to check it's there:
$
locally run az identity federated-credential list -g sample-federation --identity-name sample-workload-identity -o table
Issuer Name ResourceGroup Subject
------------------------------------------- ----------- ----------------- -------------------------------------------
https://token.actions.githubusercontent.com github-main sample-federation repo:contoso/sample-app:ref:refs/heads/main
We can see the Managed Identity in the Locally Dashboard too:
App registrations can have Federated Identity Credentials too. They live in Locally's directory rather than in a Resource Group, so we'll create one there:
$
locally run az ad app create --display-name sample-federated-app --query "{appId:appId, displayName:displayName}"
{
"appId": "b7385917-2423-410d-a1be-74fede50e779",
"displayName": "sample-federated-app"
}
We'll need its appId in the next few steps, so let's keep it in a variable:
$
export APP_ID=$(locally run az ad app list --display-name sample-federated-app --query "[0].appId" -o tsv)
For an app registration, the Federated Identity Credential is passed as JSON, with the same issuer, subject and audiences as before:
$
locally run az ad app federated-credential create --id $APP_ID --parameters '{"name":"github-main","issuer":"https://token.actions.githubusercontent.com","subject":"repo:contoso/sample-app:ref:refs/heads/main","audiences":["api://AzureADTokenExchange"]}'
{
"audiences": [
"api://AzureADTokenExchange"
],
"id": "58cc32e6-a778-4019-aca4-76760ebcde3e",
"issuer": "https://token.actions.githubusercontent.com",
"name": "github-main",
"subject": "repo:contoso/sample-app:ref:refs/heads/main"
}
$
locally run az ad app federated-credential list --id $APP_ID -o table
Issuer Name Subject
------------------------------------------- ----------- -------------------------------------------
https://token.actions.githubusercontent.com github-main repo:contoso/sample-app:ref:refs/heads/main
Locally applies the same rules as Azure here, so an app registration can't have two Federated Identity Credentials with the same issuer and subject. Trying to add a second one under a different name is refused:
$
locally run az ad app federated-credential create --id $APP_ID --parameters '{"name":"github-main-2","issuer":"https://token.actions.githubusercontent.com","subject":"repo:contoso/sample-app:ref:refs/heads/main","audiences":["api://AzureADTokenExchange"]}'
ERROR: Issuer and subject combination already exists for this application (issuer "https://token.actions.githubusercontent.com", subject "repo:contoso/sample-app:ref:refs/heads/main")
When a workload signs in with a token from the issuer, Locally checks the token's issuer, subject and audience against the identity's Federated Identity Credentials, and verifies its signature against the keys the issuer publishes. If they all match, Locally issues an access token for the Managed Identity or app registration - with no secret involved.
Note
token.actions.githubusercontent.com, needs to be reachable from your machine when the token is exchanged.Finally, we can tidy up. As the app registration lives in the directory rather than the Resource Group, it needs deleting on its own:
$
locally run az ad app delete --id $APP_ID
And to remove the Resource Group, along with the Managed Identity and its Federated Identity Credential:
$
locally run az group delete -n sample-federation --yes
Whilst this guide used the Azure CLI, Federated Identity Credentials on a Managed Identity are a Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials resource, so they provision in the same way with HashiCorp Terraform or OpenTofu, Pulumi, Bicep or an ARM Template.
Using a Managed Identity covers giving an identity access to a Key Vault, and Role Assignments covers how Locally enforces the roles you give it.
Should you encounter any issues, please take a look at the troubleshooting section.
Sign in during Public Preview to get the Team plan free, plus an early-adopter discount when we launch. Sign in
Your Azure infrastructure, running on your machine. Deploy in seconds, break things freely, and ship to Azure when you're ready.