This is a series of articles about setting up a complex Serverless backend infrastructure with AWS SAM and CloudFormation.
Here is the index of all the articles in case you want to jump to any of them:
1. Setup of AppSync and API Gateway with Multiple AWS Cognito User Pools
- Configuring S3 Buckets with Permissions and Access Roles in AWS Cognito AuthRole
3. Intro to DynamoDB Resolvers for AppSync Implementation
4. Intro to Lambda Resolvers for AppSync Implementation
5. Configuring an AWS VPC to Include Lambda Resolvers with a Fixed IP
6. Intro to Pipeline Resolvers for AppSync Implementation
7. Handling Lambda Resolver Timeouts with SNS Messages
You want users to read anything in the bucket but only write to their own folder. One wrong IAM policy and either everyone can write everywhere, or nobody can write at all.
The trick is Cognito's ${cognito-identity.amazonaws.com:sub} variable. It injects the user's identity ID directly into the IAM policy, scoping write permissions to a folder that matches their ID — without any custom backend logic.
The Setup
Create the bucket. Structure data so each user's files live under a folder named after their Cognito identity sub. Then wire up the IAM roles.
Resources:
MyS3Bucket:
Type: "AWS::S3::Bucket"The Roles
The AuthRole gets read access to the entire bucket and write access only to the user's own folder. The UnAuthRole gets read-only access. An Identity Pool ties them together.
Resources:
# Unauthenticated Role with write access to the user folder
CognitoAuthRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: "Allow"
Principal:
Service: "cognito-identity.amazonaws.com"
Action: "sts:AssumeRoleWithWebIdentity"
Policies:
- PolicyName: "S3AccessPolicy"
PolicyDocument:
Statement:
- Effect: "Allow"
Action: "s3:GetObject"
Resource: !Sub "arn:aws:s3:::${MyS3Bucket}/*"
- Effect: "Allow"
Action: "s3:PutObject"
Resource: !Sub "arn:aws:s3:::${MyS3Bucket}/${cognito-identity.amazonaws.com:sub}/*"
# Unauthenticated Role with read access to the S3 bucket
CognitoUnAuthRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Federated: "cognito-identity.amazonaws.com"
Action:
- "sts:AssumeRoleWithWebIdentity"
Condition:
StringEquals:
"cognito-identity.amazonaws.com:aud": !Ref CognitoIdentityPool
Policies:
- PolicyName: "S3ReadAccessPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action: "s3:GetObject"
Resource: !Sub "arn:aws:s3:::${MyS3Bucket}/*"
# Cognito Identity Pool
CognitoIdentityPool:
Type: "AWS::Cognito::IdentityPool"
Properties:
IdentityPoolName: "MyIdentityPool"
AllowUnauthenticatedIdentities: true
CognitoIdentityProviders:
- ClientId: !Ref UserPoolGeneralClient
ProviderName: !GetAtt [UserPoolGeneral, ProviderName]
# Attach roles to the Cognito Identity Pool
SetIdentityPoolRoles:
Type: "AWS::Cognito::IdentityPoolRoleAttachment"
Properties:
IdentityPoolId: !Ref CognitoIdentityPool
Roles:
authenticated: !GetAtt [CognitoAuthRole, Arn]
unauthenticated: !GetAtt [CognitoUnAuthRole, Arn]
# Assuming you have a user pool client for the general user pool
UserPoolGeneralClient:
Type: "AWS::Cognito::UserPoolClient"
Properties:
ClientName: "GeneralUserClient"
UserPoolId: !Ref UserPoolGeneral
GenerateSecret: falseThe ${cognito-identity.amazonaws.com:sub} in the PutObject resource does all the heavy lifting. Each authenticated user can only write to their own folder. No application-level checks needed — IAM enforces it at the infrastructure layer.
Code examples are simplified to illustrate the approach. Some adjustments may be needed for production.
Next UP: Part 3. Intro to DynamoDB Resolvers for AppSync Implementation
