dissected.io
Lesson 6 of 23
Beginnerfundamentalsiamidentitypermissionsleast-privilegesecurity11 min read

IAM — AWS IAM vs GCP IAM vs Azure AD/Entra

IAM is the most important service in your cloud account. It's also the most misconfigured. Every other security control — network rules, encryption, logging — is downstream of "who can do what to which resources." Get IAM wrong and nothing else matters. A misconfigured IAM policy has caused more cloud breaches than any vulnerability in any software.

IAM — Identity, Policies, and Escalation

AWS Resource Hierarchy
Account
OU
IAM User/Role
Resource

The Universal Model

Despite different terminology, every cloud provider's IAM follows the same core model: principal (who) + permission (what action) + resource (on what) + condition (under what circumstances). A policy statement might say: "Allow the deploy-bot role to s3:PutObject on arn:aws:s3:::my-app-assets/* only when the request comes from the VPC endpoint." Principal, permission, resource, condition.

The differences between providers are in how they structure these concepts and where policies are attached.

AWS IAM

AWS IAM is the most granular and most complex of the three.

Users are long-lived identities with access keys or passwords. Groups are collections of users that share policies. Roles are identities that can be assumed by users, services, or external principals — they provide temporary credentials via STS (Security Token Service). Roles are the modern best practice; static user access keys are the legacy pattern you're trying to eliminate.

