Cloud Knowledge

Your Go-To Hub for Cloud Solutions & Insights

Advertisement

Security & Identity-Access Management (IAM) in AWS

Security & Identity-Access Management (IAM) in AWS

Security & Identity-Access Management (IAM) in AWS — Practical guide to least-privilege, cross-account controls, detection and remediation

A hands-on, operational guide for cloud security engineers, architects and devops teams. Includes scripts, detection examples and remediation patterns.

AWS IAM Security

Why IAM matters

Misconfigurations in IAM are a leading root cause of cloud breaches. This guide helps you audit, detect, and fix risky identities, over-privileged roles, and unsafe trust relationships.

Executive summary

AWS Identity and Access Management controls who can do what in your AWS environment. When IAM is misconfigured, attackers and well-intentioned users can escalate privileges, access sensitive data, or disrupt services. This article walks you through practical, repeatable steps to:

  • Discover and enumerate principals, roles and policies at scale.
  • Identify over-privileged identities and unsafe trust relationships.
  • Instrument detection and alerting for anomalous or risky permission use.
  • Automate safe remediation and policy generation for least-privilege.

(Authoritative guidance and best-practices referenced from AWS docs and security blogs.)

Common IAM mistakes & their impact

The most frequent causes of IAM risk we see in the field:

  1. Wildcard policies or overly broad actions — grants like "Action":"*" or "Resource":"*" lead to privilege creep and make escalation trivial.
  2. Long-lived static credentials — hard-coded access keys in code, build systems, or CI/CD agents remain a sensitive attack vector.
  3. Unreviewed cross-account trusts — a single trusted role in another account can become an implicit blast radius for compromise.
  4. Using root user — root account access without MFA is a single point of failure; avoid day-to-day use.
  5. No telemetry — without CloudTrail logging, Access Analyzer findings, or GuardDuty alerts, misuse goes unseen.

These mistakes are well documented; AWS recommends prefering temporary credentials (roles), MFA for privileged users, and use of Access Analyzer to generate least-privilege policies.

Core principles

Apply these control principles across accounts and workloads:

Least privilege

Define exactly the actions required and nothing more. Use IAM Access Analyzer policy generation and start from zero-privilege, adding permissions only after validation.

Short-lived credentials & roles

Prefer IAM roles with temporary STS credentials for applications and users. Avoid embedding long-lived Access Key / Secret Key pairs.

Separation of duties

Keep administration and operational roles separate. Use AWS Organizations Service Control Policies (SCPs) and administrative accounts to limit what member accounts can do.

Assume nothing — audit everything

Continuously enumerate identities, policies, trust relationships and resource-based policies to find unintended access paths.

Audit and enumeration — where to start

To reduce IAM risk you must answer three operational questions:

  1. Which principals (users, roles, groups) exist across accounts?
  2. Which policies attach to them (identity-based or resource-based)?
  3. Which principals can act on which sensitive resources (S3, KMS, RDS, Secrets Manager)?

Key tools and services: AWS CLI, AWS SDKs/PowerShell, IAM Access Analyzer, AWS Config, CloudTrail, AWS Organizations, AWS Security Hub, GuardDuty and CloudWatch.

Quick enumeration commands (CLI)

# List roles in an account
aws iam list-roles --output table

# List users and attached policies
aws iam list-users --output table
aws iam list-attached-user-policies --user-name alice

# List resource-based policies (example S3)
aws s3api get-bucket-policy --bucket my-sensitive-bucket
    

For multi-account environments, script enumeration via AWS Organizations (assume OrganizationAccountAccessRole into each member account) and aggregate results into an S3 bucket or a database for analysis. AWS docs provide a cross-account role tutorial for delegation patterns.

PowerShell (AWSPowerShell.NetCore)

# Install module (if needed)
Install-Module -Name AWSPowerShell.NetCore -Scope CurrentUser

