惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
The Last Watchdog
The Last Watchdog
腾讯CDC
Apple Machine Learning Research
Apple Machine Learning Research
有赞技术团队
有赞技术团队
Last Week in AI
Last Week in AI
量子位
雷峰网
雷峰网
宝玉的分享
宝玉的分享
美团技术团队
阮一峰的网络日志
阮一峰的网络日志
博客园 - 叶小钗
P
Privacy International News Feed
NISL@THU
NISL@THU
V
V2EX
博客园 - 三生石上(FineUI控件)
T
The Exploit Database - CXSecurity.com
T
Threat Research - Cisco Blogs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
IT之家
IT之家
T
Tor Project blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Threatpost
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
大猫的无限游戏
大猫的无限游戏
V
Vulnerabilities – Threatpost
V
Visual Studio Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
The Hacker News
The Hacker News
Scott Helme
Scott Helme
H
Hacker News: Front Page
罗磊的独立博客
Hacker News - Newest:
Hacker News - Newest: "LLM"
K
Kaspersky official blog
S
Secure Thoughts
www.infosecurity-magazine.com
www.infosecurity-magazine.com
The Cloudflare Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cybersecurity and Infrastructure Security Agency CISA
小众软件
小众软件
月光博客
月光博客
人人都是产品经理
人人都是产品经理
AI
AI
Help Net Security
Help Net Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
酷 壳 – CoolShell
酷 壳 – CoolShell
Webroot Blog
Webroot Blog
博客园 - 【当耐特】
PCI Perspectives
PCI Perspectives
A
Arctic Wolf

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
AWS IAM Explained: Users, Roles, Policies, and Least Privilege
Raghvendra Pandey · 2026-06-20 · via DEV Community

AWS IAM (Identity and Access Management) is the authorization layer for everything in AWS. Every API call — whether from the console, CLI, an EC2 instance, or a Lambda function — is evaluated against IAM policy. Get it wrong and you either lock out legitimate services or leave your infrastructure open to lateral movement. Despite its importance, IAM is where most teams accumulate the most technical debt, usually in the form of overly broad policies that were "temporary" and became permanent.

This guide covers how IAM actually works — the principal model, how policies are evaluated, role assumption, and the patterns used most in production infrastructure.

Principals: who is making the request

Every IAM interaction starts with a principal — the entity making the API call. AWS has four types of principals:

IAM users are long-lived identities with permanent credentials. They authenticate with a username/password for the console or an access key pair for the CLI/API. Creating IAM users for human access is legacy practice now — most organizations use SSO (AWS IAM Identity Center, formerly SSO) to federate human identities from an identity provider like Okta or Azure AD, then assume roles within AWS accounts. IAM users still make sense for service accounts that need permanent, programmatic API access, though roles are preferred even there when possible.

IAM roles are identities that can be assumed. They don't have permanent credentials — instead, when a principal assumes a role, STS (Security Token Service) issues temporary credentials that expire (typically 1–12 hours). Roles are the preferred mechanism for granting AWS services access to other AWS services, for cross-account access, and for federated human access.

IAM groups are collections of users. A group has policies attached to it; all users in the group inherit those permissions. Groups don't support role assumption — they're only for organizing users and applying policies in bulk.

AWS service principals represent AWS services (EC2, Lambda, ECS, etc.) acting on your behalf. When you create an EC2 instance profile or a Lambda execution role, you're granting an AWS service the ability to assume that role. The service principal looks like ec2.amazonaws.com or lambda.amazonaws.com.

Policies: what the principal is allowed to do

Policies are JSON documents that specify what actions are allowed or denied on which resources under what conditions. IAM has six types of policies:

Identity-based policies are attached directly to users, groups, or roles. They specify what that principal is allowed (or denied) to do.

Resource-based policies are attached to resources (S3 buckets, SQS queues, KMS keys, Lambda functions, etc.) rather than to principals. They specify who is allowed to do what with that resource. S3 bucket policies are the most common example. Resource-based policies support cross-account access without role assumption — you can grant a principal in another account direct access to an S3 bucket.

Permission boundaries cap the maximum permissions an IAM user or role can have. Even if an identity-based policy grants s3:*, if the permission boundary only allows s3:GetObject, only GetObject is allowed. This is useful for delegating IAM management — you can let teams create their own roles as long as those roles don't exceed the boundary.

Service Control Policies (SCPs) apply to entire AWS Organizations accounts. An SCP can prevent all accounts in an OU from using specific regions or services regardless of what IAM policies in those accounts say. SCPs are a ceiling, not a floor — they don't grant permissions, they only restrict them.

