AWS fundamentals path · Ground rules · Topic 3 of 18

IAM: principals, policies and roles

Not started

Identity and Access Management (IAM) answers one question for every AWS API request: is this **principal** allowed to perform this **action** on this **resource**, under these **conditions**? The principal is whoever signed the request. The answer comes from **policies**, JSON documents attached to principals (identity-based policies) or to resources such as buckets, queues and keys (resource-based policies). Nothing is allowed unless a policy allows it.

Why it matters: Every AWS API call is checked by IAM. Getting it right is most of AWS security; getting it wrong is how keys leak and buckets go public.

  1. 1A principal makes the call

    A principal is whoever signs the request: a user, a role session or an AWS service acting for you.

  2. 2Policies are JSON statements

    Each statement has an Effect (Allow or Deny), Actions, Resources and optional Conditions. Without an Allow, the answer is no.

  3. 3Roles hand out temporary credentials

    A role has no password or long-lived key. Someone trusted assumes it and gets credentials that expire, which is what workloads should use.

  4. 4Least privilege is a process

    Start narrow, grant what the logs show is needed, and remove what is unused. Wildcards are where breaches widen.

1

How it works

The explanation, with examples you can run.

Principals

  • IAM users have long-lived credentials (a password, access keys). AWS recommends using them as little as possible: people should sign in through IAM Identity Center or another identity provider, and workloads should use roles.
  • Roles have permissions but no credentials of their own. A trusted principal calls sts:AssumeRole and receives temporary credentials that expire, typically after an hour. EC2 instances, ECS tasks and Lambda functions all get role credentials automatically.
  • AWS services act as principals when, for example, SNS delivers to your SQS queue or EventBridge invokes your function.
  • The root user of each account can do anything and is not limited by IAM policies in that account. Protect it with MFA and do not use it day to day.

A policy

{  "Version": "2012-10-17",  "Statement": [    {      "Sid": "ReadOrders",      "Effect": "Allow",      "Action": ["dynamodb:GetItem", "dynamodb:Query"],      "Resource": "arn:aws:dynamodb:eu-west-1:111122223333:table/orders"    }  ]}

Actions are service:Operation. Resources are ARNs, which include the partition, service, Region, account and resource path. Conditions add context checks: source VPC, tags, whether MFA was used, whether the request used TLS. "Version": "2012-10-17" is the current policy language version, not a date you should change.

Roles have two policies

A role carries a trust policy (who may assume it) and permissions policies (what it may do once assumed). An ECS task role trusts ecs-tasks.amazonaws.com; a CI role might trust your CI provider through OpenID Connect, so no AWS keys are stored in the CI system at all. Cross-account access is the same mechanism: account B's role trusts account A, and A's principals assume it.

Least privilege in practice

  • Scope actions and resources. s3:* on * is almost never what an application needs.
  • Use one role per workload, so a compromised service only has its own permissions.
  • Use the tools: IAM Access Analyzer can generate a policy from CloudTrail activity and report unused permissions and resources shared outside your account.
  • Put guard rails above accounts with AWS Organizations: service control policies (SCPs) cap what any principal in an account can do, even an administrator.

How several policies combine for one request, and why a single Deny beats any number of Allows, is the next topic.

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.An application on EC2 needs to read one S3 bucket. What is the right way to give it credentials?
  2. 2.Which part of a role decides who is allowed to assume it?
  3. 3.What does this statement allow?
    { "Effect": "Allow", "Action": "s3:GetObject",  "Resource": "arn:aws:s3:::invoices/*",  "Condition": { "Bool": { "aws:SecureTransport": "true" } } }

0 of 3 right so far. Get all three to finish this topic.