.PHONY: deploy
# Configuration variables for deployment. Can be edited for desired behavior.
# Base terraform directory
export tf_dir ?= deploy
# Location for deployed resources
export TF_VAR_REGION ?= {{gcp_region}}
# Project id for deployed resources
export TF_VAR_PROJECT_ID ?= {{gcp_project_id}}
# Artifact Repository name for pushing the Docker images
export TF_VAR_REPO_NAME ?= {{gcp_artifact_repo_name}}
# Pushed image name
export TF_VAR_IMAGE_NAME ?= {{docker_image_name}}
# Path to the service account credentials
export google_sa_creds ?= key/-service_account.json
# Cloud Storage bucket name
export TF_VAR_BUCKET_NAME ?= {{docker_image_name}}_tfstate
# Specifies where to deploy the project. Possible values: `hetzner`, `gce`, `aws`
export CSP ?= hetzner
# Helper variables for deployment.
# Helper var for tagging local image
export tag ?= $(TF_VAR_REGION)-docker.pkg.dev/$(TF_VAR_PROJECT_ID)/$(TF_VAR_REPO_NAME)/$(TF_VAR_IMAGE_NAME)
# Zone location for the resource
export TF_VAR_ZONE ?= $(TF_VAR_REGION)-a
# Hetzner Cloud auth token
export TF_VAR_HCLOUD_TOKEN ?= $(SECRET_CSP_HETZNER)
# AWS Access key for deploying to an EC2 instance
export AWS_ACCESS_KEY_ID ?= $(SECRET_AWS_ACCESS_KEY_ID)
# AWS Secret Access key for deploying to an EC2 instance
export AWS_SECRET_ACCESS_KEY ?= $(SECRET_AWS_ACCESS_KEY)
# Check Hetzner and deployment related keys
check-hetzner-keys:
@[ ! -z "${SECRET_CSP_HETZNER}" ] \
|| { echo "ERROR: Key SECRET_CSP_HETZNER does not exist"; exit 1; }
# Check AWS and deployment related keys
check-aws-keys:
@[ ! -z "${SECRET_AWS_ACCESS_KEY_ID}" ] \
|| echo "ERROR: Key SECRET_AWS_ACCESS_KEY_ID does not exist"
@[ ! -z "${SECRET_AWS_ACCESS_KEY}" ] \
|| echo "ERROR: Key SECRET_AWS_ACCESS_KEY does not exist"
@[ ! -z "${SECRET_AWS_ACCESS_KEY_ID}" ] || exit 1
@[ ! -z "${SECRET_AWS_ACCESS_KEY}" ] || exit 1
check-gce-keys:
@echo "All required GCE keys are the same as GCP keys"
# Check if required GCP keys are present
check-gcp-keys:
@[ -f key/-service_account.json ] \
|| echo "ERROR: Key file key/-service_account.json does not exist"
@[ ! -z "${SECRET_STATE_ARCHIVE_KEY}" ] \
|| echo "ERROR: Key SECRET_STATE_ARCHIVE_KEY does not exist"
@[ -f key/-service_account.json ] || exit 1
@[ ! -z "${SECRET_STATE_ARCHIVE_KEY}" ] || exit 1
# Start local docker container
start:
docker compose up -d
# Stop local docker container
stop:
docker compose down
# Remove created docker image
clean: stop
docker rmi $(TF_VAR_IMAGE_NAME)
docker buildx prune -af
# Install gcloud for Debian/Ubuntu
install-gcloud:
# GCloud
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates gnupg curl sudo
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
sudo apt-get update && sudo apt-get install -y google-cloud-cli
# Install terraform for Debian/Ubuntu
install-terraform:
sudo apt-get update && sudo apt-get install -y gnupg software-properties-common
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt-get install terraform
# Install gcloud and terraform
install: install-gcloud install-terraform
gcloud --version
terraform -version
# Login to GCP with user account
gcp-auth:
gcloud auth application-default login
# Authorize to GCP with service account
gcp-service:
gcloud auth activate-service-account --key-file=$(google_sa_creds)
# Add docker repo auth helper
gcp-docker:
gcloud auth configure-docker $(TF_VAR_REGION)-docker.pkg.dev --quiet
# Initializes all terraform projects
# Downloads required modules and validates .tf files
tf-init:
terraform -chdir=$(tf_dir)/gar init
terraform -chdir=$(tf_dir)/gce init
terraform -chdir=$(tf_dir)/hetzner init
terraform -chdir=$(tf_dir)/aws init
# Creates Artifact Registry repository on GCP in specified location
create-artifact-repo: state_storage_pull tf-init
terraform -chdir=$(tf_dir)/gar apply -auto-approve
# Builds {{docker_image_name}} image
build-image:
docker build . -f Dockerfile.1 -t name:$(TF_VAR_IMAGE_NAME) -t $(tag)_1
docker build . -f Dockerfile.2 -t name:$(TF_VAR_IMAGE_NAME) -t $(tag)_2
# Builds and pushes local docker image to the private repository
push-image: gcp-docker create-artifact-repo state_storage_push
docker push $(tag)_1
docker push $(tag)_2
# Creates GCE instance with the website configured on boot
create-gce: check-gce-keys gcp-service state_storage_pull push-image
terraform -chdir=$(tf_dir)/gce apply -auto-approve
# Creates AWS EC2 instance with the website configured on boot
create-aws: check-aws-keys gcp-service state_storage_pull push-image
terraform -chdir=$(tf_dir)/aws apply -auto-approve
# Creates Hetzner instance with the website configured on boot
create-hetzner: check-hetzner-keys gcp-service state_storage_pull push-image
terraform -chdir=$(tf_dir)/hetzner apply -auto-approve
# Deploys everything and updates terraform states
deploy-in-container:
make create-$(CSP) && make state_storage_push || { make state_storage_push; echo "Deployment failed"; exit 1; }
# Deploys using tools from the container
deploy: check-gcp-keys build-image
docker build . -t deploy-$(TF_VAR_IMAGE_NAME) -f ./$(tf_dir)/Dockerfile --build-arg google_sa_creds="$(google_sa_creds)"
@docker run -v //var/run/docker.sock:/var/run/docker.sock -v .:/app \
-e SECRET_STATE_ARCHIVE_KEY=$(SECRET_STATE_ARCHIVE_KEY) \
-e SECRET_CSP_HETZNER=$(SECRET_CSP_HETZNER) \
-e SECRET_AWS_ACCESS_KEY_ID=$(SECRET_AWS_ACCESS_KEY_ID) \
-e SECRET_AWS_ACCESS_KEY=$(SECRET_AWS_ACCESS_KEY) \
-e CSP=$(CSP) \
--rm deploy-$(TF_VAR_IMAGE_NAME)
# Review changes that terraform will do on apply
tf-plan: tf-init
terraform -chdir=$(tf_dir)/gar plan
terraform -chdir=$(tf_dir)/gce plan
terraform -chdir=$(tf_dir)/hetzner plan
terraform -chdir=$(tf_dir)/aws plan
# Destroy created infrastracture on GCP
tf-destroy: tf-init
terraform -chdir=$(tf_dir)/gar destroy
terraform -chdir=$(tf_dir)/gce destroy
terraform -chdir=$(tf_dir)/hetzner destroy
terraform -chdir=$(tf_dir)/aws destroy
# Pushes encrypted terraform state files to the GCS Bucket
state_storage_push:
@echo Pushing encrypted terraform state files to the GCS Bucket
-@gcloud storage cp $(tf_dir)/gce/terraform.tfstate gs://$(TF_VAR_BUCKET_NAME)/gce.tfstate --encryption-key="$(SECRET_STATE_ARCHIVE_KEY)"
-@gcloud storage cp $(tf_dir)/gar/terraform.tfstate gs://$(TF_VAR_BUCKET_NAME)/gar.tfstate --encryption-key="$(SECRET_STATE_ARCHIVE_KEY)"
-@gcloud storage cp $(tf_dir)/hetzner/terraform.tfstate gs://$(TF_VAR_BUCKET_NAME)/hetzner.tfstate --encryption-key="$(SECRET_STATE_ARCHIVE_KEY)"
-@gcloud storage cp $(tf_dir)/aws/terraform.tfstate gs://$(TF_VAR_BUCKET_NAME)/aws.tfstate --encryption-key="$(SECRET_STATE_ARCHIVE_KEY)"
# Pulls and decrypts terraform state files to the GCS Bucket
state_storage_pull:
@echo Pulling terraform state files to the GCS Bucket
-@gcloud storage cp gs://$(TF_VAR_BUCKET_NAME)/gce.tfstate $(tf_dir)/gce/terraform.tfstate --decryption-keys="$(SECRET_STATE_ARCHIVE_KEY)"
-@gcloud storage cp gs://$(TF_VAR_BUCKET_NAME)/gar.tfstate $(tf_dir)/gar/terraform.tfstate --decryption-keys="$(SECRET_STATE_ARCHIVE_KEY)"
-@gcloud storage cp gs://$(TF_VAR_BUCKET_NAME)/hetzner.tfstate $(tf_dir)/hetzner/terraform.tfstate --decryption-keys="$(SECRET_STATE_ARCHIVE_KEY)"
-@gcloud storage cp gs://$(TF_VAR_BUCKET_NAME)/aws.tfstate $(tf_dir)/aws/terraform.tfstate --decryption-keys="$(SECRET_STATE_ARCHIVE_KEY)"
# Creates GCS Bucket for terraform states
state_storage_init:
terraform -chdir=$(tf_dir)/gcs init
terraform -chdir=$(tf_dir)/gcs apply
# Destroys GCS Bucket for terraform states
state_storage_destroy:
terraform -chdir=$(tf_dir)/gcs destroy