# Set credentials (store profile)
Set-AWSCredential -AccessKey "AKIA..." -SecretKey "..." -StoreAs "prod-admin"

# Get IAM roles
Get-IAMRoles | Select-Object RoleName, CreateDate

# Export roles/policies to csv
Get-IAMRoles | ForEach-Object {
  $r = $_
  $policies = (Get-IAMRolePolicyList -RoleName $r.RoleName).PolicyNames -join ';'
  [PSCustomObject]@{RoleName=$r.RoleName;Policies=$policies}
} | Export-Csv roles.csv -NoTypeInformation
    

Docs: how to specify AWS credentials in PowerShell.

Cross-account access and trust boundaries

Cross-account access (via roles, resource-based policies, or AWS Organizations) is powerful but often misunderstood. Misconfigured trust relationships are a major cause of privilege expansion between accounts.

Common cross-account patterns

  • AssumeRole (user/role in Account A assumes role in Account B) — typical for multi-account admin or CI/CD jobs.
  • Resource-based policies (S3 bucket policies, KMS key policies) that allow principals in other accounts.
  • Service-linked roles and delegated admin roles in Organizations (OrganizationAccountAccessRole).

When designing cross-account access:

  1. Minimize the set of trusted principals (explicit account IDs or role ARNs).
  2. Scope trust with conditions (e.g., aws:SourceAccount, aws:PrincipalOrgID, IP or VPC conditions where possible).
  3. Use short session durations and require MFA for human-initiated assume-role flows.

AWS offers multiple ways to grant cross-account access; the security blog outlines four common methods and their tradeoffs. Use resource-based policies when appropriate and prefer roles with explicit trust policies instead of trusting entire accounts with broad principals.

Example trust policy (role in Account B trusted by Account A)

{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Effect":"Allow",
      "Principal":{"AWS":"arn:aws:iam::111122223333:role/CI-CD-Runner"},
      "Action":"sts:AssumeRole",
      "Condition":{
        "StringEquals":{"aws:PrincipalOrgID":"o-xxxxxxxxxx"}
      }
    }
  ]
}
    

Use aws:PrincipalOrgID to restrict trust to accounts in your organization. See AWS docs on OrganizationAccountAccessRole and cross-account delegation.

Serverless & managed service identity patterns

Serverless architectures (Lambda, Fargate tasks, EKS, managed RDS) rely heavily on IAM roles. Key differences from classic EC2:

  • Workloads should use roles with tight resource-scoped permissions (e.g., per-function role or a small role reused by a group of functions with shared purpose).
  • Avoid over-scoped "lambdaBasicExecution" + wildcard policies. Compose minimal policies and use service-specific conditions.
  • Use IAM roles for service accounts (IRSA) for EKS and map Kubernetes service accounts to IAM roles to avoid static credentials.

Consider using a “role-per-application” model, not “role-per-account”. Monitor role usage with CloudTrail and Access Analyzer to narrow permissions over time.

Detecting IAM misuse — telemetry and queries

Detection requires two components:

  1. Telemetry collection — enable CloudTrail across all regions, send logs to a central S3 bucket and optionally to CloudWatch or an ELK/SIEM.
  2. Signal detection — queries and alerts that find abnormal AssumeRole events, unusual policy changes, or use of root credentials.

CloudTrail-based detections (examples)

Typical suspicious events:

  • New inline policy added to a role that allows iam:PassRole or iam:CreatePolicy.
  • Unusual sts:AssumeRole calls from IP addresses/geographies not expected.
  • Creation of new access keys for a rarely-used user, then immediate use.

Athena query: detect newly created access keys and use

-- Assumes CloudTrail logs partitioned in Athena
SELECT eventTime, userIdentity.userName, eventName, sourceIPAddress, eventSource
FROM cloudtrail_logs
WHERE eventName IN ('CreateAccessKey','UpdateAccessKey','DeleteAccessKey')
AND from_iso8601_timestamp(eventTime) > current_timestamp - interval '7' day
ORDER BY eventTime DESC;
    

