IAM Policy Setup
Configure AWS IAM roles and policies to grant StackSage read-only access to your AWS resources.
Privacy-First Architecture
StackSage runs entirely in your GitHub Actions environment. Your AWS credentials never leave your infrastructure.
Overview
StackSage requires an IAM role in your AWS account with read-only permissions. This role will be assumed by GitHub Actions using OIDC (OpenID Connect) - no long-lived credentials needed.
Step 1: Create Trust Policy
Create a file trust-policy.json that allows GitHub Actions to assume the role:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::YOUR_ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:YOUR_ORG/YOUR_REPO:*"
}
}
}
]
}First-Time Setup
If you haven't set up GitHub OIDC before, you'll need to create the OIDC provider first. See GitHub's documentation.
Step 2: Create IAM Role
Create the IAM role using the trust policy:
aws iam create-role \
--role-name StackSageAuditor \
--assume-role-policy-document file://trust-policy.json \
--description "Read-only role for StackSage AWS audits"Step 3: Create Custom Policy
Create a file stacksage-policy.json with the required permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "StackSageFinOpsAndFuturePlanning",
"Effect": "Allow",
"Action": [
"access-analyzer:ListAnalyzers",
"access-analyzer:ListFindings",
"budgets:DescribeBudgets",
"ce:GetAnomalyMonitors",
"ce:GetAnomalySubscriptions",
"ce:GetCostAndUsage",
"ce:GetDimensionValues",
"cloudfront:ListDistributions",
"cloudtrail:DescribeTrails",
"cloudtrail:GetTrailStatus",
"cloudwatch:DescribeAlarms",
"cloudwatch:GetMetricStatistics",
"cloudwatch:ListMetrics",
"config:DescribeConfigurationRecorderStatus",
"dynamodb:DescribeTable",
"dynamodb:ListTables",
"ec2:DescribeAddresses",
"ec2:DescribeImages",
"ec2:DescribeInstances",
"ec2:DescribeNatGateways",
"ec2:DescribeRegions",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSnapshots",
"ec2:DescribeSubnets",
"ec2:DescribeVolumes",
"ec2:DescribeVpcEndpoints",
"ec2:DescribeVpcs",
"ec2:GetEbsEncryptionByDefault",
"ecs:DescribeClusters",
"ecs:DescribeServices",
"ecs:DescribeTaskDefinition",
"ecs:ListClusters",
"ecs:ListServices",
"ecs:ListTaskDefinitions",
"elasticache:DescribeCacheClusters",
"elasticache:DescribeReplicationGroups",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:DescribeLoadBalancers",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeTargetHealth",
"guardduty:ListDetectors",
"iam:GetAccessKeyLastUsed",
"iam:GetAccountPasswordPolicy",
"iam:GetAccountSummary",
"iam:ListAccessKeys",
"iam:ListUsers",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams",
"pricing:GetProducts",
"rds:DescribeDBClusters",
"rds:DescribeDBInstances",
"rds:DescribeDBSnapshots",
"rds:ListTagsForResource",
"route53:ListHostedZones",
"s3:GetBucketEncryption",
"s3:GetBucketLocation",
"s3:GetBucketLogging",
"s3:GetBucketPolicy",
"s3:GetBucketPolicyStatus",
"s3:GetBucketTagging",
"s3:GetBucketVersioning",
"s3:GetLifecycleConfiguration",
"s3:ListAllMyBuckets",
"s3control:GetPublicAccessBlock",
"securityhub:DescribeHub",
"sts:GetCallerIdentity",
"tag:GetResources"
],
"Resource": "*"
}
]
}Step 4: Attach Policy
Create and attach the custom policy to the role:
# Create the policy
aws iam create-policy \
--policy-name StackSageAuditPolicy \
--policy-document file://stacksage-policy.json
# Attach to role
aws iam attach-role-policy \
--role-name StackSageAuditor \
--policy-arn arn:aws:iam::YOUR_ACCOUNT_ID:policy/StackSageAuditPolicyStep 5: Configure GitHub Secrets
Add the role ARN to your GitHub repository secrets:
- Go to your repository → Settings → Secrets and variables → Actions
- Click "New repository secret"
- Name:
AWS_ROLE_ARN - Value:
arn:aws:iam::YOUR_ACCOUNT_ID:role/StackSageAuditor
Alternative: Using AWS Managed Policies
For quick setup, you can use AWS managed policies (though custom policies are more restrictive):
aws iam attach-role-policy \
--role-name StackSageAuditor \
--policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess
aws iam attach-role-policy \
--role-name StackSageAuditor \
--policy-arn arn:aws:iam::aws:policy/CloudWatchReadOnlyAccessVerification
Test your IAM setup by running the workflow manually:
- Go to your repository's Actions tab
- Select "StackSage AWS Audit"
- Click "Run workflow"
- Check for authentication errors in the logs
Multi-Account Setup
To audit multiple AWS accounts, create the role in each account with the same name, then configure your workflow to loop through accounts:
strategy:
matrix:
account:
- name: Production
role: arn:aws:iam::111111111111:role/StackSageAuditor
- name: Staging
role: arn:aws:iam::222222222222:role/StackSageAuditor
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ matrix.account.role }}Security Best Practices
- Use OIDC: Avoid long-lived access keys
- Scope trust policy: Limit to specific repositories
- Custom policies: Use least-privilege custom policies over managed policies
- Regular audits: Review role permissions quarterly
- CloudTrail: Monitor AssumeRole API calls for the StackSage role
Troubleshooting
Error: "User is not authorized to perform: sts:AssumeRoleWithWebIdentity"
The OIDC provider is not configured or the trust policy is incorrect. Verify:
- OIDC provider exists in IAM console
- Trust policy references correct GitHub org/repo
- Trust policy condition matches
sts.amazonaws.com
Error: "Access Denied" on specific API calls
The attached policy is missing required permissions. Compare against the custom policy above.