Example static Single Page Application (SPA) deployed to AWS S3 and CloudFront.
- S3 for storing static assets
- CloudFront for global CDN distribution
- ACM for SSL/TLS certificates
- Route53 for DNS management
- Atmos for infrastructure configuration management
- OpenTofu for infrastructure as code
A deployment system for static web applications using GitHub Actions, Atmos, and OpenTofu for automated deployment across multiple environments.
This system provides a complete CI/CD pipeline for deploying static web applications to AWS using:
- GitHub Actions: Automated workflows for building and deploying static assets
- Atmos: Configuration management for multi-environment deployments
- OpenTofu/Terraform: Infrastructure as Code for S3, CloudFront, ACM, and Route53
├── public/ # Static HTML/CSS/JS assets
├── terraform/
│ ├── components/
│ │ └── spa-s3-cloudfront/ # Terraform component for S3+CloudFront
│ └── stacks/ # Atmos stack configurations
│ ├── _default.yaml # Default variables
│ ├── defaults/app.yaml # App component defaults
│ ├── deps/ # Remote state dependencies
│ ├── dev.yaml # Development environment
│ ├── preview.yaml # PR preview environments
│ ├── staging.yaml # Staging environment
│ └── prod.yaml # Production environment
├── profiles/
│ └── github/ # GitHub Actions OIDC authentication
└── .github/workflows/ # GitHub Actions workflows
| Workflow | Trigger | Environment | Actions |
|---|---|---|---|
main-branch.yaml |
Push to main |
dev |
Build, deploy infra, sync to S3, invalidate cache, create draft release |
feature-branch.yml |
PR to main with deploy label |
preview |
Build, deploy isolated preview (per PR), sync and invalidate |
release.yaml |
Published GitHub release | staging → production |
Build, deploy to staging, then production |
preview-cleanup.yml |
PR closed or deploy label removed |
preview |
Destroy preview environment infrastructure |
The spa-s3-cloudfront Terraform component creates:
- S3 Bucket: Origin for static assets with encryption
- CloudFront Distribution: Global CDN with HTTPS
- ACM Certificate: SSL/TLS certificate (in us-east-1)
- Route53 Record: DNS alias to CloudFront
Environment stacks inherit from defaults/app.yaml which maps the app component to spa-s3-cloudfront:
components:
terraform:
app:
metadata:
component: spa-s3-cloudfront
vars:
name: app-cloudfront-s3
parent_zone_name: !terraform.state dns-delegated .default_domain_name
parent_zone_id: !terraform.state dns-delegated .default_dns_zone_id
web_acl_id: !terraform.state waf/cloudfront .arnThe app reads from remote state for:
dns-delegated: Parent DNS zone name and IDwaf/cloudfront: WAF ACL ARN for CloudFront protection
- AWS Account with Route53 hosted zone
- GitHub repository with OIDC configured for AWS access
- Atmos CLI installed
- OpenTofu installed (version in
.opentofu-version)
Configure these in your repository settings:
ATMOS_VERSION: Version of Atmos to install
GitHub Actions authenticate via OIDC. Configure IAM roles in profiles/github/atmos.yaml:
auth:
providers:
github-oidc:
kind: github/oidc
region: us-east-2
spec:
audience: sts.amazonaws.com
identities:
plat-dev/terraform:
kind: aws/assume-role
via:
provider: github-oidc
principal:
assume_role: arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME- Add static content to
public/directory - Push to main branch: Deploys to
devenvironment - Create PR with
deploylabel: Deploys to isolated preview environment - Publish release: Deploys to
stagingthenproduction
graph LR
A[Push to main] --> B[Build Assets]
B --> C[Deploy to dev]
C --> D[Create Draft Release]
E[Publish Release] --> F[Deploy to staging]
F --> G[Deploy to production]
H[PR with deploy label] --> I[Deploy to preview]
J[PR closed] --> K[Destroy preview]
mkdir -p dist && cp -r public/* dist/# Plan changes
atmos terraform plan app -s dev
# Deploy
atmos terraform deploy app -s dev
# Get outputs
atmos terraform output app -s dev --skip-init -- -raw urlCheck out these related projects.
- github-actions-workflows - Reusable workflows for different types of projects
- terraform-aws-cloudfront-s3-cdn - Terraform module to provision CloudFront CDN with S3 origin
Preamble to the Apache License, Version 2.0
Complete license is available in the LICENSE file.
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
All other trademarks referenced herein are the property of their respective owners.