Session policies are optional policies passed at role assumption time via sts:AssumeRole or sts:GetFederationToken. They further restrict the permissions for that specific session without modifying the role itself.

Endpoint policies restrict what specific VPC endpoints can access.

How IAM evaluates a request

When an API call arrives, IAM evaluates it through a multi-step decision tree. Understanding this order is essential for diagnosing access issues:

  1. Explicit deny check: If any applicable policy (identity, resource, SCP, permission boundary) has an explicit Deny for the action, the request is denied immediately. Explicit denies always win.
  2. SCP check: If the account is in an Organization and an SCP doesn't allow the action, the request is denied regardless of IAM policies in the account.
  3. Resource-based policy check: If a resource-based policy explicitly allows the request (and there's no explicit deny), it's allowed. This is how cross-account access without role assumption works — the resource policy grants access to a principal in another account directly.
  4. Identity-based policy check: If an identity-based policy attached to the principal allows the action, it's allowed.
  5. Permission boundary check: If a permission boundary is set, the intersection of what the identity-based policy allows and what the boundary allows is the effective permission set.
  6. Default deny: If none of the above granted access, the request is denied. AWS defaults to deny-everything.

The practical takeaway: cross-account access needs to be allowed in both the identity-based policy on the source side AND the resource-based policy on the target side. A common mistake is adding a resource-based policy without giving the source principal permission to assume the role or access the resource.

Policy anatomy

An IAM policy document is JSON with a Statement array. Each statement has an Effect (Allow or Deny), Action (which API operations), Resource (which ARNs), and optionally a Condition block:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-company-artifacts/*"
},
{
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-company-artifacts",
"Condition": {
"StringLike": {
"s3:prefix": ["releases/*"]
}
}
}
]
}

The Condition block can restrict access by source IP, time of day, MFA status, required tags, and many other attributes. Conditions don't grant permissions — they further restrict when an Allow applies.

The Resource field is where least privilege is enforced. Instead of "Resource": "*", use the actual ARN of the specific resource. For S3, arn:aws:s3:::bucket-name grants access to the bucket itself (for ListBucket), while arn:aws:s3:::bucket-name/* grants access to objects inside it (for GetObject, PutObject).

Trust policies and role assumption

Every IAM role has two components: an identity-based policy (what the role can do) and a trust policy (who can assume the role). The trust policy is a resource-based policy on the role itself.

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}

This trust policy allows EC2 instances to assume the role. For human cross-account access, the principal would be an IAM role or user ARN from another account. For OIDC-based access (GitHub Actions, EKS pods via IRSA), the principal is an OIDC provider with a condition that matches the federated identity claim.

Common patterns with Terraform

EC2 instance profile

An instance profile is the mechanism that attaches an IAM role to an EC2 instance. When EC2 starts an instance with an instance profile, the instance metadata endpoint (169.254.169.254 or IMDSv2 equivalent) provides temporary credentials for the attached role.

resource "aws_iam_role" "app_instance" {
name = "app-instance-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect    = "Allow"
Principal = { Service = "ec2.amazonaws.com" }
Action    = "sts:AssumeRole"
}]
})
}

resource "aws_iam_role_policy" "app_instance" {
role = aws_iam_role.app_instance.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect   = "Allow"
Action   = ["s3:GetObject"]
Resource = "arn:aws:s3:::${var.config_bucket}/*"
},
{
Effect   = "Allow"
Action   = ["secretsmanager:GetSecretValue"]
Resource = "arn:aws:secretsmanager:${var.region}:${var.account_id}:secret:app/*"
}
]
})
}

resource "aws_iam_instance_profile" "app" {
name = "app-instance-profile"
role = aws_iam_role.app_instance.name
}

Lambda execution role

Lambda functions need a role with at minimum the AWSLambdaBasicExecutionRole managed policy (for CloudWatch Logs). Add only the additional permissions the function actually needs:

resource "aws_iam_role" "lambda_execution" {
name = "my-function-execution-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect    = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
Action    = "sts:AssumeRole"
}]
})
}

resource "aws_iam_role_policy_attachment" "lambda_basic" {
role       = aws_iam_role.lambda_execution.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

resource "aws_iam_role_policy" "lambda_specific" {
role = aws_iam_role.lambda_execution.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect   = "Allow"
Action   = ["dynamodb:GetItem", "dynamodb:PutItem"]
Resource = aws_dynamodb_table.data.arn
}]
})
}

IRSA — IAM Roles for Service Accounts (EKS)

IRSA allows Kubernetes pods to assume IAM roles without sharing credentials at the node level. The trust policy uses the cluster's OIDC provider:

data "aws_iam_openid_connect_provider" "eks" {
url = aws_eks_cluster.main.identity[0].oidc[0].issuer
}

resource "aws_iam_role" "pod_role" {
name = "my-app-pod-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
Federated = data.aws_iam_openid_connect_provider.eks.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"${trimprefix(data.aws_iam_openid_connect_provider.eks.url, "https://")}:sub" =
"system:serviceaccount:${var.namespace}:${var.service_account_name}"
}
}
}]
})
}

The Kubernetes ServiceAccount is annotated with the role ARN. When a pod runs with that ServiceAccount, the EKS Pod Identity webhook injects environment variables that direct the AWS SDK to use WebIdentity tokens for authentication.

Cross-account access

Cross-account access requires granting permission on both sides. In the target account, create a role with a trust policy that allows the source account:

# In the target account (account B)
resource "aws_iam_role" "cross_account" {
name = "cross-account-access-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect    = "Allow"
Principal = { AWS = "arn:aws:iam::${var.source_account_id}:root" }
Action    = "sts:AssumeRole"
Condition = {
StringEquals = {
"sts:ExternalId" = var.external_id  # prevents confused deputy attacks
}
}
}]
})
}

# In the source account (account A), grant the permission to assume
resource "aws_iam_role_policy" "assume_cross_account" {
role = aws_iam_role.deployer.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect   = "Allow"
Action   = "sts:AssumeRole"
Resource = "arn:aws:iam::${var.target_account_id}:role/cross-account-access-role"
}]
})
}

Implementing least privilege

The principle of least privilege means granting only the permissions needed to perform a specific function and nothing more. In practice, it means:

  • Scope actions precisely: Use s3:GetObject not s3:*. Use ec2:DescribeInstances not ec2:*.
  • Scope resources precisely: Use the specific resource ARN rather than *. For DynamoDB: arn:aws:dynamodb:region:account:table/specific-table, not *.
  • Use conditions: Add conditions to restrict when permissions apply — source IP, MFA requirement, resource tags, etc.
  • Avoid wildcard patterns in sensitive services: iam:*, sts:*, and kms:* are especially dangerous. Wildcard access to IAM means the principal can grant itself any permission.

AWS's IAM Access Analyzer and the IAM Policy Simulator help audit existing policies. Access Analyzer also has a feature that generates least-privilege policies from CloudTrail logs — it observes what an identity actually calls over a period and generates a policy based on observed usage. This is the most practical approach for brownfield permissions: run the role in production, observe for 30–90 days, then generate a tight policy from actual usage.

Common mistakes

AdministratorAccess on service roles. The policy arn:aws:iam::aws:policy/AdministratorAccess grants full access to everything in the account. Attaching this to a Lambda function or EC2 instance because "it needs to do a lot of things" is a security catastrophe waiting to happen. A compromised instance or function with AdministratorAccess can exfiltrate data, create new users, disable logging, or delete everything.

Storing long-lived access keys in code or environment variables. Access keys associated with IAM users don't expire. If they leak (in a git commit, an S3 bucket, logs), they're valid indefinitely until rotated. Use IAM roles and instance metadata instead. For CI/CD, use OIDC federation (GitHub Actions OIDC, GitLab OIDC) rather than static keys.

Shared IAM users across services or teams. One IAM user shared by multiple services means you can't audit which service made which API call, can't rotate credentials for one service without affecting others, and can't revoke access for one service without affecting all. Each service gets its own role.

Permission boundaries not used for delegated IAM. If you let application teams create their own IAM roles, they can escalate their own privileges. A permission boundary constrains what roles they can create — the team can create roles, but those roles can't exceed what the permission boundary allows.

Debugging access issues

When an IAM error appears (AccessDeniedException or is not authorized to perform: on resource:), the debug workflow is:

  1. Identify the principal in the error — it's in the message as "User: arn:aws:iam::..."
  2. Use the IAM Policy Simulator in the console to test whether that principal has the required permission
  3. Check for explicit denies — SCPs, permission boundaries, resource policies
  4. Check that resource-based policies allow the cross-account action if applicable
  5. Enable CloudTrail data events if not already enabled — access denials are logged there with full context

IAM is complex enough that AWS's documentation for specific services often lists exactly which IAM permissions are needed. The IAM actions reference at docs.aws.amazon.com/service-authorization/latest/reference/ is the authoritative source for every service, action, resource type, and condition key.

Related articles