Policies are JSON documents that define permissions. They come in two flavors: identity-based (attached to a user, group, or role — "this principal can do X") and resource-based (attached to a resource like an S3 bucket — "this resource allows Y to access it").

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::my-bucket/*",
    "Condition": {
      "StringEquals": { "aws:PrincipalOrgID": "o-abc123" }
    }
  }]
}

Policy evaluation logic is where AWS gets complex. The rule: explicit Deny always wins. Then it looks for an explicit Allow. If neither exists, the default is Deny. When multiple policies apply (identity-based, resource-based, permission boundaries, SCPs), they're all evaluated together. Understanding this evaluation order is essential for debugging access issues.

AssumeRole is the mechanism that makes roles work. A Lambda function assumes an execution role. An EC2 instance assumes an instance profile role. A deployment pipeline assumes a deploy role. Cross-account access uses AssumeRole. This is the glue of AWS security architecture.

GCP IAM

GCP's IAM is structurally simpler. Instead of free-form JSON policies, GCP uses role bindings on a resource hierarchy.

The hierarchy is: Organization > Folder > Project > Resource. Permissions inherit downward. Grant a role at the organization level and it applies to every project and resource underneath. This is powerful but dangerous — an overly broad binding at the org level is a blast radius problem.

Members (principals) are Google accounts, service accounts, groups, or domains. Roles are collections of permissions. GCP has three types: basic roles (Owner, Editor, Viewer — too broad for production), predefined roles (curated by Google for specific services), and custom roles (you define the exact permissions).

A binding looks like: "Grant roles/storage.objectViewer to serviceAccount:my-app@project.iam.gserviceaccount.com on project my-project."

The simplicity is genuine — there are no separate policy documents, no identity-based vs resource-based distinction, and no complex evaluation logic. The tradeoff: less granular conditions and fewer escape hatches for complex scenarios.

Azure Entra ID + RBAC

Azure splits identity into two systems. Entra ID (formerly Azure Active Directory) handles authentication — who are you? Azure RBAC handles authorization — what can you do?

RBAC uses role assignments that combine a security principal, a role definition, and a scope. Scopes follow a hierarchy: Management Group > Subscription > Resource Group > Resource. Like GCP, permissions inherit downward.

Managed identities are Azure's equivalent of AWS instance profiles / GCP service accounts for workloads. A system-assigned managed identity is tied to a specific resource (like a VM or App Service) and is created and deleted with it. User-assigned managed identities can be shared across resources.

Azure's deep integration with Active Directory means it handles hybrid identity (on-prem AD synced to cloud) better than either AWS or GCP. For enterprises with existing AD infrastructure, this is a significant advantage.

Side-by-Side Comparison

AspectAWS IAMGCP IAMAzure RBAC + Entra
Identity modelUsers, Groups, RolesMembers, Service AccountsUsers, Groups, Managed Identities
Policy modelJSON policy documentsRole bindings on resource hierarchyRole assignments at scope
HierarchyAccount (flat, use Organizations for hierarchy)Org > Folder > Project > ResourceMgmt Group > Sub > RG > Resource
InheritanceNo (except SCPs)Downward through hierarchyDownward through scope
Temporary credentialsSTS AssumeRoleService account impersonationManaged identity tokens
Org-level guardrailsService Control Policies (SCPs)Organization PoliciesAzure Policy + Management Groups
Condition supportRich (IP, VPC, time, tags, etc.)Limited conditionsConditions (GA, growing)

Least Privilege in Practice

The principle of least privilege says: grant only the permissions needed to perform a task, nothing more. Everyone agrees with this in theory. Almost nobody follows it in practice.

The reason is friction. Figuring out the exact permissions a workload needs requires reading documentation, testing, failing, adding permissions, and repeating. It's much faster to attach AdministratorAccess (AWS), roles/editor (GCP), or Contributor (Azure) and move on. This is how over-permissive roles become the norm.

Practical mitigations: use access analyzer tools (AWS IAM Access Analyzer, GCP IAM Recommender, Azure Advisor) to identify unused permissions and generate least-privilege policies from actual usage. Start broad during development, then tighten before production. Review permissions quarterly.

Permission Boundaries and Guardrails

Permission boundaries (AWS) are a policy that sets the maximum permissions a role can have. Even if the role's identity policy grants s3:*, the permission boundary can restrict it to specific buckets. This is how platform teams delegate role creation to development teams without risking over-permissioned roles.

Service Control Policies (AWS Organizations) apply to entire accounts. They don't grant permissions — they restrict what permissions can be granted. "No one in the dev account can launch instances larger than m5.xlarge."

Organization Policies (GCP) constrain what's possible at the org, folder, or project level. "No VM in this org can have a public IP." "Only these regions are allowed."

Workload Identity Federation

The modern answer to long-lived static access keys is workload identity federation. Instead of creating an IAM user with access keys for your CI/CD pipeline, you configure a trust relationship: "Trust tokens issued by GitHub Actions OIDC provider for this repo." The pipeline gets temporary credentials without any static secrets.

AWS supports this via OIDC federation with AssumeRoleWithWebIdentity. GCP supports it via Workload Identity Federation. Azure supports it via federated credentials on app registrations. All three approaches eliminate the need for static keys in CI/CD pipelines, Kubernetes pods, and other external workloads.

Common Failures

Over-permissive roles: *:* policies, admin access on service accounts, Editor/Contributor on everything. The attack surface of your cloud account is defined by the broadest permission granted.

Keys in git: access keys committed to source control are scraped by bots within seconds. Even after rotating the key, the commit history contains it. Use pre-commit hooks to scan for credential patterns.

Privilege escalation via iam:PassRole (AWS): if a user can pass a high-privilege role to a Lambda function or EC2 instance, they can effectively assume that role's permissions. iam:PassRole is one of the most dangerous permissions to grant without constraints.

Key Takeaways

  • IAM follows a universal model across providers: principal + permission + resource + condition — the differences are structural, not conceptual
  • AWS IAM is the most granular and complex; GCP IAM is simpler with hierarchy-based inheritance; Azure splits identity (Entra) from authorization (RBAC)
  • Least privilege is a practice, not a one-time configuration — use access analyzers to tighten permissions based on actual usage patterns
  • Workload identity federation eliminates static access keys for CI/CD and external workloads; there's no excuse for long-lived keys in 2026
  • Permission boundaries (AWS) and organization policies (GCP/Azure) let platform teams delegate safely without risking over-permissioned roles
  • The most common IAM failures — over-permissive roles, keys in git, PassRole escalation — are preventable with tooling and process

Finally, we'll look at the outcome of all these infrastructure decisions — your cloud bill and how to manage it.

Enjoyed this breakdown?

Get new lessons in your inbox.