From 9816e4dade506fe906b7b93768e0aec15117037a Mon Sep 17 00:00:00 2001 From: Billy Ani Date: Thu, 26 Mar 2020 10:22:58 -0400 Subject: [PATCH 01/13] wip infrastructure --- bin/README.md | 199 ++++++++++++++++++ bin/cleanup.sh | 24 +++ bin/deploy.sh | 74 +++++++ bin/mfa.sh | 26 +++ bin/redeploy.sh | 38 ++++ bin/util/apply | 5 + bin/util/create-bucket | 3 + bin/util/db-create | 3 + bin/util/destroy | 5 + bin/util/ecr-login | 2 + bin/util/npm-build | 6 + bin/util/output | 5 + .../Dockerfile-Bmore-Responsive | 3 + docker/Dockerfile-Builder | 27 +++ docker/README.md | 85 ++++++++ docker/aws/config | 3 + docker/aws/credentials | 6 + terraform/README.md | 23 ++ .../modules/0daf126f48aa68f4100241c487117a28 | 1 + .../modules/2e4a68071a760a8b1062099112ed13a4 | 1 + .../modules/4216cbb4ca33ef413c69198c7a92cc50 | 1 + .../modules/541ab3affc48364d32a2e802f4095e12 | 1 + .../modules/562aa598e349828b88b903d520292895 | 1 + .../modules/b02a64b582ee9aa7b66c80d8950a113f | 1 + .../modules/f5737180a6bf1920a6197be7389278b5 | 1 + .../.terraform/modules/modules.json | 1 + terraform/components/full-cluster/README.md | 0 .../full-cluster/cfb-container.json.tpl | 15 ++ terraform/components/full-cluster/main.tf | 94 +++++++++ .../full-cluster/matching-container.json.tpl | 25 +++ terraform/components/full-cluster/outputs.tf | 44 ++++ .../components/full-cluster/userdata.sh.tpl | 3 + .../components/full-cluster/variables.tf | 5 + terraform/modules/alb/README.md | 19 ++ terraform/modules/alb/main.tf | 48 +++++ terraform/modules/alb/outputs.tf | 19 ++ terraform/modules/alb/variables.tf | 22 ++ terraform/modules/asg/README.md | 16 ++ terraform/modules/asg/main.tf | 61 ++++++ terraform/modules/asg/outputs.tf | 1 + terraform/modules/asg/variables.tf | 54 +++++ terraform/modules/db/main.tf | 57 +++++ terraform/modules/db/outputs.tf | 79 +++++++ terraform/modules/db/variables.tf | 109 ++++++++++ terraform/modules/ecs/README.md | 19 ++ terraform/modules/ecs/main.tf | 135 ++++++++++++ terraform/modules/ecs/outputs.tf | 9 + terraform/modules/ecs/variables.tf | 34 +++ terraform/modules/s3/README.md | 13 ++ terraform/modules/s3/main.tf | 6 + terraform/modules/s3/outputs.tf | 9 + terraform/modules/s3/variables.tf | 5 + terraform/modules/sg/README.md | 14 ++ terraform/modules/sg/main.tf | 53 +++++ terraform/modules/sg/outputs.tf | 9 + terraform/modules/sg/variables.tf | 9 + terraform/modules/vpc/README.md | 18 ++ terraform/modules/vpc/main.tf | 83 ++++++++ terraform/modules/vpc/outputs.tf | 14 ++ terraform/modules/vpc/variables.tf | 27 +++ 60 files changed, 1673 insertions(+) create mode 100644 bin/README.md create mode 100755 bin/cleanup.sh create mode 100755 bin/deploy.sh create mode 100755 bin/mfa.sh create mode 100755 bin/redeploy.sh create mode 100755 bin/util/apply create mode 100755 bin/util/create-bucket create mode 100755 bin/util/db-create create mode 100755 bin/util/destroy create mode 100755 bin/util/ecr-login create mode 100755 bin/util/npm-build create mode 100755 bin/util/output rename Dockerfile => docker/Dockerfile-Bmore-Responsive (86%) create mode 100644 docker/Dockerfile-Builder create mode 100644 docker/README.md create mode 100644 docker/aws/config create mode 100644 docker/aws/credentials create mode 100644 terraform/README.md create mode 120000 terraform/components/full-cluster/.terraform/modules/0daf126f48aa68f4100241c487117a28 create mode 120000 terraform/components/full-cluster/.terraform/modules/2e4a68071a760a8b1062099112ed13a4 create mode 120000 terraform/components/full-cluster/.terraform/modules/4216cbb4ca33ef413c69198c7a92cc50 create mode 120000 terraform/components/full-cluster/.terraform/modules/541ab3affc48364d32a2e802f4095e12 create mode 120000 terraform/components/full-cluster/.terraform/modules/562aa598e349828b88b903d520292895 create mode 120000 terraform/components/full-cluster/.terraform/modules/b02a64b582ee9aa7b66c80d8950a113f create mode 120000 terraform/components/full-cluster/.terraform/modules/f5737180a6bf1920a6197be7389278b5 create mode 100644 terraform/components/full-cluster/.terraform/modules/modules.json create mode 100644 terraform/components/full-cluster/README.md create mode 100644 terraform/components/full-cluster/cfb-container.json.tpl create mode 100644 terraform/components/full-cluster/main.tf create mode 100644 terraform/components/full-cluster/matching-container.json.tpl create mode 100644 terraform/components/full-cluster/outputs.tf create mode 100644 terraform/components/full-cluster/userdata.sh.tpl create mode 100644 terraform/components/full-cluster/variables.tf create mode 100644 terraform/modules/alb/README.md create mode 100644 terraform/modules/alb/main.tf create mode 100644 terraform/modules/alb/outputs.tf create mode 100644 terraform/modules/alb/variables.tf create mode 100644 terraform/modules/asg/README.md create mode 100644 terraform/modules/asg/main.tf create mode 100644 terraform/modules/asg/outputs.tf create mode 100644 terraform/modules/asg/variables.tf create mode 100644 terraform/modules/db/main.tf create mode 100644 terraform/modules/db/outputs.tf create mode 100644 terraform/modules/db/variables.tf create mode 100644 terraform/modules/ecs/README.md create mode 100644 terraform/modules/ecs/main.tf create mode 100644 terraform/modules/ecs/outputs.tf create mode 100644 terraform/modules/ecs/variables.tf create mode 100644 terraform/modules/s3/README.md create mode 100644 terraform/modules/s3/main.tf create mode 100644 terraform/modules/s3/outputs.tf create mode 100644 terraform/modules/s3/variables.tf create mode 100644 terraform/modules/sg/README.md create mode 100644 terraform/modules/sg/main.tf create mode 100644 terraform/modules/sg/outputs.tf create mode 100644 terraform/modules/sg/variables.tf create mode 100644 terraform/modules/vpc/README.md create mode 100644 terraform/modules/vpc/main.tf create mode 100644 terraform/modules/vpc/outputs.tf create mode 100644 terraform/modules/vpc/variables.tf diff --git a/bin/README.md b/bin/README.md new file mode 100644 index 00000000..43cef097 --- /dev/null +++ b/bin/README.md @@ -0,0 +1,199 @@ +# Deployment Scripts + +## Overview + +The following scripts are intended to be used for the deployment of the application infrastructure. They have been developed with ease-of-use in mind and, as such, potential input to the script runtime has been significantly restricted. + +The scripts in this repository can be divided into two categories: **Invoked Scripts** which represent scripts that a developer would call to launch and administer the application infrastructure, and **Utility Scripts** which are leveraged by the Invoked Scripts to perform common tasks as needed. + +## Invoked Scripts + +The following scripts are used to control the lifecycle of the application infrastructure. For detailed information about how to launch the infrastructure, please see the [Deployment Documentation](../DEPLOYING.md) + +**All invoked scripts should be run from the root of this repository.** + +### deploy + +##### Usage + + ```bin/deploy.sh arn:aws:iam:::mfa/``` + +The addition of an MFA resource is optional and only required if you are using MFA on your AWS account. If this value is not included, the script will not attempt to generate multi-factor credentials. + +##### Description +The deploy script is the main driver for deployment. The usage of the script is described in [DEPLOYING.md](../DEPLOYING.md) and documentation on the individual steps performed in the script are detailed inline as comments. + +The most common usage scenario for the deploy script is to perform a full standup of the application infrastructure when newly launching the application or modifying the terraform component(s) that comprise the application runtime environment. + +The deploy script completes the following steps: +- Create the Build Agent Docker image (See the [Docker Documentation](../docker/README.md) for more information) +- Ensure that the AWS is configured correctly and attempt an MFA sign-in if the user has passed an MFA device in. +- Builds the API code. +- Creates the Terraform Bucket to hold infrastructure state. (Note: If the bucket exists already, this step does nothing.) +- Runs `terraform apply` to generate the application infrastructure +- Logs into the Amazon Container Registry +- Builds and pushes Docker image for the bmore-responsive api. +- Outputs the S3 Bucket and API addresses for use in interfacing with the APIs +- Cleans up Docker containers that were created as a result of the build process + +In the event that any of the above steps return a nonzero error code, the deploy script halts. + +### redeploy + +##### Usage + +`bin/redeploy.sh` + +##### Description + +The redeploy script is intended for use when any application code for the API or Docker image is modified. When this happens, developers are able to run this script which performs the following actions: + +- Rebuilds the Docker Build Agent +- Ensures that AWS is configured correctly +- Builds the API +- Logs into the Amazon Container Registry +- Builds and pushes Docker images for the bmore-responsive api. +- Triggers a container redeployment through the AWS CLI +- Cleans up Docker containers that were created as a result of the build process + +Once the redeploy script is run, the bmore-responsive containers will go through the standard AWS rolling redeployment process. In this process, existing containers are kept running while new ones are created and registered as healthy. If the new containers are successfully marked healthy, old containers are drained and removed. This process takes about 5 minutes on average. + +The redeploy script is only intended for the redeployment of API code. In the event of a change to any terraform component(s), the `deploy.sh` script should be used to ensure that changes are applied correctly and propagated to all infrastructure components. + +**Please note** that the redeploy script does not attempt to perform the MFA login again. It assumes that you have a valid AWS session available when trying to use the script. In the event that this is not the case, the `bin/mfa.sh` script has been supplied to allow multi-factor login. + +### cleanup + +##### Usage + +##### Description + +The cleanup script is intended to be used when the application infrastructure is no longer desired. The script will destroy all terraform resources, clear the S3 buckets, and delete the terraform state bucket. **Running this script to completion will render your infrastructure unrecoverable without a full run of `deploy.sh`** + +The cleanup script performs the following steps: + +- Rebuilds the Docker Build Agent +- Ensures that AWS is configured correctly +- Runs `terraform destroy` **Note:** The developer running this script will be prompted to accept the destruction of the environment. +- Empties the terraform state bucket in use by the infrastructure +- Deletes the terraform state bucket in use by the infrastructure +- Cleans up Docker containers that were created as a result of the build process + +### mfa + +##### Usage + +```bin/mfa.sh arn:aws:iam:::mfa/``` + +The addition of an MFA resource is required. If this value is not included, the script will not do anything. + +##### Description + +This script is a simple wrapper to enable developers to log into AWS MFA for use in the `redeploy.sh` and `cleanup.sh` scripts. It should only be used in the event that a user has not run the `deploy.sh` script recently (or at all) or if their AWS credentials have expired after the given period (36 hours). The script performs the following actions: + +- Rebuilds the Docker Build Agent +- Ensures that AWS is configured correctly +- Attempts AWS MFA sign in with the given user credentials +- Cleans up Docker containers that were created as a result of the build process + +### Notes + +- Every script used for major operations rebuilds the Docker Build Agent. This is to ensure that the agent is at its latest state if changes have occurred between the latest container build and the latest source code updates. + +## Utility Scripts + +Utility scripts are simple shell scripts designed to be run by the Docker Build Agent when performing application infrastructure administration tasks. They are largely simple scripts designed to strip some complexity from the Invoked Scripts and make them more readable. + +**Note:** Most of these scripts require an active AWS session in order to run correctly. Please view the documentation above for `mfa.sh` or `deploy.sh` for more information. + +### apply + +##### Usage + +`apply ` + +- Terraform Component is the name of the terraform component being deployed. The script will cd to that component's folder and run all build actions from there. +- Extra Args are any other arguments that are normally passed to `terraform apply`. A complete list can be found at the [Terraform Docs](https://www.terraform.io/docs/commands/apply.html) + +##### Description + +The apply script is designed to be used when Invoked Scripts require terraform to deploy new resources. Apply performs three actions: + +- Enters the correct directory for the specified terraform component +- Initializes terraform +- Runs `terraform apply` with any additional arguments + + +### create-bucket + +##### Usage + +`create-bucket` + +##### Description + +This script creates the S3 bucket required for maintaining terraform state. Currently, the target bucket name is hard-coded to `cfb-healthcare-rollcall-terraform-state`. The bucket is created in the `us-east-1` region by default. + +### destroy + +##### Usage + +`destroy ` + +- Terraform Component is the name of the terraform component being deployed. The script will cd to that component's folder and run all build actions from there. +- Extra Args are any other arguments that are normally passed to `terraform destroy`. A complete list can be found at the [Terraform Docs](https://www.terraform.io/docs/commands/destroy.html) + +##### Description + +The destroy script is designed to be used when Invoked Scripts require terraform to destroy resources. The script performs three actions: + +- Enters the correct directory for the specified terraform component +- Initializes terraform +- Runs `terraform destroy` with any additional arguments + +**Note:** Unless `--force` is specified as an extra argument, invoking the `destroy` script will prompt the user for confirmation before performing any actions. + +### ecr-login + +##### Usage + +`ecr-login` + +This script is commonly used, however, to invoke a docker login command as such: + +`$(ecr-login)` + +##### Description + +The `ecr-login` script is a simple helper script to wrap the action of logging into the Amazon Elastic Container Registry for your AWS account. This is a necessary step before the execution of any `docker push` activities to the remote AWS Container Registry. + +### npm-build + +##### Usage + +`npm-build ` + +##### Description + +- installs dependencies with `npm install` +- tests code with 'npm test' +- lints code with 'npm lint' + +### output + +##### Usage + +`output ` + +- Terraform Component is the name of the terraform component being deployed. The script will cd to that component's folder and run all build actions from there. +- Extra Args are any other arguments that are normally passed to `terraform output`. A complete list can be found at the [Terraform Docs](https://www.terraform.io/docs/commands/output.html) + +##### Description + +The output script is designed to be used when Invoked Scripts require access to any of the outputs from the terraform component. The script performs three actions: + +- Enters the correct directory for the specified terraform component +- Initializes terraform +- Runs `terraform output` with any additional arguments + +This script is primarily used to pass information about terraform-created resources to other commands in subsequent steps of Invoked Scripts. Additionally, it is used to output information to developers and end-users of the deployment script(s). diff --git a/bin/cleanup.sh b/bin/cleanup.sh new file mode 100755 index 00000000..da07d63e --- /dev/null +++ b/bin/cleanup.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Exit on all nonzero exit codes. +set -e + +# Create the builder agent to help us with the rest of the build process +# We run this on all scripts to make sure that the builder is always up-to-date. +docker build -f docker/Dockerfile-Builder -t cfb-build-agent . + +# Set AWS Profile to default +export AWS_PROFILE="default" + +# Terraform Destroy all of our resources +# We still want to prompt the user for confirmation, especially with an action like this. +docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent destroy full-cluster + +# Empty out the Terraform State Bucket +docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent aws s3 rm s3://cfb-healthcare-rollcall-terraform-state --recursive + +# Delete the Terraform State Bucket +docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent aws s3api delete-bucket --bucket cfb-healthcare-rollcall-terraform-state --region us-east-1 + +# Clean up the stopped build agent containers +docker rm $(docker ps -a -q --filter ancestor=cfb-build-agent) > /dev/null 2>&1 diff --git a/bin/deploy.sh b/bin/deploy.sh new file mode 100755 index 00000000..93a4e047 --- /dev/null +++ b/bin/deploy.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +# Close the whole script after a nonzero exit code +set -e + +# Create the builder agent to help us with the rest of the build process +docker build -f docker/Dockerfile-Builder -t cfb-build-agent . + +# Set AWS Profile to default +export AWS_PROFILE="default" + +# Set values for Bellese SSO + +# Login to MFA only if the end user has passed in MFA credentials +if [ "$#" -gt 0 ] + then + # We have entered MFA Parameters + docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent aws-mfa --duration 129600 --device $1 +fi + +# Build and test the cfb healthcare rollcall api +# We mount the current directory into /app/ so the agent can see all code and scripts. +# docker run -it cfb-build-agent ./npm-build + + +# Run Terraform Apply to create the infrastructure +# We still want the end-user to have to accept the Terraform plan before executing. +docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent apply full-cluster + +# Get DB outputs +DB_NAME=$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent output full-cluster db_instance_name | tr -d '\r') +echo "Db name -> $DB_NAME" + +DB_PORT=$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent output full-cluster db_instance_port | tr -d '\r') +echo "Db port -> $DB_PORT" + +DB_USERNAME=$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent output full-cluster db_instance_username | tr -d '\r') +echo "Db username -> $DB_USERNAME" + +DB_PASSWORD=$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent output full-cluster db_instance_password | tr -d '\r') +echo "Db password -> $DB_PASSWORD" + +DB_ENDPOINT=$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent output full-cluster db_instance_endpoint | tr -d '\r') +echo "Db endpoint -> $DB_ENDPOINT" + +DB_ADDRESS=$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent output full-cluster db_instance_address | tr -d '\r') +echo "Db address -> $DB_ADDRESS" + +DB_URL="postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_ENDPOINT}:${DB_PORT}/${DB_NAME}" +echo "DB URL -> $DB_URL" +### Building and Pushing Docker Images ### +docker run -it -v $(pwd):/app/ cfb-build-agent ./db-create +# Log into the ECS Repository first +$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent ecr-login | tr -d '\r') + +# Build the container image for the API +docker build -f docker/Dockerfile-Bmore-Responsive -t bmore-responsive \ + --build-arg DB_URL=${DB_URL} . +# Get the address of the repository in AWS +CFB_REPO=$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent output full-cluster bmore-responsive_registry | tr -d '\r') +# Tag the image for pushing +docker tag bmore-responsive $CFB_REPO:latest +# Push the new docker image +docker push $CFB_REPO + +### Get important outputs for the end-user ### +S3_BUCKET=$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent output full-cluster output_bucket_name | tr -d '\r') +echo "Output Bucket -> $S3_BUCKET" + +LB_DNS=$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent output full-cluster load_balancer_address | tr -d '\r') +echo "API URL -> http://${LB_DNS}/" + +# Clean up the stopped build agent containers +docker rm $(docker ps -a -q --filter ancestor=cfb-build-agent) > /dev/null 2>&1 diff --git a/bin/mfa.sh b/bin/mfa.sh new file mode 100755 index 00000000..b25f628e --- /dev/null +++ b/bin/mfa.sh @@ -0,0 +1,26 @@ +#! /bin/bash + +# Exit on all nonzero exit codes. +set -e + +# Set AWS Profile to default +export AWS_PROFILE="default" + +# Create the builder agent to help us with the rest of the build process +# We run this on all scripts to make sure that the builder is always up-to-date. +docker build -f docker/Dockerfile-Builder -t cfb-build-agent . + +# Login to MFA only if the end user has passed in MFA credentials +# If they have not, then there is nothing to do here. +if [ "$#" -gt 0 ] + then + # We have entered MFA Parameters + docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent aws-mfa --duration 129600 --device $1 + else + # No MFA Serial Number has been entered + echo "No MFA Serial Number entered. Command usage: bin/mfa.sh " + echo "Please view DEPLOYING.md to get information about how to find your MFA Serial Number." +fi + +# Clean up the stopped build agent containers +docker rm $(docker ps -a -q --filter ancestor=cfb-build-agent) > /dev/null 2>&1 diff --git a/bin/redeploy.sh b/bin/redeploy.sh new file mode 100755 index 00000000..d9ae3563 --- /dev/null +++ b/bin/redeploy.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Exit on all nonzero exit codes. +set -e + +# Set AWS Profile to default +export AWS_PROFILE="default" + +# Create the builder agent to help us with the rest of the build process +# We run this on all scripts to make sure that the builder is always up-to-date. +docker build -f docker/Dockerfile-Builder -t cfb-build-agent . + + +# Rebuild the Java Projects +docker run -it -v $(pwd):/app/ cfb-build-agent npm-build + + +### Building and Pushing Docker Images ### +# Log into the ECS Repository first +$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent ecr-login | tr -d '\r') + +# Build the container image +docker build -f docker/Dockerfile-Bmore-Responsive -t bmore-responsive \ + --build-arg DB_URL="postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_ENDPOINT}:${DB_PORT}/${DB_NAME}" . +# Get the address of the repository in AWS +CFB_REPO=$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent output full-cluster bmore-responsive_registry | tr -d '\r') +# Tag the image for pushing +docker tag bmore-responsive $CFB_REPO:latest +# Push the new docker image +docker push $CFB_REPO + + +# Trigger AWS to Redeploy the running containers. +docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent aws ecs update-service --service bmore-responsive --cluster bmore-responsive-cluster --region us-east-1 --force-new-deployment + + +# Clean up the stopped build agent containers +docker rm $(docker ps -a -q --filter ancestor=cfb-build-agent) > /dev/null 2>&1 diff --git a/bin/util/apply b/bin/util/apply new file mode 100755 index 00000000..c4b22d04 --- /dev/null +++ b/bin/util/apply @@ -0,0 +1,5 @@ +#!/bin/bash + +cd terraform/components/$1 +terraform init +terraform apply ${@:2} diff --git a/bin/util/create-bucket b/bin/util/create-bucket new file mode 100755 index 00000000..53c36d40 --- /dev/null +++ b/bin/util/create-bucket @@ -0,0 +1,3 @@ +#!/bin/bash + +aws s3api create-bucket --bucket cfb-healthcare-rollcall-terraform-state --region us-east-1 diff --git a/bin/util/db-create b/bin/util/db-create new file mode 100755 index 00000000..3d66b3cf --- /dev/null +++ b/bin/util/db-create @@ -0,0 +1,3 @@ +#!/bin/bash + +npm run db-create \ No newline at end of file diff --git a/bin/util/destroy b/bin/util/destroy new file mode 100755 index 00000000..cb6e65c8 --- /dev/null +++ b/bin/util/destroy @@ -0,0 +1,5 @@ +#!/bin/bash + +cd terraform/components/$1 +terraform init +terraform destroy ${@:2} diff --git a/bin/util/ecr-login b/bin/util/ecr-login new file mode 100755 index 00000000..503b26a0 --- /dev/null +++ b/bin/util/ecr-login @@ -0,0 +1,2 @@ +#!/bin/bash +aws ecr get-login --no-include-email --region us-east-1 diff --git a/bin/util/npm-build b/bin/util/npm-build new file mode 100755 index 00000000..0267110e --- /dev/null +++ b/bin/util/npm-build @@ -0,0 +1,6 @@ +#!/bin/bash + +npm install +npm test +npm run lint + diff --git a/bin/util/output b/bin/util/output new file mode 100755 index 00000000..edd585ca --- /dev/null +++ b/bin/util/output @@ -0,0 +1,5 @@ +#!/bin/bash + +cd terraform/components/$1 +terraform init > /dev/null 2>&1 +terraform output ${@:2} diff --git a/Dockerfile b/docker/Dockerfile-Bmore-Responsive similarity index 86% rename from Dockerfile rename to docker/Dockerfile-Bmore-Responsive index 37af6200..6b28aefa 100644 --- a/Dockerfile +++ b/docker/Dockerfile-Bmore-Responsive @@ -15,5 +15,8 @@ COPY . . # Expose port (will not be respected by Heroku, must be defined in app) EXPOSE $port +ARG DB_URL +ENV DB_URL=$DB_URL + # Run app CMD ["npm","start"] diff --git a/docker/Dockerfile-Builder b/docker/Dockerfile-Builder new file mode 100644 index 00000000..30b5ac41 --- /dev/null +++ b/docker/Dockerfile-Builder @@ -0,0 +1,27 @@ +FROM maven:3.6.0-jdk-11-slim + +# Download package dependencies +RUN apt-get update && apt-get install wget python3-pip -y + +# Download Terraform and make it executable. +WORKDIR /tmp/ +RUN wget -O terraform.zip https://releases.hashicorp.com/terraform/0.11.13/terraform_0.11.13_linux_amd64.zip +RUN unzip terraform.zip +RUN mv terraform /usr/bin/terraform + +# Install the AWS CLI +RUN pip3 install awscli --upgrade + +# Install the AWS MFA Tool +RUN pip3 install aws-mfa + +#Install for Bellese aws sso +RUN pip3 install aws-google-auth + + +# Add Executables from docker/bin folder to PATH +ENV PATH "$PATH:/app/bin/util" + +# Create the directory for volume mounting +RUN mkdir /app/ +WORKDIR /app/ \ No newline at end of file diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000..e01ab9a8 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,85 @@ +# Docker Images + +## Docker Build Agent + +### Overview +The Docker Build Agent is a tool designed to satisfy the deployment dependencies for the Provider Matching and FQHC Pricer APIs. A common task, when modernizing systems and deploying resources to new environments such as AWS, is the installation of tools such as Terraform, Python, the AWS CLI, and Java. The introduction of these installation requirements and instructions leaves extensive room for human error or accidental misconfiguration as the result of unnoticed environmental differences. + +In order to solve this problem, the Docker Build Agent was developed in order to include all dependencies needed at known versions and configurations. All deployment scripts (as detailed in the [Deployment Script Documentation](../bin/README.md)) utilize this image to ensure that any development or evaluation machine is running the exact same version of every necessary tool with exactly the same configuration. As a direct result, developers interfacing with this repository only require Docker installed on their system, eliminating several steps in the onboarding process for new team members or modernization efforts. + +### Included Software + +The Build Agent includes the following software for development and deployment activities: + +- Java +- Apache Maven +- Terraform +- Python & pip +- AWS CLI & MFA Tools + +### Building the Agent + +The Build Agent is built during the normal operation of the `deploy`, `redeploy`, `cleanup`, and `mfa` scripts. This should ensure that the agent is always at the latest state when running infrastructure administration scripts. In the event that a build needs to be done outside of those scripts, the following command can be used: + +`docker build -f docker/Dockerfile-Builder -t mpsm-build-agent . ` + +This command assumes that you are running the build command from the root directory of the repository. If you are running it from any other directory, modify the `-f` statement with the relative path to the Dockerfile-Builder file. + +### Running the Agent + +As with the build process, the agent is most commonly run when using the Deployment Scripts in the `bin` folder. In the event, however, that the agent has to be run outside these scripts, the following command can be run: + +`docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE mpsm-build-agent ` + +Breaking down the command above, we get the following: +- `docker run -it` launches the container and enables it to log output to the console window for monitoring and accept user input as needed. +- `-v $(pwd):/app/` mounts the entire working directory to the `/app/` folder on the container. This enables the container to see all files in the project and interact with them. **Note:** Anything the container does to files in `/app/` happens on the host OS as well. Be careful with what you mount. +- `-v $(pwd)/docker/aws/:/root/.aws` is a special volume mount that enables developers to login to the AWS CLI with the files in `docker/aws/`. Additionally, the `mfa` script detailed in [Deployment Scripts](../bin/README.md) writes MFA credentials to this file which enables us to keep our MFA session across multiple Build Agent instances. +- `-e AWS_PROFILE=$AWS_PROFILE` sets the `AWS_PROFILE` environment variable on the container. This is needed for the AWS CLI if a value other than `default` is used. +- `mpsm-build-agent ` invokes the container image and a command to run. A List of helper scripts is found in the [Deployment Scripts](../bin/README.md) section. + +Please note that this example is for a run command launched from the root directory of the repository. + +### AWS Credentials + +In the `docker/` folder of the repository is a directory called `/aws/` which contains the files `config` and `credentials`. These files and directory represent an isolated AWS CLI credentials environment that is mounted to the Build Agent as needed for activities such as terraform invocations or AWS CLI calls. These files are passed to every running instance of the Build Agent and enable the developer to log into the AWS CLI without needing it installed on their local machine. + +**Warning:** The Credentials file has been added to source control for distribution purposes only. Please **DO NOT COMMIT ANY CHANGES TO THE CREDENTIALS FILE.** + +## Provider Matching and FQHC Pricer Containers + +### Overview +In addition to the Build Agent, there are also two files labeled `Dockerfile-Matching` and `Dockerfile-FQHC` which control the creation of Docker images for the Provider Matching and FQHC Pricer APIs respectively. These are both Java containers which have been configured to enable easy deployment of the application to any system running Docker. + +### Building the Images + +In the event that a build needs to be done outside of the `deploy` or `redeploy` scripts, the following command can be used: + +`docker build -f docker/Dockerfile-Matching -t provider-matching . ` + +or + +`docker build -f docker/Dockerfile-FQHC -t fqhc-pricer . ` + +This command assumes that you are running the build command from the root directory of the repository. If you are running it from any other directory, modify the `-f` statement with the relative path to the appropriate Dockerfile + +### Running the Images + +The application infrastructure for this challenge has been designed to deploy these containers into an AWS ECS cluster. This task is performed by the `deploy` and `redeploy` scripts. In the event that either container needs to be started locally, the following commands can be run: + +##### Provider Matching +The following command launches a daemonized version of the Provider Matching API listening on Port 8080: + +`docker run -d -e AWS_REGION=us-east-1 -e AWS_BUCKET= -v $(pwd)/docker/aws/:/root/.aws/ -p 8080:8080 provider-matching` + +- The environment variable `AWS_REGION` is set to `us-east-1` by default, however it can be changed to whichever region your S3 bucket is deployed to. +- The environment variable `AWS_BUCKET` is the address of the S3 bucket to output provider matching files to. +- The AWS environment variables are required for the container to output to the target S3 bucket after entries have been processed into .txt files. +- `-v $(pwd)/docker/aws/:/root/.aws/` enables the container to write to the target S3 bucket. **Note:** This assumes that you have a valid AWS session and permission to write to the target S3 bucket. +- `-p 8080:8080` binds local port 8080 to the internal docker port 8080. + +##### FQHC Pricer +The following command launches a daemonized version of the FQHC Pricer API listening on Port 8080: + +`docker run -d -p 8080:8080 fqhc-pricer` +- `-p 8080:8080` binds local port 8080 to the internal docker port 8080. diff --git a/docker/aws/config b/docker/aws/config new file mode 100644 index 00000000..d9f4cd06 --- /dev/null +++ b/docker/aws/config @@ -0,0 +1,3 @@ +[default] +region=us-east-1 +output=json diff --git a/docker/aws/credentials b/docker/aws/credentials new file mode 100644 index 00000000..abba670b --- /dev/null +++ b/docker/aws/credentials @@ -0,0 +1,6 @@ +[default] + + +[default-long-term] +aws_access_key_id = +aws_secret_access_key = diff --git a/terraform/README.md b/terraform/README.md new file mode 100644 index 00000000..4bd6dce9 --- /dev/null +++ b/terraform/README.md @@ -0,0 +1,23 @@ +# Terraform + +## Overview + +The infrastructure for this application has been designed to be launched and configured via terraform. This enables us to store the application infrastructure as code, share the infrastructure state via terraform's built-in support for AWS S3 state storage, and maintain infrastructure state easily though a series of scripts (as defined in [Deployment Scripts](../bin/README.md)). + +## Components + +### full-cluster + +The full-cluster component represents an ECS cluster running in its own, dedicated VPC. Please see the [README](components/full-cluster/README.md) for more details. + +## Modules +Each module contains its own README.md which includes information about its functionality. Please refer to individual module documentation for more information about inputs, outputs, and behaviors of each module. + +| Module | Documentation | +|---|---| +| ALB |[README.md](modules/alb/README.md)| +| ASG |[README.md](modules/asg/README.md)| +| ECS |[README.md](modules/ecs/README.md)| +| S3 |[README.md](modules/s3/README.md)| +| SG |[README.md](modules/sg/README.md)| +| VPC |[README.md](modules/vpc/README.md)| diff --git a/terraform/components/full-cluster/.terraform/modules/0daf126f48aa68f4100241c487117a28 b/terraform/components/full-cluster/.terraform/modules/0daf126f48aa68f4100241c487117a28 new file mode 120000 index 00000000..12b7c065 --- /dev/null +++ b/terraform/components/full-cluster/.terraform/modules/0daf126f48aa68f4100241c487117a28 @@ -0,0 +1 @@ +/app/terraform/modules/alb \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/2e4a68071a760a8b1062099112ed13a4 b/terraform/components/full-cluster/.terraform/modules/2e4a68071a760a8b1062099112ed13a4 new file mode 120000 index 00000000..94326497 --- /dev/null +++ b/terraform/components/full-cluster/.terraform/modules/2e4a68071a760a8b1062099112ed13a4 @@ -0,0 +1 @@ +/app/terraform/modules/vpc \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/4216cbb4ca33ef413c69198c7a92cc50 b/terraform/components/full-cluster/.terraform/modules/4216cbb4ca33ef413c69198c7a92cc50 new file mode 120000 index 00000000..4f0a2fc3 --- /dev/null +++ b/terraform/components/full-cluster/.terraform/modules/4216cbb4ca33ef413c69198c7a92cc50 @@ -0,0 +1 @@ +/app/terraform/modules/sg \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/541ab3affc48364d32a2e802f4095e12 b/terraform/components/full-cluster/.terraform/modules/541ab3affc48364d32a2e802f4095e12 new file mode 120000 index 00000000..8ad8a75d --- /dev/null +++ b/terraform/components/full-cluster/.terraform/modules/541ab3affc48364d32a2e802f4095e12 @@ -0,0 +1 @@ +/app/terraform/modules/db \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/562aa598e349828b88b903d520292895 b/terraform/components/full-cluster/.terraform/modules/562aa598e349828b88b903d520292895 new file mode 120000 index 00000000..2dc47437 --- /dev/null +++ b/terraform/components/full-cluster/.terraform/modules/562aa598e349828b88b903d520292895 @@ -0,0 +1 @@ +/app/terraform/modules/ecs \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/b02a64b582ee9aa7b66c80d8950a113f b/terraform/components/full-cluster/.terraform/modules/b02a64b582ee9aa7b66c80d8950a113f new file mode 120000 index 00000000..2a557037 --- /dev/null +++ b/terraform/components/full-cluster/.terraform/modules/b02a64b582ee9aa7b66c80d8950a113f @@ -0,0 +1 @@ +/app/terraform/modules/s3 \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/f5737180a6bf1920a6197be7389278b5 b/terraform/components/full-cluster/.terraform/modules/f5737180a6bf1920a6197be7389278b5 new file mode 120000 index 00000000..5323c251 --- /dev/null +++ b/terraform/components/full-cluster/.terraform/modules/f5737180a6bf1920a6197be7389278b5 @@ -0,0 +1 @@ +/app/terraform/modules/asg \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/modules.json b/terraform/components/full-cluster/.terraform/modules/modules.json new file mode 100644 index 00000000..f36d6b38 --- /dev/null +++ b/terraform/components/full-cluster/.terraform/modules/modules.json @@ -0,0 +1 @@ +{"Modules":[{"Source":"../../modules/s3","Key":"1.s3;../../modules/s3","Version":"","Dir":".terraform/modules/b02a64b582ee9aa7b66c80d8950a113f","Root":""},{"Source":"../../modules/vpc","Key":"1.vpc;../../modules/vpc","Version":"","Dir":".terraform/modules/2e4a68071a760a8b1062099112ed13a4","Root":""},{"Source":"../../modules/sg","Key":"1.sg;../../modules/sg","Version":"","Dir":".terraform/modules/4216cbb4ca33ef413c69198c7a92cc50","Root":""},{"Source":"../../modules/alb","Key":"1.alb;../../modules/alb","Version":"","Dir":".terraform/modules/0daf126f48aa68f4100241c487117a28","Root":""},{"Source":"../../modules/ecs","Key":"1.ecs_cluster;../../modules/ecs","Version":"","Dir":".terraform/modules/562aa598e349828b88b903d520292895","Root":""},{"Source":"../../modules/asg","Key":"1.asg;../../modules/asg","Version":"","Dir":".terraform/modules/f5737180a6bf1920a6197be7389278b5","Root":""},{"Source":"../../modules/db","Key":"1.db;../../modules/db","Version":"","Dir":".terraform/modules/541ab3affc48364d32a2e802f4095e12","Root":""}]} \ No newline at end of file diff --git a/terraform/components/full-cluster/README.md b/terraform/components/full-cluster/README.md new file mode 100644 index 00000000..e69de29b diff --git a/terraform/components/full-cluster/cfb-container.json.tpl b/terraform/components/full-cluster/cfb-container.json.tpl new file mode 100644 index 00000000..856d4c1d --- /dev/null +++ b/terraform/components/full-cluster/cfb-container.json.tpl @@ -0,0 +1,15 @@ +[ + { + "name": "bmore-responsive", + "image": "${image_address}", + "cpu": 128, + "memory": 512, + "essential": true, + "portMappings": [ + { + "containerPort": 8080, + "hostPort": 0 + } + ] + } +] diff --git a/terraform/components/full-cluster/main.tf b/terraform/components/full-cluster/main.tf new file mode 100644 index 00000000..6f143d25 --- /dev/null +++ b/terraform/components/full-cluster/main.tf @@ -0,0 +1,94 @@ +terraform { + backend "s3" { + bucket = "cfb-healthcare-rollcall-terraform-state" + key = "cfb-healthcare-rollcall/terraform/terraform.tfstate" + region = "us-east-1" + } +} + +provider "aws" { + region = "${var.aws_region}" +} + +# Set up output S3 Bucket +module "s3" { + source = "../../modules/s3" +} + +# Set up template files + +data "template_file" "cfb_ecs_task_definition" { + template = "${file("cfb-container.json.tpl")}" + vars = { + image_address = "${module.ecs_cluster.cfb_registry}" + s3_bucket = "${module.s3.output_bucket_name}" + } +} + + +data "template_file" "user_data" { + template = "${file("userdata.sh.tpl")}" + vars = { + cluster_name = "bmore-responsive-cluster" + } +} + + +# Set up AWS Resources + +module "vpc" { + source = "../../modules/vpc" + vpc_cidr = "10.0.0.0/16" +} + +module "sg" { + source = "../../modules/sg" + vpc_id = "${module.vpc.vpc-id}" +} + +module "alb" { + source = "../../modules/alb" + vpc_id = "${module.vpc.vpc-id}" + vpc_subnets = "${module.vpc.public-subnet-ids}" + lb_sg = "${module.sg.alb-sg-id}" + cfb_app_port = 8080 +} + +module "ecs_cluster" { + source = "../../modules/ecs" + cluster_name = "bmore-responsive-cluster" + output_bucket_arn = "${module.s3.output_bucket_arn}" + cfb_desired_count = "3" + cfb_target_group_arn = "${module.alb.tg-cfb-arn}" + cfb_container_name = "bmore-responsive" + cfb_container_port = "8080" + cfb_container_definitions = "${data.template_file.cfb_ecs_task_definition.rendered}" + +} + +module "asg" { + source = "../../modules/asg" + min_size = 3 + max_size = 6 + count = 3 + instance_type = "t3.medium" + user_data = "${data.template_file.user_data.rendered}" + cluster_name = "bmore-responsive-cluster" + subnet_ids = "${module.vpc.subnet-ids}" + asg_security_group_ids = ["${module.sg.ecs-sg-id}"] + ecs_role = "${module.ecs_cluster.ecs_role}" +} + +module "db" { + source = "../../modules/db" + engine_version = "10.6" + instance_class = "db.m3.medium" + username = "cfb_user" + password = "CCx71@!k09mBbv6" + port = "5432" + allocated_storage = "20" + vpc_security_group_ids = "${module.sg.alb-sg-id}" + db_subnet_group_name = "CFB Subnets" + maintenance_window = "Mon:00:00-Mon:03:00" + backup_window = "03:00-06:00" +} diff --git a/terraform/components/full-cluster/matching-container.json.tpl b/terraform/components/full-cluster/matching-container.json.tpl new file mode 100644 index 00000000..ebedb24a --- /dev/null +++ b/terraform/components/full-cluster/matching-container.json.tpl @@ -0,0 +1,25 @@ +[ + { + "name": "provider-matching", + "image": "${image_address}", + "cpu": 128, + "memory": 512, + "essential": true, + "portMappings": [ + { + "containerPort": 8080, + "hostPort": 0 + } + ], + "environment": [ + { + "name": "AWS_REGION", + "value": "us-east-1" + }, + { + "name": "AWS_BUCKET", + "value": "${s3_bucket}" + } + ] + } +] diff --git a/terraform/components/full-cluster/outputs.tf b/terraform/components/full-cluster/outputs.tf new file mode 100644 index 00000000..b2d94277 --- /dev/null +++ b/terraform/components/full-cluster/outputs.tf @@ -0,0 +1,44 @@ +output "output_bucket_name" { + description = "Name of the output s3 bucket" + value = "${module.s3.output_bucket_name}" +} + +output "bmore-responsive_registry" { + description = "Name of the Bmore Response Registry" + value = "${module.ecs_cluster.cfb_registry}" +} + + +output "load_balancer_address" { + description = "DNS Address of the Application Load Balancer" + value = "${module.alb.lb-dns}" +} + +output "db_instance_name" { + description = "The database name" + value = "${module.db.this_db_instance_name}" +} +output "db_instance_port" { + description = "The database port" + value = "${module.db.this_db_instance_port}" +} + +output "db_instance_username" { + description = "The master username for the database" + value = "${module.db.this_db_instance_username}" +} + +output "db_instance_password" { + description = "The database password (this password may be old, because Terraform doesn't track it after initial creation)" + value = "${module.db.this_db_instance_password}" +} + +output "db_instance_address" { + description = "The address of the RDS instance" + value = "${module.db.this_db_instance_address}" +} + +output "db_instance_endpoint" { + description = "The connection endpoint" + value = "${module.db.this_db_instance_endpoint}" +} \ No newline at end of file diff --git a/terraform/components/full-cluster/userdata.sh.tpl b/terraform/components/full-cluster/userdata.sh.tpl new file mode 100644 index 00000000..ba77652d --- /dev/null +++ b/terraform/components/full-cluster/userdata.sh.tpl @@ -0,0 +1,3 @@ +#!/bin/bash + +echo ECS_CLUSTER=${cluster_name} >> /etc/ecs/ecs.config diff --git a/terraform/components/full-cluster/variables.tf b/terraform/components/full-cluster/variables.tf new file mode 100644 index 00000000..29c17413 --- /dev/null +++ b/terraform/components/full-cluster/variables.tf @@ -0,0 +1,5 @@ +variable "aws_region" { + description = "AWS Region to use." + type = "string" + default = "us-east-1" +} diff --git a/terraform/modules/alb/README.md b/terraform/modules/alb/README.md new file mode 100644 index 00000000..f0848dc6 --- /dev/null +++ b/terraform/modules/alb/README.md @@ -0,0 +1,19 @@ +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| lb\_sg | Security group IDs for the lb | string | n/a | yes | +| mytags | Tags to include on the resources | map | `` | no | +| cfb\_app\_port | Port number the app will be running on | string | n/a | yes | +| vpc\_id | VPC ID that the lb will be placed in | string | n/a | yes | +| vpc\_subnets | VPC subnets the lb will use | list | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| lb-arn | ARN of the lb | +| lb-dns | DNS name for the lb | +| lb-id | ID of the lb | +| tg-cfb-arn | ARN of Target Group | + diff --git a/terraform/modules/alb/main.tf b/terraform/modules/alb/main.tf new file mode 100644 index 00000000..978e90f9 --- /dev/null +++ b/terraform/modules/alb/main.tf @@ -0,0 +1,48 @@ +# TODO: Add Documentation about adding HTTPS +# TODO: Make sure APIs have health checks that can be used +# Create ALB +resource "aws_lb" "lb" { + name = "bmore-responsive-api-alb" + internal = "false" + load_balancer_type = "application" + security_groups = ["${var.lb_sg}"] + + subnets = ["${var.vpc_subnets}"] + + enable_deletion_protection = false + + tags = "${merge(map("Name", "bmore-responsive-api-alb"), var.mytags)}" +} + +# Create ALB target group for both containers +resource "aws_lb_target_group" "tg-cfb" { + name_prefix = "bmore-responsive-" + port = "${var.cfb_app_port}" + protocol = "HTTP" + vpc_id = "${var.vpc_id}" + depends_on = [ + "aws_lb.lb", + ] + lifecycle { + create_before_destroy = true + } + + health_check { + path = "/health" + } +} + + +# Create ALB listener +# TODO: Add ALB Routing rules +resource "aws_lb_listener" "api-listener" { + load_balancer_arn = "${aws_lb.lb.arn}" + port = "80" + protocol = "HTTP" + + default_action { + target_group_arn = "${aws_lb_target_group.tg-cfb.arn}" + type = "forward" + } +} + diff --git a/terraform/modules/alb/outputs.tf b/terraform/modules/alb/outputs.tf new file mode 100644 index 00000000..837855d5 --- /dev/null +++ b/terraform/modules/alb/outputs.tf @@ -0,0 +1,19 @@ +output "lb-id" { + description = "ID of the lb" + value = "${aws_lb.lb.id}" +} + +output "lb-dns" { + description = "DNS name for the lb" + value = "${aws_lb.lb.dns_name}" +} + +output "lb-arn" { + description = "ARN of the lb" + value = "${aws_lb.lb.arn}" +} + +output "tg-pricer-arn" { + description = "ARN of Pricer Target Group" + value = "${aws_lb_target_group.tg-cfb.arn}" +} \ No newline at end of file diff --git a/terraform/modules/alb/variables.tf b/terraform/modules/alb/variables.tf new file mode 100644 index 00000000..e927b5a2 --- /dev/null +++ b/terraform/modules/alb/variables.tf @@ -0,0 +1,22 @@ +variable "vpc_id" { + description = "VPC ID that the lb will be placed in" +} + +variable "vpc_subnets" { + description = "VPC subnets the lb will use" + type = "list" +} + +variable "mytags" { + description = "Tags to include on the resources" + type = "map" + default = {} +} + +variable "cfb_app_port" { + description = "Port number the app will be running on" +} + +variable "lb_sg" { + description = "Security group IDs for the lb" +} diff --git a/terraform/modules/asg/README.md b/terraform/modules/asg/README.md new file mode 100644 index 00000000..6dcd5b13 --- /dev/null +++ b/terraform/modules/asg/README.md @@ -0,0 +1,16 @@ +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| asg\_security\_group\_ids | Additional security groups to attach to the ASG. | list | n/a | yes | +| cluster\_name | The name to be given to the ASG | string | n/a | yes | +| count | The number of instances to provision in the ASG. | string | `"1"` | no | +| default\_cooldown | Default cooldown for ASG. | string | `"300"` | no | +| ecs\_role | The ARN of the role attached to ECS Cluster instances | string | n/a | yes | +| instance\_type | The EC2 instance type. | string | n/a | yes | +| max\_size | Maximum size for ASG. Must set min, count and max. | string | n/a | yes | +| min\_size | Minimum size for ASG. Must set min, count and max. | string | n/a | yes | +| root\_block\_device | | list | `` | no | +| subnet\_ids | The subnet IDs used by the Auto Scaling Group. | list | n/a | yes | +| user\_data | The user data to provide when launching the instance. | string | n/a | yes | + diff --git a/terraform/modules/asg/main.tf b/terraform/modules/asg/main.tf new file mode 100644 index 00000000..6ead5b7f --- /dev/null +++ b/terraform/modules/asg/main.tf @@ -0,0 +1,61 @@ + +// Fill in information about the ECS host node here. +data "aws_ami" "ecs_agent_ami" { + most_recent = true + owners = ["amazon"] + + filter { + name = "name" + values = ["*amazon-ecs-optimized"] + } + + filter { + name = "virtualization-type" + values = ["hvm"] + } +} + +resource "aws_autoscaling_group" "ecs_cluster_asg" { + lifecycle { + create_before_destroy = true + } + + default_cooldown = "${var.default_cooldown}" + desired_capacity = "${var.count}" + health_check_grace_period = 300 + launch_configuration = "${aws_launch_configuration.ecs_cluster_config.name}" + max_size = "${var.max_size}" + min_size = "${var.min_size}" + name = "bmore-responsive-ecs-cluster-asg" + vpc_zone_identifier = ["${var.subnet_ids}"] + + tags = [ + { + key = "Name" + value = "bmore-responsive-ecs-cluster-asg" + propagate_at_launch = true + }, + { + key = "ECS Cluster" + value = "${var.cluster_name}" + propagate_at_launch = true + }, + ] +} + +resource "aws_launch_configuration" "ecs_cluster_config" { + lifecycle { + create_before_destroy = true + } + + root_block_device = "${var.root_block_device}" + + enable_monitoring = "true" + iam_instance_profile = "${var.ecs_role}" + image_id = "${data.aws_ami.ecs_agent_ami.image_id}" + instance_type = "${var.instance_type}" + name_prefix = "bmore-responsive-ecs-cluster-" + security_groups = ["${var.asg_security_group_ids}"] + + user_data = "${var.user_data}" +} diff --git a/terraform/modules/asg/outputs.tf b/terraform/modules/asg/outputs.tf new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/terraform/modules/asg/outputs.tf @@ -0,0 +1 @@ + diff --git a/terraform/modules/asg/variables.tf b/terraform/modules/asg/variables.tf new file mode 100644 index 00000000..33958153 --- /dev/null +++ b/terraform/modules/asg/variables.tf @@ -0,0 +1,54 @@ +variable "cluster_name"{ + description = "The name to be given to the ASG" + type = "string" +} + +variable "asg_security_group_ids" { + description = "Additional security groups to attach to the ASG." + type = "list" +} + +variable "count" { + description = "The number of instances to provision in the ASG." + type = "string" +} + +variable "default_cooldown" { + description = "Default cooldown for ASG." + default = "300" +} + +variable "instance_type" { + description = "The EC2 instance type." + type = "string" +} + +variable "max_size" { + description = "Maximum size for ASG. Must set min, count and max." + type = "string" +} + +variable "min_size" { + description = "Minimum size for ASG. Must set min, count and max." + type = "string" +} + +variable "subnet_ids" { + description = "The subnet IDs used by the Auto Scaling Group." + type = "list" +} + +variable "user_data" { + description = "The user data to provide when launching the instance." + type = "string" +} + +variable "root_block_device" { + type = "list" + default = [{volume_size = "30"}] +} + +variable "ecs_role" { + description = "The ARN of the role attached to ECS Cluster instances" + type = "string" +} diff --git a/terraform/modules/db/main.tf b/terraform/modules/db/main.tf new file mode 100644 index 00000000..0cd2a059 --- /dev/null +++ b/terraform/modules/db/main.tf @@ -0,0 +1,57 @@ +resource "aws_db_instance" "this" { + identifier = "healthcare-rollcall-postgres" + + engine = "postgres" + engine_version = "${var.engine_version}" + instance_class = "${var.instance_class}" + allocated_storage = "${var.allocated_storage}" + username = "${var.username}" + password = "${var.password}" + port = "${var.port}" + allocated_storage = "${var.allocated_storage}" + storage_type = "${var.storage_type}" + iops = "${var.iops}" + storage_encrypted = "${var.storage_encrypted}" + kms_key_id = "${var.kms_key_id}" + name = "healthcare-rollcall_db" + + # NOTE: Do NOT use 'user' as the value for 'username' as it throws: + # "Error creating DB Instance: InvalidParameterValue: MasterUsername + # user cannot be used as it is a reserved word used by the engine" + + vpc_security_group_ids = [ + "${var.vpc_security_group_ids}" ] + + publicly_accessible = false + + maintenance_window = "${var.maintenance_window}" + backup_window = "${var.backup_window}" + + # disable backups to create DB faster + backup_retention_period = 0 + + tags = { + Owner = "user" + Environment = "dev" + Name = "healthcare-rollcall_db" + } + + enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"] + + # DB subnet group + db_subnet_group_name = "${var.db_subnet_group_name}" + + + # DB parameter group + parameter_group_name = "${var.parameter_group_name}" + + # DB option group + major_engine_version = "9.6" + + # Snapshot name upon DB deletion + final_snapshot_identifier = "healthcare-rollcall_db" + + # Database Deletion Protection + deletion_protection = false + +} \ No newline at end of file diff --git a/terraform/modules/db/outputs.tf b/terraform/modules/db/outputs.tf new file mode 100644 index 00000000..0f336058 --- /dev/null +++ b/terraform/modules/db/outputs.tf @@ -0,0 +1,79 @@ +output "this_db_instance_address" { + description = "The address of the RDS instance" + value = "${this_db_instance_address}" +} + +output "this_db_instance_arn" { + description = "The ARN of the RDS instance" + value = "${this_db_instance_arn}" +} + +output "this_db_instance_availability_zone" { + description = "The availability zone of the RDS instance" + value = "${this_db_instance_availability_zone}" +} + +output "this_db_instance_endpoint" { + description = "The connection endpoint" + value = "${this_db_instance_endpoint}" +} + +output "this_db_instance_hosted_zone_id" { + description = "The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record)" + value = "${this_db_instance_hosted_zone_id}" +} + +output "this_db_instance_id" { + description = "The RDS instance ID" + value = "${this_db_instance_id}" +} + +output "this_db_instance_resource_id" { + description = "The RDS Resource ID of this instance" + value = "${this_db_instance_resource_id}" +} + +output "this_db_instance_status" { + description = "The RDS instance status" + value = "${this_db_instance_status}" +} + +output "this_db_instance_name" { + description = "The database name" + value = "${this_db_instance_name}" +} + +output "this_db_instance_username" { + description = "The master username for the database" + value = "${this_db_instance_username}" +} + +output "this_db_instance_password" { + description = "The database password (this password may be old, because Terraform doesn't track it after initial creation)" + value = "${this_db_instance_password}" +} + +output "this_db_instance_port" { + description = "The database port" + value = "${this_db_instance_port}" +} + +output "this_db_subnet_group_id" { + description = "The db subnet group name" + value = "${this_db_subnet_group_id}" +} + +output "this_db_subnet_group_arn" { + description = "The ARN of the db subnet group" + value = "${this_db_subnet_group_arn}" +} + +output "this_db_parameter_group_id" { + description = "The db parameter group id" + value = "${this_db_parameter_group_id}" +} + +output "this_db_parameter_group_arn" { + description = "The ARN of the db parameter group" + value = "${this_db_parameter_group_arn}" +} \ No newline at end of file diff --git a/terraform/modules/db/variables.tf b/terraform/modules/db/variables.tf new file mode 100644 index 00000000..972fecae --- /dev/null +++ b/terraform/modules/db/variables.tf @@ -0,0 +1,109 @@ +variable "engine_version" { + type = "string" + description = "The postgres engine version" + default = "" +} + +variable "instance_class" { + type = "string" + description = "The instance type of the RDS instance" +} + +variable "username" { + type = "string" + description = "Username for the master DB user" + default = "postgres" +} + +variable "password" { + type = "string" + description = "password for the master DB user" +} + +variable "port" { + type = "string" + description = "The port on which the DB accepts connections" + default = "5432" +} + +variable "allocated_storage" { + type = "string" + description = "The allocated storage in gigabytes. For read replica, set the same value as master's" +} + +variable "storage_type" { + type = "string" + description = "One of standard (magnetic), gp2 (general purpose SSD), or io1 (provisioned IOPS SSD)" + default = "gp2" +} + +variable "iops" { + type = "string" + description = "The amount of provisioned IOPS. Setting this implies a storage_type of io1" + default = "0" +} + +variable "storage_encrypted" { + type = "string" + description = "Specifies whether the DB instance is encrypted" + default = "true" +} + +variable "kms_key_id" { + type = "string" + description = "Specifies a custom KMS key to be used to encrypt" + default = "" +} + +variable "vpc_security_group_ids" { + type = "list" + description = "List of VPC security groups to associate" +} + + +variable "db_subnet_group_name" { + type = "string" + description = "Name of DB subnet group" + default = "" +} + +variable "parameter_group_name" { + type = "string" + description = "Name of the DB parameter group to associate" +} + +variable "availability_zone" { + type = "string" + description = "The AZ for the RDS instance. It is recommended to only use this when creating a read replica instance" + default = "" +} + + +variable "auto_minor_version_upgrade" { + type = "string" + description = "Indicates that minor engine upgrades will be applied automatically to the DB instance during the maintenance window" + default = "false" +} + +variable "apply_immediately" { + type = "string" + description = "Specifies whether any database modifications are applied immediately, or during the next maintenance window" + default = "false" +} + +variable "maintenance_window" { + type = "string" + description = "The window to perform maintenance in. Syntax: 'ddd:hh24:mi-ddd:hh24:mi'" +} + +variable "backup_retention_period" { + type = "string" + description = "The days to retain backups for" + default = 7 +} + +variable "backup_window" { + type = "string" + description = "The daily time range (in UTC) during which automated backups are created if they are enabled. Before and not overlap with maintenance_window" + default = "" +} diff --git a/terraform/modules/ecs/README.md b/terraform/modules/ecs/README.md new file mode 100644 index 00000000..d59c2463 --- /dev/null +++ b/terraform/modules/ecs/README.md @@ -0,0 +1,19 @@ +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| cluster\_name | The name to be given to the ASG | string | n/a | yes | +| output\_bucket\_arn | ARN of the S3 bucket that contains the output files | string | n/a | yes | +| bmore-responsive\_container\_definitions | The Rendered JSON of a container definition array. See example-container.json for a sample of valid JSON input. | string | n/a | yes | +| bmore-responsive\_container\_name | The name of the container to associate with the Load Balancer. Must equal the container name in the container definition JSON | string | n/a | yes | +| bmore-responsive\_container\_port | The port on the container to associate with the Load Balancer | string | n/a | yes | +| bmore-responsive\_desired\_count | The number of tasks to run in the service | string | n/a | yes | +| bmore-responsive\_target\_group\_arn | The ARN of the Target Group for Load Balancing | string | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| ecs\_role | ARN for the role attached to ECS Cluster instances. | +| cfb\_registry | Address for the Registry | + diff --git a/terraform/modules/ecs/main.tf b/terraform/modules/ecs/main.tf new file mode 100644 index 00000000..6bf27673 --- /dev/null +++ b/terraform/modules/ecs/main.tf @@ -0,0 +1,135 @@ +# Set up necessary IAM Roles for ECS Hosts + +data "aws_iam_policy_document" "ecs_cluster_asg_policy" { + + statement { + actions = [ + "ecs:DeregisterContainerInstance", + "ecs:DiscoverPollEndpoint", + "ecs:Poll", + "ecs:RegisterContainerInstance", + "ecs:StartTelemetrySession", + "ecs:Submit*", + "ecr:GetAuthorizationToken", + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "logs:CreateLogStream", + "logs:PutLogEvents" + ] + + resources = [ + "*", + ] + } + + statement { + actions = [ + "s3:*" + ] + + resources = [ + "${var.output_bucket_arn}", + "${var.output_bucket_arn}/*" + ] + } +} + +resource "aws_iam_role" "ecs_cluster" { + path = "/" + name = "bmore-responsive_ecs_cluster_role" + + assume_role_policy = <` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| output\_bucket\_arn | ARN for the S3 output bucket | +| output\_bucket\_name | Name for the S3 Bucket | + diff --git a/terraform/modules/s3/main.tf b/terraform/modules/s3/main.tf new file mode 100644 index 00000000..05e0f9df --- /dev/null +++ b/terraform/modules/s3/main.tf @@ -0,0 +1,6 @@ +resource "aws_s3_bucket" "output-bucket" { + bucket = "mpsm-provider-matching-output" + acl = "private" + force_destroy = "true" + tags = "${merge(map("Name", "mpsm-provider-matching-output"), var.mytags)}" +} diff --git a/terraform/modules/s3/outputs.tf b/terraform/modules/s3/outputs.tf new file mode 100644 index 00000000..7f64de0c --- /dev/null +++ b/terraform/modules/s3/outputs.tf @@ -0,0 +1,9 @@ +output "output_bucket_arn" { + description = "ARN for the S3 output bucket" + value = "${aws_s3_bucket.output-bucket.arn}" +} + +output "output_bucket_name" { + description = "Name for the S3 Bucket" + value = "${aws_s3_bucket.output-bucket.id}" +} diff --git a/terraform/modules/s3/variables.tf b/terraform/modules/s3/variables.tf new file mode 100644 index 00000000..70571336 --- /dev/null +++ b/terraform/modules/s3/variables.tf @@ -0,0 +1,5 @@ +variable "mytags" { + description = "Tags to include on the resources" + type = "map" + default = {} +} diff --git a/terraform/modules/sg/README.md b/terraform/modules/sg/README.md new file mode 100644 index 00000000..956ec661 --- /dev/null +++ b/terraform/modules/sg/README.md @@ -0,0 +1,14 @@ +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| mytags | Tags to include on the resources | map | `` | no | +| vpc\_id | VPC ID | string | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| alb-sg-id | ALB Security Group ID | +| ecs-sg-id | ECS Security Group ID | + diff --git a/terraform/modules/sg/main.tf b/terraform/modules/sg/main.tf new file mode 100644 index 00000000..d763ac94 --- /dev/null +++ b/terraform/modules/sg/main.tf @@ -0,0 +1,53 @@ +# Create security group + +resource "aws_security_group" "sg-alb" { + name = "bmore-responsive-alb-access" + vpc_id = "${var.vpc_id}" + + # Merge tags from environment tfvars and create name tag + tags = "${merge(map("Name", "bmore-responsive-alb-access"), var.mytags)}" + + ingress { + # TLS (change to whatever ports you need) + from_port = 80 + to_port = 80 + protocol = "tcp" + + # We open to 0.0.0.0/0 here to support the testing activities. + # In a production environment, these connections would be limited to + # approved internal IPs. (10.x.x.x/x block(s)) + cidr_blocks = ["0.0.0.0/0"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} + + +resource "aws_security_group" "sg-ecs" { + name = "bmore-responsive-ecs-host-access" + vpc_id = "${var.vpc_id}" + + # Merge tags from environment tfvars and create name tag + tags = "${merge(map("Name", "bmore-responsive-ecs-host-access"), var.mytags)}" + + ingress { + from_port = 32768 + to_port = 61000 + protocol = "tcp" + security_groups = ["${aws_security_group.sg-alb.id}"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} + +# TODO: Add SG Rules for ECS ASG and Load Balancer. diff --git a/terraform/modules/sg/outputs.tf b/terraform/modules/sg/outputs.tf new file mode 100644 index 00000000..6969ec43 --- /dev/null +++ b/terraform/modules/sg/outputs.tf @@ -0,0 +1,9 @@ +output "ecs-sg-id" { + description = "ECS Security Group ID" + value = "${aws_security_group.sg-ecs.id}" +} + +output "alb-sg-id" { + description = "ALB Security Group ID" + value = "${aws_security_group.sg-alb.id}" +} diff --git a/terraform/modules/sg/variables.tf b/terraform/modules/sg/variables.tf new file mode 100644 index 00000000..183a1a0d --- /dev/null +++ b/terraform/modules/sg/variables.tf @@ -0,0 +1,9 @@ +variable "vpc_id" { + description = "VPC ID" +} + +variable "mytags" { + description = "Tags to include on the resources" + type = "map" + default = {} +} diff --git a/terraform/modules/vpc/README.md b/terraform/modules/vpc/README.md new file mode 100644 index 00000000..b4864398 --- /dev/null +++ b/terraform/modules/vpc/README.md @@ -0,0 +1,18 @@ +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| availability\_zones | The Availability Zones to use in the VPC | list | `` | no | +| mytags | Tags to include on the resources | map | `` | no | +| private\_subnet\_cidrs | The CIDR Blocks of the Private Subnets | list | `` | no | +| public\_subnet\_cidrs | The CIDR Blocks of the Public Subnets | list | `` | no | +| vpc\_cidr | The CIDR block for the VPC | string | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| public-subnet-ids | Subnet IDs | +| subnet-ids | Subnet IDs | +| vpc-id | VPC ID | + diff --git a/terraform/modules/vpc/main.tf b/terraform/modules/vpc/main.tf new file mode 100644 index 00000000..25384c4a --- /dev/null +++ b/terraform/modules/vpc/main.tf @@ -0,0 +1,83 @@ +resource "aws_vpc" "vpc" { + cidr_block = "${var.vpc_cidr}" + tags = "${merge(map("Name", "cfb-healthcare-rollcall-vpc"), var.mytags)}" +} + +resource "aws_subnet" "public-subnet" { + count = 3 + vpc_id = "${aws_vpc.vpc.id}" + cidr_block = "${element(var.public_subnet_cidrs, count.index)}" + availability_zone = "${element(var.availability_zones, count.index)}" + + tags { + Name = "cfb-public-subnet-${count.index}" + } +} + +resource "aws_subnet" "private-subnet" { + count = 3 + vpc_id = "${aws_vpc.vpc.id}" + cidr_block = "${element(var.private_subnet_cidrs, count.index)}" + availability_zone = "${element(var.availability_zones, count.index)}" + + tags { + Name = "cfb-private-subnet-${count.index}" + } +} + +resource "aws_internet_gateway" "igw" { + vpc_id = "${aws_vpc.vpc.id}" + + tags { + Name = "CFB VPC IGW" + } +} + +# Set up Elastic IPs and NAT Gateways +resource "aws_eip" "nat-gw" { + count = 3 +} +resource "aws_nat_gateway" "nat-gw"{ + count = 3 + allocation_id = "${aws_eip.nat-gw.*.id[count.index]}" + subnet_id = "${aws_subnet.public-subnet.*.id[count.index]}" +} + +# Associate public routing rules to their subnets. +resource "aws_route_table" "public-route-table" { + vpc_id = "${aws_vpc.vpc.id}" + route { + cidr_block = "0.0.0.0/0" + gateway_id = "${aws_internet_gateway.igw.id}" + } + + tags { + Name = "CFB Public Subnets" + } +} + +resource "aws_route_table_association" "public-table-association" { + count = 3 + subnet_id = "${aws_subnet.public-subnet.*.id[count.index]}" + route_table_id = "${aws_route_table.public-route-table.id}" +} + +# Associate private routing rules to their subnets. +resource "aws_route_table" "private-route-table" { + count = 3 + vpc_id = "${aws_vpc.vpc.id}" + route { + cidr_block = "0.0.0.0/0" + nat_gateway_id = "${aws_nat_gateway.nat-gw.*.id[count.index]}" + } + + tags { + Name = "CFB Subnets" + } +} + +resource "aws_route_table_association" "private-table-association" { + count = 3 + subnet_id = "${aws_subnet.private-subnet.*.id[count.index]}" + route_table_id = "${aws_route_table.private-route-table.*.id[count.index]}" +} diff --git a/terraform/modules/vpc/outputs.tf b/terraform/modules/vpc/outputs.tf new file mode 100644 index 00000000..39525491 --- /dev/null +++ b/terraform/modules/vpc/outputs.tf @@ -0,0 +1,14 @@ +output "vpc-id" { + description = "VPC ID" + value = "${aws_vpc.vpc.id}" +} + +output "subnet-ids" { + description = "Subnet IDs" + value = ["${aws_subnet.private-subnet.*.id}"] +} + +output "public-subnet-ids" { + description = "Subnet IDs" + value = ["${aws_subnet.public-subnet.*.id}"] +} diff --git a/terraform/modules/vpc/variables.tf b/terraform/modules/vpc/variables.tf new file mode 100644 index 00000000..b70261ff --- /dev/null +++ b/terraform/modules/vpc/variables.tf @@ -0,0 +1,27 @@ +variable "vpc_cidr" { + description = "The CIDR block for the VPC" +} + +variable "mytags" { + description = "Tags to include on the resources" + type = "map" + default = {} +} + +variable "private_subnet_cidrs" { + description = "The CIDR Blocks of the Private Subnets" + type = "list" + default = ["10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24"] +} + +variable "public_subnet_cidrs" { + description = "The CIDR Blocks of the Public Subnets" + type = "list" + default = ["10.0.3.0/24", "10.0.4.0/24", "10.0.5.0/24"] +} + +variable "availability_zones" { + description = "The Availability Zones to use in the VPC" + type = "list" + default = ["us-east-1a", "us-east-1b", "us-east-1c"] +} From 0d1807c1ec65aedc0ebac85fd86152ec4e1e0d28 Mon Sep 17 00:00:00 2001 From: Billy Ani Date: Thu, 26 Mar 2020 17:18:17 -0400 Subject: [PATCH 02/13] infrastructure terraform finished no https as it has to be added after domain name is finalized. --- bin/deploy.sh | 2 -- docker/Dockerfile-Builder | 4 --- terraform/components/full-cluster/main.tf | 11 +++--- terraform/modules/alb/main.tf | 2 +- terraform/modules/alb/outputs.tf | 4 +-- terraform/modules/db/main.tf | 4 +-- terraform/modules/db/outputs.tf | 44 +++++++---------------- terraform/modules/ecs/main.tf | 2 +- terraform/modules/ecs/outputs.tf | 2 +- 9 files changed, 24 insertions(+), 51 deletions(-) diff --git a/bin/deploy.sh b/bin/deploy.sh index 93a4e047..eaf8f8d8 100755 --- a/bin/deploy.sh +++ b/bin/deploy.sh @@ -9,8 +9,6 @@ docker build -f docker/Dockerfile-Builder -t cfb-build-agent . # Set AWS Profile to default export AWS_PROFILE="default" -# Set values for Bellese SSO - # Login to MFA only if the end user has passed in MFA credentials if [ "$#" -gt 0 ] then diff --git a/docker/Dockerfile-Builder b/docker/Dockerfile-Builder index 30b5ac41..0952d055 100644 --- a/docker/Dockerfile-Builder +++ b/docker/Dockerfile-Builder @@ -15,10 +15,6 @@ RUN pip3 install awscli --upgrade # Install the AWS MFA Tool RUN pip3 install aws-mfa -#Install for Bellese aws sso -RUN pip3 install aws-google-auth - - # Add Executables from docker/bin folder to PATH ENV PATH "$PATH:/app/bin/util" diff --git a/terraform/components/full-cluster/main.tf b/terraform/components/full-cluster/main.tf index 6f143d25..19790e03 100644 --- a/terraform/components/full-cluster/main.tf +++ b/terraform/components/full-cluster/main.tf @@ -58,11 +58,11 @@ module "ecs_cluster" { source = "../../modules/ecs" cluster_name = "bmore-responsive-cluster" output_bucket_arn = "${module.s3.output_bucket_arn}" - cfb_desired_count = "3" - cfb_target_group_arn = "${module.alb.tg-cfb-arn}" - cfb_container_name = "bmore-responsive" - cfb_container_port = "8080" - cfb_container_definitions = "${data.template_file.cfb_ecs_task_definition.rendered}" + bmore-responsive_desired_count = "3" + bmore-responsive_target_group_arn = "${module.alb.tg-cfb-arn}" + bmore-responsive_container_name = "bmore-responsive" + bmore-responsive_container_port = "8080" + bmore-responsive_container_definitions = "${data.template_file.cfb_ecs_task_definition.rendered}" } @@ -91,4 +91,5 @@ module "db" { db_subnet_group_name = "CFB Subnets" maintenance_window = "Mon:00:00-Mon:03:00" backup_window = "03:00-06:00" + parameter_group_name = "db_parameter_group" } diff --git a/terraform/modules/alb/main.tf b/terraform/modules/alb/main.tf index 978e90f9..9b6ccadf 100644 --- a/terraform/modules/alb/main.tf +++ b/terraform/modules/alb/main.tf @@ -16,7 +16,7 @@ resource "aws_lb" "lb" { # Create ALB target group for both containers resource "aws_lb_target_group" "tg-cfb" { - name_prefix = "bmore-responsive-" + name_prefix = "cfb-" port = "${var.cfb_app_port}" protocol = "HTTP" vpc_id = "${var.vpc_id}" diff --git a/terraform/modules/alb/outputs.tf b/terraform/modules/alb/outputs.tf index 837855d5..5301c506 100644 --- a/terraform/modules/alb/outputs.tf +++ b/terraform/modules/alb/outputs.tf @@ -13,7 +13,7 @@ output "lb-arn" { value = "${aws_lb.lb.arn}" } -output "tg-pricer-arn" { - description = "ARN of Pricer Target Group" +output "tg-cfb-arn" { + description = "ARN of the Target Group" value = "${aws_lb_target_group.tg-cfb.arn}" } \ No newline at end of file diff --git a/terraform/modules/db/main.tf b/terraform/modules/db/main.tf index 0cd2a059..b1380788 100644 --- a/terraform/modules/db/main.tf +++ b/terraform/modules/db/main.tf @@ -45,11 +45,9 @@ resource "aws_db_instance" "this" { # DB parameter group parameter_group_name = "${var.parameter_group_name}" - # DB option group - major_engine_version = "9.6" # Snapshot name upon DB deletion - final_snapshot_identifier = "healthcare-rollcall_db" + final_snapshot_identifier = "healthcare-rollcall-db" # Database Deletion Protection deletion_protection = false diff --git a/terraform/modules/db/outputs.tf b/terraform/modules/db/outputs.tf index 0f336058..4b561f31 100644 --- a/terraform/modules/db/outputs.tf +++ b/terraform/modules/db/outputs.tf @@ -1,79 +1,59 @@ output "this_db_instance_address" { description = "The address of the RDS instance" - value = "${this_db_instance_address}" + value = "${aws_db_instance.this.address}" } output "this_db_instance_arn" { description = "The ARN of the RDS instance" - value = "${this_db_instance_arn}" + value = "${aws_db_instance.this.arn}" } output "this_db_instance_availability_zone" { description = "The availability zone of the RDS instance" - value = "${this_db_instance_availability_zone}" + value = "${aws_db_instance.this.availability_zone}" } output "this_db_instance_endpoint" { description = "The connection endpoint" - value = "${this_db_instance_endpoint}" + value = "${aws_db_instance.this.endpoint}" } output "this_db_instance_hosted_zone_id" { description = "The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record)" - value = "${this_db_instance_hosted_zone_id}" + value = "${aws_db_instance.this.hosted_zone_id}" } output "this_db_instance_id" { description = "The RDS instance ID" - value = "${this_db_instance_id}" + value = "${aws_db_instance.this.id}" } output "this_db_instance_resource_id" { description = "The RDS Resource ID of this instance" - value = "${this_db_instance_resource_id}" + value = "${aws_db_instance.this.resource_id}" } output "this_db_instance_status" { description = "The RDS instance status" - value = "${this_db_instance_status}" + value = "${aws_db_instance.this.status}" } output "this_db_instance_name" { description = "The database name" - value = "${this_db_instance_name}" + value = "${aws_db_instance.this.name}" } output "this_db_instance_username" { description = "The master username for the database" - value = "${this_db_instance_username}" + value = "${aws_db_instance.this.username}" } output "this_db_instance_password" { description = "The database password (this password may be old, because Terraform doesn't track it after initial creation)" - value = "${this_db_instance_password}" + value = "${aws_db_instance.this.password}" } output "this_db_instance_port" { description = "The database port" - value = "${this_db_instance_port}" + value = "${aws_db_instance.this.port}" } - -output "this_db_subnet_group_id" { - description = "The db subnet group name" - value = "${this_db_subnet_group_id}" -} - -output "this_db_subnet_group_arn" { - description = "The ARN of the db subnet group" - value = "${this_db_subnet_group_arn}" -} - -output "this_db_parameter_group_id" { - description = "The db parameter group id" - value = "${this_db_parameter_group_id}" -} - -output "this_db_parameter_group_arn" { - description = "The ARN of the db parameter group" - value = "${this_db_parameter_group_arn}" -} \ No newline at end of file diff --git a/terraform/modules/ecs/main.tf b/terraform/modules/ecs/main.tf index 6bf27673..a75c5b13 100644 --- a/terraform/modules/ecs/main.tf +++ b/terraform/modules/ecs/main.tf @@ -74,7 +74,7 @@ resource "aws_ecr_repository" "bmore-responsive-api" { resource "aws_ecr_repository_policy" "bmore-responsive-api-policy" { - repository = "${aws_ecr_repository.cfb-api.name}" + repository = "${aws_ecr_repository.bmore-responsive-api.name}" policy = < Date: Thu, 26 Mar 2020 18:38:15 -0500 Subject: [PATCH 03/13] improvement(infrastructure): upgraded modules to v12 and made resources more dynamic. also added waf, acm certificate, and https listener on the alb. --- .gitignore | 5 + .pre-commit-config.yaml | 5 + .../modules/0daf126f48aa68f4100241c487117a28 | 1 - .../modules/2e4a68071a760a8b1062099112ed13a4 | 1 - .../modules/4216cbb4ca33ef413c69198c7a92cc50 | 1 - .../modules/541ab3affc48364d32a2e802f4095e12 | 1 - .../modules/562aa598e349828b88b903d520292895 | 1 - .../modules/b02a64b582ee9aa7b66c80d8950a113f | 1 - .../modules/f5737180a6bf1920a6197be7389278b5 | 1 - .../.terraform/modules/modules.json | 1 - terraform/components/full-cluster/README.md | 41 +++++ terraform/components/full-cluster/main.tf | 141 +++++++++++------- terraform/components/full-cluster/outputs.tf | 30 ++-- .../components/full-cluster/terraform.tfvars | 2 + .../components/full-cluster/variables.tf | 25 +++- terraform/components/full-cluster/versions.tf | 4 + terraform/modules/alb/README.md | 27 +++- terraform/modules/alb/main.tf | 42 ++++-- terraform/modules/alb/outputs.tf | 15 +- terraform/modules/alb/variables.tf | 6 +- terraform/modules/asg/README.md | 39 +++-- terraform/modules/asg/main.tf | 52 ++++--- terraform/modules/asg/outputs.tf | 1 - terraform/modules/asg/variables.tf | 29 ++-- terraform/modules/certificate/README.md | 27 ++++ terraform/modules/certificate/main.tf | 30 ++++ terraform/modules/certificate/outputs.tf | 3 + terraform/modules/certificate/variables.tf | 24 +++ terraform/modules/db/README.md | 54 +++++++ terraform/modules/db/main.tf | 55 ++++--- terraform/modules/db/outputs.tf | 25 ++-- terraform/modules/db/variables.tf | 51 ++++--- terraform/modules/dns_record/README.md | 24 +++ terraform/modules/dns_record/main.tf | 15 ++ terraform/modules/dns_record/outputs.tf | 0 terraform/modules/dns_record/variables.tf | 7 + terraform/modules/ecs/README.md | 29 ++-- terraform/modules/ecs/main.tf | 45 +++--- terraform/modules/ecs/outputs.tf | 5 +- terraform/modules/ecs/variables.tf | 27 ++-- terraform/modules/s3/README.md | 18 ++- terraform/modules/s3/main.tf | 12 +- terraform/modules/s3/outputs.tf | 6 +- terraform/modules/s3/variables.tf | 8 +- terraform/modules/sg/README.md | 19 ++- terraform/modules/sg/main.tf | 10 +- terraform/modules/sg/outputs.tf | 6 +- terraform/modules/vpc/README.md | 25 +++- terraform/modules/vpc/main.tf | 77 ++++++---- terraform/modules/vpc/outputs.tf | 11 +- terraform/modules/vpc/variables.tf | 9 +- terraform/modules/waf | 1 + 52 files changed, 776 insertions(+), 319 deletions(-) create mode 100644 .pre-commit-config.yaml delete mode 120000 terraform/components/full-cluster/.terraform/modules/0daf126f48aa68f4100241c487117a28 delete mode 120000 terraform/components/full-cluster/.terraform/modules/2e4a68071a760a8b1062099112ed13a4 delete mode 120000 terraform/components/full-cluster/.terraform/modules/4216cbb4ca33ef413c69198c7a92cc50 delete mode 120000 terraform/components/full-cluster/.terraform/modules/541ab3affc48364d32a2e802f4095e12 delete mode 120000 terraform/components/full-cluster/.terraform/modules/562aa598e349828b88b903d520292895 delete mode 120000 terraform/components/full-cluster/.terraform/modules/b02a64b582ee9aa7b66c80d8950a113f delete mode 120000 terraform/components/full-cluster/.terraform/modules/f5737180a6bf1920a6197be7389278b5 delete mode 100644 terraform/components/full-cluster/.terraform/modules/modules.json create mode 100644 terraform/components/full-cluster/terraform.tfvars create mode 100644 terraform/components/full-cluster/versions.tf delete mode 100644 terraform/modules/asg/outputs.tf create mode 100644 terraform/modules/certificate/README.md create mode 100644 terraform/modules/certificate/main.tf create mode 100644 terraform/modules/certificate/outputs.tf create mode 100644 terraform/modules/certificate/variables.tf create mode 100644 terraform/modules/db/README.md create mode 100644 terraform/modules/dns_record/README.md create mode 100644 terraform/modules/dns_record/main.tf create mode 100644 terraform/modules/dns_record/outputs.tf create mode 100644 terraform/modules/dns_record/variables.tf create mode 160000 terraform/modules/waf diff --git a/.gitignore b/.gitignore index ce1d6f9d..1067fa0c 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,8 @@ dist # Mac stupid DS_Store files *.DS_Store + +.idea +.terraform +*.plan +*.out \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..175af7e7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +- repo: git://github.com/antonbabenko/pre-commit-terraform + rev: v1.27.0 # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases + hooks: + - id: terraform_fmt + - id: terraform_docs diff --git a/terraform/components/full-cluster/.terraform/modules/0daf126f48aa68f4100241c487117a28 b/terraform/components/full-cluster/.terraform/modules/0daf126f48aa68f4100241c487117a28 deleted file mode 120000 index 12b7c065..00000000 --- a/terraform/components/full-cluster/.terraform/modules/0daf126f48aa68f4100241c487117a28 +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/alb \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/2e4a68071a760a8b1062099112ed13a4 b/terraform/components/full-cluster/.terraform/modules/2e4a68071a760a8b1062099112ed13a4 deleted file mode 120000 index 94326497..00000000 --- a/terraform/components/full-cluster/.terraform/modules/2e4a68071a760a8b1062099112ed13a4 +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/vpc \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/4216cbb4ca33ef413c69198c7a92cc50 b/terraform/components/full-cluster/.terraform/modules/4216cbb4ca33ef413c69198c7a92cc50 deleted file mode 120000 index 4f0a2fc3..00000000 --- a/terraform/components/full-cluster/.terraform/modules/4216cbb4ca33ef413c69198c7a92cc50 +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/sg \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/541ab3affc48364d32a2e802f4095e12 b/terraform/components/full-cluster/.terraform/modules/541ab3affc48364d32a2e802f4095e12 deleted file mode 120000 index 8ad8a75d..00000000 --- a/terraform/components/full-cluster/.terraform/modules/541ab3affc48364d32a2e802f4095e12 +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/db \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/562aa598e349828b88b903d520292895 b/terraform/components/full-cluster/.terraform/modules/562aa598e349828b88b903d520292895 deleted file mode 120000 index 2dc47437..00000000 --- a/terraform/components/full-cluster/.terraform/modules/562aa598e349828b88b903d520292895 +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/ecs \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/b02a64b582ee9aa7b66c80d8950a113f b/terraform/components/full-cluster/.terraform/modules/b02a64b582ee9aa7b66c80d8950a113f deleted file mode 120000 index 2a557037..00000000 --- a/terraform/components/full-cluster/.terraform/modules/b02a64b582ee9aa7b66c80d8950a113f +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/s3 \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/f5737180a6bf1920a6197be7389278b5 b/terraform/components/full-cluster/.terraform/modules/f5737180a6bf1920a6197be7389278b5 deleted file mode 120000 index 5323c251..00000000 --- a/terraform/components/full-cluster/.terraform/modules/f5737180a6bf1920a6197be7389278b5 +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/asg \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/modules.json b/terraform/components/full-cluster/.terraform/modules/modules.json deleted file mode 100644 index f36d6b38..00000000 --- a/terraform/components/full-cluster/.terraform/modules/modules.json +++ /dev/null @@ -1 +0,0 @@ -{"Modules":[{"Source":"../../modules/s3","Key":"1.s3;../../modules/s3","Version":"","Dir":".terraform/modules/b02a64b582ee9aa7b66c80d8950a113f","Root":""},{"Source":"../../modules/vpc","Key":"1.vpc;../../modules/vpc","Version":"","Dir":".terraform/modules/2e4a68071a760a8b1062099112ed13a4","Root":""},{"Source":"../../modules/sg","Key":"1.sg;../../modules/sg","Version":"","Dir":".terraform/modules/4216cbb4ca33ef413c69198c7a92cc50","Root":""},{"Source":"../../modules/alb","Key":"1.alb;../../modules/alb","Version":"","Dir":".terraform/modules/0daf126f48aa68f4100241c487117a28","Root":""},{"Source":"../../modules/ecs","Key":"1.ecs_cluster;../../modules/ecs","Version":"","Dir":".terraform/modules/562aa598e349828b88b903d520292895","Root":""},{"Source":"../../modules/asg","Key":"1.asg;../../modules/asg","Version":"","Dir":".terraform/modules/f5737180a6bf1920a6197be7389278b5","Root":""},{"Source":"../../modules/db","Key":"1.db;../../modules/db","Version":"","Dir":".terraform/modules/541ab3affc48364d32a2e802f4095e12","Root":""}]} \ No newline at end of file diff --git a/terraform/components/full-cluster/README.md b/terraform/components/full-cluster/README.md index e69de29b..fb337992 100644 --- a/terraform/components/full-cluster/README.md +++ b/terraform/components/full-cluster/README.md @@ -0,0 +1,41 @@ +# Infrastructure Requirements + - [ ] Public DNS Zone - Route53 + - [ ] SSL/TLS termination at the ALB + - [ ] ALB + WAF + + +## Providers + +| Name | Version | +|------|---------| +| aws | 2.54.0 | +| random | n/a | +| template | n/a | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:-----:| +| aws\_region | AWS Region to use. | `string` | `"us-east-2"` | no | +| create\_waf | n/a | `bool` | `false` | no | +| db\_password | n/a | `string` | n/a | yes | +| public\_hosted\_zone\_name | n/a | `any` | n/a | yes | +| waf\_whitelist\_cidrs | n/a | `list(string)` |
[
"0.0.0.0/0"
]
| no | +| zone\_id | n/a | `any` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| bmore-responsive\_registry | Name of the Bmore Response Registry | +| db\_instance\_address | The address of the RDS instance | +| db\_instance\_endpoint | The connection endpoint | +| db\_instance\_name | The database name | +| db\_instance\_password | The database password (this password may be old, because Terraform doesn't track it after initial creation) | +| db\_instance\_port | The database port | +| db\_instance\_username | The master username for the database | +| load\_balancer\_address | DNS Address of the Application Load Balancer | +| output\_bucket\_name | Name of the output s3 bucket | +| public\_hosted\_zone\_nameservers | n/a | + + \ No newline at end of file diff --git a/terraform/components/full-cluster/main.tf b/terraform/components/full-cluster/main.tf index 19790e03..08be83da 100644 --- a/terraform/components/full-cluster/main.tf +++ b/terraform/components/full-cluster/main.tf @@ -1,95 +1,130 @@ terraform { + required_version = "0.12.6" backend "s3" { - bucket = "cfb-healthcare-rollcall-terraform-state" - key = "cfb-healthcare-rollcall/terraform/terraform.tfstate" - region = "us-east-1" + bucket = "cfb-healthcare-rollcall-us-east-2-terraform-state" + key = "cfb-healthcare-rollcall/terraform/terraform.tfstate" + region = "us-east-2" + encrypt = true } } provider "aws" { - region = "${var.aws_region}" + version = "2.54.0" + region = var.aws_region } # Set up output S3 Bucket module "s3" { - source = "../../modules/s3" + source = "../../modules/s3" + resource_suffix = random_pet.random_pet.id + aws_region = var.aws_region } # Set up template files data "template_file" "cfb_ecs_task_definition" { - template = "${file("cfb-container.json.tpl")}" - vars = { - image_address = "${module.ecs_cluster.cfb_registry}" - s3_bucket = "${module.s3.output_bucket_name}" + template = file("cfb-container.json.tpl") + vars = { + image_address = module.ecs_cluster.cfb_registry + s3_bucket = module.s3.output_bucket_name } } - data "template_file" "user_data" { - template = "${file("userdata.sh.tpl")}" - vars = { - cluster_name = "bmore-responsive-cluster" + template = file("userdata.sh.tpl") + vars = { + cluster_name = "bmore-responsive-cluster" } } - # Set up AWS Resources +resource "random_pet" "random_pet" { + length = 2 +} + module "vpc" { - source = "../../modules/vpc" - vpc_cidr = "10.0.0.0/16" + source = "../../modules/vpc" + vpc_cidr = "10.0.0.0/16" } module "sg" { - source = "../../modules/sg" - vpc_id = "${module.vpc.vpc-id}" + source = "../../modules/sg" + vpc_id = module.vpc.vpc-id +} + +data "aws_route53_zone" "hosted_zone" { + zone_id = var.zone_id +} + +module "certificate" { + source = "../../modules/certificate" + dns_zone_id = data.aws_route53_zone.hosted_zone.zone_id + domain_name = "api.${data.aws_route53_zone.hosted_zone.name}" +} + +module "dns_record" { + source = "../../modules/dns_record" + name = "api.${data.aws_route53_zone.hosted_zone.name}" + zone_id = data.aws_route53_zone.hosted_zone.zone_id + lb_dns_name = module.alb.lb-dns + lb_zone_id = module.alb.zone_id } module "alb" { - source = "../../modules/alb" - vpc_id = "${module.vpc.vpc-id}" - vpc_subnets = "${module.vpc.public-subnet-ids}" - lb_sg = "${module.sg.alb-sg-id}" - cfb_app_port = 8080 + source = "../../modules/alb" + vpc_id = module.vpc.vpc-id + vpc_subnets = module.vpc.public-subnet-ids + lb_sg = module.sg.alb-sg-id + cfb_app_port = 8080 + certificate_arn = module.certificate.certificate_arn } -module "ecs_cluster" { - source = "../../modules/ecs" - cluster_name = "bmore-responsive-cluster" - output_bucket_arn = "${module.s3.output_bucket_arn}" - bmore-responsive_desired_count = "3" - bmore-responsive_target_group_arn = "${module.alb.tg-cfb-arn}" - bmore-responsive_container_name = "bmore-responsive" - bmore-responsive_container_port = "8080" - bmore-responsive_container_definitions = "${data.template_file.cfb_ecs_task_definition.rendered}" +module "waf" { + source = "../../modules/waf" + whitelist_cidrs = var.waf_whitelist_cidrs + lb_arn = module.alb.lb-arn + resource_suffix = random_pet.random_pet.id +} +module "ecs_cluster" { + source = "../../modules/ecs" + cluster_name = "bmore-responsive-cluster" + output_bucket_arn = module.s3.output_bucket_arn + bmore-responsive_desired_count = "3" + bmore-responsive_target_group_arn = module.alb.tg-cfb-arn + bmore-responsive_container_name = "bmore-responsive" + bmore-responsive_container_port = "8080" + bmore-responsive_container_definitions = data.template_file.cfb_ecs_task_definition.rendered } module "asg" { - source = "../../modules/asg" - min_size = 3 - max_size = 6 - count = 3 - instance_type = "t3.medium" - user_data = "${data.template_file.user_data.rendered}" - cluster_name = "bmore-responsive-cluster" - subnet_ids = "${module.vpc.subnet-ids}" - asg_security_group_ids = ["${module.sg.ecs-sg-id}"] - ecs_role = "${module.ecs_cluster.ecs_role}" + source = "../../modules/asg" + min_size = 3 + max_size = 6 + instance_count = 3 + instance_type = "t3.medium" + user_data = data.template_file.user_data.rendered + cluster_name = "bmore-responsive-cluster" + subnet_ids = module.vpc.subnet_ids + asg_security_group_ids = [module.sg.ecs_sg_id] + ecs_role = module.ecs_cluster.ecs_role } module "db" { - source = "../../modules/db" - engine_version = "10.6" - instance_class = "db.m3.medium" - username = "cfb_user" - password = "CCx71@!k09mBbv6" - port = "5432" - allocated_storage = "20" - vpc_security_group_ids = "${module.sg.alb-sg-id}" + source = "../../modules/db" + resource_suffix = random_pet.random_pet.id + engine_version = "10.6" + instance_class = "db.t3.medium" + username = "cfb_user" + password = var.db_password + port = "5432" + allocated_storage = "20" + vpc_security_group_ids = [module.sg.alb-sg-id] db_subnet_group_name = "CFB Subnets" - maintenance_window = "Mon:00:00-Mon:03:00" - backup_window = "03:00-06:00" - parameter_group_name = "db_parameter_group" + maintenance_window = "Mon:00:00-Mon:03:00" + backup_window = "03:00-06:00" + parameter_group_name = "db_parameter_group" + subnet_ids = module.vpc.subnet_ids } + diff --git a/terraform/components/full-cluster/outputs.tf b/terraform/components/full-cluster/outputs.tf index b2d94277..90b3146c 100644 --- a/terraform/components/full-cluster/outputs.tf +++ b/terraform/components/full-cluster/outputs.tf @@ -1,44 +1,48 @@ output "output_bucket_name" { - description = "Name of the output s3 bucket" - value = "${module.s3.output_bucket_name}" + description = "Name of the output s3 bucket" + value = module.s3.output_bucket_name } output "bmore-responsive_registry" { - description = "Name of the Bmore Response Registry" - value = "${module.ecs_cluster.cfb_registry}" + description = "Name of the Bmore Response Registry" + value = module.ecs_cluster.cfb_registry } - output "load_balancer_address" { - description = "DNS Address of the Application Load Balancer" - value = "${module.alb.lb-dns}" + description = "DNS Address of the Application Load Balancer" + value = module.alb.lb-dns } output "db_instance_name" { description = "The database name" - value = "${module.db.this_db_instance_name}" + value = module.db.this_db_instance_name } + output "db_instance_port" { description = "The database port" - value = "${module.db.this_db_instance_port}" + value = module.db.this_db_instance_port } output "db_instance_username" { description = "The master username for the database" - value = "${module.db.this_db_instance_username}" + value = module.db.this_db_instance_username } output "db_instance_password" { description = "The database password (this password may be old, because Terraform doesn't track it after initial creation)" - value = "${module.db.this_db_instance_password}" + value = module.db.this_db_instance_password } output "db_instance_address" { description = "The address of the RDS instance" - value = "${module.db.this_db_instance_address}" + value = module.db.this_db_instance_address } output "db_instance_endpoint" { description = "The connection endpoint" - value = "${module.db.this_db_instance_endpoint}" + value = module.db.this_db_instance_endpoint +} + +output "public_hosted_zone_nameservers" { + value = data.aws_route53_zone.hosted_zone.zone_id } \ No newline at end of file diff --git a/terraform/components/full-cluster/terraform.tfvars b/terraform/components/full-cluster/terraform.tfvars new file mode 100644 index 00000000..34a66263 --- /dev/null +++ b/terraform/components/full-cluster/terraform.tfvars @@ -0,0 +1,2 @@ +public_hosted_zone_name = "bmoreres.codeforbaltimore.org" +zone_id = "Z06944204X33SC5R1R7Z" \ No newline at end of file diff --git a/terraform/components/full-cluster/variables.tf b/terraform/components/full-cluster/variables.tf index 29c17413..f22a6976 100644 --- a/terraform/components/full-cluster/variables.tf +++ b/terraform/components/full-cluster/variables.tf @@ -1,5 +1,26 @@ variable "aws_region" { description = "AWS Region to use." - type = "string" - default = "us-east-1" + type = string + default = "us-east-2" } + +variable "db_password" { + type = string + default = null +} + +variable "public_hosted_zone_name" {} + +variable "waf_whitelist_cidrs" { + type = list(string) + default = [ + "0.0.0.0/0" + ] +} + +variable "create_waf" { + type = bool + default = false +} + +variable "zone_id" {} \ No newline at end of file diff --git a/terraform/components/full-cluster/versions.tf b/terraform/components/full-cluster/versions.tf new file mode 100644 index 00000000..ac97c6ac --- /dev/null +++ b/terraform/components/full-cluster/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +} diff --git a/terraform/modules/alb/README.md b/terraform/modules/alb/README.md index f0848dc6..ecf083db 100644 --- a/terraform/modules/alb/README.md +++ b/terraform/modules/alb/README.md @@ -1,12 +1,22 @@ +# alb + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| lb\_sg | Security group IDs for the lb | string | n/a | yes | -| mytags | Tags to include on the resources | map | `` | no | -| cfb\_app\_port | Port number the app will be running on | string | n/a | yes | -| vpc\_id | VPC ID that the lb will be placed in | string | n/a | yes | -| vpc\_subnets | VPC subnets the lb will use | list | n/a | yes | +|------|-------------|------|---------|:-----:| +| certificate\_arn | n/a | `any` | n/a | yes | +| cfb\_app\_port | Port number the app will be running on | `any` | n/a | yes | +| lb\_sg | Security group IDs for the lb | `any` | n/a | yes | +| mytags | Tags to include on the resources | `map(string)` | `{}` | no | +| vpc\_id | VPC ID that the lb will be placed in | `any` | n/a | yes | +| vpc\_subnets | VPC subnets the lb will use | `list(string)` | n/a | yes | ## Outputs @@ -15,5 +25,8 @@ | lb-arn | ARN of the lb | | lb-dns | DNS name for the lb | | lb-id | ID of the lb | -| tg-cfb-arn | ARN of Target Group | +| tg-cfb-arn | ARN of the Target Group | +| zone\_id | n/a | + + diff --git a/terraform/modules/alb/main.tf b/terraform/modules/alb/main.tf index 9b6ccadf..d74b846e 100644 --- a/terraform/modules/alb/main.tf +++ b/terraform/modules/alb/main.tf @@ -1,3 +1,7 @@ +terraform { + required_version = ">= 0.12" +} + # TODO: Add Documentation about adding HTTPS # TODO: Make sure APIs have health checks that can be used # Create ALB @@ -5,43 +9,49 @@ resource "aws_lb" "lb" { name = "bmore-responsive-api-alb" internal = "false" load_balancer_type = "application" - security_groups = ["${var.lb_sg}"] + security_groups = [var.lb_sg] - subnets = ["${var.vpc_subnets}"] + subnets = var.vpc_subnets + + enable_cross_zone_load_balancing = true enable_deletion_protection = false - tags = "${merge(map("Name", "bmore-responsive-api-alb"), var.mytags)}" + tags = merge( + { + "Name" = "bmore-responsive-api-alb" + }, + var.mytags, + ) } # Create ALB target group for both containers resource "aws_lb_target_group" "tg-cfb" { - name_prefix = "cfb-" - port = "${var.cfb_app_port}" - protocol = "HTTP" - vpc_id = "${var.vpc_id}" - depends_on = [ - "aws_lb.lb", - ] + name_prefix = "cfb-" + port = var.cfb_app_port + protocol = "HTTP" + vpc_id = var.vpc_id + depends_on = [aws_lb.lb] lifecycle { create_before_destroy = true } health_check { - path = "/health" + path = "/health" } } - # Create ALB listener # TODO: Add ALB Routing rules resource "aws_lb_listener" "api-listener" { - load_balancer_arn = "${aws_lb.lb.arn}" - port = "80" - protocol = "HTTP" + load_balancer_arn = aws_lb.lb.arn + port = "443" + protocol = "HTTPS" + ssl_policy = "ELBSecurityPolicy-2016-08" + certificate_arn = var.certificate_arn default_action { - target_group_arn = "${aws_lb_target_group.tg-cfb.arn}" + target_group_arn = aws_lb_target_group.tg-cfb.arn type = "forward" } } diff --git a/terraform/modules/alb/outputs.tf b/terraform/modules/alb/outputs.tf index 5301c506..644c575f 100644 --- a/terraform/modules/alb/outputs.tf +++ b/terraform/modules/alb/outputs.tf @@ -1,19 +1,24 @@ output "lb-id" { description = "ID of the lb" - value = "${aws_lb.lb.id}" + value = aws_lb.lb.id } output "lb-dns" { description = "DNS name for the lb" - value = "${aws_lb.lb.dns_name}" + value = aws_lb.lb.dns_name } output "lb-arn" { description = "ARN of the lb" - value = "${aws_lb.lb.arn}" + value = aws_lb.lb.arn } output "tg-cfb-arn" { description = "ARN of the Target Group" - value = "${aws_lb_target_group.tg-cfb.arn}" -} \ No newline at end of file + value = aws_lb_target_group.tg-cfb.arn +} + +output "zone_id" { + value = aws_lb.lb.zone_id +} + diff --git a/terraform/modules/alb/variables.tf b/terraform/modules/alb/variables.tf index e927b5a2..a998e4f3 100644 --- a/terraform/modules/alb/variables.tf +++ b/terraform/modules/alb/variables.tf @@ -4,12 +4,12 @@ variable "vpc_id" { variable "vpc_subnets" { description = "VPC subnets the lb will use" - type = "list" + type = list(string) } variable "mytags" { description = "Tags to include on the resources" - type = "map" + type = map(string) default = {} } @@ -20,3 +20,5 @@ variable "cfb_app_port" { variable "lb_sg" { description = "Security group IDs for the lb" } + +variable "certificate_arn" {} \ No newline at end of file diff --git a/terraform/modules/asg/README.md b/terraform/modules/asg/README.md index 6dcd5b13..d3b57e0a 100644 --- a/terraform/modules/asg/README.md +++ b/terraform/modules/asg/README.md @@ -1,16 +1,31 @@ +# asg + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| asg\_security\_group\_ids | Additional security groups to attach to the ASG. | list | n/a | yes | -| cluster\_name | The name to be given to the ASG | string | n/a | yes | -| count | The number of instances to provision in the ASG. | string | `"1"` | no | -| default\_cooldown | Default cooldown for ASG. | string | `"300"` | no | -| ecs\_role | The ARN of the role attached to ECS Cluster instances | string | n/a | yes | -| instance\_type | The EC2 instance type. | string | n/a | yes | -| max\_size | Maximum size for ASG. Must set min, count and max. | string | n/a | yes | -| min\_size | Minimum size for ASG. Must set min, count and max. | string | n/a | yes | -| root\_block\_device | | list | `` | no | -| subnet\_ids | The subnet IDs used by the Auto Scaling Group. | list | n/a | yes | -| user\_data | The user data to provide when launching the instance. | string | n/a | yes | +|------|-------------|------|---------|:-----:| +| asg\_security\_group\_ids | Additional security groups to attach to the ASG. | `list(string)` | n/a | yes | +| cluster\_name | The name to be given to the ASG | `string` | n/a | yes | +| default\_cooldown | Default cooldown for ASG. | `string` | `"300"` | no | +| ecs\_role | The ARN of the role attached to ECS Cluster instances | `string` | n/a | yes | +| instance\_count | The number of instances to provision in the ASG. | `string` | n/a | yes | +| instance\_type | The EC2 instance type. | `string` | n/a | yes | +| max\_size | Maximum size for ASG. Must set min, count and max. | `string` | n/a | yes | +| min\_size | Minimum size for ASG. Must set min, count and max. | `string` | n/a | yes | +| root\_block\_device | n/a | `list(map(string))` |
[
{
"volume_size": "30"
}
]
| no | +| subnet\_ids | The subnet IDs used by the Auto Scaling Group. | `list(string)` | n/a | yes | +| user\_data | The user data to provide when launching the instance. | `string` | n/a | yes | + +## Outputs + +No output. + + diff --git a/terraform/modules/asg/main.tf b/terraform/modules/asg/main.tf index 6ead5b7f..bb38a6e6 100644 --- a/terraform/modules/asg/main.tf +++ b/terraform/modules/asg/main.tf @@ -1,3 +1,6 @@ +terraform { + required_version = ">= 0.12" +} // Fill in information about the ECS host node here. data "aws_ami" "ecs_agent_ami" { @@ -5,13 +8,13 @@ data "aws_ami" "ecs_agent_ami" { owners = ["amazon"] filter { - name = "name" - values = ["*amazon-ecs-optimized"] + name = "name" + values = ["*amazon-ecs-optimized"] } filter { - name = "virtualization-type" - values = ["hvm"] + name = "virtualization-type" + values = ["hvm"] } } @@ -20,14 +23,14 @@ resource "aws_autoscaling_group" "ecs_cluster_asg" { create_before_destroy = true } - default_cooldown = "${var.default_cooldown}" - desired_capacity = "${var.count}" + default_cooldown = var.default_cooldown + desired_capacity = var.instance_count health_check_grace_period = 300 - launch_configuration = "${aws_launch_configuration.ecs_cluster_config.name}" - max_size = "${var.max_size}" - min_size = "${var.min_size}" + launch_configuration = aws_launch_configuration.ecs_cluster_config.name + max_size = var.max_size + min_size = var.min_size name = "bmore-responsive-ecs-cluster-asg" - vpc_zone_identifier = ["${var.subnet_ids}"] + vpc_zone_identifier = var.subnet_ids tags = [ { @@ -37,7 +40,7 @@ resource "aws_autoscaling_group" "ecs_cluster_asg" { }, { key = "ECS Cluster" - value = "${var.cluster_name}" + value = var.cluster_name propagate_at_launch = true }, ] @@ -48,14 +51,29 @@ resource "aws_launch_configuration" "ecs_cluster_config" { create_before_destroy = true } - root_block_device = "${var.root_block_device}" + dynamic "root_block_device" { + for_each = var.root_block_device + content { + # TF-UPGRADE-TODO: The automatic upgrade tool can't predict + # which keys might be set in maps assigned here, so it has + # produced a comprehensive set here. Consider simplifying + # this after confirming which keys can be set in practice. + + delete_on_termination = lookup(root_block_device.value, "delete_on_termination", null) + encrypted = lookup(root_block_device.value, "encrypted", null) + iops = lookup(root_block_device.value, "iops", null) + volume_size = lookup(root_block_device.value, "volume_size", null) + volume_type = lookup(root_block_device.value, "volume_type", null) + } + } enable_monitoring = "true" - iam_instance_profile = "${var.ecs_role}" - image_id = "${data.aws_ami.ecs_agent_ami.image_id}" - instance_type = "${var.instance_type}" + iam_instance_profile = var.ecs_role + image_id = data.aws_ami.ecs_agent_ami.image_id + instance_type = var.instance_type name_prefix = "bmore-responsive-ecs-cluster-" - security_groups = ["${var.asg_security_group_ids}"] + security_groups = var.asg_security_group_ids - user_data = "${var.user_data}" + user_data = var.user_data } + diff --git a/terraform/modules/asg/outputs.tf b/terraform/modules/asg/outputs.tf deleted file mode 100644 index 8b137891..00000000 --- a/terraform/modules/asg/outputs.tf +++ /dev/null @@ -1 +0,0 @@ - diff --git a/terraform/modules/asg/variables.tf b/terraform/modules/asg/variables.tf index 33958153..691306bd 100644 --- a/terraform/modules/asg/variables.tf +++ b/terraform/modules/asg/variables.tf @@ -1,16 +1,16 @@ -variable "cluster_name"{ +variable "cluster_name" { description = "The name to be given to the ASG" - type = "string" + type = string } variable "asg_security_group_ids" { description = "Additional security groups to attach to the ASG." - type = "list" + type = list(string) } -variable "count" { +variable "instance_count" { description = "The number of instances to provision in the ASG." - type = "string" + type = string } variable "default_cooldown" { @@ -20,35 +20,38 @@ variable "default_cooldown" { variable "instance_type" { description = "The EC2 instance type." - type = "string" + type = string } variable "max_size" { description = "Maximum size for ASG. Must set min, count and max." - type = "string" + type = string } variable "min_size" { description = "Minimum size for ASG. Must set min, count and max." - type = "string" + type = string } variable "subnet_ids" { description = "The subnet IDs used by the Auto Scaling Group." - type = "list" + type = list(string) } variable "user_data" { description = "The user data to provide when launching the instance." - type = "string" + type = string } variable "root_block_device" { - type = "list" - default = [{volume_size = "30"}] + type = list(map(string)) + default = [{ + volume_size = "30" + }] } variable "ecs_role" { description = "The ARN of the role attached to ECS Cluster instances" - type = "string" + type = string } + diff --git a/terraform/modules/certificate/README.md b/terraform/modules/certificate/README.md new file mode 100644 index 00000000..4ea86a81 --- /dev/null +++ b/terraform/modules/certificate/README.md @@ -0,0 +1,27 @@ +# certificate + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:-----:| +| dns\_ttl | DNS records TTL | `number` | `60` | no | +| dns\_zone\_id | Route53 Zone id handleling the domains on the certificate | `any` | n/a | yes | +| domain\_name | Main domain name for the SSL certificate | `any` | n/a | yes | +| subject\_alternative\_names | Alternate domain names for the SSL certificate | `list(string)` | `[]` | no | +| tags | Tags associated to the certificate | `map(string)` | `{}` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| certificate\_arn | n/a | + + + diff --git a/terraform/modules/certificate/main.tf b/terraform/modules/certificate/main.tf new file mode 100644 index 00000000..f54bc403 --- /dev/null +++ b/terraform/modules/certificate/main.tf @@ -0,0 +1,30 @@ +terraform { + required_version = ">= 0.12" +} + +resource "aws_acm_certificate" "acm_certificate" { + domain_name = var.domain_name + validation_method = "DNS" + subject_alternative_names = var.subject_alternative_names + tags = var.tags + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_route53_record" "acm_certificate_validation_record" { + count = length(var.subject_alternative_names) + 1 + name = aws_acm_certificate.acm_certificate.domain_validation_options[count.index]["resource_record_name"] + type = "CNAME" + zone_id = var.dns_zone_id + records = [aws_acm_certificate.acm_certificate.domain_validation_options[count.index]["resource_record_value"]] + ttl = var.dns_ttl + allow_overwrite = true +} + +resource "aws_acm_certificate_validation" "acm_certificate_validation_record" { + depends_on = [aws_acm_certificate.acm_certificate] + certificate_arn = aws_acm_certificate.acm_certificate.arn + validation_record_fqdns = aws_route53_record.acm_certificate_validation_record.*.fqdn +} diff --git a/terraform/modules/certificate/outputs.tf b/terraform/modules/certificate/outputs.tf new file mode 100644 index 00000000..e99d9371 --- /dev/null +++ b/terraform/modules/certificate/outputs.tf @@ -0,0 +1,3 @@ +output "certificate_arn" { + value = aws_acm_certificate_validation.acm_certificate_validation_record.certificate_arn +} diff --git a/terraform/modules/certificate/variables.tf b/terraform/modules/certificate/variables.tf new file mode 100644 index 00000000..03d04e0a --- /dev/null +++ b/terraform/modules/certificate/variables.tf @@ -0,0 +1,24 @@ +variable "dns_zone_id" { + description = "Route53 Zone id handleling the domains on the certificate" +} + +variable "domain_name" { + description = "Main domain name for the SSL certificate" +} + +variable "dns_ttl" { + description = "DNS records TTL" + default = 60 +} + +variable "tags" { + description = "Tags associated to the certificate" + type = map(string) + default = {} +} + +variable "subject_alternative_names" { + description = "Alternate domain names for the SSL certificate" + type = list(string) + default = [] +} diff --git a/terraform/modules/db/README.md b/terraform/modules/db/README.md new file mode 100644 index 00000000..b7c12561 --- /dev/null +++ b/terraform/modules/db/README.md @@ -0,0 +1,54 @@ +# db + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:-----:| +| allocated\_storage | The allocated storage in gigabytes. For read replica, set the same value as master's | `string` | n/a | yes | +| apply\_immediately | Specifies whether any database modifications are applied immediately, or during the next maintenance window | `string` | `"false"` | no | +| auto\_minor\_version\_upgrade | Indicates that minor engine upgrades will be applied automatically to the DB instance during the maintenance window | `string` | `"false"` | no | +| availability\_zone | The AZ for the RDS instance. It is recommended to only use this when creating a read replica instance | `string` | `""` | no | +| backup\_retention\_period | The days to retain backups for | `string` | `7` | no | +| backup\_window | The daily time range (in UTC) during which automated backups are created if they are enabled. Before and not overlap with maintenance\_window | `string` | `""` | no | +| db\_subnet\_group\_name | Name of DB subnet group | `string` | `""` | no | +| engine\_version | The postgres engine version | `string` | `""` | no | +| instance\_class | The instance type of the RDS instance | `string` | n/a | yes | +| iops | The amount of provisioned IOPS. Setting this implies a storage\_type of io1 | `string` | `"0"` | no | +| kms\_key\_id | Specifies a custom KMS key to be used to encrypt | `string` | `""` | no | +| maintenance\_window | The window to perform maintenance in. Syntax: 'ddd:hh24:mi-ddd:hh24:mi' | `string` | n/a | yes | +| parameter\_group\_name | Name of the DB parameter group to associate | `string` | n/a | yes | +| password | password for the master DB user | `string` | n/a | yes | +| port | The port on which the DB accepts connections | `string` | `"5432"` | no | +| resource\_suffix | n/a | `string` | `"default"` | no | +| storage\_encrypted | Specifies whether the DB instance is encrypted | `string` | `"true"` | no | +| storage\_type | One of standard (magnetic), gp2 (general purpose SSD), or io1 (provisioned IOPS SSD) | `string` | `"gp2"` | no | +| subnet\_ids | n/a | `any` | n/a | yes | +| username | Username for the master DB user | `string` | `"postgres"` | no | +| vpc\_security\_group\_ids | List of VPC security groups to associate | `list(string)` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| this\_db\_instance\_address | The address of the RDS instance | +| this\_db\_instance\_arn | The ARN of the RDS instance | +| this\_db\_instance\_availability\_zone | The availability zone of the RDS instance | +| this\_db\_instance\_endpoint | The connection endpoint | +| this\_db\_instance\_hosted\_zone\_id | The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record) | +| this\_db\_instance\_id | The RDS instance ID | +| this\_db\_instance\_name | The database name | +| this\_db\_instance\_password | The database password (this password may be old, because Terraform doesn't track it after initial creation) | +| this\_db\_instance\_port | The database port | +| this\_db\_instance\_resource\_id | The RDS Resource ID of this instance | +| this\_db\_instance\_status | The RDS instance status | +| this\_db\_instance\_username | The master username for the database | + + + diff --git a/terraform/modules/db/main.tf b/terraform/modules/db/main.tf index b1380788..6a8037ae 100644 --- a/terraform/modules/db/main.tf +++ b/terraform/modules/db/main.tf @@ -1,31 +1,39 @@ +terraform { + required_version = ">= 0.12" +} + +resource "aws_db_subnet_group" "subnet_group" { + subnet_ids = var.subnet_ids +} + resource "aws_db_instance" "this" { identifier = "healthcare-rollcall-postgres" engine = "postgres" - engine_version = "${var.engine_version}" - instance_class = "${var.instance_class}" - allocated_storage = "${var.allocated_storage}" - username = "${var.username}" - password = "${var.password}" - port = "${var.port}" - allocated_storage = "${var.allocated_storage}" - storage_type = "${var.storage_type}" - iops = "${var.iops}" - storage_encrypted = "${var.storage_encrypted}" - kms_key_id = "${var.kms_key_id}" - name = "healthcare-rollcall_db" + engine_version = var.engine_version + instance_class = var.instance_class + allocated_storage = var.allocated_storage + username = var.username + password = var.password + port = var.port + + // allocated_storage = "${var.allocated_storage}" + storage_type = var.storage_type + iops = var.iops + storage_encrypted = var.storage_encrypted + kms_key_id = var.kms_key_id + name = "healthcareRollcallDB" # NOTE: Do NOT use 'user' as the value for 'username' as it throws: # "Error creating DB Instance: InvalidParameterValue: MasterUsername # user cannot be used as it is a reserved word used by the engine" - vpc_security_group_ids = [ - "${var.vpc_security_group_ids}" ] + vpc_security_group_ids = var.vpc_security_group_ids publicly_accessible = false - maintenance_window = "${var.maintenance_window}" - backup_window = "${var.backup_window}" + maintenance_window = var.maintenance_window + backup_window = var.backup_window # disable backups to create DB faster backup_retention_period = 0 @@ -39,17 +47,16 @@ resource "aws_db_instance" "this" { enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"] # DB subnet group - db_subnet_group_name = "${var.db_subnet_group_name}" - - - # DB parameter group - parameter_group_name = "${var.parameter_group_name}" - + db_subnet_group_name = aws_db_subnet_group.subnet_group.name # Snapshot name upon DB deletion - final_snapshot_identifier = "healthcare-rollcall-db" + final_snapshot_identifier = "healthcare-rollcall-db-${var.resource_suffix}" # Database Deletion Protection deletion_protection = false -} \ No newline at end of file + lifecycle { + ignore_changes = [password] + } +} + diff --git a/terraform/modules/db/outputs.tf b/terraform/modules/db/outputs.tf index 4b561f31..05c48023 100644 --- a/terraform/modules/db/outputs.tf +++ b/terraform/modules/db/outputs.tf @@ -1,59 +1,60 @@ output "this_db_instance_address" { description = "The address of the RDS instance" - value = "${aws_db_instance.this.address}" + value = aws_db_instance.this.address } output "this_db_instance_arn" { description = "The ARN of the RDS instance" - value = "${aws_db_instance.this.arn}" + value = aws_db_instance.this.arn } output "this_db_instance_availability_zone" { description = "The availability zone of the RDS instance" - value = "${aws_db_instance.this.availability_zone}" + value = aws_db_instance.this.availability_zone } output "this_db_instance_endpoint" { description = "The connection endpoint" - value = "${aws_db_instance.this.endpoint}" + value = aws_db_instance.this.endpoint } output "this_db_instance_hosted_zone_id" { description = "The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record)" - value = "${aws_db_instance.this.hosted_zone_id}" + value = aws_db_instance.this.hosted_zone_id } output "this_db_instance_id" { description = "The RDS instance ID" - value = "${aws_db_instance.this.id}" + value = aws_db_instance.this.id } output "this_db_instance_resource_id" { description = "The RDS Resource ID of this instance" - value = "${aws_db_instance.this.resource_id}" + value = aws_db_instance.this.resource_id } output "this_db_instance_status" { description = "The RDS instance status" - value = "${aws_db_instance.this.status}" + value = aws_db_instance.this.status } output "this_db_instance_name" { description = "The database name" - value = "${aws_db_instance.this.name}" + value = aws_db_instance.this.name } output "this_db_instance_username" { description = "The master username for the database" - value = "${aws_db_instance.this.username}" + value = aws_db_instance.this.username } output "this_db_instance_password" { description = "The database password (this password may be old, because Terraform doesn't track it after initial creation)" - value = "${aws_db_instance.this.password}" + value = aws_db_instance.this.password } output "this_db_instance_port" { description = "The database port" - value = "${aws_db_instance.this.port}" + value = aws_db_instance.this.port } + diff --git a/terraform/modules/db/variables.tf b/terraform/modules/db/variables.tf index 972fecae..2b6290c0 100644 --- a/terraform/modules/db/variables.tf +++ b/terraform/modules/db/variables.tf @@ -1,109 +1,116 @@ variable "engine_version" { - type = "string" + type = string description = "The postgres engine version" default = "" } variable "instance_class" { - type = "string" + type = string description = "The instance type of the RDS instance" } variable "username" { - type = "string" + type = string description = "Username for the master DB user" default = "postgres" } variable "password" { - type = "string" - description = "password for the master DB user" + type = string + description = "password for the master DB user" } variable "port" { - type = "string" + type = string description = "The port on which the DB accepts connections" default = "5432" } variable "allocated_storage" { - type = "string" + type = string description = "The allocated storage in gigabytes. For read replica, set the same value as master's" } variable "storage_type" { - type = "string" + type = string description = "One of standard (magnetic), gp2 (general purpose SSD), or io1 (provisioned IOPS SSD)" default = "gp2" } variable "iops" { - type = "string" + type = string description = "The amount of provisioned IOPS. Setting this implies a storage_type of io1" default = "0" } variable "storage_encrypted" { - type = "string" + type = string description = "Specifies whether the DB instance is encrypted" default = "true" } variable "kms_key_id" { - type = "string" + type = string description = "Specifies a custom KMS key to be used to encrypt" default = "" } variable "vpc_security_group_ids" { - type = "list" + type = list(string) description = "List of VPC security groups to associate" } - variable "db_subnet_group_name" { - type = "string" + type = string description = "Name of DB subnet group" default = "" } variable "parameter_group_name" { - type = "string" + type = string description = "Name of the DB parameter group to associate" } variable "availability_zone" { - type = "string" + type = string description = "The AZ for the RDS instance. It is recommended to only use this when creating a read replica instance" default = "" } - variable "auto_minor_version_upgrade" { - type = "string" + type = string description = "Indicates that minor engine upgrades will be applied automatically to the DB instance during the maintenance window" default = "false" } variable "apply_immediately" { - type = "string" + type = string description = "Specifies whether any database modifications are applied immediately, or during the next maintenance window" default = "false" } variable "maintenance_window" { - type = "string" + type = string description = "The window to perform maintenance in. Syntax: 'ddd:hh24:mi-ddd:hh24:mi'" } variable "backup_retention_period" { - type = "string" + type = string description = "The days to retain backups for" default = 7 } variable "backup_window" { - type = "string" + type = string description = "The daily time range (in UTC) during which automated backups are created if they are enabled. Before and not overlap with maintenance_window" default = "" } + +variable "subnet_ids" { + // type = list(string) +} + +variable "resource_suffix" { + default = "default" +} + diff --git a/terraform/modules/dns_record/README.md b/terraform/modules/dns_record/README.md new file mode 100644 index 00000000..0252d98a --- /dev/null +++ b/terraform/modules/dns_record/README.md @@ -0,0 +1,24 @@ +# dns_record + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:-----:| +| lb\_dns\_name | n/a | `any` | n/a | yes | +| lb\_zone\_id | n/a | `any` | n/a | yes | +| name | n/a | `any` | n/a | yes | +| zone\_id | n/a | `any` | n/a | yes | + +## Outputs + +No output. + + + diff --git a/terraform/modules/dns_record/main.tf b/terraform/modules/dns_record/main.tf new file mode 100644 index 00000000..bcec285d --- /dev/null +++ b/terraform/modules/dns_record/main.tf @@ -0,0 +1,15 @@ +terraform { + required_version = ">= 0.12" +} + +resource "aws_route53_record" "www" { + zone_id = var.zone_id + name = var.name + type = "A" + + alias { + name = var.lb_dns_name + zone_id = var.lb_zone_id + evaluate_target_health = true + } +} \ No newline at end of file diff --git a/terraform/modules/dns_record/outputs.tf b/terraform/modules/dns_record/outputs.tf new file mode 100644 index 00000000..e69de29b diff --git a/terraform/modules/dns_record/variables.tf b/terraform/modules/dns_record/variables.tf new file mode 100644 index 00000000..d28bbe36 --- /dev/null +++ b/terraform/modules/dns_record/variables.tf @@ -0,0 +1,7 @@ +variable "name" {} + +variable "zone_id" {} + +variable "lb_dns_name" {} + +variable "lb_zone_id" {} \ No newline at end of file diff --git a/terraform/modules/ecs/README.md b/terraform/modules/ecs/README.md index d59c2463..8634da4f 100644 --- a/terraform/modules/ecs/README.md +++ b/terraform/modules/ecs/README.md @@ -1,19 +1,30 @@ +# ecs + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| cluster\_name | The name to be given to the ASG | string | n/a | yes | -| output\_bucket\_arn | ARN of the S3 bucket that contains the output files | string | n/a | yes | -| bmore-responsive\_container\_definitions | The Rendered JSON of a container definition array. See example-container.json for a sample of valid JSON input. | string | n/a | yes | -| bmore-responsive\_container\_name | The name of the container to associate with the Load Balancer. Must equal the container name in the container definition JSON | string | n/a | yes | -| bmore-responsive\_container\_port | The port on the container to associate with the Load Balancer | string | n/a | yes | -| bmore-responsive\_desired\_count | The number of tasks to run in the service | string | n/a | yes | -| bmore-responsive\_target\_group\_arn | The ARN of the Target Group for Load Balancing | string | n/a | yes | +|------|-------------|------|---------|:-----:| +| bmore-responsive\_container\_definitions | The Rendered JSON of a container definition array. See example-container.json for a sample of valid JSON input. | `string` | n/a | yes | +| bmore-responsive\_container\_name | The name of the container to associate with the Load Balancer. Must equal the container name in the container definition JSON | `string` | n/a | yes | +| bmore-responsive\_container\_port | The port on the container to associate with the Load Balancer | `string` | n/a | yes | +| bmore-responsive\_desired\_count | The number of tasks to run in the service | `string` | n/a | yes | +| bmore-responsive\_target\_group\_arn | The ARN of the Target Group for Load Balancing | `string` | n/a | yes | +| cluster\_name | The name to be given to the ASG | `string` | n/a | yes | +| output\_bucket\_arn | ARN of the S3 bucket that contains the output files | `string` | n/a | yes | ## Outputs | Name | Description | |------|-------------| -| ecs\_role | ARN for the role attached to ECS Cluster instances. | | cfb\_registry | Address for the Registry | +| ecs\_role | ARN for the role attached to ECS Cluster instances. | + + diff --git a/terraform/modules/ecs/main.tf b/terraform/modules/ecs/main.tf index a75c5b13..0d36fbe3 100644 --- a/terraform/modules/ecs/main.tf +++ b/terraform/modules/ecs/main.tf @@ -1,7 +1,10 @@ +terraform { + required_version = ">= 0.12" +} + # Set up necessary IAM Roles for ECS Hosts data "aws_iam_policy_document" "ecs_cluster_asg_policy" { - statement { actions = [ "ecs:DeregisterContainerInstance", @@ -15,7 +18,7 @@ data "aws_iam_policy_document" "ecs_cluster_asg_policy" { "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "logs:CreateLogStream", - "logs:PutLogEvents" + "logs:PutLogEvents", ] resources = [ @@ -25,12 +28,12 @@ data "aws_iam_policy_document" "ecs_cluster_asg_policy" { statement { actions = [ - "s3:*" + "s3:*", ] resources = [ - "${var.output_bucket_arn}", - "${var.output_bucket_arn}/*" + var.output_bucket_arn, + "${var.output_bucket_arn}/*", ] } } @@ -54,17 +57,18 @@ resource "aws_iam_role" "ecs_cluster" { ] } EOF + } resource "aws_iam_role_policy" "ecs_cluster_asg_policy" { name_prefix = "ecs-cluster-policy-" - role = "${aws_iam_role.ecs_cluster.id}" - policy = "${data.aws_iam_policy_document.ecs_cluster_asg_policy.json}" + role = aws_iam_role.ecs_cluster.id + policy = data.aws_iam_policy_document.ecs_cluster_asg_policy.json } resource "aws_iam_instance_profile" "ecs_cluster_asg_profile" { name_prefix = "ecs_cluster_asg_profile-" - role = "${aws_iam_role.ecs_cluster.name}" + role = aws_iam_role.ecs_cluster.name } # Create the ECR Repositories for the containers @@ -72,9 +76,8 @@ resource "aws_ecr_repository" "bmore-responsive-api" { name = "bmore-responsive" } - resource "aws_ecr_repository_policy" "bmore-responsive-api-policy" { - repository = "${aws_ecr_repository.bmore-responsive-api.name}" + repository = aws_ecr_repository.bmore-responsive-api.name policy = < +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | +| random | n/a | + ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| mytags | Tags to include on the resources | map | `` | no | +|------|-------------|------|---------|:-----:| +| aws\_region | n/a | `any` | n/a | yes | +| mytags | Tags to include on the resources | `map(string)` | `{}` | no | +| resource\_suffix | n/a | `string` | `"default"` | no | ## Outputs @@ -11,3 +23,5 @@ | output\_bucket\_arn | ARN for the S3 output bucket | | output\_bucket\_name | Name for the S3 Bucket | + + diff --git a/terraform/modules/s3/main.tf b/terraform/modules/s3/main.tf index 05e0f9df..f35eccb1 100644 --- a/terraform/modules/s3/main.tf +++ b/terraform/modules/s3/main.tf @@ -1,6 +1,14 @@ +terraform { + required_version = ">= 0.12" +} + +resource "random_pet" "random_pet" { + length = 2 +} + resource "aws_s3_bucket" "output-bucket" { - bucket = "mpsm-provider-matching-output" + bucket = "cfb-healthcare-rollcall-${var.aws_region}-${var.resource_suffix}" acl = "private" force_destroy = "true" - tags = "${merge(map("Name", "mpsm-provider-matching-output"), var.mytags)}" + tags = merge(map("Name", "cfb-healthcare-rollcall-${var.aws_region}-${random_pet.random_pet.id}"), var.mytags) } diff --git a/terraform/modules/s3/outputs.tf b/terraform/modules/s3/outputs.tf index 7f64de0c..d6da2ab4 100644 --- a/terraform/modules/s3/outputs.tf +++ b/terraform/modules/s3/outputs.tf @@ -1,9 +1,9 @@ output "output_bucket_arn" { description = "ARN for the S3 output bucket" - value = "${aws_s3_bucket.output-bucket.arn}" + value = aws_s3_bucket.output-bucket.arn } output "output_bucket_name" { - description = "Name for the S3 Bucket" - value = "${aws_s3_bucket.output-bucket.id}" + description = "Name for the S3 Bucket" + value = aws_s3_bucket.output-bucket.id } diff --git a/terraform/modules/s3/variables.tf b/terraform/modules/s3/variables.tf index 70571336..f4cd02d8 100644 --- a/terraform/modules/s3/variables.tf +++ b/terraform/modules/s3/variables.tf @@ -1,5 +1,11 @@ variable "mytags" { description = "Tags to include on the resources" - type = "map" + type = map(string) default = {} } + +variable "aws_region" {} + +variable "resource_suffix" { + default = "default" +} \ No newline at end of file diff --git a/terraform/modules/sg/README.md b/terraform/modules/sg/README.md index 956ec661..7a18d31f 100644 --- a/terraform/modules/sg/README.md +++ b/terraform/modules/sg/README.md @@ -1,14 +1,25 @@ +# sg + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| mytags | Tags to include on the resources | map | `` | no | -| vpc\_id | VPC ID | string | n/a | yes | +|------|-------------|------|---------|:-----:| +| mytags | Tags to include on the resources | `map` | `{}` | no | +| vpc\_id | VPC ID | `any` | n/a | yes | ## Outputs | Name | Description | |------|-------------| | alb-sg-id | ALB Security Group ID | -| ecs-sg-id | ECS Security Group ID | +| ecs\_sg\_id | ECS Security Group ID | + + diff --git a/terraform/modules/sg/main.tf b/terraform/modules/sg/main.tf index d763ac94..e20a6462 100644 --- a/terraform/modules/sg/main.tf +++ b/terraform/modules/sg/main.tf @@ -1,3 +1,7 @@ +terraform { + required_version = ">= 0.12" +} + # Create security group resource "aws_security_group" "sg-alb" { @@ -9,9 +13,9 @@ resource "aws_security_group" "sg-alb" { ingress { # TLS (change to whatever ports you need) - from_port = 80 - to_port = 80 - protocol = "tcp" + from_port = 443 + to_port = 443 + protocol = "tcp" # We open to 0.0.0.0/0 here to support the testing activities. # In a production environment, these connections would be limited to diff --git a/terraform/modules/sg/outputs.tf b/terraform/modules/sg/outputs.tf index 6969ec43..a3add6b9 100644 --- a/terraform/modules/sg/outputs.tf +++ b/terraform/modules/sg/outputs.tf @@ -1,9 +1,9 @@ -output "ecs-sg-id" { +output "ecs_sg_id" { description = "ECS Security Group ID" - value = "${aws_security_group.sg-ecs.id}" + value = aws_security_group.sg-ecs.id } output "alb-sg-id" { description = "ALB Security Group ID" - value = "${aws_security_group.sg-alb.id}" + value = aws_security_group.sg-alb.id } diff --git a/terraform/modules/vpc/README.md b/terraform/modules/vpc/README.md index b4864398..e4d87a97 100644 --- a/terraform/modules/vpc/README.md +++ b/terraform/modules/vpc/README.md @@ -1,18 +1,29 @@ +# vpc + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| availability\_zones | The Availability Zones to use in the VPC | list | `` | no | -| mytags | Tags to include on the resources | map | `` | no | -| private\_subnet\_cidrs | The CIDR Blocks of the Private Subnets | list | `` | no | -| public\_subnet\_cidrs | The CIDR Blocks of the Public Subnets | list | `` | no | -| vpc\_cidr | The CIDR block for the VPC | string | n/a | yes | +|------|-------------|------|---------|:-----:| +| availability\_zones | The Availability Zones to use in the VPC | `list(string)` |
[
"us-east-1a",
"us-east-1b",
"us-east-1c"
]
| no | +| mytags | Tags to include on the resources | `map(string)` | `{}` | no | +| private\_subnet\_cidrs | The CIDR Blocks of the Private Subnets | `list(string)` |
[
"10.0.0.0/24",
"10.0.1.0/24",
"10.0.2.0/24"
]
| no | +| public\_subnet\_cidrs | The CIDR Blocks of the Public Subnets | `list(string)` |
[
"10.0.3.0/24",
"10.0.4.0/24",
"10.0.5.0/24"
]
| no | +| vpc\_cidr | The CIDR block for the VPC | `any` | n/a | yes | ## Outputs | Name | Description | |------|-------------| | public-subnet-ids | Subnet IDs | -| subnet-ids | Subnet IDs | +| subnet\_ids | Subnet IDs | | vpc-id | VPC ID | + + diff --git a/terraform/modules/vpc/main.tf b/terraform/modules/vpc/main.tf index 25384c4a..1eaffe55 100644 --- a/terraform/modules/vpc/main.tf +++ b/terraform/modules/vpc/main.tf @@ -1,83 +1,98 @@ +terraform { + required_version = ">= 0.12" +} + resource "aws_vpc" "vpc" { - cidr_block = "${var.vpc_cidr}" - tags = "${merge(map("Name", "cfb-healthcare-rollcall-vpc"), var.mytags)}" + cidr_block = var.vpc_cidr + tags = merge( + { + "Name" = "cfb-healthcare-rollcall-vpc" + }, + var.mytags, + ) +} + +data "aws_availability_zones" "available" { + state = "available" } resource "aws_subnet" "public-subnet" { - count = 3 - vpc_id = "${aws_vpc.vpc.id}" - cidr_block = "${element(var.public_subnet_cidrs, count.index)}" - availability_zone = "${element(var.availability_zones, count.index)}" + count = 3 + vpc_id = aws_vpc.vpc.id + cidr_block = element(var.public_subnet_cidrs, count.index) + availability_zone = data.aws_availability_zones.available.names[count.index] - tags { + tags = { Name = "cfb-public-subnet-${count.index}" } } resource "aws_subnet" "private-subnet" { - count = 3 - vpc_id = "${aws_vpc.vpc.id}" - cidr_block = "${element(var.private_subnet_cidrs, count.index)}" - availability_zone = "${element(var.availability_zones, count.index)}" + count = 3 + vpc_id = aws_vpc.vpc.id + cidr_block = element(var.private_subnet_cidrs, count.index) + availability_zone = data.aws_availability_zones.available.names[count.index] - tags { + tags = { Name = "cfb-private-subnet-${count.index}" } } resource "aws_internet_gateway" "igw" { - vpc_id = "${aws_vpc.vpc.id}" + vpc_id = aws_vpc.vpc.id - tags { + tags = { Name = "CFB VPC IGW" } } # Set up Elastic IPs and NAT Gateways resource "aws_eip" "nat-gw" { - count = 3 + count = 3 } -resource "aws_nat_gateway" "nat-gw"{ + +resource "aws_nat_gateway" "nat-gw" { count = 3 - allocation_id = "${aws_eip.nat-gw.*.id[count.index]}" - subnet_id = "${aws_subnet.public-subnet.*.id[count.index]}" + allocation_id = aws_eip.nat-gw[count.index].id + subnet_id = aws_subnet.public-subnet[count.index].id } # Associate public routing rules to their subnets. resource "aws_route_table" "public-route-table" { - vpc_id = "${aws_vpc.vpc.id}" + vpc_id = aws_vpc.vpc.id route { cidr_block = "0.0.0.0/0" - gateway_id = "${aws_internet_gateway.igw.id}" + gateway_id = aws_internet_gateway.igw.id } - tags { + tags = { Name = "CFB Public Subnets" } } resource "aws_route_table_association" "public-table-association" { - count = 3 - subnet_id = "${aws_subnet.public-subnet.*.id[count.index]}" - route_table_id = "${aws_route_table.public-route-table.id}" + count = 3 + subnet_id = aws_subnet.public-subnet[count.index].id + route_table_id = aws_route_table.public-route-table.id } # Associate private routing rules to their subnets. resource "aws_route_table" "private-route-table" { count = 3 - vpc_id = "${aws_vpc.vpc.id}" + vpc_id = aws_vpc.vpc.id route { - cidr_block = "0.0.0.0/0" - nat_gateway_id = "${aws_nat_gateway.nat-gw.*.id[count.index]}" + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.nat-gw[count.index].id } - tags { + tags = { Name = "CFB Subnets" } } resource "aws_route_table_association" "private-table-association" { - count = 3 - subnet_id = "${aws_subnet.private-subnet.*.id[count.index]}" - route_table_id = "${aws_route_table.private-route-table.*.id[count.index]}" + count = 3 + subnet_id = aws_subnet.private-subnet[count.index].id + route_table_id = aws_route_table.private-route-table[count.index].id } + diff --git a/terraform/modules/vpc/outputs.tf b/terraform/modules/vpc/outputs.tf index 39525491..8813161f 100644 --- a/terraform/modules/vpc/outputs.tf +++ b/terraform/modules/vpc/outputs.tf @@ -1,14 +1,13 @@ output "vpc-id" { description = "VPC ID" - value = "${aws_vpc.vpc.id}" + value = aws_vpc.vpc.id } - -output "subnet-ids" { +output "subnet_ids" { description = "Subnet IDs" - value = ["${aws_subnet.private-subnet.*.id}"] + value = aws_subnet.private-subnet.*.id } output "public-subnet-ids" { description = "Subnet IDs" - value = ["${aws_subnet.public-subnet.*.id}"] -} + value = aws_subnet.public-subnet.*.id +} \ No newline at end of file diff --git a/terraform/modules/vpc/variables.tf b/terraform/modules/vpc/variables.tf index b70261ff..3e213cc5 100644 --- a/terraform/modules/vpc/variables.tf +++ b/terraform/modules/vpc/variables.tf @@ -4,24 +4,25 @@ variable "vpc_cidr" { variable "mytags" { description = "Tags to include on the resources" - type = "map" + type = map(string) default = {} } variable "private_subnet_cidrs" { description = "The CIDR Blocks of the Private Subnets" - type = "list" + type = list(string) default = ["10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24"] } variable "public_subnet_cidrs" { description = "The CIDR Blocks of the Public Subnets" - type = "list" + type = list(string) default = ["10.0.3.0/24", "10.0.4.0/24", "10.0.5.0/24"] } variable "availability_zones" { description = "The Availability Zones to use in the VPC" - type = "list" + type = list(string) default = ["us-east-1a", "us-east-1b", "us-east-1c"] } + diff --git a/terraform/modules/waf b/terraform/modules/waf new file mode 160000 index 00000000..f9df9cc8 --- /dev/null +++ b/terraform/modules/waf @@ -0,0 +1 @@ +Subproject commit f9df9cc82b9a41bc85c45fd6372d3586c163f2e1 From 8e96a10d4c6c2f7f8492670b24f2c6f1aa0e26d4 Mon Sep 17 00:00:00 2001 From: Scott Hamrick Date: Thu, 26 Mar 2020 18:38:15 -0500 Subject: [PATCH 04/13] improvement(infrastructure): upgraded modules to v12 and made resources more dynamic. also added waf, acm certificate, and https listener on the alb. --- .gitignore | 5 + .pre-commit-config.yaml | 5 + .../modules/0daf126f48aa68f4100241c487117a28 | 1 - .../modules/2e4a68071a760a8b1062099112ed13a4 | 1 - .../modules/4216cbb4ca33ef413c69198c7a92cc50 | 1 - .../modules/541ab3affc48364d32a2e802f4095e12 | 1 - .../modules/562aa598e349828b88b903d520292895 | 1 - .../modules/b02a64b582ee9aa7b66c80d8950a113f | 1 - .../modules/f5737180a6bf1920a6197be7389278b5 | 1 - .../.terraform/modules/modules.json | 1 - terraform/components/full-cluster/README.md | 41 +++++ terraform/components/full-cluster/main.tf | 141 +++++++++++------- terraform/components/full-cluster/outputs.tf | 30 ++-- .../components/full-cluster/terraform.tfvars | 2 + .../components/full-cluster/variables.tf | 25 +++- terraform/components/full-cluster/versions.tf | 4 + terraform/modules/alb/README.md | 27 +++- terraform/modules/alb/main.tf | 42 ++++-- terraform/modules/alb/outputs.tf | 15 +- terraform/modules/alb/variables.tf | 6 +- terraform/modules/asg/README.md | 39 +++-- terraform/modules/asg/main.tf | 52 ++++--- terraform/modules/asg/outputs.tf | 1 - terraform/modules/asg/variables.tf | 29 ++-- .../modules/aws_wafregional_web_acl/README.md | 26 ++++ .../modules/aws_wafregional_web_acl/main.tf | 55 +++++++ .../aws_wafregional_web_acl/outputs.tf | 3 + .../aws_wafregional_web_acl/variables.tf | 12 ++ terraform/modules/certificate/README.md | 27 ++++ terraform/modules/certificate/main.tf | 30 ++++ terraform/modules/certificate/outputs.tf | 3 + terraform/modules/certificate/variables.tf | 24 +++ terraform/modules/db/README.md | 54 +++++++ terraform/modules/db/main.tf | 55 ++++--- terraform/modules/db/outputs.tf | 25 ++-- terraform/modules/db/variables.tf | 51 ++++--- terraform/modules/dns_record/README.md | 24 +++ terraform/modules/dns_record/main.tf | 15 ++ terraform/modules/dns_record/outputs.tf | 0 terraform/modules/dns_record/variables.tf | 7 + terraform/modules/ecs/README.md | 29 ++-- terraform/modules/ecs/main.tf | 45 +++--- terraform/modules/ecs/outputs.tf | 5 +- terraform/modules/ecs/variables.tf | 27 ++-- terraform/modules/s3/README.md | 18 ++- terraform/modules/s3/main.tf | 12 +- terraform/modules/s3/outputs.tf | 6 +- terraform/modules/s3/variables.tf | 8 +- terraform/modules/sg/README.md | 19 ++- terraform/modules/sg/main.tf | 10 +- terraform/modules/sg/outputs.tf | 6 +- terraform/modules/vpc/README.md | 25 +++- terraform/modules/vpc/main.tf | 77 ++++++---- terraform/modules/vpc/outputs.tf | 11 +- terraform/modules/vpc/variables.tf | 9 +- 55 files changed, 871 insertions(+), 319 deletions(-) create mode 100644 .pre-commit-config.yaml delete mode 120000 terraform/components/full-cluster/.terraform/modules/0daf126f48aa68f4100241c487117a28 delete mode 120000 terraform/components/full-cluster/.terraform/modules/2e4a68071a760a8b1062099112ed13a4 delete mode 120000 terraform/components/full-cluster/.terraform/modules/4216cbb4ca33ef413c69198c7a92cc50 delete mode 120000 terraform/components/full-cluster/.terraform/modules/541ab3affc48364d32a2e802f4095e12 delete mode 120000 terraform/components/full-cluster/.terraform/modules/562aa598e349828b88b903d520292895 delete mode 120000 terraform/components/full-cluster/.terraform/modules/b02a64b582ee9aa7b66c80d8950a113f delete mode 120000 terraform/components/full-cluster/.terraform/modules/f5737180a6bf1920a6197be7389278b5 delete mode 100644 terraform/components/full-cluster/.terraform/modules/modules.json create mode 100644 terraform/components/full-cluster/terraform.tfvars create mode 100644 terraform/components/full-cluster/versions.tf delete mode 100644 terraform/modules/asg/outputs.tf create mode 100644 terraform/modules/aws_wafregional_web_acl/README.md create mode 100644 terraform/modules/aws_wafregional_web_acl/main.tf create mode 100644 terraform/modules/aws_wafregional_web_acl/outputs.tf create mode 100644 terraform/modules/aws_wafregional_web_acl/variables.tf create mode 100644 terraform/modules/certificate/README.md create mode 100644 terraform/modules/certificate/main.tf create mode 100644 terraform/modules/certificate/outputs.tf create mode 100644 terraform/modules/certificate/variables.tf create mode 100644 terraform/modules/db/README.md create mode 100644 terraform/modules/dns_record/README.md create mode 100644 terraform/modules/dns_record/main.tf create mode 100644 terraform/modules/dns_record/outputs.tf create mode 100644 terraform/modules/dns_record/variables.tf diff --git a/.gitignore b/.gitignore index ce1d6f9d..1067fa0c 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,8 @@ dist # Mac stupid DS_Store files *.DS_Store + +.idea +.terraform +*.plan +*.out \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..175af7e7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +- repo: git://github.com/antonbabenko/pre-commit-terraform + rev: v1.27.0 # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases + hooks: + - id: terraform_fmt + - id: terraform_docs diff --git a/terraform/components/full-cluster/.terraform/modules/0daf126f48aa68f4100241c487117a28 b/terraform/components/full-cluster/.terraform/modules/0daf126f48aa68f4100241c487117a28 deleted file mode 120000 index 12b7c065..00000000 --- a/terraform/components/full-cluster/.terraform/modules/0daf126f48aa68f4100241c487117a28 +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/alb \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/2e4a68071a760a8b1062099112ed13a4 b/terraform/components/full-cluster/.terraform/modules/2e4a68071a760a8b1062099112ed13a4 deleted file mode 120000 index 94326497..00000000 --- a/terraform/components/full-cluster/.terraform/modules/2e4a68071a760a8b1062099112ed13a4 +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/vpc \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/4216cbb4ca33ef413c69198c7a92cc50 b/terraform/components/full-cluster/.terraform/modules/4216cbb4ca33ef413c69198c7a92cc50 deleted file mode 120000 index 4f0a2fc3..00000000 --- a/terraform/components/full-cluster/.terraform/modules/4216cbb4ca33ef413c69198c7a92cc50 +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/sg \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/541ab3affc48364d32a2e802f4095e12 b/terraform/components/full-cluster/.terraform/modules/541ab3affc48364d32a2e802f4095e12 deleted file mode 120000 index 8ad8a75d..00000000 --- a/terraform/components/full-cluster/.terraform/modules/541ab3affc48364d32a2e802f4095e12 +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/db \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/562aa598e349828b88b903d520292895 b/terraform/components/full-cluster/.terraform/modules/562aa598e349828b88b903d520292895 deleted file mode 120000 index 2dc47437..00000000 --- a/terraform/components/full-cluster/.terraform/modules/562aa598e349828b88b903d520292895 +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/ecs \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/b02a64b582ee9aa7b66c80d8950a113f b/terraform/components/full-cluster/.terraform/modules/b02a64b582ee9aa7b66c80d8950a113f deleted file mode 120000 index 2a557037..00000000 --- a/terraform/components/full-cluster/.terraform/modules/b02a64b582ee9aa7b66c80d8950a113f +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/s3 \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/f5737180a6bf1920a6197be7389278b5 b/terraform/components/full-cluster/.terraform/modules/f5737180a6bf1920a6197be7389278b5 deleted file mode 120000 index 5323c251..00000000 --- a/terraform/components/full-cluster/.terraform/modules/f5737180a6bf1920a6197be7389278b5 +++ /dev/null @@ -1 +0,0 @@ -/app/terraform/modules/asg \ No newline at end of file diff --git a/terraform/components/full-cluster/.terraform/modules/modules.json b/terraform/components/full-cluster/.terraform/modules/modules.json deleted file mode 100644 index f36d6b38..00000000 --- a/terraform/components/full-cluster/.terraform/modules/modules.json +++ /dev/null @@ -1 +0,0 @@ -{"Modules":[{"Source":"../../modules/s3","Key":"1.s3;../../modules/s3","Version":"","Dir":".terraform/modules/b02a64b582ee9aa7b66c80d8950a113f","Root":""},{"Source":"../../modules/vpc","Key":"1.vpc;../../modules/vpc","Version":"","Dir":".terraform/modules/2e4a68071a760a8b1062099112ed13a4","Root":""},{"Source":"../../modules/sg","Key":"1.sg;../../modules/sg","Version":"","Dir":".terraform/modules/4216cbb4ca33ef413c69198c7a92cc50","Root":""},{"Source":"../../modules/alb","Key":"1.alb;../../modules/alb","Version":"","Dir":".terraform/modules/0daf126f48aa68f4100241c487117a28","Root":""},{"Source":"../../modules/ecs","Key":"1.ecs_cluster;../../modules/ecs","Version":"","Dir":".terraform/modules/562aa598e349828b88b903d520292895","Root":""},{"Source":"../../modules/asg","Key":"1.asg;../../modules/asg","Version":"","Dir":".terraform/modules/f5737180a6bf1920a6197be7389278b5","Root":""},{"Source":"../../modules/db","Key":"1.db;../../modules/db","Version":"","Dir":".terraform/modules/541ab3affc48364d32a2e802f4095e12","Root":""}]} \ No newline at end of file diff --git a/terraform/components/full-cluster/README.md b/terraform/components/full-cluster/README.md index e69de29b..fb337992 100644 --- a/terraform/components/full-cluster/README.md +++ b/terraform/components/full-cluster/README.md @@ -0,0 +1,41 @@ +# Infrastructure Requirements + - [ ] Public DNS Zone - Route53 + - [ ] SSL/TLS termination at the ALB + - [ ] ALB + WAF + + +## Providers + +| Name | Version | +|------|---------| +| aws | 2.54.0 | +| random | n/a | +| template | n/a | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:-----:| +| aws\_region | AWS Region to use. | `string` | `"us-east-2"` | no | +| create\_waf | n/a | `bool` | `false` | no | +| db\_password | n/a | `string` | n/a | yes | +| public\_hosted\_zone\_name | n/a | `any` | n/a | yes | +| waf\_whitelist\_cidrs | n/a | `list(string)` |
[
"0.0.0.0/0"
]
| no | +| zone\_id | n/a | `any` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| bmore-responsive\_registry | Name of the Bmore Response Registry | +| db\_instance\_address | The address of the RDS instance | +| db\_instance\_endpoint | The connection endpoint | +| db\_instance\_name | The database name | +| db\_instance\_password | The database password (this password may be old, because Terraform doesn't track it after initial creation) | +| db\_instance\_port | The database port | +| db\_instance\_username | The master username for the database | +| load\_balancer\_address | DNS Address of the Application Load Balancer | +| output\_bucket\_name | Name of the output s3 bucket | +| public\_hosted\_zone\_nameservers | n/a | + + \ No newline at end of file diff --git a/terraform/components/full-cluster/main.tf b/terraform/components/full-cluster/main.tf index 19790e03..16919059 100644 --- a/terraform/components/full-cluster/main.tf +++ b/terraform/components/full-cluster/main.tf @@ -1,95 +1,130 @@ terraform { + required_version = "0.12.6" backend "s3" { - bucket = "cfb-healthcare-rollcall-terraform-state" - key = "cfb-healthcare-rollcall/terraform/terraform.tfstate" - region = "us-east-1" + bucket = "cfb-healthcare-rollcall-us-east-2-terraform-state" + key = "cfb-healthcare-rollcall/terraform/terraform.tfstate" + region = "us-east-2" + encrypt = true } } provider "aws" { - region = "${var.aws_region}" + version = "2.54.0" + region = var.aws_region } # Set up output S3 Bucket module "s3" { - source = "../../modules/s3" + source = "../../modules/s3" + resource_suffix = random_pet.random_pet.id + aws_region = var.aws_region } # Set up template files data "template_file" "cfb_ecs_task_definition" { - template = "${file("cfb-container.json.tpl")}" - vars = { - image_address = "${module.ecs_cluster.cfb_registry}" - s3_bucket = "${module.s3.output_bucket_name}" + template = file("cfb-container.json.tpl") + vars = { + image_address = module.ecs_cluster.cfb_registry + s3_bucket = module.s3.output_bucket_name } } - data "template_file" "user_data" { - template = "${file("userdata.sh.tpl")}" - vars = { - cluster_name = "bmore-responsive-cluster" + template = file("userdata.sh.tpl") + vars = { + cluster_name = "bmore-responsive-cluster" } } - # Set up AWS Resources +resource "random_pet" "random_pet" { + length = 2 +} + module "vpc" { - source = "../../modules/vpc" - vpc_cidr = "10.0.0.0/16" + source = "../../modules/vpc" + vpc_cidr = "10.0.0.0/16" } module "sg" { - source = "../../modules/sg" - vpc_id = "${module.vpc.vpc-id}" + source = "../../modules/sg" + vpc_id = module.vpc.vpc-id +} + +data "aws_route53_zone" "hosted_zone" { + zone_id = var.zone_id +} + +module "certificate" { + source = "../../modules/certificate" + dns_zone_id = data.aws_route53_zone.hosted_zone.zone_id + domain_name = "api.${data.aws_route53_zone.hosted_zone.name}" +} + +module "dns_record" { + source = "../../modules/dns_record" + name = "api.${data.aws_route53_zone.hosted_zone.name}" + zone_id = data.aws_route53_zone.hosted_zone.zone_id + lb_dns_name = module.alb.lb-dns + lb_zone_id = module.alb.zone_id } module "alb" { - source = "../../modules/alb" - vpc_id = "${module.vpc.vpc-id}" - vpc_subnets = "${module.vpc.public-subnet-ids}" - lb_sg = "${module.sg.alb-sg-id}" - cfb_app_port = 8080 + source = "../../modules/alb" + vpc_id = module.vpc.vpc-id + vpc_subnets = module.vpc.public-subnet-ids + lb_sg = module.sg.alb-sg-id + cfb_app_port = 8080 + certificate_arn = module.certificate.certificate_arn } -module "ecs_cluster" { - source = "../../modules/ecs" - cluster_name = "bmore-responsive-cluster" - output_bucket_arn = "${module.s3.output_bucket_arn}" - bmore-responsive_desired_count = "3" - bmore-responsive_target_group_arn = "${module.alb.tg-cfb-arn}" - bmore-responsive_container_name = "bmore-responsive" - bmore-responsive_container_port = "8080" - bmore-responsive_container_definitions = "${data.template_file.cfb_ecs_task_definition.rendered}" +module "waf" { + source = "../../modules/aws_wafregional_web_acl" + whitelist_cidrs = var.waf_whitelist_cidrs + lb_arn = module.alb.lb-arn + resource_suffix = random_pet.random_pet.id +} +module "ecs_cluster" { + source = "../../modules/ecs" + cluster_name = "bmore-responsive-cluster" + output_bucket_arn = module.s3.output_bucket_arn + bmore-responsive_desired_count = "3" + bmore-responsive_target_group_arn = module.alb.tg-cfb-arn + bmore-responsive_container_name = "bmore-responsive" + bmore-responsive_container_port = "8080" + bmore-responsive_container_definitions = data.template_file.cfb_ecs_task_definition.rendered } module "asg" { - source = "../../modules/asg" - min_size = 3 - max_size = 6 - count = 3 - instance_type = "t3.medium" - user_data = "${data.template_file.user_data.rendered}" - cluster_name = "bmore-responsive-cluster" - subnet_ids = "${module.vpc.subnet-ids}" - asg_security_group_ids = ["${module.sg.ecs-sg-id}"] - ecs_role = "${module.ecs_cluster.ecs_role}" + source = "../../modules/asg" + min_size = 3 + max_size = 6 + instance_count = 3 + instance_type = "t3.medium" + user_data = data.template_file.user_data.rendered + cluster_name = "bmore-responsive-cluster" + subnet_ids = module.vpc.subnet_ids + asg_security_group_ids = [module.sg.ecs_sg_id] + ecs_role = module.ecs_cluster.ecs_role } module "db" { - source = "../../modules/db" - engine_version = "10.6" - instance_class = "db.m3.medium" - username = "cfb_user" - password = "CCx71@!k09mBbv6" - port = "5432" - allocated_storage = "20" - vpc_security_group_ids = "${module.sg.alb-sg-id}" + source = "../../modules/db" + resource_suffix = random_pet.random_pet.id + engine_version = "10.6" + instance_class = "db.t3.medium" + username = "cfb_user" + password = var.db_password + port = "5432" + allocated_storage = "20" + vpc_security_group_ids = [module.sg.alb-sg-id] db_subnet_group_name = "CFB Subnets" - maintenance_window = "Mon:00:00-Mon:03:00" - backup_window = "03:00-06:00" - parameter_group_name = "db_parameter_group" + maintenance_window = "Mon:00:00-Mon:03:00" + backup_window = "03:00-06:00" + parameter_group_name = "db_parameter_group" + subnet_ids = module.vpc.subnet_ids } + diff --git a/terraform/components/full-cluster/outputs.tf b/terraform/components/full-cluster/outputs.tf index b2d94277..90b3146c 100644 --- a/terraform/components/full-cluster/outputs.tf +++ b/terraform/components/full-cluster/outputs.tf @@ -1,44 +1,48 @@ output "output_bucket_name" { - description = "Name of the output s3 bucket" - value = "${module.s3.output_bucket_name}" + description = "Name of the output s3 bucket" + value = module.s3.output_bucket_name } output "bmore-responsive_registry" { - description = "Name of the Bmore Response Registry" - value = "${module.ecs_cluster.cfb_registry}" + description = "Name of the Bmore Response Registry" + value = module.ecs_cluster.cfb_registry } - output "load_balancer_address" { - description = "DNS Address of the Application Load Balancer" - value = "${module.alb.lb-dns}" + description = "DNS Address of the Application Load Balancer" + value = module.alb.lb-dns } output "db_instance_name" { description = "The database name" - value = "${module.db.this_db_instance_name}" + value = module.db.this_db_instance_name } + output "db_instance_port" { description = "The database port" - value = "${module.db.this_db_instance_port}" + value = module.db.this_db_instance_port } output "db_instance_username" { description = "The master username for the database" - value = "${module.db.this_db_instance_username}" + value = module.db.this_db_instance_username } output "db_instance_password" { description = "The database password (this password may be old, because Terraform doesn't track it after initial creation)" - value = "${module.db.this_db_instance_password}" + value = module.db.this_db_instance_password } output "db_instance_address" { description = "The address of the RDS instance" - value = "${module.db.this_db_instance_address}" + value = module.db.this_db_instance_address } output "db_instance_endpoint" { description = "The connection endpoint" - value = "${module.db.this_db_instance_endpoint}" + value = module.db.this_db_instance_endpoint +} + +output "public_hosted_zone_nameservers" { + value = data.aws_route53_zone.hosted_zone.zone_id } \ No newline at end of file diff --git a/terraform/components/full-cluster/terraform.tfvars b/terraform/components/full-cluster/terraform.tfvars new file mode 100644 index 00000000..34a66263 --- /dev/null +++ b/terraform/components/full-cluster/terraform.tfvars @@ -0,0 +1,2 @@ +public_hosted_zone_name = "bmoreres.codeforbaltimore.org" +zone_id = "Z06944204X33SC5R1R7Z" \ No newline at end of file diff --git a/terraform/components/full-cluster/variables.tf b/terraform/components/full-cluster/variables.tf index 29c17413..f22a6976 100644 --- a/terraform/components/full-cluster/variables.tf +++ b/terraform/components/full-cluster/variables.tf @@ -1,5 +1,26 @@ variable "aws_region" { description = "AWS Region to use." - type = "string" - default = "us-east-1" + type = string + default = "us-east-2" } + +variable "db_password" { + type = string + default = null +} + +variable "public_hosted_zone_name" {} + +variable "waf_whitelist_cidrs" { + type = list(string) + default = [ + "0.0.0.0/0" + ] +} + +variable "create_waf" { + type = bool + default = false +} + +variable "zone_id" {} \ No newline at end of file diff --git a/terraform/components/full-cluster/versions.tf b/terraform/components/full-cluster/versions.tf new file mode 100644 index 00000000..ac97c6ac --- /dev/null +++ b/terraform/components/full-cluster/versions.tf @@ -0,0 +1,4 @@ + +terraform { + required_version = ">= 0.12" +} diff --git a/terraform/modules/alb/README.md b/terraform/modules/alb/README.md index f0848dc6..ecf083db 100644 --- a/terraform/modules/alb/README.md +++ b/terraform/modules/alb/README.md @@ -1,12 +1,22 @@ +# alb + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| lb\_sg | Security group IDs for the lb | string | n/a | yes | -| mytags | Tags to include on the resources | map | `` | no | -| cfb\_app\_port | Port number the app will be running on | string | n/a | yes | -| vpc\_id | VPC ID that the lb will be placed in | string | n/a | yes | -| vpc\_subnets | VPC subnets the lb will use | list | n/a | yes | +|------|-------------|------|---------|:-----:| +| certificate\_arn | n/a | `any` | n/a | yes | +| cfb\_app\_port | Port number the app will be running on | `any` | n/a | yes | +| lb\_sg | Security group IDs for the lb | `any` | n/a | yes | +| mytags | Tags to include on the resources | `map(string)` | `{}` | no | +| vpc\_id | VPC ID that the lb will be placed in | `any` | n/a | yes | +| vpc\_subnets | VPC subnets the lb will use | `list(string)` | n/a | yes | ## Outputs @@ -15,5 +25,8 @@ | lb-arn | ARN of the lb | | lb-dns | DNS name for the lb | | lb-id | ID of the lb | -| tg-cfb-arn | ARN of Target Group | +| tg-cfb-arn | ARN of the Target Group | +| zone\_id | n/a | + + diff --git a/terraform/modules/alb/main.tf b/terraform/modules/alb/main.tf index 9b6ccadf..d74b846e 100644 --- a/terraform/modules/alb/main.tf +++ b/terraform/modules/alb/main.tf @@ -1,3 +1,7 @@ +terraform { + required_version = ">= 0.12" +} + # TODO: Add Documentation about adding HTTPS # TODO: Make sure APIs have health checks that can be used # Create ALB @@ -5,43 +9,49 @@ resource "aws_lb" "lb" { name = "bmore-responsive-api-alb" internal = "false" load_balancer_type = "application" - security_groups = ["${var.lb_sg}"] + security_groups = [var.lb_sg] - subnets = ["${var.vpc_subnets}"] + subnets = var.vpc_subnets + + enable_cross_zone_load_balancing = true enable_deletion_protection = false - tags = "${merge(map("Name", "bmore-responsive-api-alb"), var.mytags)}" + tags = merge( + { + "Name" = "bmore-responsive-api-alb" + }, + var.mytags, + ) } # Create ALB target group for both containers resource "aws_lb_target_group" "tg-cfb" { - name_prefix = "cfb-" - port = "${var.cfb_app_port}" - protocol = "HTTP" - vpc_id = "${var.vpc_id}" - depends_on = [ - "aws_lb.lb", - ] + name_prefix = "cfb-" + port = var.cfb_app_port + protocol = "HTTP" + vpc_id = var.vpc_id + depends_on = [aws_lb.lb] lifecycle { create_before_destroy = true } health_check { - path = "/health" + path = "/health" } } - # Create ALB listener # TODO: Add ALB Routing rules resource "aws_lb_listener" "api-listener" { - load_balancer_arn = "${aws_lb.lb.arn}" - port = "80" - protocol = "HTTP" + load_balancer_arn = aws_lb.lb.arn + port = "443" + protocol = "HTTPS" + ssl_policy = "ELBSecurityPolicy-2016-08" + certificate_arn = var.certificate_arn default_action { - target_group_arn = "${aws_lb_target_group.tg-cfb.arn}" + target_group_arn = aws_lb_target_group.tg-cfb.arn type = "forward" } } diff --git a/terraform/modules/alb/outputs.tf b/terraform/modules/alb/outputs.tf index 5301c506..644c575f 100644 --- a/terraform/modules/alb/outputs.tf +++ b/terraform/modules/alb/outputs.tf @@ -1,19 +1,24 @@ output "lb-id" { description = "ID of the lb" - value = "${aws_lb.lb.id}" + value = aws_lb.lb.id } output "lb-dns" { description = "DNS name for the lb" - value = "${aws_lb.lb.dns_name}" + value = aws_lb.lb.dns_name } output "lb-arn" { description = "ARN of the lb" - value = "${aws_lb.lb.arn}" + value = aws_lb.lb.arn } output "tg-cfb-arn" { description = "ARN of the Target Group" - value = "${aws_lb_target_group.tg-cfb.arn}" -} \ No newline at end of file + value = aws_lb_target_group.tg-cfb.arn +} + +output "zone_id" { + value = aws_lb.lb.zone_id +} + diff --git a/terraform/modules/alb/variables.tf b/terraform/modules/alb/variables.tf index e927b5a2..a998e4f3 100644 --- a/terraform/modules/alb/variables.tf +++ b/terraform/modules/alb/variables.tf @@ -4,12 +4,12 @@ variable "vpc_id" { variable "vpc_subnets" { description = "VPC subnets the lb will use" - type = "list" + type = list(string) } variable "mytags" { description = "Tags to include on the resources" - type = "map" + type = map(string) default = {} } @@ -20,3 +20,5 @@ variable "cfb_app_port" { variable "lb_sg" { description = "Security group IDs for the lb" } + +variable "certificate_arn" {} \ No newline at end of file diff --git a/terraform/modules/asg/README.md b/terraform/modules/asg/README.md index 6dcd5b13..d3b57e0a 100644 --- a/terraform/modules/asg/README.md +++ b/terraform/modules/asg/README.md @@ -1,16 +1,31 @@ +# asg + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| asg\_security\_group\_ids | Additional security groups to attach to the ASG. | list | n/a | yes | -| cluster\_name | The name to be given to the ASG | string | n/a | yes | -| count | The number of instances to provision in the ASG. | string | `"1"` | no | -| default\_cooldown | Default cooldown for ASG. | string | `"300"` | no | -| ecs\_role | The ARN of the role attached to ECS Cluster instances | string | n/a | yes | -| instance\_type | The EC2 instance type. | string | n/a | yes | -| max\_size | Maximum size for ASG. Must set min, count and max. | string | n/a | yes | -| min\_size | Minimum size for ASG. Must set min, count and max. | string | n/a | yes | -| root\_block\_device | | list | `` | no | -| subnet\_ids | The subnet IDs used by the Auto Scaling Group. | list | n/a | yes | -| user\_data | The user data to provide when launching the instance. | string | n/a | yes | +|------|-------------|------|---------|:-----:| +| asg\_security\_group\_ids | Additional security groups to attach to the ASG. | `list(string)` | n/a | yes | +| cluster\_name | The name to be given to the ASG | `string` | n/a | yes | +| default\_cooldown | Default cooldown for ASG. | `string` | `"300"` | no | +| ecs\_role | The ARN of the role attached to ECS Cluster instances | `string` | n/a | yes | +| instance\_count | The number of instances to provision in the ASG. | `string` | n/a | yes | +| instance\_type | The EC2 instance type. | `string` | n/a | yes | +| max\_size | Maximum size for ASG. Must set min, count and max. | `string` | n/a | yes | +| min\_size | Minimum size for ASG. Must set min, count and max. | `string` | n/a | yes | +| root\_block\_device | n/a | `list(map(string))` |
[
{
"volume_size": "30"
}
]
| no | +| subnet\_ids | The subnet IDs used by the Auto Scaling Group. | `list(string)` | n/a | yes | +| user\_data | The user data to provide when launching the instance. | `string` | n/a | yes | + +## Outputs + +No output. + + diff --git a/terraform/modules/asg/main.tf b/terraform/modules/asg/main.tf index 6ead5b7f..bb38a6e6 100644 --- a/terraform/modules/asg/main.tf +++ b/terraform/modules/asg/main.tf @@ -1,3 +1,6 @@ +terraform { + required_version = ">= 0.12" +} // Fill in information about the ECS host node here. data "aws_ami" "ecs_agent_ami" { @@ -5,13 +8,13 @@ data "aws_ami" "ecs_agent_ami" { owners = ["amazon"] filter { - name = "name" - values = ["*amazon-ecs-optimized"] + name = "name" + values = ["*amazon-ecs-optimized"] } filter { - name = "virtualization-type" - values = ["hvm"] + name = "virtualization-type" + values = ["hvm"] } } @@ -20,14 +23,14 @@ resource "aws_autoscaling_group" "ecs_cluster_asg" { create_before_destroy = true } - default_cooldown = "${var.default_cooldown}" - desired_capacity = "${var.count}" + default_cooldown = var.default_cooldown + desired_capacity = var.instance_count health_check_grace_period = 300 - launch_configuration = "${aws_launch_configuration.ecs_cluster_config.name}" - max_size = "${var.max_size}" - min_size = "${var.min_size}" + launch_configuration = aws_launch_configuration.ecs_cluster_config.name + max_size = var.max_size + min_size = var.min_size name = "bmore-responsive-ecs-cluster-asg" - vpc_zone_identifier = ["${var.subnet_ids}"] + vpc_zone_identifier = var.subnet_ids tags = [ { @@ -37,7 +40,7 @@ resource "aws_autoscaling_group" "ecs_cluster_asg" { }, { key = "ECS Cluster" - value = "${var.cluster_name}" + value = var.cluster_name propagate_at_launch = true }, ] @@ -48,14 +51,29 @@ resource "aws_launch_configuration" "ecs_cluster_config" { create_before_destroy = true } - root_block_device = "${var.root_block_device}" + dynamic "root_block_device" { + for_each = var.root_block_device + content { + # TF-UPGRADE-TODO: The automatic upgrade tool can't predict + # which keys might be set in maps assigned here, so it has + # produced a comprehensive set here. Consider simplifying + # this after confirming which keys can be set in practice. + + delete_on_termination = lookup(root_block_device.value, "delete_on_termination", null) + encrypted = lookup(root_block_device.value, "encrypted", null) + iops = lookup(root_block_device.value, "iops", null) + volume_size = lookup(root_block_device.value, "volume_size", null) + volume_type = lookup(root_block_device.value, "volume_type", null) + } + } enable_monitoring = "true" - iam_instance_profile = "${var.ecs_role}" - image_id = "${data.aws_ami.ecs_agent_ami.image_id}" - instance_type = "${var.instance_type}" + iam_instance_profile = var.ecs_role + image_id = data.aws_ami.ecs_agent_ami.image_id + instance_type = var.instance_type name_prefix = "bmore-responsive-ecs-cluster-" - security_groups = ["${var.asg_security_group_ids}"] + security_groups = var.asg_security_group_ids - user_data = "${var.user_data}" + user_data = var.user_data } + diff --git a/terraform/modules/asg/outputs.tf b/terraform/modules/asg/outputs.tf deleted file mode 100644 index 8b137891..00000000 --- a/terraform/modules/asg/outputs.tf +++ /dev/null @@ -1 +0,0 @@ - diff --git a/terraform/modules/asg/variables.tf b/terraform/modules/asg/variables.tf index 33958153..691306bd 100644 --- a/terraform/modules/asg/variables.tf +++ b/terraform/modules/asg/variables.tf @@ -1,16 +1,16 @@ -variable "cluster_name"{ +variable "cluster_name" { description = "The name to be given to the ASG" - type = "string" + type = string } variable "asg_security_group_ids" { description = "Additional security groups to attach to the ASG." - type = "list" + type = list(string) } -variable "count" { +variable "instance_count" { description = "The number of instances to provision in the ASG." - type = "string" + type = string } variable "default_cooldown" { @@ -20,35 +20,38 @@ variable "default_cooldown" { variable "instance_type" { description = "The EC2 instance type." - type = "string" + type = string } variable "max_size" { description = "Maximum size for ASG. Must set min, count and max." - type = "string" + type = string } variable "min_size" { description = "Minimum size for ASG. Must set min, count and max." - type = "string" + type = string } variable "subnet_ids" { description = "The subnet IDs used by the Auto Scaling Group." - type = "list" + type = list(string) } variable "user_data" { description = "The user data to provide when launching the instance." - type = "string" + type = string } variable "root_block_device" { - type = "list" - default = [{volume_size = "30"}] + type = list(map(string)) + default = [{ + volume_size = "30" + }] } variable "ecs_role" { description = "The ARN of the role attached to ECS Cluster instances" - type = "string" + type = string } + diff --git a/terraform/modules/aws_wafregional_web_acl/README.md b/terraform/modules/aws_wafregional_web_acl/README.md new file mode 100644 index 00000000..1db32df5 --- /dev/null +++ b/terraform/modules/aws_wafregional_web_acl/README.md @@ -0,0 +1,26 @@ +# waf + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:-----:| +| create | n/a | `bool` | `false` | no | +| lb\_arn | n/a | `any` | n/a | yes | +| resource\_suffix | n/a | `any` | n/a | yes | +| whitelist\_cidrs | n/a | `list(string)` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| web\_acl\_id | n/a | + + + diff --git a/terraform/modules/aws_wafregional_web_acl/main.tf b/terraform/modules/aws_wafregional_web_acl/main.tf new file mode 100644 index 00000000..1ffb2a64 --- /dev/null +++ b/terraform/modules/aws_wafregional_web_acl/main.tf @@ -0,0 +1,55 @@ +terraform { + required_version = ">= 0.12" +} + +resource "aws_wafregional_web_acl_association" "web_acl_association" { + count = var.create ? 1 : 0 + resource_arn = var.lb_arn + web_acl_id = aws_wafregional_web_acl.web_acl[0].id +} + +resource "aws_wafregional_ipset" "external_ipset" { + count = var.create ? 1 : 0 + name = "GenericMatchExternalIPs${var.resource_suffix}" + dynamic "ip_set_descriptor" { + for_each = var.whitelist_cidrs + content { + type = "IPV4" + value = ip_set_descriptor.value + } + } + lifecycle { + create_before_destroy = true + } +} + +resource "aws_wafregional_rule" "detect_external_access" { + count = var.create ? 1 : 0 + name = "DetectExternalAccess${var.resource_suffix}" + metric_name = "DetectExternalAccess${var.resource_suffix}" + + predicate { + data_id = aws_wafregional_ipset.external_ipset[0].id + negated = false + type = "IPMatch" + } +} + +resource "aws_wafregional_web_acl" "web_acl" { + count = var.create ? 1 : 0 + name = "WebAcl${var.resource_suffix}" + metric_name = "WebAcl${var.resource_suffix}" + + default_action { + type = "BLOCK" + } + + rule { + action { + type = "ALLOW" + } + + priority = 1 + rule_id = aws_wafregional_rule.detect_external_access[0].id + } +} diff --git a/terraform/modules/aws_wafregional_web_acl/outputs.tf b/terraform/modules/aws_wafregional_web_acl/outputs.tf new file mode 100644 index 00000000..78b9b1ab --- /dev/null +++ b/terraform/modules/aws_wafregional_web_acl/outputs.tf @@ -0,0 +1,3 @@ +output "web_acl_id" { + value = aws_wafregional_web_acl.web_acl[0].id +} diff --git a/terraform/modules/aws_wafregional_web_acl/variables.tf b/terraform/modules/aws_wafregional_web_acl/variables.tf new file mode 100644 index 00000000..4ef2d3c4 --- /dev/null +++ b/terraform/modules/aws_wafregional_web_acl/variables.tf @@ -0,0 +1,12 @@ +variable "whitelist_cidrs" { + type = list(string) +} + +variable "resource_suffix" {} + +variable "lb_arn" {} + +variable "create" { + type = bool + default = false +} \ No newline at end of file diff --git a/terraform/modules/certificate/README.md b/terraform/modules/certificate/README.md new file mode 100644 index 00000000..4ea86a81 --- /dev/null +++ b/terraform/modules/certificate/README.md @@ -0,0 +1,27 @@ +# certificate + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:-----:| +| dns\_ttl | DNS records TTL | `number` | `60` | no | +| dns\_zone\_id | Route53 Zone id handleling the domains on the certificate | `any` | n/a | yes | +| domain\_name | Main domain name for the SSL certificate | `any` | n/a | yes | +| subject\_alternative\_names | Alternate domain names for the SSL certificate | `list(string)` | `[]` | no | +| tags | Tags associated to the certificate | `map(string)` | `{}` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| certificate\_arn | n/a | + + + diff --git a/terraform/modules/certificate/main.tf b/terraform/modules/certificate/main.tf new file mode 100644 index 00000000..f54bc403 --- /dev/null +++ b/terraform/modules/certificate/main.tf @@ -0,0 +1,30 @@ +terraform { + required_version = ">= 0.12" +} + +resource "aws_acm_certificate" "acm_certificate" { + domain_name = var.domain_name + validation_method = "DNS" + subject_alternative_names = var.subject_alternative_names + tags = var.tags + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_route53_record" "acm_certificate_validation_record" { + count = length(var.subject_alternative_names) + 1 + name = aws_acm_certificate.acm_certificate.domain_validation_options[count.index]["resource_record_name"] + type = "CNAME" + zone_id = var.dns_zone_id + records = [aws_acm_certificate.acm_certificate.domain_validation_options[count.index]["resource_record_value"]] + ttl = var.dns_ttl + allow_overwrite = true +} + +resource "aws_acm_certificate_validation" "acm_certificate_validation_record" { + depends_on = [aws_acm_certificate.acm_certificate] + certificate_arn = aws_acm_certificate.acm_certificate.arn + validation_record_fqdns = aws_route53_record.acm_certificate_validation_record.*.fqdn +} diff --git a/terraform/modules/certificate/outputs.tf b/terraform/modules/certificate/outputs.tf new file mode 100644 index 00000000..e99d9371 --- /dev/null +++ b/terraform/modules/certificate/outputs.tf @@ -0,0 +1,3 @@ +output "certificate_arn" { + value = aws_acm_certificate_validation.acm_certificate_validation_record.certificate_arn +} diff --git a/terraform/modules/certificate/variables.tf b/terraform/modules/certificate/variables.tf new file mode 100644 index 00000000..03d04e0a --- /dev/null +++ b/terraform/modules/certificate/variables.tf @@ -0,0 +1,24 @@ +variable "dns_zone_id" { + description = "Route53 Zone id handleling the domains on the certificate" +} + +variable "domain_name" { + description = "Main domain name for the SSL certificate" +} + +variable "dns_ttl" { + description = "DNS records TTL" + default = 60 +} + +variable "tags" { + description = "Tags associated to the certificate" + type = map(string) + default = {} +} + +variable "subject_alternative_names" { + description = "Alternate domain names for the SSL certificate" + type = list(string) + default = [] +} diff --git a/terraform/modules/db/README.md b/terraform/modules/db/README.md new file mode 100644 index 00000000..b7c12561 --- /dev/null +++ b/terraform/modules/db/README.md @@ -0,0 +1,54 @@ +# db + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:-----:| +| allocated\_storage | The allocated storage in gigabytes. For read replica, set the same value as master's | `string` | n/a | yes | +| apply\_immediately | Specifies whether any database modifications are applied immediately, or during the next maintenance window | `string` | `"false"` | no | +| auto\_minor\_version\_upgrade | Indicates that minor engine upgrades will be applied automatically to the DB instance during the maintenance window | `string` | `"false"` | no | +| availability\_zone | The AZ for the RDS instance. It is recommended to only use this when creating a read replica instance | `string` | `""` | no | +| backup\_retention\_period | The days to retain backups for | `string` | `7` | no | +| backup\_window | The daily time range (in UTC) during which automated backups are created if they are enabled. Before and not overlap with maintenance\_window | `string` | `""` | no | +| db\_subnet\_group\_name | Name of DB subnet group | `string` | `""` | no | +| engine\_version | The postgres engine version | `string` | `""` | no | +| instance\_class | The instance type of the RDS instance | `string` | n/a | yes | +| iops | The amount of provisioned IOPS. Setting this implies a storage\_type of io1 | `string` | `"0"` | no | +| kms\_key\_id | Specifies a custom KMS key to be used to encrypt | `string` | `""` | no | +| maintenance\_window | The window to perform maintenance in. Syntax: 'ddd:hh24:mi-ddd:hh24:mi' | `string` | n/a | yes | +| parameter\_group\_name | Name of the DB parameter group to associate | `string` | n/a | yes | +| password | password for the master DB user | `string` | n/a | yes | +| port | The port on which the DB accepts connections | `string` | `"5432"` | no | +| resource\_suffix | n/a | `string` | `"default"` | no | +| storage\_encrypted | Specifies whether the DB instance is encrypted | `string` | `"true"` | no | +| storage\_type | One of standard (magnetic), gp2 (general purpose SSD), or io1 (provisioned IOPS SSD) | `string` | `"gp2"` | no | +| subnet\_ids | n/a | `any` | n/a | yes | +| username | Username for the master DB user | `string` | `"postgres"` | no | +| vpc\_security\_group\_ids | List of VPC security groups to associate | `list(string)` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| this\_db\_instance\_address | The address of the RDS instance | +| this\_db\_instance\_arn | The ARN of the RDS instance | +| this\_db\_instance\_availability\_zone | The availability zone of the RDS instance | +| this\_db\_instance\_endpoint | The connection endpoint | +| this\_db\_instance\_hosted\_zone\_id | The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record) | +| this\_db\_instance\_id | The RDS instance ID | +| this\_db\_instance\_name | The database name | +| this\_db\_instance\_password | The database password (this password may be old, because Terraform doesn't track it after initial creation) | +| this\_db\_instance\_port | The database port | +| this\_db\_instance\_resource\_id | The RDS Resource ID of this instance | +| this\_db\_instance\_status | The RDS instance status | +| this\_db\_instance\_username | The master username for the database | + + + diff --git a/terraform/modules/db/main.tf b/terraform/modules/db/main.tf index b1380788..6a8037ae 100644 --- a/terraform/modules/db/main.tf +++ b/terraform/modules/db/main.tf @@ -1,31 +1,39 @@ +terraform { + required_version = ">= 0.12" +} + +resource "aws_db_subnet_group" "subnet_group" { + subnet_ids = var.subnet_ids +} + resource "aws_db_instance" "this" { identifier = "healthcare-rollcall-postgres" engine = "postgres" - engine_version = "${var.engine_version}" - instance_class = "${var.instance_class}" - allocated_storage = "${var.allocated_storage}" - username = "${var.username}" - password = "${var.password}" - port = "${var.port}" - allocated_storage = "${var.allocated_storage}" - storage_type = "${var.storage_type}" - iops = "${var.iops}" - storage_encrypted = "${var.storage_encrypted}" - kms_key_id = "${var.kms_key_id}" - name = "healthcare-rollcall_db" + engine_version = var.engine_version + instance_class = var.instance_class + allocated_storage = var.allocated_storage + username = var.username + password = var.password + port = var.port + + // allocated_storage = "${var.allocated_storage}" + storage_type = var.storage_type + iops = var.iops + storage_encrypted = var.storage_encrypted + kms_key_id = var.kms_key_id + name = "healthcareRollcallDB" # NOTE: Do NOT use 'user' as the value for 'username' as it throws: # "Error creating DB Instance: InvalidParameterValue: MasterUsername # user cannot be used as it is a reserved word used by the engine" - vpc_security_group_ids = [ - "${var.vpc_security_group_ids}" ] + vpc_security_group_ids = var.vpc_security_group_ids publicly_accessible = false - maintenance_window = "${var.maintenance_window}" - backup_window = "${var.backup_window}" + maintenance_window = var.maintenance_window + backup_window = var.backup_window # disable backups to create DB faster backup_retention_period = 0 @@ -39,17 +47,16 @@ resource "aws_db_instance" "this" { enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"] # DB subnet group - db_subnet_group_name = "${var.db_subnet_group_name}" - - - # DB parameter group - parameter_group_name = "${var.parameter_group_name}" - + db_subnet_group_name = aws_db_subnet_group.subnet_group.name # Snapshot name upon DB deletion - final_snapshot_identifier = "healthcare-rollcall-db" + final_snapshot_identifier = "healthcare-rollcall-db-${var.resource_suffix}" # Database Deletion Protection deletion_protection = false -} \ No newline at end of file + lifecycle { + ignore_changes = [password] + } +} + diff --git a/terraform/modules/db/outputs.tf b/terraform/modules/db/outputs.tf index 4b561f31..05c48023 100644 --- a/terraform/modules/db/outputs.tf +++ b/terraform/modules/db/outputs.tf @@ -1,59 +1,60 @@ output "this_db_instance_address" { description = "The address of the RDS instance" - value = "${aws_db_instance.this.address}" + value = aws_db_instance.this.address } output "this_db_instance_arn" { description = "The ARN of the RDS instance" - value = "${aws_db_instance.this.arn}" + value = aws_db_instance.this.arn } output "this_db_instance_availability_zone" { description = "The availability zone of the RDS instance" - value = "${aws_db_instance.this.availability_zone}" + value = aws_db_instance.this.availability_zone } output "this_db_instance_endpoint" { description = "The connection endpoint" - value = "${aws_db_instance.this.endpoint}" + value = aws_db_instance.this.endpoint } output "this_db_instance_hosted_zone_id" { description = "The canonical hosted zone ID of the DB instance (to be used in a Route 53 Alias record)" - value = "${aws_db_instance.this.hosted_zone_id}" + value = aws_db_instance.this.hosted_zone_id } output "this_db_instance_id" { description = "The RDS instance ID" - value = "${aws_db_instance.this.id}" + value = aws_db_instance.this.id } output "this_db_instance_resource_id" { description = "The RDS Resource ID of this instance" - value = "${aws_db_instance.this.resource_id}" + value = aws_db_instance.this.resource_id } output "this_db_instance_status" { description = "The RDS instance status" - value = "${aws_db_instance.this.status}" + value = aws_db_instance.this.status } output "this_db_instance_name" { description = "The database name" - value = "${aws_db_instance.this.name}" + value = aws_db_instance.this.name } output "this_db_instance_username" { description = "The master username for the database" - value = "${aws_db_instance.this.username}" + value = aws_db_instance.this.username } output "this_db_instance_password" { description = "The database password (this password may be old, because Terraform doesn't track it after initial creation)" - value = "${aws_db_instance.this.password}" + value = aws_db_instance.this.password } output "this_db_instance_port" { description = "The database port" - value = "${aws_db_instance.this.port}" + value = aws_db_instance.this.port } + diff --git a/terraform/modules/db/variables.tf b/terraform/modules/db/variables.tf index 972fecae..2b6290c0 100644 --- a/terraform/modules/db/variables.tf +++ b/terraform/modules/db/variables.tf @@ -1,109 +1,116 @@ variable "engine_version" { - type = "string" + type = string description = "The postgres engine version" default = "" } variable "instance_class" { - type = "string" + type = string description = "The instance type of the RDS instance" } variable "username" { - type = "string" + type = string description = "Username for the master DB user" default = "postgres" } variable "password" { - type = "string" - description = "password for the master DB user" + type = string + description = "password for the master DB user" } variable "port" { - type = "string" + type = string description = "The port on which the DB accepts connections" default = "5432" } variable "allocated_storage" { - type = "string" + type = string description = "The allocated storage in gigabytes. For read replica, set the same value as master's" } variable "storage_type" { - type = "string" + type = string description = "One of standard (magnetic), gp2 (general purpose SSD), or io1 (provisioned IOPS SSD)" default = "gp2" } variable "iops" { - type = "string" + type = string description = "The amount of provisioned IOPS. Setting this implies a storage_type of io1" default = "0" } variable "storage_encrypted" { - type = "string" + type = string description = "Specifies whether the DB instance is encrypted" default = "true" } variable "kms_key_id" { - type = "string" + type = string description = "Specifies a custom KMS key to be used to encrypt" default = "" } variable "vpc_security_group_ids" { - type = "list" + type = list(string) description = "List of VPC security groups to associate" } - variable "db_subnet_group_name" { - type = "string" + type = string description = "Name of DB subnet group" default = "" } variable "parameter_group_name" { - type = "string" + type = string description = "Name of the DB parameter group to associate" } variable "availability_zone" { - type = "string" + type = string description = "The AZ for the RDS instance. It is recommended to only use this when creating a read replica instance" default = "" } - variable "auto_minor_version_upgrade" { - type = "string" + type = string description = "Indicates that minor engine upgrades will be applied automatically to the DB instance during the maintenance window" default = "false" } variable "apply_immediately" { - type = "string" + type = string description = "Specifies whether any database modifications are applied immediately, or during the next maintenance window" default = "false" } variable "maintenance_window" { - type = "string" + type = string description = "The window to perform maintenance in. Syntax: 'ddd:hh24:mi-ddd:hh24:mi'" } variable "backup_retention_period" { - type = "string" + type = string description = "The days to retain backups for" default = 7 } variable "backup_window" { - type = "string" + type = string description = "The daily time range (in UTC) during which automated backups are created if they are enabled. Before and not overlap with maintenance_window" default = "" } + +variable "subnet_ids" { + // type = list(string) +} + +variable "resource_suffix" { + default = "default" +} + diff --git a/terraform/modules/dns_record/README.md b/terraform/modules/dns_record/README.md new file mode 100644 index 00000000..0252d98a --- /dev/null +++ b/terraform/modules/dns_record/README.md @@ -0,0 +1,24 @@ +# dns_record + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:-----:| +| lb\_dns\_name | n/a | `any` | n/a | yes | +| lb\_zone\_id | n/a | `any` | n/a | yes | +| name | n/a | `any` | n/a | yes | +| zone\_id | n/a | `any` | n/a | yes | + +## Outputs + +No output. + + + diff --git a/terraform/modules/dns_record/main.tf b/terraform/modules/dns_record/main.tf new file mode 100644 index 00000000..bcec285d --- /dev/null +++ b/terraform/modules/dns_record/main.tf @@ -0,0 +1,15 @@ +terraform { + required_version = ">= 0.12" +} + +resource "aws_route53_record" "www" { + zone_id = var.zone_id + name = var.name + type = "A" + + alias { + name = var.lb_dns_name + zone_id = var.lb_zone_id + evaluate_target_health = true + } +} \ No newline at end of file diff --git a/terraform/modules/dns_record/outputs.tf b/terraform/modules/dns_record/outputs.tf new file mode 100644 index 00000000..e69de29b diff --git a/terraform/modules/dns_record/variables.tf b/terraform/modules/dns_record/variables.tf new file mode 100644 index 00000000..d28bbe36 --- /dev/null +++ b/terraform/modules/dns_record/variables.tf @@ -0,0 +1,7 @@ +variable "name" {} + +variable "zone_id" {} + +variable "lb_dns_name" {} + +variable "lb_zone_id" {} \ No newline at end of file diff --git a/terraform/modules/ecs/README.md b/terraform/modules/ecs/README.md index d59c2463..8634da4f 100644 --- a/terraform/modules/ecs/README.md +++ b/terraform/modules/ecs/README.md @@ -1,19 +1,30 @@ +# ecs + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| cluster\_name | The name to be given to the ASG | string | n/a | yes | -| output\_bucket\_arn | ARN of the S3 bucket that contains the output files | string | n/a | yes | -| bmore-responsive\_container\_definitions | The Rendered JSON of a container definition array. See example-container.json for a sample of valid JSON input. | string | n/a | yes | -| bmore-responsive\_container\_name | The name of the container to associate with the Load Balancer. Must equal the container name in the container definition JSON | string | n/a | yes | -| bmore-responsive\_container\_port | The port on the container to associate with the Load Balancer | string | n/a | yes | -| bmore-responsive\_desired\_count | The number of tasks to run in the service | string | n/a | yes | -| bmore-responsive\_target\_group\_arn | The ARN of the Target Group for Load Balancing | string | n/a | yes | +|------|-------------|------|---------|:-----:| +| bmore-responsive\_container\_definitions | The Rendered JSON of a container definition array. See example-container.json for a sample of valid JSON input. | `string` | n/a | yes | +| bmore-responsive\_container\_name | The name of the container to associate with the Load Balancer. Must equal the container name in the container definition JSON | `string` | n/a | yes | +| bmore-responsive\_container\_port | The port on the container to associate with the Load Balancer | `string` | n/a | yes | +| bmore-responsive\_desired\_count | The number of tasks to run in the service | `string` | n/a | yes | +| bmore-responsive\_target\_group\_arn | The ARN of the Target Group for Load Balancing | `string` | n/a | yes | +| cluster\_name | The name to be given to the ASG | `string` | n/a | yes | +| output\_bucket\_arn | ARN of the S3 bucket that contains the output files | `string` | n/a | yes | ## Outputs | Name | Description | |------|-------------| -| ecs\_role | ARN for the role attached to ECS Cluster instances. | | cfb\_registry | Address for the Registry | +| ecs\_role | ARN for the role attached to ECS Cluster instances. | + + diff --git a/terraform/modules/ecs/main.tf b/terraform/modules/ecs/main.tf index a75c5b13..0d36fbe3 100644 --- a/terraform/modules/ecs/main.tf +++ b/terraform/modules/ecs/main.tf @@ -1,7 +1,10 @@ +terraform { + required_version = ">= 0.12" +} + # Set up necessary IAM Roles for ECS Hosts data "aws_iam_policy_document" "ecs_cluster_asg_policy" { - statement { actions = [ "ecs:DeregisterContainerInstance", @@ -15,7 +18,7 @@ data "aws_iam_policy_document" "ecs_cluster_asg_policy" { "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage", "logs:CreateLogStream", - "logs:PutLogEvents" + "logs:PutLogEvents", ] resources = [ @@ -25,12 +28,12 @@ data "aws_iam_policy_document" "ecs_cluster_asg_policy" { statement { actions = [ - "s3:*" + "s3:*", ] resources = [ - "${var.output_bucket_arn}", - "${var.output_bucket_arn}/*" + var.output_bucket_arn, + "${var.output_bucket_arn}/*", ] } } @@ -54,17 +57,18 @@ resource "aws_iam_role" "ecs_cluster" { ] } EOF + } resource "aws_iam_role_policy" "ecs_cluster_asg_policy" { name_prefix = "ecs-cluster-policy-" - role = "${aws_iam_role.ecs_cluster.id}" - policy = "${data.aws_iam_policy_document.ecs_cluster_asg_policy.json}" + role = aws_iam_role.ecs_cluster.id + policy = data.aws_iam_policy_document.ecs_cluster_asg_policy.json } resource "aws_iam_instance_profile" "ecs_cluster_asg_profile" { name_prefix = "ecs_cluster_asg_profile-" - role = "${aws_iam_role.ecs_cluster.name}" + role = aws_iam_role.ecs_cluster.name } # Create the ECR Repositories for the containers @@ -72,9 +76,8 @@ resource "aws_ecr_repository" "bmore-responsive-api" { name = "bmore-responsive" } - resource "aws_ecr_repository_policy" "bmore-responsive-api-policy" { - repository = "${aws_ecr_repository.bmore-responsive-api.name}" + repository = aws_ecr_repository.bmore-responsive-api.name policy = < +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | +| random | n/a | + ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| mytags | Tags to include on the resources | map | `` | no | +|------|-------------|------|---------|:-----:| +| aws\_region | n/a | `any` | n/a | yes | +| mytags | Tags to include on the resources | `map(string)` | `{}` | no | +| resource\_suffix | n/a | `string` | `"default"` | no | ## Outputs @@ -11,3 +23,5 @@ | output\_bucket\_arn | ARN for the S3 output bucket | | output\_bucket\_name | Name for the S3 Bucket | + + diff --git a/terraform/modules/s3/main.tf b/terraform/modules/s3/main.tf index 05e0f9df..f35eccb1 100644 --- a/terraform/modules/s3/main.tf +++ b/terraform/modules/s3/main.tf @@ -1,6 +1,14 @@ +terraform { + required_version = ">= 0.12" +} + +resource "random_pet" "random_pet" { + length = 2 +} + resource "aws_s3_bucket" "output-bucket" { - bucket = "mpsm-provider-matching-output" + bucket = "cfb-healthcare-rollcall-${var.aws_region}-${var.resource_suffix}" acl = "private" force_destroy = "true" - tags = "${merge(map("Name", "mpsm-provider-matching-output"), var.mytags)}" + tags = merge(map("Name", "cfb-healthcare-rollcall-${var.aws_region}-${random_pet.random_pet.id}"), var.mytags) } diff --git a/terraform/modules/s3/outputs.tf b/terraform/modules/s3/outputs.tf index 7f64de0c..d6da2ab4 100644 --- a/terraform/modules/s3/outputs.tf +++ b/terraform/modules/s3/outputs.tf @@ -1,9 +1,9 @@ output "output_bucket_arn" { description = "ARN for the S3 output bucket" - value = "${aws_s3_bucket.output-bucket.arn}" + value = aws_s3_bucket.output-bucket.arn } output "output_bucket_name" { - description = "Name for the S3 Bucket" - value = "${aws_s3_bucket.output-bucket.id}" + description = "Name for the S3 Bucket" + value = aws_s3_bucket.output-bucket.id } diff --git a/terraform/modules/s3/variables.tf b/terraform/modules/s3/variables.tf index 70571336..f4cd02d8 100644 --- a/terraform/modules/s3/variables.tf +++ b/terraform/modules/s3/variables.tf @@ -1,5 +1,11 @@ variable "mytags" { description = "Tags to include on the resources" - type = "map" + type = map(string) default = {} } + +variable "aws_region" {} + +variable "resource_suffix" { + default = "default" +} \ No newline at end of file diff --git a/terraform/modules/sg/README.md b/terraform/modules/sg/README.md index 956ec661..7a18d31f 100644 --- a/terraform/modules/sg/README.md +++ b/terraform/modules/sg/README.md @@ -1,14 +1,25 @@ +# sg + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| mytags | Tags to include on the resources | map | `` | no | -| vpc\_id | VPC ID | string | n/a | yes | +|------|-------------|------|---------|:-----:| +| mytags | Tags to include on the resources | `map` | `{}` | no | +| vpc\_id | VPC ID | `any` | n/a | yes | ## Outputs | Name | Description | |------|-------------| | alb-sg-id | ALB Security Group ID | -| ecs-sg-id | ECS Security Group ID | +| ecs\_sg\_id | ECS Security Group ID | + + diff --git a/terraform/modules/sg/main.tf b/terraform/modules/sg/main.tf index d763ac94..e20a6462 100644 --- a/terraform/modules/sg/main.tf +++ b/terraform/modules/sg/main.tf @@ -1,3 +1,7 @@ +terraform { + required_version = ">= 0.12" +} + # Create security group resource "aws_security_group" "sg-alb" { @@ -9,9 +13,9 @@ resource "aws_security_group" "sg-alb" { ingress { # TLS (change to whatever ports you need) - from_port = 80 - to_port = 80 - protocol = "tcp" + from_port = 443 + to_port = 443 + protocol = "tcp" # We open to 0.0.0.0/0 here to support the testing activities. # In a production environment, these connections would be limited to diff --git a/terraform/modules/sg/outputs.tf b/terraform/modules/sg/outputs.tf index 6969ec43..a3add6b9 100644 --- a/terraform/modules/sg/outputs.tf +++ b/terraform/modules/sg/outputs.tf @@ -1,9 +1,9 @@ -output "ecs-sg-id" { +output "ecs_sg_id" { description = "ECS Security Group ID" - value = "${aws_security_group.sg-ecs.id}" + value = aws_security_group.sg-ecs.id } output "alb-sg-id" { description = "ALB Security Group ID" - value = "${aws_security_group.sg-alb.id}" + value = aws_security_group.sg-alb.id } diff --git a/terraform/modules/vpc/README.md b/terraform/modules/vpc/README.md index b4864398..e4d87a97 100644 --- a/terraform/modules/vpc/README.md +++ b/terraform/modules/vpc/README.md @@ -1,18 +1,29 @@ +# vpc + + +## Providers + +| Name | Version | +|------|---------| +| aws | n/a | + ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|:----:|:-----:|:-----:| -| availability\_zones | The Availability Zones to use in the VPC | list | `` | no | -| mytags | Tags to include on the resources | map | `` | no | -| private\_subnet\_cidrs | The CIDR Blocks of the Private Subnets | list | `` | no | -| public\_subnet\_cidrs | The CIDR Blocks of the Public Subnets | list | `` | no | -| vpc\_cidr | The CIDR block for the VPC | string | n/a | yes | +|------|-------------|------|---------|:-----:| +| availability\_zones | The Availability Zones to use in the VPC | `list(string)` |
[
"us-east-1a",
"us-east-1b",
"us-east-1c"
]
| no | +| mytags | Tags to include on the resources | `map(string)` | `{}` | no | +| private\_subnet\_cidrs | The CIDR Blocks of the Private Subnets | `list(string)` |
[
"10.0.0.0/24",
"10.0.1.0/24",
"10.0.2.0/24"
]
| no | +| public\_subnet\_cidrs | The CIDR Blocks of the Public Subnets | `list(string)` |
[
"10.0.3.0/24",
"10.0.4.0/24",
"10.0.5.0/24"
]
| no | +| vpc\_cidr | The CIDR block for the VPC | `any` | n/a | yes | ## Outputs | Name | Description | |------|-------------| | public-subnet-ids | Subnet IDs | -| subnet-ids | Subnet IDs | +| subnet\_ids | Subnet IDs | | vpc-id | VPC ID | + + diff --git a/terraform/modules/vpc/main.tf b/terraform/modules/vpc/main.tf index 25384c4a..1eaffe55 100644 --- a/terraform/modules/vpc/main.tf +++ b/terraform/modules/vpc/main.tf @@ -1,83 +1,98 @@ +terraform { + required_version = ">= 0.12" +} + resource "aws_vpc" "vpc" { - cidr_block = "${var.vpc_cidr}" - tags = "${merge(map("Name", "cfb-healthcare-rollcall-vpc"), var.mytags)}" + cidr_block = var.vpc_cidr + tags = merge( + { + "Name" = "cfb-healthcare-rollcall-vpc" + }, + var.mytags, + ) +} + +data "aws_availability_zones" "available" { + state = "available" } resource "aws_subnet" "public-subnet" { - count = 3 - vpc_id = "${aws_vpc.vpc.id}" - cidr_block = "${element(var.public_subnet_cidrs, count.index)}" - availability_zone = "${element(var.availability_zones, count.index)}" + count = 3 + vpc_id = aws_vpc.vpc.id + cidr_block = element(var.public_subnet_cidrs, count.index) + availability_zone = data.aws_availability_zones.available.names[count.index] - tags { + tags = { Name = "cfb-public-subnet-${count.index}" } } resource "aws_subnet" "private-subnet" { - count = 3 - vpc_id = "${aws_vpc.vpc.id}" - cidr_block = "${element(var.private_subnet_cidrs, count.index)}" - availability_zone = "${element(var.availability_zones, count.index)}" + count = 3 + vpc_id = aws_vpc.vpc.id + cidr_block = element(var.private_subnet_cidrs, count.index) + availability_zone = data.aws_availability_zones.available.names[count.index] - tags { + tags = { Name = "cfb-private-subnet-${count.index}" } } resource "aws_internet_gateway" "igw" { - vpc_id = "${aws_vpc.vpc.id}" + vpc_id = aws_vpc.vpc.id - tags { + tags = { Name = "CFB VPC IGW" } } # Set up Elastic IPs and NAT Gateways resource "aws_eip" "nat-gw" { - count = 3 + count = 3 } -resource "aws_nat_gateway" "nat-gw"{ + +resource "aws_nat_gateway" "nat-gw" { count = 3 - allocation_id = "${aws_eip.nat-gw.*.id[count.index]}" - subnet_id = "${aws_subnet.public-subnet.*.id[count.index]}" + allocation_id = aws_eip.nat-gw[count.index].id + subnet_id = aws_subnet.public-subnet[count.index].id } # Associate public routing rules to their subnets. resource "aws_route_table" "public-route-table" { - vpc_id = "${aws_vpc.vpc.id}" + vpc_id = aws_vpc.vpc.id route { cidr_block = "0.0.0.0/0" - gateway_id = "${aws_internet_gateway.igw.id}" + gateway_id = aws_internet_gateway.igw.id } - tags { + tags = { Name = "CFB Public Subnets" } } resource "aws_route_table_association" "public-table-association" { - count = 3 - subnet_id = "${aws_subnet.public-subnet.*.id[count.index]}" - route_table_id = "${aws_route_table.public-route-table.id}" + count = 3 + subnet_id = aws_subnet.public-subnet[count.index].id + route_table_id = aws_route_table.public-route-table.id } # Associate private routing rules to their subnets. resource "aws_route_table" "private-route-table" { count = 3 - vpc_id = "${aws_vpc.vpc.id}" + vpc_id = aws_vpc.vpc.id route { - cidr_block = "0.0.0.0/0" - nat_gateway_id = "${aws_nat_gateway.nat-gw.*.id[count.index]}" + cidr_block = "0.0.0.0/0" + nat_gateway_id = aws_nat_gateway.nat-gw[count.index].id } - tags { + tags = { Name = "CFB Subnets" } } resource "aws_route_table_association" "private-table-association" { - count = 3 - subnet_id = "${aws_subnet.private-subnet.*.id[count.index]}" - route_table_id = "${aws_route_table.private-route-table.*.id[count.index]}" + count = 3 + subnet_id = aws_subnet.private-subnet[count.index].id + route_table_id = aws_route_table.private-route-table[count.index].id } + diff --git a/terraform/modules/vpc/outputs.tf b/terraform/modules/vpc/outputs.tf index 39525491..8813161f 100644 --- a/terraform/modules/vpc/outputs.tf +++ b/terraform/modules/vpc/outputs.tf @@ -1,14 +1,13 @@ output "vpc-id" { description = "VPC ID" - value = "${aws_vpc.vpc.id}" + value = aws_vpc.vpc.id } - -output "subnet-ids" { +output "subnet_ids" { description = "Subnet IDs" - value = ["${aws_subnet.private-subnet.*.id}"] + value = aws_subnet.private-subnet.*.id } output "public-subnet-ids" { description = "Subnet IDs" - value = ["${aws_subnet.public-subnet.*.id}"] -} + value = aws_subnet.public-subnet.*.id +} \ No newline at end of file diff --git a/terraform/modules/vpc/variables.tf b/terraform/modules/vpc/variables.tf index b70261ff..3e213cc5 100644 --- a/terraform/modules/vpc/variables.tf +++ b/terraform/modules/vpc/variables.tf @@ -4,24 +4,25 @@ variable "vpc_cidr" { variable "mytags" { description = "Tags to include on the resources" - type = "map" + type = map(string) default = {} } variable "private_subnet_cidrs" { description = "The CIDR Blocks of the Private Subnets" - type = "list" + type = list(string) default = ["10.0.0.0/24", "10.0.1.0/24", "10.0.2.0/24"] } variable "public_subnet_cidrs" { description = "The CIDR Blocks of the Public Subnets" - type = "list" + type = list(string) default = ["10.0.3.0/24", "10.0.4.0/24", "10.0.5.0/24"] } variable "availability_zones" { description = "The Availability Zones to use in the VPC" - type = "list" + type = list(string) default = ["us-east-1a", "us-east-1b", "us-east-1c"] } + From a18f55b499ddaf788e4a11f475a48c5eb5fabfb8 Mon Sep 17 00:00:00 2001 From: Scott Hamrick Date: Thu, 26 Mar 2020 22:39:36 -0500 Subject: [PATCH 05/13] fix(infrastructure): cleaned up some broken references --- terraform/components/full-cluster/main.tf | 2 +- terraform/modules/{aws_wafregional_web_acl => waf}/README.md | 0 terraform/modules/{aws_wafregional_web_acl => waf}/main.tf | 0 terraform/modules/{aws_wafregional_web_acl => waf}/outputs.tf | 0 terraform/modules/{aws_wafregional_web_acl => waf}/variables.tf | 0 5 files changed, 1 insertion(+), 1 deletion(-) rename terraform/modules/{aws_wafregional_web_acl => waf}/README.md (100%) rename terraform/modules/{aws_wafregional_web_acl => waf}/main.tf (100%) rename terraform/modules/{aws_wafregional_web_acl => waf}/outputs.tf (100%) rename terraform/modules/{aws_wafregional_web_acl => waf}/variables.tf (100%) diff --git a/terraform/components/full-cluster/main.tf b/terraform/components/full-cluster/main.tf index 16919059..08be83da 100644 --- a/terraform/components/full-cluster/main.tf +++ b/terraform/components/full-cluster/main.tf @@ -81,7 +81,7 @@ module "alb" { } module "waf" { - source = "../../modules/aws_wafregional_web_acl" + source = "../../modules/waf" whitelist_cidrs = var.waf_whitelist_cidrs lb_arn = module.alb.lb-arn resource_suffix = random_pet.random_pet.id diff --git a/terraform/modules/aws_wafregional_web_acl/README.md b/terraform/modules/waf/README.md similarity index 100% rename from terraform/modules/aws_wafregional_web_acl/README.md rename to terraform/modules/waf/README.md diff --git a/terraform/modules/aws_wafregional_web_acl/main.tf b/terraform/modules/waf/main.tf similarity index 100% rename from terraform/modules/aws_wafregional_web_acl/main.tf rename to terraform/modules/waf/main.tf diff --git a/terraform/modules/aws_wafregional_web_acl/outputs.tf b/terraform/modules/waf/outputs.tf similarity index 100% rename from terraform/modules/aws_wafregional_web_acl/outputs.tf rename to terraform/modules/waf/outputs.tf diff --git a/terraform/modules/aws_wafregional_web_acl/variables.tf b/terraform/modules/waf/variables.tf similarity index 100% rename from terraform/modules/aws_wafregional_web_acl/variables.tf rename to terraform/modules/waf/variables.tf From 41cb6f078d4c458fbedd60e56df5ccd1ceef495d Mon Sep 17 00:00:00 2001 From: Scott Hamrick Date: Thu, 26 Mar 2020 22:44:49 -0500 Subject: [PATCH 06/13] docs(infrastructure): updated readme --- terraform/README.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/terraform/README.md b/terraform/README.md index 4bd6dce9..1b3e22be 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -13,11 +13,15 @@ The full-cluster component represents an ECS cluster running in its own, dedicat ## Modules Each module contains its own README.md which includes information about its functionality. Please refer to individual module documentation for more information about inputs, outputs, and behaviors of each module. -| Module | Documentation | -|---|---| -| ALB |[README.md](modules/alb/README.md)| -| ASG |[README.md](modules/asg/README.md)| -| ECS |[README.md](modules/ecs/README.md)| -| S3 |[README.md](modules/s3/README.md)| -| SG |[README.md](modules/sg/README.md)| -| VPC |[README.md](modules/vpc/README.md)| +| Module | Documentation | +|-------------|------------------------------------------| +| ALB |[README.md](modules/alb/README.md) | +| ASG |[README.md](modules/asg/README.md) | +| CERTIFICATE |[README.md](modules/certificate/README.md)| +| DB |[README.md](modules/db/README.md) | +| DNS_RECORD |[README.md](modules/dns_record/README.md) | +| ECS |[README.md](modules/ecs/README.md) | +| S3 |[README.md](modules/s3/README.md) | +| SG |[README.md](modules/sg/README.md) | +| VPC |[README.md](modules/vpc/README.md) | +| WAF |[README.md](modules/waf/README.md) | From 25337d2ce58cc55e1deef95bc083c79e8e623c25 Mon Sep 17 00:00:00 2001 From: Scott Hamrick Date: Thu, 26 Mar 2020 22:50:20 -0500 Subject: [PATCH 07/13] docs(infrastructure): updated readme --- terraform/components/full-cluster/README.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/terraform/components/full-cluster/README.md b/terraform/components/full-cluster/README.md index fb337992..99292b98 100644 --- a/terraform/components/full-cluster/README.md +++ b/terraform/components/full-cluster/README.md @@ -1,7 +1,10 @@ -# Infrastructure Requirements - - [ ] Public DNS Zone - Route53 - - [ ] SSL/TLS termination at the ALB - - [ ] ALB + WAF +# full-cluster + +## Infrastructure Requirements + - [x] Public DNS Record + - [x] SSL/TLS termination at the ALB + - [x] IP whitelisting via WAF + - [ ] Auto db credential rotation via Lambda ## Providers From 61a78b410750127889dd31699bd4df1a4368ab16 Mon Sep 17 00:00:00 2001 From: Scott Hamrick Date: Sun, 29 Mar 2020 12:34:33 -0500 Subject: [PATCH 08/13] style(infrastructure): fixed format and docs --- .../full-cluster/cfb-container.json.tpl | 16 +++++++++- terraform/components/full-cluster/main.tf | 29 +++++++++++++++---- .../components/full-cluster/userdata.sh.tpl | 5 ++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/terraform/components/full-cluster/cfb-container.json.tpl b/terraform/components/full-cluster/cfb-container.json.tpl index 856d4c1d..1327482b 100644 --- a/terraform/components/full-cluster/cfb-container.json.tpl +++ b/terraform/components/full-cluster/cfb-container.json.tpl @@ -10,6 +10,20 @@ "containerPort": 8080, "hostPort": 0 } - ] + ], + "environment": [ + { "name" : "VUE_APP_BASE_API_URL", "value" : "${vue_app_base_api_url}" }, + { "name" : "NODE_ENV", "value" : "${node_env}" }, + { "name" : "DATABASE_HOST", "value" : "${database_host}" }, + { "name" : "DATABASE_USER", "value" : "${database_user}" }, + { "name" : "DATABASE_PORT", "value" : "${database_port}" }, + { "name" : "DATABASE_NAME", "value" : "${database_name}" }, + { "name" : "JWT_KEY", "value" : "${jwt_key}" }, + { "name" : "BYPASS_LOGIN", "value" : "${bypass_login}" } + ], + "secrets": [{ + "name": "DATABASE_PASSWORD", + "valueFrom": "${database_password_arn}" + }] } ] diff --git a/terraform/components/full-cluster/main.tf b/terraform/components/full-cluster/main.tf index 08be83da..9b58ec49 100644 --- a/terraform/components/full-cluster/main.tf +++ b/terraform/components/full-cluster/main.tf @@ -25,11 +25,29 @@ module "s3" { data "template_file" "cfb_ecs_task_definition" { template = file("cfb-container.json.tpl") vars = { - image_address = module.ecs_cluster.cfb_registry - s3_bucket = module.s3.output_bucket_name + image_address = module.ecs_cluster.cfb_registry + s3_bucket = module.s3.output_bucket_name + vue_app_base_api_url = "bmoreres.codeforbaltimore.org" + node_env = "development" + database_host = module.db.this_db_instance_address + database_user = module.db.this_db_instance_username + database_password_arn = aws_secretsmanager_secret_version.db_password.arn + database_port = module.db.this_db_instance_port + database_name = "healthcareRollcallDB" + jwt_key = "abc123" + bypass_login = "false" } } +resource "aws_secretsmanager_secret" "db_password" { + name = "db_password" +} + +resource "aws_secretsmanager_secret_version" "db_password" { + secret_id = aws_secretsmanager_secret.db_password.id + secret_string = var.db_password +} + data "template_file" "user_data" { template = file("userdata.sh.tpl") vars = { @@ -49,8 +67,9 @@ module "vpc" { } module "sg" { - source = "../../modules/sg" - vpc_id = module.vpc.vpc-id + source = "../../modules/sg" + vpc_id = module.vpc.vpc-id + db_ingress_cidrs = module.vpc.private_subnet_cidrs } data "aws_route53_zone" "hosted_zone" { @@ -120,7 +139,7 @@ module "db" { password = var.db_password port = "5432" allocated_storage = "20" - vpc_security_group_ids = [module.sg.alb-sg-id] + vpc_security_group_ids = [module.sg.sg_postgresql_id] db_subnet_group_name = "CFB Subnets" maintenance_window = "Mon:00:00-Mon:03:00" backup_window = "03:00-06:00" diff --git a/terraform/components/full-cluster/userdata.sh.tpl b/terraform/components/full-cluster/userdata.sh.tpl index ba77652d..16d4b6b7 100644 --- a/terraform/components/full-cluster/userdata.sh.tpl +++ b/terraform/components/full-cluster/userdata.sh.tpl @@ -1,3 +1,8 @@ #!/bin/bash +# Install the SSM Agent RPM +yum install -y amazon-ssm-agent + +yum install -y postgresql-server postgresql-devel + echo ECS_CLUSTER=${cluster_name} >> /etc/ecs/ecs.config From d8399bd628210855f95892cf1fd4b7b6e72751e3 Mon Sep 17 00:00:00 2001 From: Scott Hamrick Date: Sun, 29 Mar 2020 12:36:01 -0500 Subject: [PATCH 09/13] style(infrastructure): fixed format and docs --- terraform/modules/ecs/main.tf | 36 +++++++++++++++++++++++++++++++ terraform/modules/sg/README.md | 2 ++ terraform/modules/sg/main.tf | 27 +++++++++++++++++++++++ terraform/modules/sg/outputs.tf | 4 ++++ terraform/modules/sg/variables.tf | 4 ++++ terraform/modules/vpc/README.md | 1 + terraform/modules/vpc/outputs.tf | 4 ++++ 7 files changed, 78 insertions(+) diff --git a/terraform/modules/ecs/main.tf b/terraform/modules/ecs/main.tf index 0d36fbe3..e8c7a23f 100644 --- a/terraform/modules/ecs/main.tf +++ b/terraform/modules/ecs/main.tf @@ -5,6 +5,37 @@ terraform { # Set up necessary IAM Roles for ECS Hosts data "aws_iam_policy_document" "ecs_cluster_asg_policy" { + statement { + actions = [ + "ssmmessages:CreateControlChannel", + "ssmmessages:CreateDataChannel", + "ssmmessages:OpenControlChannel", + "ssmmessages:OpenDataChannel" + ] + effect = "Allow" + + resources = ["*"] + } + + statement { + actions = [ + "s3:GetEncryptionConfiguration" + ] + effect = "Allow" + resources = ["*"] + } + + statement { + + actions = [ + "kms:Decrypt" + ] + + effect = "Allow" + + resources = ["*"] + } + statement { actions = [ "ecs:DeregisterContainerInstance", @@ -66,6 +97,11 @@ resource "aws_iam_role_policy" "ecs_cluster_asg_policy" { policy = data.aws_iam_policy_document.ecs_cluster_asg_policy.json } +resource "aws_iam_role_policy_attachment" "ssm_policy_attachment" { + policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" + role = aws_iam_role.ecs_cluster.name +} + resource "aws_iam_instance_profile" "ecs_cluster_asg_profile" { name_prefix = "ecs_cluster_asg_profile-" role = aws_iam_role.ecs_cluster.name diff --git a/terraform/modules/sg/README.md b/terraform/modules/sg/README.md index 7a18d31f..9d426f55 100644 --- a/terraform/modules/sg/README.md +++ b/terraform/modules/sg/README.md @@ -11,6 +11,7 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:-----:| +| db\_ingress\_cidrs | n/a | `list(string)` | n/a | yes | | mytags | Tags to include on the resources | `map` | `{}` | no | | vpc\_id | VPC ID | `any` | n/a | yes | @@ -20,6 +21,7 @@ |------|-------------| | alb-sg-id | ALB Security Group ID | | ecs\_sg\_id | ECS Security Group ID | +| sg\_postgresql\_id | n/a | diff --git a/terraform/modules/sg/main.tf b/terraform/modules/sg/main.tf index e20a6462..6e0510e5 100644 --- a/terraform/modules/sg/main.tf +++ b/terraform/modules/sg/main.tf @@ -54,4 +54,31 @@ resource "aws_security_group" "sg-ecs" { } } +resource "aws_security_group" "sg_postgresql" { + name = "bmore-responsive-db-access" + vpc_id = var.vpc_id + + # Merge tags from environment tfvars and create name tag + tags = merge(map("Name", "bmore-responsive-db-access"), var.mytags) + + ingress { + # TLS (change to whatever ports you need) + from_port = 5432 + to_port = 5432 + protocol = "tcp" + + # We open to 0.0.0.0/0 here to support the testing activities. + # In a production environment, these connections would be limited to + # approved internal IPs. (10.x.x.x/x block(s)) + cidr_blocks = var.db_ingress_cidrs + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } +} + # TODO: Add SG Rules for ECS ASG and Load Balancer. diff --git a/terraform/modules/sg/outputs.tf b/terraform/modules/sg/outputs.tf index a3add6b9..2989da50 100644 --- a/terraform/modules/sg/outputs.tf +++ b/terraform/modules/sg/outputs.tf @@ -7,3 +7,7 @@ output "alb-sg-id" { description = "ALB Security Group ID" value = aws_security_group.sg-alb.id } + +output "sg_postgresql_id" { + value = aws_security_group.sg_postgresql.id +} \ No newline at end of file diff --git a/terraform/modules/sg/variables.tf b/terraform/modules/sg/variables.tf index 183a1a0d..76031398 100644 --- a/terraform/modules/sg/variables.tf +++ b/terraform/modules/sg/variables.tf @@ -7,3 +7,7 @@ variable "mytags" { type = "map" default = {} } + +variable "db_ingress_cidrs" { + type = list(string) +} \ No newline at end of file diff --git a/terraform/modules/vpc/README.md b/terraform/modules/vpc/README.md index e4d87a97..0dcff33c 100644 --- a/terraform/modules/vpc/README.md +++ b/terraform/modules/vpc/README.md @@ -21,6 +21,7 @@ | Name | Description | |------|-------------| +| private\_subnet\_cidrs | n/a | | public-subnet-ids | Subnet IDs | | subnet\_ids | Subnet IDs | | vpc-id | VPC ID | diff --git a/terraform/modules/vpc/outputs.tf b/terraform/modules/vpc/outputs.tf index 8813161f..c2bb0a9a 100644 --- a/terraform/modules/vpc/outputs.tf +++ b/terraform/modules/vpc/outputs.tf @@ -10,4 +10,8 @@ output "subnet_ids" { output "public-subnet-ids" { description = "Subnet IDs" value = aws_subnet.public-subnet.*.id +} + +output "private_subnet_cidrs" { + value = var.private_subnet_cidrs } \ No newline at end of file From 40edf2a330b5f365bea9456cefca2d0b139fd49f Mon Sep 17 00:00:00 2001 From: Billy Ani Date: Sun, 29 Mar 2020 15:29:34 -0400 Subject: [PATCH 10/13] added in policy for secrets manager to ecs task definition --- bin/deploy.sh | 3 +- bin/redeploy.sh | 9 +++--- docker-compose.yml | 4 +-- docker/Dockerfile-Builder | 2 +- docker/aws/config | 2 +- terraform/components/full-cluster/main.tf | 3 +- .../full-cluster/matching-container.json.tpl | 25 ---------------- terraform/components/full-cluster/policy.json | 16 ++++++++++ terraform/modules/ecs/main.tf | 30 +++++++++++++++++++ terraform/versions.tf | 4 +++ 10 files changed, 62 insertions(+), 36 deletions(-) delete mode 100644 terraform/components/full-cluster/matching-container.json.tpl create mode 100644 terraform/components/full-cluster/policy.json create mode 100644 terraform/versions.tf diff --git a/bin/deploy.sh b/bin/deploy.sh index eaf8f8d8..4ebcaa34 100755 --- a/bin/deploy.sh +++ b/bin/deploy.sh @@ -52,8 +52,7 @@ docker run -it -v $(pwd):/app/ cfb-build-agent ./db-create $(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent ecr-login | tr -d '\r') # Build the container image for the API -docker build -f docker/Dockerfile-Bmore-Responsive -t bmore-responsive \ - --build-arg DB_URL=${DB_URL} . +docker build -f docker/Dockerfile-Bmore-Responsive -t bmore-responsive . # Get the address of the repository in AWS CFB_REPO=$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent output full-cluster bmore-responsive_registry | tr -d '\r') # Tag the image for pushing diff --git a/bin/redeploy.sh b/bin/redeploy.sh index d9ae3563..d65ea7e0 100755 --- a/bin/redeploy.sh +++ b/bin/redeploy.sh @@ -12,7 +12,7 @@ docker build -f docker/Dockerfile-Builder -t cfb-build-agent . # Rebuild the Java Projects -docker run -it -v $(pwd):/app/ cfb-build-agent npm-build +#docker run -it -v $(pwd):/app/ cfb-build-agent npm-build ### Building and Pushing Docker Images ### @@ -20,12 +20,13 @@ docker run -it -v $(pwd):/app/ cfb-build-agent npm-build $(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent ecr-login | tr -d '\r') # Build the container image -docker build -f docker/Dockerfile-Bmore-Responsive -t bmore-responsive \ - --build-arg DB_URL="postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_ENDPOINT}:${DB_PORT}/${DB_NAME}" . +docker build -f docker/Dockerfile-Bmore-Responsive -t bmore-responsive . # Get the address of the repository in AWS CFB_REPO=$(docker run -it -v $(pwd):/app/ -v $(pwd)/docker/aws/:/root/.aws/ -e AWS_PROFILE=$AWS_PROFILE cfb-build-agent output full-cluster bmore-responsive_registry | tr -d '\r') +echo "CFB_REPO -> $CFB_REPO" + # Tag the image for pushing -docker tag bmore-responsive $CFB_REPO:latest +docker tag bmore-responsive:latest $CFB_REPO:latest # Push the new docker image docker push $CFB_REPO diff --git a/docker-compose.yml b/docker-compose.yml index 96d30fbd..f9df904e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,13 @@ version: '3' services: api: - build: . + image: 180104022864.dkr.ecr.us-east-2.amazonaws.com/bmore-responsive depends_on: - db links: - "db: database" ports: - - '3000:3000' + - '8080:80' command: > sh -c "npm run db-delete && npm run db-create && diff --git a/docker/Dockerfile-Builder b/docker/Dockerfile-Builder index 0952d055..9ae8c834 100644 --- a/docker/Dockerfile-Builder +++ b/docker/Dockerfile-Builder @@ -5,7 +5,7 @@ RUN apt-get update && apt-get install wget python3-pip -y # Download Terraform and make it executable. WORKDIR /tmp/ -RUN wget -O terraform.zip https://releases.hashicorp.com/terraform/0.11.13/terraform_0.11.13_linux_amd64.zip +RUN wget -O terraform.zip https://releases.hashicorp.com/terraform/0.12.24/terraform_0.12.24_linux_amd64.zip RUN unzip terraform.zip RUN mv terraform /usr/bin/terraform diff --git a/docker/aws/config b/docker/aws/config index d9f4cd06..acb21815 100644 --- a/docker/aws/config +++ b/docker/aws/config @@ -1,3 +1,3 @@ [default] -region=us-east-1 +region=us-east-2 output=json diff --git a/terraform/components/full-cluster/main.tf b/terraform/components/full-cluster/main.tf index 9b58ec49..a7ddface 100644 --- a/terraform/components/full-cluster/main.tf +++ b/terraform/components/full-cluster/main.tf @@ -40,7 +40,8 @@ data "template_file" "cfb_ecs_task_definition" { } resource "aws_secretsmanager_secret" "db_password" { - name = "db_password" + name_prefix = "db_password" + } resource "aws_secretsmanager_secret_version" "db_password" { diff --git a/terraform/components/full-cluster/matching-container.json.tpl b/terraform/components/full-cluster/matching-container.json.tpl deleted file mode 100644 index ebedb24a..00000000 --- a/terraform/components/full-cluster/matching-container.json.tpl +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "name": "provider-matching", - "image": "${image_address}", - "cpu": 128, - "memory": 512, - "essential": true, - "portMappings": [ - { - "containerPort": 8080, - "hostPort": 0 - } - ], - "environment": [ - { - "name": "AWS_REGION", - "value": "us-east-1" - }, - { - "name": "AWS_BUCKET", - "value": "${s3_bucket}" - } - ] - } -] diff --git a/terraform/components/full-cluster/policy.json b/terraform/components/full-cluster/policy.json new file mode 100644 index 00000000..56a0ffea --- /dev/null +++ b/terraform/components/full-cluster/policy.json @@ -0,0 +1,16 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ssm:GetParameters", + "secretsmanager:GetSecretValue", + "kms:Decrypt" + ], + "Resource": [ + "*" + ] + } + ] +} diff --git a/terraform/modules/ecs/main.tf b/terraform/modules/ecs/main.tf index e8c7a23f..7e762bea 100644 --- a/terraform/modules/ecs/main.tf +++ b/terraform/modules/ecs/main.tf @@ -148,6 +148,34 @@ EOF } +resource "aws_iam_role" "task_execution_role" { + name = "ecsTaskExecutionRole" + assume_role_policy = < Date: Sun, 29 Mar 2020 16:56:04 -0500 Subject: [PATCH 11/13] Fixed format and docs. --- .../full-cluster/cfb-container.json.tpl | 13 ++-- terraform/components/full-cluster/main.tf | 31 ++++---- .../components/full-cluster/userdata.sh.tpl | 5 -- terraform/modules/ecs/README.md | 1 + terraform/modules/ecs/main.tf | 75 +++++++++++++------ terraform/modules/ecs/variables.tf | 1 + 6 files changed, 78 insertions(+), 48 deletions(-) diff --git a/terraform/components/full-cluster/cfb-container.json.tpl b/terraform/components/full-cluster/cfb-container.json.tpl index 1327482b..8c830aba 100644 --- a/terraform/components/full-cluster/cfb-container.json.tpl +++ b/terraform/components/full-cluster/cfb-container.json.tpl @@ -7,7 +7,7 @@ "essential": true, "portMappings": [ { - "containerPort": 8080, + "containerPort": 3000, "hostPort": 0 } ], @@ -15,15 +15,12 @@ { "name" : "VUE_APP_BASE_API_URL", "value" : "${vue_app_base_api_url}" }, { "name" : "NODE_ENV", "value" : "${node_env}" }, { "name" : "DATABASE_HOST", "value" : "${database_host}" }, - { "name" : "DATABASE_USER", "value" : "${database_user}" }, + { "name" : "DATABASE_USERNAME", "value" : "${database_user}" }, { "name" : "DATABASE_PORT", "value" : "${database_port}" }, { "name" : "DATABASE_NAME", "value" : "${database_name}" }, { "name" : "JWT_KEY", "value" : "${jwt_key}" }, - { "name" : "BYPASS_LOGIN", "value" : "${bypass_login}" } - ], - "secrets": [{ - "name": "DATABASE_PASSWORD", - "valueFrom": "${database_password_arn}" - }] + { "name" : "BYPASS_LOGIN", "value" : "${bypass_login}" }, + { "name" : "DATABASE_PASSWORD", "value" : "${database_password}" } + ] } ] diff --git a/terraform/components/full-cluster/main.tf b/terraform/components/full-cluster/main.tf index a7ddface..5958e6ce 100644 --- a/terraform/components/full-cluster/main.tf +++ b/terraform/components/full-cluster/main.tf @@ -25,23 +25,25 @@ module "s3" { data "template_file" "cfb_ecs_task_definition" { template = file("cfb-container.json.tpl") vars = { - image_address = module.ecs_cluster.cfb_registry - s3_bucket = module.s3.output_bucket_name - vue_app_base_api_url = "bmoreres.codeforbaltimore.org" - node_env = "development" - database_host = module.db.this_db_instance_address - database_user = module.db.this_db_instance_username - database_password_arn = aws_secretsmanager_secret_version.db_password.arn - database_port = module.db.this_db_instance_port - database_name = "healthcareRollcallDB" - jwt_key = "abc123" - bypass_login = "false" + image_address = "codeforbaltimore/bmore-responsive" + s3_bucket = module.s3.output_bucket_name + vue_app_base_api_url = "bmoreres.codeforbaltimore.org" + node_env = "development" + database_host = module.db.this_db_instance_address + database_user = module.db.this_db_instance_username + // database_password_arn = aws_secretsmanager_secret_version.db_password.arn + database_port = module.db.this_db_instance_port + database_name = "healthcareRollcallDB" + jwt_key = "abc123" + bypass_login = "false" + aws_region = var.aws_region + database_password = var.db_password } } resource "aws_secretsmanager_secret" "db_password" { name_prefix = "db_password" - + } resource "aws_secretsmanager_secret_version" "db_password" { @@ -96,7 +98,7 @@ module "alb" { vpc_id = module.vpc.vpc-id vpc_subnets = module.vpc.public-subnet-ids lb_sg = module.sg.alb-sg-id - cfb_app_port = 8080 + cfb_app_port = 3000 certificate_arn = module.certificate.certificate_arn } @@ -114,8 +116,9 @@ module "ecs_cluster" { bmore-responsive_desired_count = "3" bmore-responsive_target_group_arn = module.alb.tg-cfb-arn bmore-responsive_container_name = "bmore-responsive" - bmore-responsive_container_port = "8080" + bmore-responsive_container_port = "3000" bmore-responsive_container_definitions = data.template_file.cfb_ecs_task_definition.rendered + aws_region = var.aws_region } module "asg" { diff --git a/terraform/components/full-cluster/userdata.sh.tpl b/terraform/components/full-cluster/userdata.sh.tpl index 16d4b6b7..ba77652d 100644 --- a/terraform/components/full-cluster/userdata.sh.tpl +++ b/terraform/components/full-cluster/userdata.sh.tpl @@ -1,8 +1,3 @@ #!/bin/bash -# Install the SSM Agent RPM -yum install -y amazon-ssm-agent - -yum install -y postgresql-server postgresql-devel - echo ECS_CLUSTER=${cluster_name} >> /etc/ecs/ecs.config diff --git a/terraform/modules/ecs/README.md b/terraform/modules/ecs/README.md index 8634da4f..cb667086 100644 --- a/terraform/modules/ecs/README.md +++ b/terraform/modules/ecs/README.md @@ -11,6 +11,7 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:-----:| +| aws\_region | n/a | `any` | n/a | yes | | bmore-responsive\_container\_definitions | The Rendered JSON of a container definition array. See example-container.json for a sample of valid JSON input. | `string` | n/a | yes | | bmore-responsive\_container\_name | The name of the container to associate with the Load Balancer. Must equal the container name in the container definition JSON | `string` | n/a | yes | | bmore-responsive\_container\_port | The port on the container to associate with the Load Balancer | `string` | n/a | yes | diff --git a/terraform/modules/ecs/main.tf b/terraform/modules/ecs/main.tf index 7e762bea..38e7903b 100644 --- a/terraform/modules/ecs/main.tf +++ b/terraform/modules/ecs/main.tf @@ -69,6 +69,8 @@ data "aws_iam_policy_document" "ecs_cluster_asg_policy" { } } +data "aws_caller_identity" "current" {} + resource "aws_iam_role" "ecs_cluster" { path = "/" name = "bmore-responsive_ecs_cluster_role" @@ -148,31 +150,62 @@ EOF } -resource "aws_iam_role" "task_execution_role" { - name = "ecsTaskExecutionRole" - assume_role_policy = < Date: Sun, 29 Mar 2020 19:00:19 -0400 Subject: [PATCH 12/13] added in awslogs for ecs containers --- .../full-cluster/cfb-container.json.tpl | 10 ++++++++++ .../components/full-cluster/userdata.sh.tpl | 6 ++++++ terraform/modules/ecs/main.tf | 16 ++++++++++++---- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/terraform/components/full-cluster/cfb-container.json.tpl b/terraform/components/full-cluster/cfb-container.json.tpl index 8c830aba..0791cbaa 100644 --- a/terraform/components/full-cluster/cfb-container.json.tpl +++ b/terraform/components/full-cluster/cfb-container.json.tpl @@ -11,6 +11,16 @@ "hostPort": 0 } ], + "logConfiguration": + { + "logDriver": "awslogs", + "options": + { + "awslogs-group": "cfb-api-logs", + "awslogs-region": "us-east-2", + "awslogs-stream-prefix": "cfb-api-" + } + }, "environment": [ { "name" : "VUE_APP_BASE_API_URL", "value" : "${vue_app_base_api_url}" }, { "name" : "NODE_ENV", "value" : "${node_env}" }, diff --git a/terraform/components/full-cluster/userdata.sh.tpl b/terraform/components/full-cluster/userdata.sh.tpl index ba77652d..151a4fb6 100644 --- a/terraform/components/full-cluster/userdata.sh.tpl +++ b/terraform/components/full-cluster/userdata.sh.tpl @@ -1,3 +1,9 @@ #!/bin/bash +curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/linux_64bit/session-manager-plugin.rpm" -o "session-manager-plugin.rpm" +sudo yum install -y session-manager-plugin.rpm +sudo systemctl enable amazon-ssm-agent +sudo systemctl start amazon-ssm-agent +yum install -y postgresql-server postgresql-devel + echo ECS_CLUSTER=${cluster_name} >> /etc/ecs/ecs.config diff --git a/terraform/modules/ecs/main.tf b/terraform/modules/ecs/main.tf index 38e7903b..55a4f307 100644 --- a/terraform/modules/ecs/main.tf +++ b/terraform/modules/ecs/main.tf @@ -213,11 +213,19 @@ resource "aws_ecs_cluster" "ecs_cluster" { name = var.cluster_name } +resource "aws_cloudwatch_log_group" "cfb-api-logs" { + name = "cfb-api-logs" +} + resource "aws_ecs_task_definition" "bmore-responsive_ecs_task_definition" { - family = "bmore-responsive" - container_definitions = var.bmore-responsive_container_definitions - // task_role_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" - execution_role_arn = aws_iam_role.task_execution_role.arn + family = "bmore-responsive" + container_definitions = var.bmore-responsive_container_definitions + //task_role_arn = "arn:aws:iam::180104022864:role/bmore-responsive_ecs_cluster_role" + execution_role_arn = aws_iam_role.task_execution_role.arn + //awslogs-create-group = "true" + //awslogs-region = "${var.aws_region}" + //awslogs-group = aws_cloudwatch_log_group.cfb-api-logs.arn + //awslogs-stream-prefix = "cfb-api-" } resource "aws_ecs_service" "pricer_ecs_service" { From a94315b7c4aded5ed89732a10e4ba8360ec9f5d2 Mon Sep 17 00:00:00 2001 From: Jason Anton Date: Mon, 6 Apr 2020 09:43:20 -0400 Subject: [PATCH 13/13] fix: Updating Docker Adding in a .dockerignore file as well as various other Docker-cleanup related items. --- .dockerignore | 125 ++++++++++++++++++++++ .gitignore | 1 + Dockerfile | 20 ++++ docs/swagger/.swagger-codegen-ignore | 23 ---- docs/swagger/.swagger-codegen/VERSION | 1 - docs/swagger/README.md | 2 - src/index.js | 2 +- docs/swagger/swagger.json => swagger.json | 0 8 files changed, 147 insertions(+), 27 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile delete mode 100644 docs/swagger/.swagger-codegen-ignore delete mode 100644 docs/swagger/.swagger-codegen/VERSION delete mode 100644 docs/swagger/README.md rename docs/swagger/swagger.json => swagger.json (100%) diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..066b8c38 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,125 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# Mac stupid DS_Store files +*.DS_Store + +# terraform +terraform/ +.idea +.terraform +*.plan +*.out + +# Docker +Dockerfile +docker-compose.yml +docker/ + +# docs +docs/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1067fa0c..ff84b186 100644 --- a/.gitignore +++ b/.gitignore @@ -109,6 +109,7 @@ dist # Mac stupid DS_Store files *.DS_Store +# terraform .idea .terraform *.plan diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..0e2f88ea --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# Build from Node Alpine image +FROM node:12.11.0-alpine + +# Create app directory +RUN mkdir -p /usr/src +WORKDIR /usr/src + +# Install app dependencies +COPY ./package*.json ./ +RUN npm install + +# Bundle app source +COPY . . + + +# Expose port (will not be respected by Heroku, must be defined in app) +EXPOSE $port + +# Run app +CMD ["npm","start"] diff --git a/docs/swagger/.swagger-codegen-ignore b/docs/swagger/.swagger-codegen-ignore deleted file mode 100644 index c5fa491b..00000000 --- a/docs/swagger/.swagger-codegen-ignore +++ /dev/null @@ -1,23 +0,0 @@ -# Swagger Codegen Ignore -# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen - -# Use this file to prevent files from being overwritten by the generator. -# The patterns follow closely to .gitignore or .dockerignore. - -# As an example, the C# client generator defines ApiClient.cs. -# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: -#ApiClient.cs - -# You can match any string of characters against a directory, file or extension with a single asterisk (*): -#foo/*/qux -# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux - -# You can recursively match patterns against a directory, file or extension with a double asterisk (**): -#foo/**/qux -# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux - -# You can also negate patterns with an exclamation (!). -# For example, you can ignore all files in a docs folder with the file extension .md: -#docs/*.md -# Then explicitly reverse the ignore rule for a single file: -#!docs/README.md diff --git a/docs/swagger/.swagger-codegen/VERSION b/docs/swagger/.swagger-codegen/VERSION deleted file mode 100644 index edd1851a..00000000 --- a/docs/swagger/.swagger-codegen/VERSION +++ /dev/null @@ -1 +0,0 @@ -3.0.18 \ No newline at end of file diff --git a/docs/swagger/README.md b/docs/swagger/README.md deleted file mode 100644 index b334639e..00000000 --- a/docs/swagger/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Swagger -To view our Swagger docs please visit [SwaggerHub](https://app.swaggerhub.com/apis/codeforbaltimore/bmoreResponsive/1.0.0#/) or http://localhost:3000 with the application running locally. \ No newline at end of file diff --git a/src/index.js b/src/index.js index 3a0ec53d..57abf6bd 100644 --- a/src/index.js +++ b/src/index.js @@ -8,7 +8,7 @@ import morgan from 'morgan'; import swaggerUi from 'swagger-ui-express'; import nunjucks from 'nunjucks'; -import swaggerDocument from '../docs/swagger/swagger.json'; +import swaggerDocument from '../swagger.json'; import models, { sequelize } from './models'; import routes from './routes'; import utils from './utils'; diff --git a/docs/swagger/swagger.json b/swagger.json similarity index 100% rename from docs/swagger/swagger.json rename to swagger.json