- React
- TypeScript
- Vite
- Tailwind CSS
See serverless-iac repo for more details.
GitHub Actions uses OIDC to assume an IAM Role without long-lived access keys.
AWS_ROLE_ARN: IAM Role ARN for OIDC (example:arn:aws:iam::123456789012:role/gh-actions-null4u)AWS_REGION: AWS region (example:ap-northeast-2)S3_BUCKET: Frontend artifact S3 bucket name (example:null4u-frontend-bucket)CLOUDFRONT_DISTRIBUTION_ID(optional): CloudFront distribution ID for cache invalidation (example:E1A2B3C4D5E6F7)
Create an IAM OIDC provider in AWS IAM:
- Role name:
gh-actions-null4u - Provider URL:
https://token.actions.githubusercontent.com - Audience:
sts.amazonaws.com
Create an IAM Role with this trust policy (replace placeholders):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:<OWNER>/<REPO>:ref:refs/heads/main"
}
}
}
]
}Attach an IAM policy to the role that allows S3 sync (policy name: GitHubActionsS3Access):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::<BUCKET_NAME>"
},
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:DeleteObject", "s3:PutObjectAcl"],
"Resource": "arn:aws:s3:::<BUCKET_NAME>/*"
}
]
}and cloudfront invalidation if needed. (policy name: GitHubActionsCloudFrontInvalidation):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["cloudfront:CreateInvalidation"],
"Resource": "arn:aws:cloudfront::<ACCOUNT_ID>:distribution/<DISTRIBUTION_ID>"
}
]
}gh workflow run "Deploy static site to S3" # --ref main
gh workflow view "Deploy static site to S3"
gh workflow run "Invalidate CloudFront cache"
# or
gh workflow run "Invalidate CloudFront cache" \
-f paths="/index.html /assets/*"

