Chenwi Ngu
6 min readDec 17, 2023

SECURE DEPLOYMENT OF STATIC WEBSITE ON AWS S3 + AWS CLOUDFRONT USING TERRAFORM, FROM GITHUB ACTIONS WITH OPENID CONNECT

Terraform pipeline automation using GitOps approach. Here, we’ll use Terraform and GitHub Actions to host a static website on AWS S3 and serve it through CloudFront. Note, instead of using AWS API Keys and Secret Keys, and storing them in GitHub secrets, we can make it more secure by using the GitHub OIDC (OpenID Connect) Provider and only allowing these credentials to be run from our GitHub action in a specific repository.

Prerequisites

You will need these three things setup and configured before being able to use GitHub Actions to automate Terraform:

  1. OIDC Connection.
  2. You will need an AWS account and add OIDC Provider. Assign a role to the OIDC Provider. Create IAM policy to give the role access to the s3 bucket where terraform states will be stored. Create another IAM policy allowing the role to create an S3 bucket where the website files will be hosted, as well as create CloudFront distributions and Origin Access Controls (OAC).
  3. Finally, you will need to set up a GitHub repository with the following structure:
  • A directory s3_files where you will store your Terraform configuration files and then create a src directory in and store your website code.
  • A .github directory which you will then create a workflows directory in and store your GitHub Action configuration files.

GitHub repository

Create a private GitHub repository, take note of your username & repository name as you will need it later.

Example: Chenwingu/aws-s3-website

Login to AWS and create the AWS IAM Identity Provider

Create an IAM Identity Provider in AWS.

Go to IAM » Identity Providers.

Click Add Provider on the right side.

Provider type: OpenID Connect.

For the provider URL: https://token.actions.githubusercontent.com

For the Audience: sts.amazonaws.com

Click on “Add Provider”

Create S3 bucket (Backend)

Create an S3 bucket to store the Terraform states.

S3 » Create bucket > choose your unique “Bucket name” > AWS Region & Click on “Create bucket”.

Create AWS IAM Role and policies.

Go to IAM » Roles » Create Role

Select trusted entity: choose “Custom trust policy”.

{
"Version": "2008-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::YOUR_ACCOUNT_NUMBER:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:YOUR_GITHUB_USERNAME/YOUR_REPO_NAME:*"
}
}
}
]
}

Remember to use: YOUR_ACCOUNT_NUMBER, YOUR_GITHUB_USERNAME & YOUR_REPO_NAME

You can set optionally the specific branch, the GitHub action will be allowed.

Once you are ready, click on Next >> Next >> Give a name to your trusted entity policy, check your trusted entity policy >> Click on “Create role”

Add permissions to the trusted entity policy:

Create a new policy and attach it to the role. This policy allows the Role to access the s3 bucket where the terraform states will be stored.

Up right: Create Policy >> Click on “JSON”.

NOTE: Modify <YOUR_BUCKET> name.

Then: Next > Next > Choose a name you want for the Policy > Create Policy

Now I’ll create another policy and attach it to the Role! And this policy allows the role to create S3 bucket and upload static content, and create CloudFront distributions and Origin Access Control (OAC) to serve the web content.

Note: We cannot specify a particular Origin Access Control (OAC) as the resource in our policy. We have to use ”Resource”: “*” to allow the action on all OACs.

Upload Terraform Code on our GitHub Repository

provider.tf

s3.tf

cloudfront_distr.tf

outputs.tf

GitHub Secrets

We will create some secrets in our GitHub Repository, for GitHub Actions to use during execution of the Terraform code.

Click on the Settings >> Left panel » Secrets » Actions >> Click on: New repository secret

Enter the secrets you need to create (I created the below secrets)

· AWS_BUCKET_NAME: Your Backend Bucket Name

· AWS_BUCKET_KEY_NAME: The Path For Terraform State

· AWS_REGION: Your Region

· AWS_ROLE: ARN Of Your Role (Trusted Entity)

Setting Up GitHub Actions

We can now create the GitHub Actions! We need to create a file in our GitHub repository: “.github/workflows/pipeline.yml”

You may want to know! Why do I add my Terraform configuration to GitHub Actions or other CI/CD Pipelines?” Pipelines create more visibility, traceability, repeatability and simplicity.

Note: In GitHub Actions, a workflow can be triggered by various events, such as a push to a branch, a pull request being opened, or a release being published. These events are specified in the on section of the workflow file. In my workflow, the terraform apply command is wrapped in an if statement.

This if statement checks two conditions:

  1. "${{ github.ref }}" == "refs/heads/main" checks if the current branch is main.
  2. "${{ github.event_name }}" == "push" checks if the event that triggered the workflow is a push.

If both conditions are true, then the terraform apply command is executed. This means that the Terraform changes will only be applied when there is a push event on the main branch. If the event is a pull request or if the push is to a different branch, the terraform apply command will not be executed.

This is a common practice in Infrastructure as Code (IaC) workflows to ensure that changes are only applied to the infrastructure when the changes are merged into the main branch. It prevents potentially destructive changes from being applied when a pull request is opened or when changes are pushed to a feature branch.

SUCCESS!

In summary, deploying a static website on AWS using S3, CloudFront, and Terraform from GitHub Actions with OpenID Connect provides a streamlined, easy to manage, secure, traceable and scalable process.

Note! We can further enhance the functionality and security of our static website hosted on AWS with other AWS services including Route 53, AWS Certificate Manager, and WAF.

· AWS Route 53: a highly scalable and reliable domain name system (DNS) service that we can use to connect our custom domain name to the CloudFront distribution.

· AWS Certificate Manager: we can obtain an SSL/TLS certificate from ACM to provide secure browsing experience for our website visitors.

· Web Application Firewall (WAF): to protect our website from potential attacks.

Reminder

Thank you for following up to this point. I hope you found the article helpful, consider showing your support with a few claps! 👏👏👏

I look forward to connecting with you on LinkedIn https://www.linkedin.com/in/chenwingu/

Chenwi Ngu

Passionate DevOps and Cloud enthusiast, dedicated to helping teams leverage cloud technologies to engineer efficient software solutions.