visit
As you may have seen, we just launched our of env0 last month. As part of the run-up to the launch, our dev team had to go through and make sure everything about our infrastructure was ready for ongoing public use: one element being creating a maintenance mode for both our Application and our.
Why do you need a maintenance mode for something like env0, which is built to be highly available? Well whether it’s human error (as in the case of ), a DDOS attack (like with), or just a major upgrade (like), even the most highly available applications from the most experienced providers sometimes need to be able to be taken offline for a short period of time. And when you have to do that, you want to make sure that your users understand what is happening and still have a good experience.Here’s how our team put together our maintenance mode using Terraform, AWS, and Github Pages (including all the code at the end!)resource "aws_cloudfront_origin_access_identity" "origin_access_identity" {
comment = "${aws_s3_bucket.website_bucket.bucket} access identity"
}
resource "aws_cloudfront_distribution" "website_cdn" {
count = 2 # one for the actual cloudfront and one is for the backdoor
enabled = true
price_class = "PriceClass_200"
http_version = "http2"
default_root_object = "index.html"
origin {
origin_id = "origin-bucket-${aws_s3_bucket.website_bucket.id}"
domain_name = aws_s3_bucket.website_bucket.bucket_domain_name
s3_origin_config {
origin_access_identity = aws_cloudfront_origin_access_identity.origin_access_identity.cloudfront_access_identity_path
}
}
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
forwarded_values {
query_string = false
headers = []
cookies {
forward = "none"
}
}
min_ttl = 0
default_ttl = 86400 // 1 day
max_ttl = 31536000 // 1 year
target_origin_id = "origin-bucket-${aws_s3_bucket.website_bucket.id}"
viewer_protocol_policy = "redirect-to-https"
compress = true
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
viewer_certificate {
acm_certificate_arn = count.index == 0 ? var.acm_certificate_arn : var.acm_certificate_arn_backdoor
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2018"
}
aliases = list(count.index == 0 ? var.dns_name : var.backdoor_dns_name)
}
resource "aws_route53_record" "dns_record" {
zone_id = var.aws_route53_zone_id
name = var.dns_name
type = "CNAME"
records = list(var.maintenance_mode_enabled ? "${var.github_organization}.github.io" : aws_cloudfront_distribution.website_cdn[0].domain_name)
ttl = 60
}
resource "aws_route53_record" "dns_backdoor_record" {
zone_id = var.aws_route53_zone_id
name = var.backdoor_dns_name
type = "A"
alias {
name = aws_cloudfront_distribution.website_cdn[1].domain_name
zone_id = aws_cloudfront_distribution.website_cdn[1].hosted_zone_id
evaluate_target_health = false
}
}
⚠️ Pay attention that this Terraform code does not create the Route53 hosted zone, nor the SSL certificates — you need to complete those as appropriate for your own setup ⚠️
Next, we need a git repo containing the html files for the new maintenance mode site. So we’ve created a simple Terraform code that will create the repo as well as add all existing files in the “maintenance_mode_website” folder to the repo, which in our case is the maintenance mode html file:
resource "github_repository" "maintenance-mode-repo" {
name = var.github_repo_name
description = "Maintenance mode repo for ${var.github_repo_name}"
private = true
auto_init = true
default_branch = "master"
}
resource "github_repository_collaborator" "users_repos" {
repository = github_repository.maintenance-mode-repo.name
username = var.github_username
permission = "admin"
depends_on = [github_repository.maintenance-mode-repo]
}
resource "github_repository_file" "maintenance-mode-files" {
repository = github_repository.maintenance-mode-repo.name
for_each = {
for key, value in fileset(path.root, "maintenance_mode_website/*.*"): value => value
}
// There is a limitation here, Terraform file function supports UTF-8 files,
// so any images or other non text files can't be included in the folder.
file = trimprefix(each.value, "maintenance_mode_website/")
content = file(each.value)
depends_on = [github_repository_collaborator.users_repos]
}
version: 1
deploy:
steps:
terraformOutput:
after:
- scripts/set_maintanance_mode.sh ${TF_VAR_github_organization} "$(terraform output github_repo_name)" "$(terraform output website_endpoint)" ${TF_VAR_github_username} ${TF_VAR_github_token}
#!/usr/bin/env bash
set -e
REPO_ORGANIZATION=$1
REPO_NAME=$2
CNAME=$3
GITHUB_USERNAME=$4
GITHUB_TOKEN=$5
if [[ ${REPO_NAME} != "" && CNAME != "" ]]; then
echo "Going to set environment to maintenance mode, repo name: ${REPO_NAME}, CNAME: ${CNAME}"
GIT_AUTH="${GITHUB_USERNAME}:${GITHUB_TOKEN}"
curl --user "${GIT_AUTH}" \
--request POST \
--header "Content-Type: application/json" \
--header "Accept: application/vnd.github.switcheroo-preview+json" \
--url "//api.github.com/repos/${REPO_ORGANIZATION}/${REPO_NAME}/pages" \
--data "{ \"source\": { \"branch\": \"master\" } }"
curl --user "${GIT_AUTH}" \
--request PUT \
--header "Content-Type: application/json" \
--url "//api.github.com/repos/${REPO_ORGANIZATION}/${REPO_NAME}/pages" \
--data "{ \"cname\": \"${CNAME}\", \"source\": \"master\" }"
else
echo "No need to set environment to maintenance mode"
fi