IAM for Data Teams cheat sheet
Roles versus users, assume-role patterns, least privilege for pipelines, and the policy conditions that pass audits.
Core model
Roles for workloads, users for humans- Pipelines should assume roles, never hold long-lived user keys. A leaked access key is the most common cloud incident.
Principal → Policy → Resource- Access is granted when an identity policy allows it and no resource policy or SCP denies it. Explicit deny always wins.
aws sts assume-role --role-arn ... --role-session-name etl-run- Returns temporary credentials. Session names show up in CloudTrail, which is why they should identify the caller.
OIDC federation from GitHub Actions- Removes stored cloud keys from CI entirely. The trust policy pins the repo and branch.
Least privilege for pipelines
{ "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject"], "Resource": "arn:aws:s3:::lake/silver/orders/*" }- Scope to the exact prefix the job writes. s3:* on the whole bucket is the finding auditors always flag.
s3:ListBucket needs the bucket ARN, not the object ARN- The most common IAM confusion. List is a bucket action; Get and Put are object actions with a trailing wildcard.
One role per pipeline- Shared roles make blast radius unknowable. Per-pipeline roles keep CloudTrail attribution meaningful.
Condition: {"StringEquals": {"aws:SourceVpce": "vpce-123"}}- Restrict access to your VPC endpoint so stolen credentials are useless from the public internet.
Policy conditions worth knowing
"aws:MultiFactorAuthPresent": "true"- Require MFA for destructive actions like table deletion or key rotation.
"aws:PrincipalTag/team": "${s3:ExistingObjectTag/team}"- Attribute-based access control. Scales far better than writing a policy per team.
"aws:RequestedRegion": ["us-east-1"]- Data-residency enforcement, and it also blocks resource creation in unmonitored regions.
"s3:x-amz-server-side-encryption": "aws:kms"- Deny unencrypted writes so encryption cannot be skipped by a misconfigured client.
Warehouse-side identity
CREATE STORAGE INTEGRATION with STORAGE_AWS_ROLE_ARN- Snowflake assumes your role instead of holding keys. Rotate nothing; the trust relationship does the work.
CREATE STORAGE CREDENTIAL / EXTERNAL LOCATION- Unity Catalog's equivalent. Grant on the external location, not on raw cloud paths scattered in notebooks.
Snowflake: functional roles granted to access roles- Access roles hold object privileges; functional roles are what people get. Two layers keep the hierarchy auditable.
Workload Identity Federation (GCP)- Lets an external service impersonate a service account without a downloadable JSON key.
Secrets
aws secretsmanager get-secret-value --secret-id prod/warehouse- Fetch at run time from the pipeline's role. Never bake credentials into images or Airflow variables.
Rotation: 90 days with a Lambda rotator- Automated rotation is the only kind that actually happens. Manual rotation reminders get snoozed.
IAM permission secretsmanager:GetSecretValue scoped to one secret ARN- A role that can read every secret in the account defeats the purpose of using a secret store.
Never log the resolved secret- Mask it in logging config. Secrets in CloudWatch or Airflow logs are the second most common leak after Git.
Auditing
CloudTrail data events for S3- Management events are on by default; object-level reads are not. Enable data events on sensitive prefixes.
aws iam get-credential-report- Every user, key age, and MFA status in one CSV. The fastest way to find a five-year-old access key.
IAM Access Analyzer- Flags resources shared outside your account and generates least-privilege policies from real CloudTrail usage.
SNOWFLAKE.ACCOUNT_USAGE.ACCESS_HISTORY- Warehouse-side audit of who touched which column. Pair it with cloud audit logs for end-to-end lineage.
From DataLane — tutorials at/blog, practice SQL live in theplayground.