Skip to content

Commit

Permalink
Added deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
dosaki committed Aug 11, 2024
1 parent e141cd2 commit 404f61e
Show file tree
Hide file tree
Showing 14 changed files with 351 additions and 0 deletions.
19 changes: 19 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Describe what this pull request is achieving

# Changelog

## Fixed
* Something no longer has a broken behaviour and now correctly does what it's meant to do.

## Added
* A new feature has been added to the project

## Changed
* This thing that used to work like this, now works like that

## Breaking changes
* This change will break the behaviour of this thing, so you need to do this other thing to fix it.

## Followup
* Identified X issue, see [this issue](https://link-to-issue)
* Created [tech-debt ticket](https://link-to-issue)
66 changes: 66 additions & 0 deletions .github/workflows/build-deployment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: build-artifacts
on:
push:
branches:
- master

jobs:
non-conditional-step-to-make-this-valid:
runs-on: ubuntu-latest
steps:
- run: echo "Github actions need a step without any conditions"

build_and_deploy:
timeout-minutes: 10
if: "!contains(github.event.head_commit.message, 'NO_DEPLOY')"
environment:
name: production
defaults:
run:
working-directory: .
runs-on: ubuntu-latest
env:
DEPLOYMENT_ENV: production
AWS_DEFAULT_REGION: eu-west-1
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Terraform setup
uses: hashicorp/setup-terraform@v3

- name: Install dependencies
run: npm install -g uglify-js uglifycss

- name: Build website
run: ./build.sh

- name: Terraform Init
run: |
cd ./terraform
terraform init
terraform workspace select ${DEPLOYMENT_ENV}
terraform get
- name: Terraform Validate
run: |
cd ./terraform
terraform validate
- name: Terraform Plan
run: |
cd ./terraform
terraform plan \
-var profile=pipeline \
-var hosted_zone_id=${{ secrets.AWS_HOSTED_ZONE }} \
-var cert_arn=${{secrets.AWS_CERT_ARN}}
- name: Terraform Apply
run: |
cd ./terraform
terraform apply -auto-approve -input=false \
-var profile=pipeline \
-var hosted_zone_id=${{ secrets.AWS_HOSTED_ZONE }} \
-var cert_arn=${{secrets.AWS_CERT_ARN}}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
dist/
build/
upload.sh
terraform/.terraform
terraform/.terraform.lock.hcl
20 changes: 20 additions & 0 deletions terraform/cloudfront/input_vars.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
variable "domain_name" {
description = "The domain name of the site"
}

variable "website_endpoint" {
}

variable "website_bucket" {
}

variable "default_root_object" {
description = "The default root object for the website. Usually index.html"
}

variable "custom_error_response_page_path" {
description = "The path to the custom error response page"
}

variable "cert_arn" {
}
64 changes: 64 additions & 0 deletions terraform/cloudfront/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# https://medium.com/@dblencowe/adding-ssl-to-a-s3-static-website-managed-by-terraform-817d994e30d4
# https://github.com/hashicorp/terraform-provider-aws/issues/7847

resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {}

resource "aws_cloudfront_distribution" "distribution" {
origin {
domain_name = var.website_endpoint
origin_id = var.website_bucket

custom_origin_config {
http_port = "80"
https_port = "443"
origin_protocol_policy = "http-only"
origin_ssl_protocols = ["TLSv1", "TLSv1.1", "TLSv1.2", "SSLv3"]
}
}

# By default, show index.html file
default_root_object = var.default_root_object
enabled = true
is_ipv6_enabled = true
aliases = [var.domain_name]

# If there is a 404, return index.html with a HTTP 200 Response
custom_error_response {
error_caching_min_ttl = 3000
error_code = 404
response_code = 200
response_page_path = "/${var.custom_error_response_page_path}"
}

default_cache_behavior {
allowed_methods = ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"]
cached_methods = ["GET", "HEAD"]
target_origin_id = var.website_bucket

# Forward all query strings, cookies and headers
forwarded_values {
query_string = true
cookies {
forward = "none"
}
}
viewer_protocol_policy = "redirect-to-https"
min_ttl = 0
default_ttl = 3600
max_ttl = 86400
}

# See https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PriceClass.html
price_class = "PriceClass_100" # North America & Europe/Israel (other regions will have slower access)
restrictions {
geo_restriction {
restriction_type = "none"
}
}

# SSL certificate for the service.
viewer_certificate {
acm_certificate_arn = var.cert_arn
ssl_support_method = "sni-only"
}
}
7 changes: 7 additions & 0 deletions terraform/cloudfront/output_vars.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "distribution_domain_name" {
value = aws_cloudfront_distribution.distribution.domain_name
}

output "distribution_zone_id" {
value = aws_cloudfront_distribution.distribution.hosted_zone_id
}
37 changes: 37 additions & 0 deletions terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
provider "aws" {
profile = var.profile == "pipeline" ? "" : var.profile
region = var.region
}

module "template_files" {
source = "hashicorp/dir/template"
base_dir = "${var.content_path}/"
}

module "s3" {
source = "./s3"

domain_name = var.domain_name
website_content_path = var.content_path
website_dir_module = module.template_files
}

module "cloudfront" {
source = "./cloudfront"

cert_arn = var.cert_arn
custom_error_response_page_path = module.s3.error_document
default_root_object = module.s3.index_document
domain_name = var.domain_name
website_endpoint = module.s3.website_endpoint
website_bucket = module.s3.website_bucket_regional_domain_name
}

module "route53" {
source = "./route53"

domain_name = var.domain_name
domain_hosted_zone_id = var.hosted_zone_id
distribution_domain_name = module.cloudfront.distribution_domain_name
distribution_hosted_zone_id = module.cloudfront.distribution_zone_id
}
6 changes: 6 additions & 0 deletions terraform/route53/input_vars.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
variable "domain_name" {}
variable "domain_hosted_zone_id" {
description = "The ID of the hosted zone for the main domain"
}
variable "distribution_hosted_zone_id" {}
variable "distribution_domain_name" {}
10 changes: 10 additions & 0 deletions terraform/route53/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
resource "aws_route53_record" "url" {
zone_id = var.domain_hosted_zone_id
name = var.domain_name
type = "A"
alias {
name = var.distribution_domain_name
zone_id = var.distribution_hosted_zone_id
evaluate_target_health = false
}
}
11 changes: 11 additions & 0 deletions terraform/s3/input_vars.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
variable "domain_name" {
description = "The domain name of the site"
}

variable "website_content_path" {
description = "The path to the built website content"
}

variable "website_dir_module" {
description = "The dir module of the website content"
}
53 changes: 53 additions & 0 deletions terraform/s3/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
resource "aws_s3_bucket" "static_site" {
bucket = var.domain_name
}

resource "aws_s3_bucket_ownership_controls" "static_site_ownership_controls" {
bucket = aws_s3_bucket.static_site.id
rule {
object_ownership = "ObjectWriter"
}
}

resource "aws_s3_bucket_public_access_block" "static_site_public_access_block" {
bucket = aws_s3_bucket.static_site.id

block_public_acls = false
block_public_policy = false
ignore_public_acls = false
restrict_public_buckets = false
}

resource "aws_s3_bucket_acl" "static_site_acl" {
depends_on = [
aws_s3_bucket_ownership_controls.static_site_ownership_controls,
aws_s3_bucket_public_access_block.static_site_public_access_block
]

bucket = aws_s3_bucket.static_site.id
acl = "public-read"
}

resource "aws_s3_bucket_website_configuration" "static_site_config" {
bucket = aws_s3_bucket.static_site.bucket

index_document {
suffix = "index.html"
}

error_document {
key = "index.html"
}
}


resource "aws_s3_object" "static_site_content" {
for_each = var.website_dir_module.files
depends_on = [aws_s3_bucket_acl.static_site_acl]
bucket = aws_s3_bucket.static_site.bucket
key = each.key
source = each.value.source_path
content_type = each.value.content_type
acl = "public-read"
etag = each.value.digests.md5
}
23 changes: 23 additions & 0 deletions terraform/s3/output_vars.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
output "website_endpoint" {
value = aws_s3_bucket_website_configuration.static_site_config.website_endpoint
}

output "website_hosted_zone_id" {
value = aws_s3_bucket.static_site.hosted_zone_id
}

output "website_bucket" {
value = aws_s3_bucket.static_site.bucket
}

output "website_bucket_regional_domain_name" {
value = aws_s3_bucket.static_site.bucket_regional_domain_name
}

output "index_document" {
value = aws_s3_bucket_website_configuration.static_site_config.index_document[0].suffix
}

output "error_document" {
value = aws_s3_bucket_website_configuration.static_site_config.error_document[0].key
}
8 changes: 8 additions & 0 deletions terraform/terraform.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
terraform {
backend "s3" {
encrypt = true
bucket = "tiago-websites-tf-state-files"
region = "eu-west-1"
key = "analyser.dosaki.net/terraform.tfstate"
}
}
25 changes: 25 additions & 0 deletions terraform/vars.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
variable "region" {
default = "eu-west-1"
}

variable "profile" {
default = "production"
}

variable "domain_name" {
description = "The domain name for the website - this will be used to create a public bucket with the exact same name"
default = "analyser.dosaki.net"
}

variable "content_path" {
description = "The path to the built website content (should have index.html inside)"
default = "../build"
}

variable "hosted_zone_id" {
default = ""
}

variable "cert_arn" {
default = "ARN for the certificate ARN - See AWS Certificate Manager"
}

0 comments on commit 404f61e

Please sign in to comment.