Athena query: suspicious AssumeRole usage

SELECT eventTime, userIdentity.arn, requestParameters.roleArn, sourceIPAddress
FROM cloudtrail_logs
WHERE eventName='AssumeRole'
AND parse_datetime(eventTime,'yyyy-MM-dd''T''HH:mm:ssZ') > date_sub('day',7,current_timestamp)
ORDER BY eventTime DESC;
    

GuardDuty and AWS Security Hub can generate higher-level findings for anomalous IAM activity. Use Access Analyzer to highlight resource-based policies that allow external principals access to your resources.

Automated remediation & safe workflows

When detection finds an incident or risky config, your remediation should follow a safe, reversible workflow:

  1. Enrichment — gather context (who assumed, which role/policy, time, source IP, recent Terraform/CloudFormation changes).
  2. Assessment — categorize risk (high/medium/low) and whether it’s a false positive.
  3. Remediation action — for high risk: remove trust, rotate keys, disable role; for low risk: create ticket and notify owners.
  4. Post-mortem — root cause and permanent fix (policy change, SCOPs, CI gating).

Example: Lambda auto-remediate new wildcard policy

A pattern: detect PutRolePolicy or CreatePolicy events that include dangerous wildcards, invoke a Lambda (with minimal role) that tags the role for review and sends a message to PagerDuty/Slack. Keep the remediation conservative — tagging and disabling assume-role should be manual unless confidence is high.

Simple pseudo-workflow

  1. CloudTrail → EventBridge rule for PutRolePolicy/CreatePolicy events.
  2. EventBridge → Lambda function that analyzes policy JSON for wildcards or iam:* actions.
  3. If high risk → Lambda adds tag iam-risk:quarantine=true and sends alert; optionally add inline deny policy to block sensitive actions.

Concrete examples: policies, PowerShell, CLI & policy generation

Example 1 — Minimal S3 read-only policy for a Lambda

{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Effect":"Allow",
      "Action":[
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource":[
        "arn:aws:s3:::my-app-bucket",
        "arn:aws:s3:::my-app-bucket/*"
      ]
    }
  ]
}
    

Example 2 — Deny sensitive KMS usage unless from specific role

{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Sid":"DenyKMSIfNotFromSpecificRole",
      "Effect":"Deny",
      "Action":"kms:Decrypt",
      "Resource":"arn:aws:kms:us-east-1:123456789012:key/abcd-ef00-1234",
      "Condition":{
        "StringNotEquals":{
          "aws:PrincipalArn":"arn:aws:iam::123456789012:role/MyAuthorizedRole"
        }
      }
    }
  ]
}
    

PowerShell scripts (practical)

Rotate access keys for users who haven't used their keys in 90+ days:

# Requires AWSPowerShell.NetCore
$threshold = (Get-Date).AddDays(-90)
$users = Get-IAMUsers
foreach ($u in $users) {
  $keys = Get-IAMAccessKey -UserName $u.UserName
  foreach ($k in $keys) {
    if ($k.CreateDate -lt $threshold) {
      Write-Host "Rotating key for $($u.UserName): $($k.AccessKeyId)"
      # Option: disable old key, create new, store securely, notify owner
      # Update: follow your org policy to rotate and distribute new key
    }
  }
}
    

Docs on specifying credentials in PowerShell are useful for safe automation.

AWS CLI snippet: find roles with inline policies containing wildcards

for role in $(aws iam list-roles --query 'Roles[].RoleName' --output text); do
  aws iam list-role-policies --role-name "$role" --query 'PolicyNames[]' --output text | \
  while read policy; do
    aws iam get-role-policy --role-name "$role" --policy-name "$policy" \
      --query 'PolicyDocument.Statement[?contains(Action, `*`)]' --output json | \
    grep -q '"*"' && echo "Role $role has wildcard in inline policy $policy"
  done
