Skip to content

Commit

Permalink
Added deployment and hosting with AWS & Firebase
Browse files Browse the repository at this point in the history
  • Loading branch information
mrz1836 committed Jul 27, 2021
1 parent 953bf7b commit 30abe47
Show file tree
Hide file tree
Showing 8 changed files with 829 additions and 2 deletions.
Binary file added .github/IMAGES/infrastructure-diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 160 additions & 0 deletions .make/aws.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
## Default region for the application
ifndef AWS_REGION
override AWS_REGION=us-east-1
export AWS_REGION
endif

## Set capabilities for the sam deploy option
ifndef IAM_CAPABILITIES
override IAM_CAPABILITIES="CAPABILITY_IAM"
export IAM_CAPABILITIES
endif

## Raw cloud formation template for the application
ifndef TEMPLATE_RAW
override TEMPLATE_RAW=application.yaml
export TEMPLATE_RAW
endif

## Packaged cloud formation template
ifndef TEMPLATE_PACKAGED
override TEMPLATE_PACKAGED=packaged.yaml
export TEMPLATE_PACKAGED
endif

## Set if defined (alias variable for ease of use)
ifdef tags
override AWS_TAGS=$(tags)
export AWS_TAGS
endif

## Set if defined (alias variable for ease of use)
ifdef bucket
override APPLICATION_BUCKET=$(bucket)
export APPLICATION_BUCKET
endif

## Set if defined (alias variable for ease of use)
ifdef domain
override APPLICATION_DOMAIN_NAME=$(domain)
export APPLICATION_DOMAIN_NAME
endif

## Set if defined (alias variable for ease of use)
ifdef stage
override APPLICATION_STAGE_NAME=$(stage)
export APPLICATION_STAGE_NAME
endif

## Set if defined (alias variable for ease of use)
ifdef feature
override APPLICATION_FEATURE_NAME=$(feature)
export APPLICATION_FEATURE_NAME
endif

aws-param-zone: ## Returns the ssm location for the host zone id
@test $(domain)
@echo "/$(domain)/zone_id"

aws-param-certificate: ## Returns the ssm location for the domain ssl certificate id
@test $(domain)
@echo "/$(domain)/certificate_id"

create-env-key: ## Creates a new key in KMS for a new stage
@test $(APPLICATION_NAME)
@test $(APPLICATION_STAGE_NAME)
@$(eval kms_key_id := $(shell aws kms create-key --description "Used to encrypt environment variables for $(APPLICATION_NAME)" --query 'KeyMetadata.KeyId' --output text))
@aws kms enable-key-rotation --key-id $(kms_key_id)
@$(eval param_location := $(shell $(MAKE) env-key-location app=$(APPLICATION_NAME) stage=$(APPLICATION_STAGE_NAME) ))
@aws kms create-alias --alias-name "alias/$(APPLICATION_NAME)/$(APPLICATION_STAGE_NAME)" --target-key-id $(kms_key_id)
@$(MAKE) save-param param_name="$(param_location)" param_value=$(kms_key_id)
@echo "Saved parameter: $(param_location) with key id: $(kms_key_id)"

create-secret: ## Creates an secret into AWS SecretsManager
@# Example: make create-secret name='production/test' description='This is a test' secret_value='{\"Key\":\"my_key\",\"Another\":\"value\"}' kms_key_id=b329...
@test "$(name)"
@test "$(description)"
@test "$(secret_value)"
@test $(kms_key_id)
@aws secretsmanager create-secret \
--name "$(name)" \
--description "$(description)" \
--kms-key-id $(kms_key_id) \
--secret-string "$(secret_value)"

decrypt: ## Decrypts data using a KMY Key ID (awscli v2)
@# Example: make decrypt decrypt_value=AQICAHgrSMx+3O7...
@test "$(decrypt_value)"
@aws kms decrypt --ciphertext-blob "$(decrypt_value)" --output text --query Plaintext | base64 --decode

decrypt-deprecated: ## Decrypts data using a KMY Key ID (awscli v1)
@# Example: make decrypt decrypt_value=AQICAHgrSMx+3O7...
@test "$(decrypt_value)"
@echo $(decrypt_value) | base64 --decode >> tempfile
@aws kms decrypt --ciphertext-blob fileb://tempfile --output text --query Plaintext | base64 --decode
@rm -rf tempfile

env-key-location: ## Returns the environment encryption key location
@test $(app)
@test $(stage)
@echo "/$(app)/$(stage)/kms_key_id"

encrypt: ## Encrypts data using a KMY Key ID (awscli v2)
@# Example make encrypt kms_key_id=b329... encrypt_value=YourSecret
@test $(kms_key_id)
@test "$(encrypt_value)"
@aws kms encrypt --output text --query CiphertextBlob --key-id $(kms_key_id) --plaintext "$(shell echo "$(encrypt_value)" | base64)"

invalidate-cache: ## Invalidates a cloudfront cache based on path
@test $(APPLICATION_DISTRIBUTION_ID)
@aws cloudfront create-invalidation --distribution-id $(APPLICATION_DISTRIBUTION_ID) --paths "/*"

