This document explains the minimal IAM permissions required to deploy both the backend and Streamlit stacks.
The IAM permissions are split into two policies due to AWS's 6144 character limit for managed policies:
- Part 1 (
iam-deployment-policy-part1.json): Core services (CloudFormation, Lambda, API Gateway, DynamoDB, S3, IAM) - Part 2 (
iam-deployment-policy-part2.json): Container services (ECR, ECS, VPC, ALB, ACM)
Both policies must be attached to the same IAM user or role.
# Create IAM user
aws iam create-user --user-name sleep-quality-advisor-deployer
# Create both policies
aws iam create-policy \
--policy-name SleepQualityAdvisorDeployment-Part1 \
--policy-document file://iam-deployment-policy-part1.json
aws iam create-policy \
--policy-name SleepQualityAdvisorDeployment-Part2 \
--policy-document file://iam-deployment-policy-part2.json
# Attach both policies to user (replace ACCOUNT_ID with your AWS account ID)
aws iam attach-user-policy \
--user-name sleep-quality-advisor-deployer \
--policy-arn arn:aws:iam::ACCOUNT_ID:policy/SleepQualityAdvisorDeployment-Part1
aws iam attach-user-policy \
--user-name sleep-quality-advisor-deployer \
--policy-arn arn:aws:iam::ACCOUNT_ID:policy/SleepQualityAdvisorDeployment-Part2
# Create access keys
aws iam create-access-key --user-name sleep-quality-advisor-deployer# Create trust policy for EC2 or your CI/CD service
cat > trust-policy.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
# Create role
aws iam create-role \
--role-name SleepQualityAdvisorDeploymentRole \
--assume-role-policy-document file://trust-policy.json
# Create both policies
aws iam create-policy \
--policy-name SleepQualityAdvisorDeployment-Part1 \
--policy-document file://iam-deployment-policy-part1.json
aws iam create-policy \
--policy-name SleepQualityAdvisorDeployment-Part2 \
--policy-document file://iam-deployment-policy-part2.json
# Attach both policies to role
aws iam attach-role-policy \
--role-name SleepQualityAdvisorDeploymentRole \
--policy-arn arn:aws:iam::ACCOUNT_ID:policy/SleepQualityAdvisorDeployment-Part1
aws iam attach-role-policy \
--role-name SleepQualityAdvisorDeploymentRole \
--policy-arn arn:aws:iam::ACCOUNT_ID:policy/SleepQualityAdvisorDeployment-Part2If you prefer the AWS Console:
- Go to IAM → Policies → Create policy
- Click JSON tab
- Paste contents of
iam-deployment-policy-part1.json - Name it:
SleepQualityAdvisorDeployment-Part1 - Click Create policy
- Repeat steps 1-5 for
iam-deployment-policy-part2.json(name:SleepQualityAdvisorDeployment-Part2) - Go to Users → Create user → Name:
sleep-quality-advisor-deployer - Click Next → Attach policies directly
- Search and select both policies:
SleepQualityAdvisorDeployment-Part1SleepQualityAdvisorDeployment-Part2
- Click Next → Create user
- Go to user → Security credentials → Create access key
These permissions are primarily for the backend stack and SAM deployments:
- Why: SAM deployments use CloudFormation to create, update, and delete infrastructure
- Actions: Create/update/delete stacks, change sets, describe resources, list exports
- Scope: All stacks
- Why: SAM packages Lambda code and stores it in S3
- Actions: Create bucket, put/get objects, manage lifecycle
- Scope: Only
aws-sam-cli-managed-default-*buckets
- Why: Create execution roles for Lambda, ECS tasks, etc.
- Actions: Create/delete roles, attach/detach policies, pass role
- Scope: Roles with prefix
sleep-quality-advisor-*andstreamlit-*
- Why: Serverless functions for API endpoints and scheduled tasks
- Actions: Create/delete/update functions, event source mappings, permissions
- Scope: Functions with prefix
sleep-quality-advisor-*
- Why: REST API for data ingestion and Fitbit OAuth
- Actions: Create/update/delete REST APIs and resources
- Scope: All REST APIs in the region
- Why: Store environmental readings and sleep sessions
- Actions: Create/delete/update tables, manage backups, tagging
- Scope: All tables
- Why: Queue for asynchronous processing of environmental data
- Actions: Create/delete queues, manage attributes
- Scope: All queues
- Why: Store API keys, OAuth tokens, and shared secrets
- Actions: Create/delete/update secrets, get secret values
- Scope: Secrets under
ingest/*,fitbit/*, andstreamlit/*
- Why: Store Fitbit client ID
- Actions: Put/get/delete parameters
- Scope: Parameters under
/fitbit/*
- Why: Schedule daily Fitbit data fetch
- Actions: Create/delete rules, put/remove targets
- Scope: All rules
- Why: Lambda function logs
- Actions: Create/delete log groups, set retention
- Scope: All log groups
- Why: Distributed tracing for Lambda functions
- Actions: Put trace segments
- Scope: All resources
- Why: Used by taskipy scripts to get AWS account ID
- Actions: GetCallerIdentity
- Scope: All resources
These permissions are required for the Streamlit containerized deployment:
- Why: Store Docker images for Streamlit app
- Actions: Create repositories, push/pull images, manage lifecycle policies
- Scope: Repositories with prefix
sleep-quality-advisor-*
- Why: Run Streamlit app as containerized service
- Actions: Create clusters, services, task definitions
- Scope: All ECS resources
- Why: Simplified container deployment with built-in ALB
- Actions: Create/update/delete Express Gateway services
- Scope: All resources
- Why: ECS Express automatically creates VPC, subnets, security groups
- Actions: Create/manage VPCs, subnets, route tables, security groups, internet gateways
- Scope: All VPC resources
- Why: ECS Express creates ALB with SSL/TLS
- Actions: Create/manage ALBs, target groups, listeners
- Scope: All load balancers
- Why: ECS Express creates SSL/TLS certificates
- Actions: Request/delete certificates
- Scope: All certificates
The policy follows least privilege principles:
- Scoped Resources: Where possible, permissions are limited to specific resource patterns (e.g.,
sleep-quality-advisor-*) - No Write Access to Data: Deployment user cannot read/write DynamoDB data or secrets content
- No Production Access: Deployment permissions are separate from runtime permissions
- Limited IAM Actions: Can only create roles with specific name patterns
Some resources use wildcards (*) because:
- CloudFormation requires broad describe permissions
- ECS Express dynamically creates VPC resources with unpredictable names
- ECR GetAuthorizationToken works at account level
-
Condition Keys: Add condition keys to limit deployments to specific regions:
"Condition": { "StringEquals": { "aws:RequestedRegion": "us-east-1" } }
-
MFA Required: Require MFA for deployment user:
"Condition": { "BoolIfExists": { "aws:MultiFactorAuthPresent": "true" } }
-
IP Restrictions: Limit deployments to specific IP ranges:
"Condition": { "IpAddress": { "aws:SourceIp": ["YOUR_IP_RANGE"] } }
These permissions allow creation of resources that incur costs:
- Backend: ~$5-10/month (Lambda, DynamoDB on-demand, API Gateway)
- Streamlit: ~$33-35/month (ECS Fargate, ALB)
- Storage: <$1/month (S3, ECR, CloudWatch Logs)
Consider setting up AWS Budgets to monitor spending:
aws budgets create-budget \
--account-id ACCOUNT_ID \
--budget file://budget.json \
--notifications-with-subscribers file://notifications.jsonBefore deploying to production, test in a sandbox account:
- Create test IAM user with the policy
- Attempt backend deployment:
cd backend && uv run task deploy - Attempt streamlit deployment:
cd streamlit && uv run task deploy-infra - Verify all resources are created successfully
- Clean up: Delete both stacks
If you encounter authorization errors:
- Check the error message for the specific action and resource
- Verify the resource name matches the policy patterns
- Check if the action is included in the policy
- Review CloudTrail logs for denied API calls
- IAM PassRole errors: Ensure IAM:PassRole is included for role ARNs
- S3 bucket errors: Bucket names must match
aws-sam-cli-managed-default-* - ECR push errors: Ensure ECR GetAuthorizationToken is allowed
- Cross-stack reference errors: Ensure CloudFormation ListExports is allowed
To remove deployment permissions:
# Detach both policies
aws iam detach-user-policy \
--user-name sleep-quality-advisor-deployer \
--policy-arn arn:aws:iam::ACCOUNT_ID:policy/SleepQualityAdvisorDeployment-Part1
aws iam detach-user-policy \
--user-name sleep-quality-advisor-deployer \
--policy-arn arn:aws:iam::ACCOUNT_ID:policy/SleepQualityAdvisorDeployment-Part2
# Delete both policies
aws iam delete-policy \
--policy-arn arn:aws:iam::ACCOUNT_ID:policy/SleepQualityAdvisorDeployment-Part1
aws iam delete-policy \
--policy-arn arn:aws:iam::ACCOUNT_ID:policy/SleepQualityAdvisorDeployment-Part2
# Delete user (optional)
aws iam delete-user --user-name sleep-quality-advisor-deployerIf you need to modify the policy for your specific use case:
- Deploy in a test environment first
- Review CloudTrail logs for any denied actions
- Add only the specific actions that failed
- Test again before applying to production