What is OIDC?
OpenID Connect (OIDC) allows your GitHub Actions workflows to access Azure resources without storing any credentials as long-lived GitHub secrets. Azure natively supports OIDC through Entra ID federated credentials. This is the most secure authentication method for production environments.
Before you begin, ensure you have the following:
Azure CLI
An Azure subscription with permissions to create App Registrations and assign RBAC roles
az account show --query ' {subscriptionId:id, tenantId:tenantId} ' -o json
Example output:
"subscriptionId" : " 00000000-0000-0000-0000-000000000000 " ,
"tenantId" : " 00000000-0000-0000-0000-000000000000 "
export SUBSCRIPTION_ID = " <subscription-id> "
export TENANT_ID = " <tenant-id> "
export GITHUB_ORG = " <github-org-or-username> "
export REPO_NAME = " <repository-name> "
az ad app create --display-name " terrateam "
Note the appId from the output:
"appId" : " 00000000-0000-0000-0000-000000000000 " ,
export CLIENT_ID = " <appId> "
az ad sp create --id " $CLIENT_ID "
Get the App Registration’s object ID (different from the client/app ID):
APP_OBJECT_ID = $( az ad app show --id " $CLIENT_ID " --query " id " -o tsv )
Create a flexible federated credential using the Microsoft Graph beta API. This uses claimsMatchingExpression with the matches operator and * wildcard to allow authentication from any branch, tag, or pull request in your repository:
--uri " https://graph.microsoft.com/beta/applications/${ APP_OBJECT_ID }/federatedIdentityCredentials " \
--headers " Content-Type=application/json " \
--body " {'name': 'terrateam-github', 'issuer': 'https://token.actions.githubusercontent.com', 'audiences': ['api://AzureADTokenExchange'], 'description': 'Terrateam GitHub Actions OIDC', 'claimsMatchingExpression': {'value': 'claims[\'sub\'] matches \'repo:${ GITHUB_ORG }/${ REPO_NAME }:*\'', 'languageVersion': 1}} "
Verify the credential was created:
--uri " https://graph.microsoft.com/beta/applications/${ APP_OBJECT_ID }/federatedIdentityCredentials " \
--query " value[].{name:name, expression:claimsMatchingExpression.value} " -o table
Why claimsMatchingExpression?
Terrateam runs on branches and pull requests, so the federated credential must match any ref in your repository. Azure’s standard subject field requires an exact match and does not support wildcards. The claimsMatchingExpression property with the matches operator supports * (multi-character) and ? (single-character) wildcards against the sub claim. This is a preview feature called Flexible Federated Identity Credentials .
The expression claims['sub'] matches 'repo:org/repo:*' matches any subject starting with your repository, including all branches, tags, and pull requests.
Other useful patterns:
claims['sub'] matches 'repo:org/repo:ref:refs/heads/*' - any branch only
claims['sub'] matches 'repo:org/repo:ref:refs/tags/*' - any tag only
claims['sub'] eq 'repo:org/repo:ref:refs/heads/main' - specific branch only
Grant the service principal permissions on your subscription:
az role assignment create \
--assignee " $CLIENT_ID " \
--scope " /subscriptions/ $SUBSCRIPTION_ID "
After setting up Azure resources, configure Terrateam to use OIDC authentication:
Create the .terrateam/config.yml configuration file at the root of your Terraform repository.
subscription_id : " SUBSCRIPTION_ID "
Test that OIDC authentication is working:
Create a simple Terraform configuration in your repository
Open a pull request with the changes
Comment terrateam plan on the pull request
Terrateam should successfully authenticate using OIDC and show the plan output
Azure OIDC with Terrateam works by:
Terrateam requests a GitHub OIDC token with audience api://AzureADTokenExchange
The token is passed directly to the Terraform AzureRM/AzAPI providers via environment variables
The providers exchange the token with Entra ID for Azure credentials
The following environment variables are automatically set:
Variable Value ARM_USE_OIDCtrueARM_CLIENT_IDYour App Registration client ID ARM_TENANT_IDYour Entra ID tenant ID ARM_OIDC_TOKENThe GitHub OIDC token ARM_SUBSCRIPTION_IDYour Azure subscription ID (if configured)
Multiple Environments
You can use different App Registrations for different environments and operations. For example:
- tag_query : " dir:terraform/production/** "
client_id : " PROD_CLIENT_ID "
subscription_id : " PROD_SUBSCRIPTION_ID "
client_id : " PROD_CLIENT_ID "
subscription_id : " PROD_SUBSCRIPTION_ID "
- tag_query : " dir:terraform/staging/** "
client_id : " STAGING_CLIENT_ID "
subscription_id : " STAGING_SUBSCRIPTION_ID "
client_id : " STAGING_CLIENT_ID "
subscription_id : " STAGING_SUBSCRIPTION_ID "
For more details, read the Cloud Credentials documentation .
Now that you have Azure authentication configured, you are now able to use Terrateam for plan and apply operations against Azure resources.