We have several Terraform repositories hosted on an on-premises GitHub Enterprise Server instance. They deploy Azure infrastructure across one or more subscriptions. Until now, deployments have been run manually with az login, but we want to move to proper GitHub Actions CI/CD using service identities.
The required permissions vary considerably. One Terraform project creates a storage account, VNet, VNet-to-vWAN connectivity in another subscription, and private DNS resources there. Another only deploys a VM. Some workflows may also create new subscriptions, which requires permissions above the individual subscription scope.
The workflows will run on self-hosted GHES runners. For a production setup, should we use Entra application registrations, user-assigned managed identities, federated credentials, or another approach? What identity and permission structure works well for deployments with different scopes across subscriptions?
2 Answers
The usual preferred approach is workload identity federation rather than storing a client secret. Create an Entra application or, where appropriate, a user-assigned managed identity, then configure a federated credential matching the repository, branch, tag, or environment that is allowed to deploy. The workflow can authenticate with Azure using the federated token and use whatever Azure RBAC roles are assigned to that identity.
For least privilege, use separate deployment identities for different scopes. For example, give one identity access only to a particular subscription, while a separate, tightly controlled identity handles subscription creation or management-group-level operations. You can also use different identities for different repositories or environments instead of giving every workflow broad tenant permissions.
A user-assigned managed identity is a good fit when the deployment runs on Azure-hosted compute that can use managed identity directly, or when your organization has standardized on that model. It can also be assigned to multiple runners or workflows and given RBAC roles at the required management group, subscription, or resource-group scope.
However, the identity type does not remove the need to design the permissions carefully. Keep the VM deployment identity narrow, use a separate identity for the cross-subscription networking and DNS deployment, and reserve a higher-privilege identity for creating subscriptions. Avoid one all-powerful service principal for every Terraform repository.
Since the runners are on-premises, they cannot automatically use Azure managed identity just because they are self-hosted. They still need a supported federation mechanism or a credential such as a certificate or secret. Federation is preferable when GHES can provide an issuer that Entra can reach; otherwise a certificate-based application identity is generally safer than a long-lived client secret.

Because these are self-hosted runners on GHES, make sure the federation design is supported by your GHES version and network topology. Azure must be able to validate the issuer and signing keys for the OIDC token; an entirely internal GHES endpoint may not be reachable by Entra without additional configuration or exposure.