TL;DR
Now that you are here to read the post, I can admit to the fact that there are scenarios where you will still want to use secrets for authentication. There might also be situations where you can only use legacy authentication, but there are more scenarios where you can and should authenticate without those dirty secrets. This post will provide you with some basic information about the different identity types, and ways of authenticating to get you started!
Some images are clearly AI generated and included for your amusement 😄
For quite some time we have been using Service Principals/App Registrations/Enterprise applications as service accounts in Azure AD. Before that we were even using service accounts *shudder* in on-premises Active Directory with permanent passwords 👴
Credentials have been entered into local powershell prompts, stored in automation account secrets or key vaults, and (god forbid) even sometimes stored in plaintext ðŸ¤
The time for using client secrets in most cases is now over, and the age of secretless authentication has begun. Workload identity federation and Managed Identities will be the focus of this post.
Lately managed identities has even been included in Azure DevOps, but this has been available for some time in GitHub already. It is also supported for Azure ARC enabled servers, which enables you to use them for on-premises servers as well 🎉
A clear benefit of managed identities is the fact that they are Azure resources and enabling them requires absolutely no permissions in Entra ID. You can create a user-assigned managed identity, or enable system-assigned managed identity, without even having read access to anything in Entra ID. The Entra ID configuration happens entirely in the backend, and is automated by Microsoft 🧙
There are two kinds of managed identities: User-assigned and System-assigned. I will very briefly explain the differences below.
This is the most versatile of the managed identities. You can create a UAMI in Azure for use with many different external resources: Gitlab pipelines, GitHub Actions, Azure DevOps Pipelines, and Kubernetes pods.
The UAMI has an independent lifecycle, and is created/deleted by a user with sufficient access in a resource group. When it is created, a hidden, corresponding service principal is created automatically in Entra ID. The necessary information for authentication is provided as properties on the UAMI.
To authenticate with a UAMI you have a couple of choices: assigned to a resource or federated credentials/workload identity federation.
Remember: Workload identity federation can be used for both user-assigned managed identities and regular service principals for legacy cases.
The UAMI can be assigned to several resources in Azure: Virtual machine, Azure Function, Automation account, and many more. When the UAMI is assigned to a resource, you can authenticate from within the resource by using a simple PowerShell-command as shown below.
No clientSecret or password required, but you need to reference the correct UAMI if there are more than one associated to a resource.
If you delete the Azure resource associated to a UAMI, the UAMI is unaffected. If you delete the UAMI the associated resource is unaffected, though you can’t authenticate any more. The resources are not tied to each others lifecycle.
This is an easier managed identity to use. The SAMI is something you enable on a specific resource, and you can’t create it directly. It is tied directly to the resource lifecycle. It can be enabled during e.g. creation of a VM, or on existing VMs.
Many resource types in Azure support SAMIs. You can’t manage the SAMI outside of the resource, and you won’t see them anywhere in the portal except for as assignees in role assignments.
The whole purpose of using a managed identity is for something or someone to access something. This can be a VM authenticating to a Key Vault, a function app authenticating to a storage account, or many other scenarios. In all cases, you must enable/create the managed identity and then assign a permission to it on a specific scope.
This is fairly straightforward, and you can find the managed identity by name when adding permissions.
Prerequisites for performing these logins is that one or more managed identities are enabled on the relevant resources.
Managed identitites simplifies authentication, and makes your environment more secure. You can sign in with generic commands, and you don’t need to include any secrets in your code. Here are some examples.
This method is at least supported on:
Powershell for system-assigned managed identity on the VM or a single user-assigned managed identity on the VM:
Connect-AzAccount -identity
# Call Azure Resource Manager to get the service principal ID for the VM's managed identity for Azure resources.
$vmInfoPs = Get-AzVM -ResourceGroupName <RESOURCE-GROUP> -Name <VM-NAME>
$spID = $vmInfoPs.Identity.PrincipalId
echo "The managed identity for Azure resources service principal ID is $spID"
If you have multiple user-assigned managed identities and no system-assigned managed identity, you can use the following command:
Connect-AzAccount -Identity -AccountId <ClientID>
You can also use the Azure CLI:
az login --identity
$spID=$(az resource list -n <VM-NAME> --query [*].identity.principalId --out tsv)
echo The managed identity for Azure resources service principal ID is $spID
If you have multiple user-assigned managed identities and no system-assigned managed identity, you can use the following command:
az login --identity --username <client_id|object_id|resource_id>
I have used managed identities in my CI/CD pipelines for a while now, but mostly in Github workflows. This works by setting up a user-assigned managed identity in Azure, and then using the managed identity to authenticate to Azure resources. This is a great way to avoid storing secrets in your repository, and it is a great way to avoid having to rotate secrets. The feature is called “Federated credentials”, and has been available for a while now.
Using federated credentials, you can allow authentication from workflows in Github to Azure resources. I have already described how to do authentication with OpenID Connect in a previous post. This has not changed much since then.
Authenticating with managed identities in Terraform is quite straighforward, but possible in many ways.
Remember to configure your backend and AzureRM provider to use the managed identity (keyword is use_oidc).
# Provider configuration
provider "azurerm" {
use_oidc = true
features {}
}
# Backend configuration
terraform {
backend "azurerm" {
resource_group_name = "StorageAccount-ResourceGroup"
storage_account_name = "abcd1234"
container_name = "tfstate"
key = "prod.terraform.tfstate"
use_oidc = true
subscription_id = "00000000-0000-0000-0000-000000000000"
tenant_id = "00000000-0000-0000-0000-000000000000"
}
}
In early days service principals were used for authentication to Azure by way of a client id or application id, and a client secret. The secret was generated on creation, and you would create more client secrets as needed. Client secrets are for all intents and purposes a password, and should be treated as such. With workload identity federation, you can use a service principal without a client secret, and authenticate with a federated credential instead.
I am mentioning this simply because it is an option, but this is a legacy way of authenticating. You should use managed identities if you can.
Instead of creating a service principal, consider using managed identities for Azure resources for your application identity. If your code runs on a service that supports managed identities and accesses resources that support Microsoft Entra authentication, managed identities are a better option for you. - Microsoft
There aren’t many challenges, but I have experiences one firsthand in Gitlab. This list can be updated when I encounter more.
You can use UAMIs for authenticating a Gitlab pipeline. This works quite well, but there are some caveats. Entra ID does not support wildcard branch names for federated identities, and this makes granular authentication cumbersome. You can only created federated credentials for a specific branch, and not for an environment in Gitlab. Hopefully this is supported in the future, so you can use UAMIs to authenticate dev/test/prod environments with different identities.
There can be challenges getting granular authentication to work with GitHub also, but this is of a smaller concern because of the environment possibilities. I have outlined the environment authentication process in a previous post. You can use Terraform to deploy GitHub repositories and GitHub configuration with custom oidc subject claims, but this requires a considerable amount of effort to do. It is possible but you would most likely be better off using environment based federation, IMHO.
In this post I have given some explanations on what managed identities are and why you should use them. I have also listed a couple of known challenges in usage, which might help you in determining if this is a right fit for you.
Please let me know if I have missed something, or if you see any blatantly wrong statements. I am always looking to improve and learn 💪