package: ## Process the CF template and prepare for deployment
@SAM_CLI_TELEMETRY=0 sam package \
--template-file $(TEMPLATE_RAW) \
--output-template-file $(TEMPLATE_PACKAGED) \
--s3-bucket $(APPLICATION_BUCKET) \
--s3-prefix $(APPLICATION_BUCKET_PREFIX) \
--region $(AWS_REGION)

save-domain-info: ## Saves the zone id and the ssl id for use by CloudFormation
@test $(domain)
@test $(zone_id)
@test $(certificate_id)
@$(MAKE) save-param param_name="/$(domain)/zone_id" param_value=$(zone_id)
@$(MAKE) save-param param_name="/$(domain)/certificate_id" param_value=$(certificate_id)

save-param: ## Saves a plain-text string parameter in SSM
@# Example: make save-param param_name='test' param_value='This is a test'
@test "$(param_value)"
@test "$(param_name)"
@aws ssm put-parameter --name "$(param_name)" --value "$(param_value)" --type String --overwrite

save-param-encrypted: ## Saves an encrypted string value as a parameter in SSM
@# Example: make save-param-encrypted param_name='test' param_value='This is a test' kms_key_id=b329...
@test "$(param_value)"
@test "$(param_name)"
@test $(kms_key_id)
@aws ssm put-parameter \
--type String \
--overwrite \
--name "$(param_name)" \
--value "$(shell $(MAKE) encrypt kms_key_id=$(kms_key_id) encrypt_value="$(param_value)")"

upload-files: ## Upload/puts files into S3 bucket
@test "$(source)"
@test "$(destination)"
@test $(APPLICATION_BUCKET)
@aws s3 cp $(source) s3://$(APPLICATION_BUCKET)/$(destination) --recursive

update-secret: ## Updates an existing secret in AWS SecretsManager
@# Example: make update-secret name='production/test' secret_value='{\"Key\":\"my_key\",\"Another\":\"value\"}'
@test "$(name)"
@test "$(secret_value)"
@aws secretsmanager update-secret \
--secret-id "$(name)" \
--secret-string "$(secret_value)"

teardown: ## Deletes the entire stack
@test $(APPLICATION_STACK_NAME)
@aws cloudformation delete-stack --stack-name $(APPLICATION_STACK_NAME)
41 changes: 41 additions & 0 deletions .make/firebase.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

firebase-param-app-id: ## Returns the location of the app_id parameter in SSM
@$(MAKE) firebase-param-location app=$(app) stage=$(stage) param=app_id

firebase-param-sender-id: ## Returns the location of the sender_id parameter in SSM
@$(MAKE) firebase-param-location app=$(app) stage=$(stage) param=sender_id

firebase-param-project: ## Returns the location of the project-id parameter in SSM
@$(MAKE) firebase-param-location app=$(app) stage=$(stage) param=project

firebase-param-location: ## Creates a parameter location (for Firebase details in SSM)
@test $(app)
@test $(stage)
@test $(param)
@echo "/$(app)/$(stage)/firebase/$(param)"

firebase-save-project: ## Saves the firebase project information for use by CloudFormation
@test $(APPLICATION_NAME)
@test $(APPLICATION_STAGE_NAME)
@test $(project)
@test $(sender_id)
@test $(app_id)
@$(MAKE) save-param param_name="$(shell $(MAKE) firebase-param-project app=$(APPLICATION_NAME) stage=$(APPLICATION_STAGE_NAME))" param_value=$(project)
@$(MAKE) save-param param_name="$(shell $(MAKE) firebase-param-sender-id app=$(APPLICATION_NAME) stage=$(APPLICATION_STAGE_NAME))" param_value=$(sender_id)
@$(MAKE) save-param param_name="$(shell $(MAKE) firebase-param-app-id app=$(APPLICATION_NAME) stage=$(APPLICATION_STAGE_NAME))" param_value=$(app_id)

firebase-deploy-simple: ## Deploys to firebase with limited flags
@test "$(project)"
@test "$(token)"
@firebase deploy --project $(project) --token $(token)

firebase-update: ## Update the firebase tools
@npm i -g firebase-tools

firebase-set-env: ## Set an environment variable in a firebase project
@test "$(key)"
@test "$(value)"
@firebase functions:config:set $(key)="$(value)"

firebase-get-env: ## Gets the current environment variables in the associated project
@firebase functions:config:get
102 changes: 100 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,47 @@
# Common makefile commands & variables between projects
include .make/common.mk

# Common aws commands & variables between projects
include .make/aws.mk

# Common firebase commands & variables between projects
include .make/firebase.mk

## Stage or environment for the application
ifndef APPLICATION_STAGE_NAME
override APPLICATION_STAGE_NAME="production"
endif

## Tags for the application in AWS
ifndef AWS_TAGS
override AWS_TAGS="Stage=$(APPLICATION_STAGE_NAME) Product=bitcoinschema"
endif

## Default S3 bucket (already exists) to store distribution files
ifndef APPLICATION_BUCKET
override APPLICATION_BUCKET="cloudformation-distribution-raw-files"
endif