done
    

Use IAM Access Analyzer policy generation

IAM Access Analyzer can produce candidate least-privilege policies by analyzing CloudTrail access activity. This is a practical starting point for narrowing broad managed policies. AWS docs recommend using policy generation and then iterating with policy simulator.

Microsoft Graph snippet — when you need to investigate a user in Azure AD (hybrid teams)

Useful if your identity lifecycle or SSO touches Azure AD / Entra and you need to correlate a user across clouds.

# MS Graph sample (curl) - get user by UPN (requires token)
curl -H "Authorization: Bearer $ACCESS_TOKEN" "https://graph.microsoft.com/v1.0/users/jane.doe@contoso.com" | jq
    

Use this to confirm the user's attributes, MFA status, and sign-in logs in Azure AD (then correlate to AWS SSO/Identity Center or application logs).

Practical steps to achieve least privilege at scale

Moving to least privilege is a program, not a single change. Key stages:

  1. Inventory — collect current policies, roles, resource-based policies across accounts.
  2. Measure — gather usage data (CloudTrail) to see what actions are actually used by each principal.
  3. Generate candidate policies — use Access Analyzer and policy generation based on usage logs.
  4. Test in staging — run policy in simulated environment and/or use Policy Simulator for coverage gaps.
  5. Rollout with canary — gradually enforce narrow policies, monitor breakages, add exceptions as necessary.
  6. Continuous audit — schedule periodic re-generation and review (quarterly or on major app change).

AWS published a multi-part strategy for least-privilege at scale with practical recommendations — follow that guidance as a program roadmap.

Operational checklist & recommended cadence

Task Cadence Reason
Export roles/policies to inventory Weekly Track drift and changes
Review Access Analyzer findings Weekly Detect unintended resource sharing
Rotate/disable unused access keys Monthly Reduce long-lived key risk
Threat hunt for unusual AssumeRole activity Daily/On alert Early detection of lateral movement
Policy generation & test rollout Quarterly Continuous least-privilege refinement

FAQ

Q: Should I create one role per Lambda or reuse roles?

A: Prefer role-per-application or role-per-team if function count is very high. For highly sensitive functions, a dedicated role is best. Monitor role usage to avoid explosion of roles.

Q: How to handle third-party SaaS that requires cross-account access?

A: Provide a narrowly-scoped role with exact required permissions and a short session duration. Use resource policies with conditions (aws:SourceArn, aws:SourceAccount) when possible, and require the provider to present specific external IDs to prevent confused deputy attacks.

Q: Are AWS managed policies safe?

A: AWS managed policies are convenient but often broader than necessary. Use them only as a starting point, and generate least-privilege policies for production workloads.

Short case study: Fixing cross-account blast radius

A mid-sized org discovered that their dev account trusted the entire management account (`"Principal":{"AWS":"arn:aws:iam::111122223333:root"}`) and had an S3 bucket with a relax policy. The remediation steps were:

  1. Identify the trust via Access Analyzer and CloudTrail.
  2. Replace account-wide trust with a specific role ARN and condition on `aws:PrincipalOrgID`.
  3. Run a staged rollback plan and test application behavior for 48 hours.
  4. Create an Access Analyzer rule to flag any future `root` principal in trust policies.

Following this process reduced the organization’s cross-account attack surface significantly and made future reviews automatable.

Resources & further reading

Additionally, consult AWS official documentation and the IAM whitepapers for deep dives on multi-account organization design and policy best practices.

Conclusion — practical next steps

  1. Enable CloudTrail across all regions and enable Access Analyzer.
  2. Inventory identities and resource policies, aggregate into a central store (S3/Glue/Athena).
  3. Run least-privilege policy generation and stage enforcement with canaries.
  4. Automate safe remediation (tagging/quarantine) and alert owners for manual review.

Last updated: October 25, 2025

Leave a Reply

Your email address will not be published. Required fields are marked *