Managed identities vs IAM roles: a translation guide for one-cloud people
Azure and AWS both let code run without secrets, but the mental models differ. What each side gets wrong about the other, and a translation table.
- Published
- Reading time
- 6 min
On this page
Both clouds solved the same problem the same way: code shouldn’t hold passwords or keys. Your function, container or VM gets an identity from the platform, and the platform hands it short-lived credentials when it needs them.
Both also use similar words for it. That’s where the trouble starts. People who know one cloud well carry its mental model to the other, and the models differ in ways that cause real outages and real over-permissioning. This guide is the translation.
The one-sentence versions
AWS: an IAM role is an identity that carries its own permissions. A trust policy says who may use it.
Azure: a managed identity is a principal with no permissions of its own. A role assignment puts it on a scope, and everything below that scope inherits the access.
Everything else follows from that difference. On AWS, to find out what something can do, you read its role. On Azure, you look for role assignments at the resource and at every scope above it.
What AWS people get wrong on Azure
1. Looking at the identity to see what it can do. A managed identity has no policies attached. Its access is the sum of role assignments made at any level: management group, subscription, resource group or resource. An assignment made at the subscription quietly grants access to everything in it, including resources created next year. To see the full picture, list the identity’s assignments including inherited ones, using its principal (object) ID, and repeat for each subscription: az role assignment list --assignee-object-id <principal-id> --all --include-inherited.
2. Assuming one role covers the data. Azure separates managing a resource from using its data. Reader on a storage account lets you see the account, not read the blobs. That needs a data role such as Storage Blob Data Reader. The same split applies to Key Vault, where Microsoft now recommends Azure RBAC over the older access policies. With access policies, anyone with Contributor on the vault can grant themselves access to its secrets. New vaults default to RBAC from API version 2026-02-01.
3. Expecting an explicit deny. Azure RBAC is additive: you get access by being granted it, and you can’t write your own “deny” to take it away. Deny assignments exist, but you can’t create them directly; deployment stacks can apply them, and customer-managed deny assignments are only in private preview. Azure Policy is a deny system, but for how resources are configured, not for who can act on them. Least privilege on Azure means not granting, rather than granting broadly and denying.
4. Expecting changes to apply quickly. Microsoft documents that role assignment changes can take up to 10 minutes, and several hours for data access granted at management-group scope. Group membership is slower still. Managed-identity tokens are cached for around 24 hours per target service, and group memberships travel inside those tokens, so if you grant access through an Entra group, a running app may not see the change for up to a day. Assigning roles directly to the identity avoids that delay.
5. Treating a token as general-purpose. AWS credentials are one set of keys that work against any service the role allows. An Azure token is issued for one audience: one token for Storage, another for Key Vault, another for Microsoft Graph. The SDKs handle this for you, but it explains errors you’ll see when a token for one service is sent to another.
One more choice has no AWS equivalent: system-assigned identities are created with a resource and deleted with it. User-assigned identities live on their own and can be shared by several resources. Shared identities are convenient and easy to over-grant, so use them deliberately.
What Azure people get wrong on AWS
1. Forgetting the trust policy. A role does nothing until something is allowed to assume it. The trust policy is half of every role: it names the service, account or identity provider that may use it. For access across accounts, both sides must agree: the target role trusts the source account, and the source grants permission to assume the role.
2. Looking for the scope tree. There’s no inheritance from a parent. Permissions are statements of actions on resources, written out in policies. And they can live in two places: policies on the role, and resource-based policies on the resource itself, such as an S3 bucket policy or a KMS key policy. Both can grant access, so check both.
3. Not knowing an explicit deny exists, and wins. An explicit deny anywhere overrides any allow: in the role’s own policies, or in organization-wide service control policies and resource control policies. When “the role clearly allows it” but the call fails, look for a deny higher up.
4. Missing iam:PassRole. Whoever creates a Lambda function or an EC2 instance chooses its role, and could choose a powerful one. iam:PassRole controls which roles a person may hand to a service. It’s a key guard against privilege escalation in AWS. Azure’s closest equivalent is the Managed Identity Operator role, which lets someone attach a user-assigned identity to a resource.
5. Forgetting that credentials expire. Role sessions last an hour by default and up to 12 hours if you configure it. A role assumed from another role is capped at one hour. On AWS compute, the SDKs refresh credentials for you; in scripts and long batch jobs, plan for it.
The translation table
-
The identity your code runs as
AWS: IAM role Lambda execution role, EC2 instance profile, ECS task role.Azure: Managed identity System-assigned (one resource) or user-assigned (shareable). -
Who may use the identity
AWS: Trust policy, plus iam:PassRole Names who may assume the role, and who may hand it to a service.Azure: Attaching it to a resource User-assigned needs the Managed Identity Operator role. -
Where permissions are defined
AWS: Policies on the role and on resources Actions on resource ARNs.Azure: Role assignments at a scope Role + identity + scope. Inherited downward. -
Ready-made permission sets
AWS: AWS managed policies Attach to the role.Azure: Built-in roles Assign at a scope. -
Access to data
AWS: Same policy language For example s3:GetObject.Azure: Separate data-plane roles For example Storage Blob Data Reader. -
Explicit deny
AWS: Yes, and it always wins In policies, SCPs and RCPs.Azure: Not by you, directly RBAC is additive. Deny assignments via deployment stacks. -
Guardrails above accounts
AWS: SCPs and RCPs Set maximum permissions across the organization. Permission boundaries do the same per role.Azure: Management groups and Azure Policy Policy governs resource configuration. -
Kubernetes pods
AWS: EKS Pod Identity AWS's recommended option over IRSA.Azure: AKS Workload ID Replaces the retired pod-managed identity. -
CI/CD without secrets
AWS: OIDC identity provider The role is assumed with a web identity token, e.g. from GitHub Actions.Azure: Federated identity credentials Up to 20 per identity or app registration.
Crossing between the two, still without secrets
Plenty of teams run something on each cloud, and the worst version of that is an AWS access key stored in Azure, or an Azure client secret stored in AWS. Neither is necessary any more.
- Azure to AWS. An Azure workload’s managed-identity token can be exchanged for AWS credentials with
AssumeRoleWithWebIdentity. Register your Entra tenant as an OIDC provider in IAM, create an Entra app registration whose ID becomes the token’s audience, and give the managed identity a role on that app. The AWS role’s trust policy then checks both the audience and the identity’s object ID. AWS documents this pattern on its security blog. - AWS to Azure. Since November 2025, AWS can issue signed identity tokens for your own workloads with
sts:GetWebIdentityToken. Turn on outbound identity federation once per account, add a federated identity credential for that issuer to a user-assigned managed identity or an app registration in Entra, and your AWS code can get Azure tokens with no stored secret. Microsoft lists it as a supported source.
Both directions use short-lived tokens that are never written down, which is the whole point.
A checklist for either cloud
- Every workload has its own identity. Share one only on purpose.
- AWS: read the trust policy as carefully as the permissions. Restrict
iam:PassRole. - Azure: grant at the smallest scope that works, usually the resource or resource group, not the subscription.
- Azure: grant data roles explicitly. Use RBAC for Key Vault.
- Look for permissions in every place they can live: AWS resource policies and organization policies, Azure assignments at every scope above the resource.
- Plan for delay: minutes on Azure for new assignments, up to a day when access comes through a group.
- No long-lived keys in CI/CD or between clouds. Use federation.
Status as of November 2026. Sources: Managed identities overview, Managed identity best practices, Listing role assignments with the CLI, Azure RBAC overview, Blob data access roles, Key Vault RBAC vs access policies, Deny assignments, RBAC troubleshooting, Identity built-in roles, Workload identity federation, Federation considerations, AKS Workload ID, IAM policies, Using IAM roles, iam:PassRole, Policy evaluation logic, EKS Pod Identity, OIDC identity providers, Accessing AWS from Entra ID tenants (AWS Security Blog), Outbound identity federation.
- azure
- aws
- identity
- entra-id
- iam
- rbac
- security