## Application name (the name of the application, lowercase, no spaces)
ifndef APPLICATION_NAME
override APPLICATION_NAME="bitcoinschema"
endif

## Cloud formation stack name (combines the app name with the stage for unique stacks)
ifndef APPLICATION_STACK_NAME
override APPLICATION_STACK_NAME=$(subst _,-,"$(APPLICATION_NAME)-$(APPLICATION_STAGE_NAME)")
endif

## Application feature name (if it's a feature branch of a stage) (feature="some-feature")
ifdef APPLICATION_FEATURE_NAME
override APPLICATION_STACK_NAME=$(subst _,-,"$(APPLICATION_NAME)-$(APPLICATION_STAGE_NAME)-$(APPLICATION_FEATURE_NAME)")
endif

## S3 prefix to store the distribution files
ifndef APPLICATION_BUCKET_PREFIX
override APPLICATION_BUCKET_PREFIX=$(APPLICATION_STACK_NAME)
endif

## Set the distribution folder
ifndef DISTRIBUTIONS_DIR
override DISTRIBUTIONS_DIR=./release
Expand Down Expand Up @@ -36,6 +77,36 @@ clean: ## Remove previous builds and any test cache data
@if [ -d build_cache ]; then rm -r build_cache; fi
@if [ -d node_modules ]; then rm -r node_modules; fi

deploy: ## Build, prepare and deploy
@$(MAKE) package
@sam deploy \
--template-file $(TEMPLATE_PACKAGED) \
--stack-name $(APPLICATION_STACK_NAME) \
--region $(AWS_REGION) \
--parameter-overrides ApplicationName=$(APPLICATION_NAME) \
ApplicationStackName=$(APPLICATION_STACK_NAME) \
ApplicationStageName=$(APPLICATION_STAGE_NAME) \
ApplicationBucket=$(APPLICATION_BUCKET) \
RepoOwner=$(REPO_OWNER) \
RepoName=$(REPO_NAME) \
RepoBranch=$(REPO_BRANCH) \
FirebaseProject="$(shell $(MAKE) firebase-param-project \
app=$(APPLICATION_NAME) \
stage=$(APPLICATION_STAGE_NAME))" \
FirebaseAppId="$(shell $(MAKE) firebase-param-app-id \
app=$(APPLICATION_NAME) \
stage=$(APPLICATION_STAGE_NAME))" \
FirebaseSenderId="$(shell $(MAKE) firebase-param-sender-id \
app=$(APPLICATION_NAME) \
stage=$(APPLICATION_STAGE_NAME))" \
EncryptionKeyId="$(shell $(MAKE) env-key-location \
app=$(APPLICATION_NAME) \
stage=$(APPLICATION_STAGE_NAME))" \
--capabilities $(IAM_CAPABILITIES) \
--tags $(AWS_TAGS) \
--no-fail-on-empty-changeset \
--no-confirm-changeset

install: ## Installs the dependencies for the package
@npm install

Expand All @@ -45,8 +116,35 @@ lint: ## Runs the standard-js lint tool
outdated: ## Checks for outdated packages via npm
@npm outdated

release:: ## Deploy to npm
@npm run deploy
save-secrets: ## Helper for saving sensitive credentials to Secrets Manager
@# Example: make save-secrets github_token=12345... firebase_token=12345... firebase_api_key=12345... kms_key_id=b329... stage=<stage>
@test "$(firebase_token)"
@test $(firebase_api_key)
@test $(github_token)
@test $(kms_key_id)

@$(eval firebase_api_key_encrypted := $(shell $(MAKE) encrypt kms_key_id=$(kms_key_id) encrypt_value="$(firebase_api_key)"))
@$(eval firebase_token_encrypted := $(shell $(MAKE) encrypt kms_key_id=$(kms_key_id) encrypt_value="$(firebase_token)"))
@$(eval secret_value := $(shell echo '{' \
'\"github_personal_token\":\"$(github_token)\"' \
',\"firebase_token_encrypted\":\"$(firebase_token_encrypted)\"' \
',\"firebase_api_key_encrypted\":\"$(firebase_api_key_encrypted)\"' \
'}'))

@$(eval existing_secret := $(shell aws secretsmanager describe-secret --secret-id "$(APPLICATION_STAGE_NAME)/$(APPLICATION_NAME)" --output text))
@if [ '$(existing_secret)' = "" ]; then\
echo "Creating a new secret..."; \
$(MAKE) create-secret \
name="$(APPLICATION_STAGE_NAME)/$(APPLICATION_NAME)" \
description="Sensitive credentials for $(APPLICATION_NAME):$(APPLICATION_STAGE_NAME)" \
secret_value='$(secret_value)' \
kms_key_id=$(kms_key_id); \
else\
echo "Updating an existing secret..."; \
$(MAKE) update-secret \
name="$(APPLICATION_STAGE_NAME)/$(APPLICATION_NAME)" \
secret_value='$(secret_value)'; \
fi

start: ## Start the documentation site
@npm run start
Expand Down
Loading

0 comments on commit 30abe47

Please sign in to comment.