Least Privilege Role for Your AWS Static Site

pirx
2 min readJul 4, 2022

Let’s say you’ve deployed a static site on our AWS account using Cloudfront and S3, such as described in Automated Static Site Deployment in AWS Using Terraform. When it comes time to update its content or tweak some settings, using an all-powerful AWS admin account is bad security practice (not to mention overkill).

The Terraform Module

I wrote the terraform-static-site-deploy-role Terraform module to make it easier to set up a role that grants just the right amount of access to manage a site’s AWS resources. What it does is quite simple:

  • Create a role
  • Attach a policy to the role that gives it permission to manage some aspects of a static site’s Cloudfront distribution and S3 bucket
  • Allow one or more specified IAM users to assume the role

Note that the module does NOT create the IAM user(s) — it either should already exist, or has to be created separately.

When deployed, the IAM user(s) that can assume the role will then be able to do the most common tasks:

  • Update the static site content in the S3 bucket
  • Create a new invalidation in the Cloudfront distribution. This effectively refreshes the cache before expiration.

A few more (possibly less frequent) tasks can be done using this role, such as updating the Cloudfront distribution. For a complete list of actions, see policies.tf in the module.

To use the module, add something like this to your Terraform deployment:

module "mysite_deploy_role" {
source = "github.com/pirxthepilot/terraform-aws-static-site-deploy-role"
name = "mysite_deploy_role"
domain = "example.com"
iam_user_arns = ["arn:aws:iam::111111111111:user/user1", "arn:aws:iam::111111111111:user/user2"]
s3_bucket_arn = "arn:aws:s3:::example.com"
cloudfront_distribution_arn = "arn:aws:cloudfront::111111111111:distribution/ABCDE12345"
}

If used with terraform-aws-static-site:

module "mysite" {
source = "github.com/pirxthepilot/terraform-aws-static-site"
name = "mysite"
domain = var.domain
subdomains = var.subdomains
route53_zone_id = var.route53_zone_id
}
module "mysite_deploy_role" {
source = "github.com/pirxthepilot/terraform-aws-static-site-deploy-role"
name = "mysite_deploy_role"
domain = var.domain
iam_user_arns = ["arn:aws:iam::111111111111:user/user1", "arn:aws:iam::111111111111:user/user2"]
s3_bucket_arn = module.mysite.s3_bucket_arn
cloudfront_distribution_arn = module.mysite.cloudfront_distribution_arn
}

If you are creating the IAM user as part of your Terraform deployment:

resource "aws_iam_user" "deploy" {
name = "deploy"
}
module "mysite_deploy_role" {
source = "github.com/pirxthepilot/terraform-aws-static-site-deploy-role"
name = "mysite_deploy_role"
domain = "example.com"
iam_user_arns = [aws_iam_user.deploy.arn]
s3_bucket_arn = "arn:aws:s3:::example.com"
cloudfront_distribution_arn = "arn:aws:cloudfront::111111111111:distribution/ABCDE12345"
}

It is highly recommended that the source URL is pinned to a specific release tag or commit ID by appending ?ref=<tag_or_commit>, to minimize unwanted future breaking changes. E.g.:

  source = "github.com/pirxthepilot/terraform-aws-static-site-deploy-role?ref=v0.0.2"

If you have multiple static sites, simply create multiple module declarations, one per site. Of course, you can use the same IAM user for all, so that this user would be able to manage all your static sites.

Originally published at https://pirx.io on July 4, 2022.

--

--

pirx

Security engineer when not distracted by other things