diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..db87e1f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/.gitignore b/.gitignore index 46ca4b6..d55a782 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .terraform *.tfplan *.pem -*.zip \ No newline at end of file +*.zip + +.idea \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..e8fc6c6 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +# See https://github.com/gruntwork-io/pre-commit +# Will need to install some pre-requisites +# pre-commit https://pre-commit.com/#install +# tflint https://github.com/terraform-linters/tflint/ +# Can then install with +# pre-commit install +# Can do a manual run with +# pre-commit run + +repos: + - repo: https://github.com/gruntwork-io/pre-commit + rev: v0.1.9 # Get the latest from: https://github.com/gruntwork-io/pre-commit/releases + hooks: + - id: terraform-fmt + - id: tflint + args: ["--deep", "--module"] diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..acc885e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,34 @@ +# Change Log + +All notable changes to this project will be documented in this file. + +## [Unreleased] +- Added: ALB logging - both ALBs log to the same bucket, using distinct prefixes - api and push +- Updated: Upgraded AWS provider from = 2.68.0 to ~> 2.70.0 +- Updated: Switched to using templatefile function rather than deprecated template provider + + +## [v0.1.1] 2020-08-13 +- Added: Added ability to set ECS image url and image tag overrides, this is based on @segfault's PR at https://github.com/covidgreen/covid-green-infra/pull/4 +- Updated: Switched lambdas from using the AWSLambdaBasicExecutionRole to AWSLambdaVPCAccessExecutionRole managed policy +- Updated: Renamed the "root_profile" var and "root" AWS provider to "dns" as this is confusing, removed a redundant aws provider "root_us" +- Updated: Added changes @segfault added re explicit usage of the AWS CLI `--output json` usage in some of the scripts +- Removed: Removed Terraform validate from the pre-commit hook config as this is being used as a module, have left the s3 backend config for now +- Updated: Switched all the lambdas from nodejs10.x to nodejs12.x +- Added: Use variables for the lambda memory size and timeout attributes with defaults, so we can configure via env-vars files +- Removed: Extracted cti, gct and ni content into specific repos - Will not be managed by this repo +- Added: Added new AWS parameters certificate_audience and jwt_issuer and removed security_exposure_limit AWS parameter +- Added: Docs on the 2 approaches to managing a project - external to this repo and internal to this repo +- Added: Added RDS reader/writer endpoint outputs +- Added: Surfaced bastion ASG desired count as a variable, will need when we use this repo as a module +- Added: Switched to using path.module prefixes for the CloudWatch dashboard template and the ECS container defintion templates +- Fixed: Changed the create TF store backend script to cater for us-east-1 being a special case - Location constraints +- Fixed: Replace all refs to cti, gct and ni with xyz in docs and shell script comments - this is just prep for the open source branch +- Added: Added the following optional lambdas to the operators group execute list: daily-registrations-reporter, download and upload +- Added: Pre-commit hook to include TF fmt, validation and linting +- Fixed: Linting issues - no logic +- Added: Split RDS user usage so we no longer need to use the master credentials + + +## [v0.1.0] 2020-08-13 +- Initial content diff --git a/Makefile b/Makefile index 584deb0..b29b8c2 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,8 @@ # make xyz-dev-init # make xyz-dev-plan # make xyz-dev-apply -# We assume you have an AWS profile with the the PROJECT-ENVIRONMENT so in above case xyz-dev -# This is the same profile that should exist in the env-vars file +# We assume you have an AWS profile named PROJECT-ENVIRONMENT i.e. xyz-dev +# This is the same profile that should exist in the env-vars files .ONESHELL: .SHELL := /bin/bash @@ -48,6 +48,7 @@ AWS_PROFILE = $(PROJECT_ENVIRONMENT_KEY) # Determine from tf vars file(s) - will use the first match AWS_REGION = $(shell grep '^aws_region' $(PROJECT_ENVIRONMENT_TFVAR_FILE) $(PROJECT_TFVAR_FILE) | head -n 1 | cut -d '"' -f 2) +# NOTE: Uses a convention TERRAFORM_BACKEND_BUCKET ?= $(PROJECT_ENVIRONMENT_KEY)-terraform-store TERRAFORM_BACKEND_KEY ?= $(PROJECT_KEY) TERRAFORM_BACKEND_TABLE ?= $(PROJECT_ENVIRONMENT_KEY)-terraform-lock @@ -65,24 +66,6 @@ xyz-dev-plan: xyz-dev-apply: @$(MAKE_TF_APPLY) ENVIRONMENT=dev PROJECT_KEY=xyz -# QA -.PHONY: xyz-qa-init xyz-qa-plan xyz-qa-apply -xyz-qa-init: - @$(MAKE_TF_INIT) ENVIRONMENT=qa PROJECT_KEY=xyz -xyz-qa-plan: - @$(MAKE_TF_PLAN) ENVIRONMENT=qa PROJECT_KEY=xyz -xyz-qa-apply: - @$(MAKE_TF_APPLY) ENVIRONMENT=qa PROJECT_KEY=xyz - -# PROD -.PHONY: xyz-prod-init xyz-prod-plan xyz-prod-apply -xyz-prod-init: - @$(MAKE_TF_INIT) ENVIRONMENT=prod PROJECT_KEY=xyz -xyz-prod-plan: - @$(MAKE_TF_PLAN) ENVIRONMENT=prod PROJECT_KEY=xyz -xyz-prod-apply: - @$(MAKE_TF_APPLY) ENVIRONMENT=prod PROJECT_KEY=xyz - # ######################################### # Generic targets - not dependant on a project diff --git a/README.md b/README.md index f0af19e..95a7688 100644 --- a/README.md +++ b/README.md @@ -26,20 +26,32 @@ The RDS Aurora cluster is avilable to both `private` and `intra` subnets. ## Development +We use a git pre-commit hook to do some checks and linting, see the top of [.pre-commit-config.yaml](./.pre-commit-config.yaml) for installation instructions + You need first to set up AWS profiles locally for every AWS account/project/environment you're going to work on. Once done change it in the project/environment variables override files in `env-vars`. The project uses 2 different AWS profiles, one to manage the infrastructure and one to manage DNS entries. This is because the AWS account used to spin up an environments could be different from the account from where the DNS zone is registered. See [Creating a new project](./docs/creating-a-new-project.md) for setting up the Terraform backend setup. Make file usage ``` -# Using the cti project (HSE) and dev environment -make cti-dev-init -make cti-dev-plan -make cti-dev-apply +# Using the xyz project and dev environment +make xyz-dev-init +make xyz-dev-plan +make xyz-dev-apply ``` Every `terraform` command can be launched via the `Makefile`, it will take care of initializing the folder to use different backends, planning, applying, and destroying changes. +### Change log + +We maintain a change log [here](./CHANGELOG.md) + +### Note +Sometimes on the plan/apply change(s) will appear for the bastions +- The bastion AMI is based on using the latest **Amazon Linux 2** AMI - this is fine to apply and will not terminate any running instances +- The bastion ASG count will show a change from 1 -> 0, this is due to someone having a bastion instance running, in this case you do not want to terminate their instance + - Easiest thing to do is alter your local bastion.tf **desired_capacity = 1** and re-run the plan, this way the instance will remain running and this change will no longer appear in the plan + ## Lambdas ### authorizer Checks a JWT is valid when the Gateway tried to access items in the S3 bucket. @@ -48,25 +60,34 @@ Checks a JWT is valid when the Gateway tried to access items in the S3 bucket. Used to collect symptom info from the app from an SQS queue. Currently unused. ### cso - Optional -This lambda is specific to the Irish app and compiles symptom info into a CSV file. The file is then encrypted using GPG (symmetric key in Secrets) and then uploaded via SFTP to the Central Statistics Office in Ireland. +This lambda is specific to the Irish app and compiles symptom info into a CSV file. The file is then encrypted using GPG (symmetric key in Secrets) and then uploaded via SFTP to the Central Statistics Office in Ireland. This is obviously not used in Gibraltar. + +### daily-registrations-reporter - Optional +This lambda is currently specific to the Gibraltar app and generates a report of cumulative API registrations by day for the app, which it sends to an SNS topic. + +### download - Optional +This lambda is for environments involved in testing the interoperability service. Downloads any new batches of exposure keys from the interop service since the lambda last ran, and stores them ready to be included in future export files. ### exposures Generates exposure files in zip format for the S3 bucket. Those files contain the encrypted contact tracing information the phone API uses to determine if you have had a close contact with someone. This lambda runs on a schedule, selecting the exposure info from the database and making the archive available once complete. -### notify_slack -For calling Slack web hooks with info. +### settings +This lambda is used to generate a settings.json file which contains values that can override the app defaults. +This saves us having to go through a full App Store release cycle to change minor details like phone numbers etc. + +### sms +This lambda is triggered by the SMS SQS queue, and allows project specific handling of sending SMS via different providers. ### stats This lambda is used to generate a daily stats.json file from a web service run by the Central Statistics Office in Ireland. This info is used in the Irish app to power various graphs and info screens. -### settings -This lambda is used to generate a settings.json file which contains values that can override the app defaults. -This saves us having to go through a full App Store release cycle to change minor details like phone numbers etc. - ### token This lambda is used to generate tokens for testing. It is not used by clients or end users. The phone app and backend APIs make use of a service called Device check which validates that we are talking to an actual device. To get around this for testing, we have a lambda that can generate two different kinds of token, one for register and one for push. The register token allows you to bypass the checks in the backend API and the push token works for the push API service. +### upload - Optional +This lambda is for environments involved in testing the interoperability service. Uploads any new exposure keys to the interop service, ready for other back-ends to download. + ## AWS secrets and parameters Secrets are stored in AWS Secrets Manager, these are populated outside of this Terraform content. - Some are optional as they are not used by all instances diff --git a/alb.tf b/alb.tf index b8cfe4a..ceba697 100644 --- a/alb.tf +++ b/alb.tf @@ -29,8 +29,13 @@ resource "aws_lb" "api" { enable_http2 = true ip_address_type = "dualstack" enable_deletion_protection = true + tags = module.labels.tags - tags = module.labels.tags + access_logs { + bucket = module.alb_logs.aws_logs_bucket + prefix = "api" + enabled = true + } } resource "aws_lb_target_group" "api" { @@ -120,8 +125,13 @@ resource "aws_lb" "push" { enable_http2 = true ip_address_type = "dualstack" enable_deletion_protection = true + tags = module.labels.tags - tags = module.labels.tags + access_logs { + bucket = module.alb_logs.aws_logs_bucket + prefix = "push" + enabled = true + } } resource "aws_lb_target_group" "push" { @@ -160,3 +170,20 @@ resource "aws_lb_listener" "push_https" { type = "forward" } } + +# ######################################### +# ALB logs - single bucket for both ALBs each with their own prefix +# ######################################### +module "alb_logs" { + source = "trussworks/logs/aws" + version = "8.1.0" + + alb_logs_prefixes = ["api", "push"] + allow_alb = true + default_allow = false + force_destroy = true + region = var.aws_region + s3_bucket_name = format("%s-alb-logs", module.labels.id) + s3_log_bucket_retention = var.logs_retention_days + tags = module.labels.tags +} diff --git a/bastion.tf b/bastion.tf index 0f538e7..ee9c8dd 100644 --- a/bastion.tf +++ b/bastion.tf @@ -14,7 +14,7 @@ data "aws_ami" "amazon_linux_2" { resource "aws_autoscaling_group" "bastion" { count = local.bastion_enabled_count - desired_capacity = 1 + desired_capacity = var.bastion_asg_desired_count max_size = 1 min_size = 0 name = format("%s-bastion", module.labels.id) diff --git a/dashboard.tf b/dashboard.tf index f520d7e..bacb79a 100644 --- a/dashboard.tf +++ b/dashboard.tf @@ -1,25 +1,21 @@ -data "template_file" "dashboard" { - template = file("templates/dashboard.json") - - vars = { - account_id = data.aws_caller_identity.current.account_id - region = var.aws_region - environment = var.environment - gateway_name = "${module.labels.id}-gw" - ecs_cluster_name = module.labels.id - ecs_push_service_name = aws_ecs_service.push.name - ecs_api_service_name = aws_ecs_service.api.name - rds_db_cluster_name = module.rds_cluster_aurora_postgres.cluster_identifier - lambda_token_fn_name = "${module.labels.id}-token" - lambda_cso_fn_name = "${module.labels.id}-cso" - lambda_stats_fn_name = "${module.labels.id}-stats" - api_lb_arn_suffix = aws_lb.api.arn_suffix - push_lb_arn_suffix = aws_lb.push.arn_suffix - api_log_group = "${module.labels.id}-api" - } -} - resource "aws_cloudwatch_dashboard" "monitoring_alarms_dashboard" { dashboard_name = module.labels.id - dashboard_body = data.template_file.dashboard.rendered -} \ No newline at end of file + + dashboard_body = templatefile(format("%s/templates/dashboard.json", path.module), + { + account_id = data.aws_caller_identity.current.account_id + region = var.aws_region + environment = var.environment + gateway_name = "${module.labels.id}-gw" + ecs_cluster_name = module.labels.id + ecs_push_service_name = aws_ecs_service.push.name + ecs_api_service_name = aws_ecs_service.api.name + rds_db_cluster_name = module.rds_cluster_aurora_postgres.cluster_identifier + lambda_token_fn_name = "${module.labels.id}-token" + lambda_cso_fn_name = "${module.labels.id}-cso" + lambda_stats_fn_name = "${module.labels.id}-stats" + api_lb_arn_suffix = aws_lb.api.arn_suffix + push_lb_arn_suffix = aws_lb.push.arn_suffix + api_log_group = "${module.labels.id}-api" + }) +} diff --git a/dns.tf b/dns.tf index 012a3dd..2f17b22 100644 --- a/dns.tf +++ b/dns.tf @@ -3,7 +3,7 @@ # ######################################### data "aws_route53_zone" "primary" { count = local.enable_dns_count - provider = aws.root + provider = aws.dns name = var.route53_zone private_zone = false } @@ -24,7 +24,7 @@ resource "aws_acm_certificate" "wildcard_cert" { resource "aws_route53_record" "wildcard_cert_validation" { count = local.enable_certificates_count - provider = aws.root + provider = aws.dns name = aws_acm_certificate.wildcard_cert[0].domain_validation_options.0.resource_record_name type = aws_acm_certificate.wildcard_cert[0].domain_validation_options.0.resource_record_type zone_id = data.aws_route53_zone.primary[0].id @@ -49,7 +49,7 @@ resource "aws_acm_certificate_validation" "wildcard_cert" { resource "aws_acm_certificate" "wildcard_cert_us" { count = local.enable_certificates_count - provider = aws.us + provider = aws.us_east_1 domain_name = var.wildcard_domain validation_method = "DNS" @@ -60,7 +60,7 @@ resource "aws_acm_certificate" "wildcard_cert_us" { resource "aws_route53_record" "wildcard_cert_validation_us" { count = local.enable_certificates_count - provider = aws.root + provider = aws.dns name = aws_acm_certificate.wildcard_cert_us[0].domain_validation_options.0.resource_record_name type = aws_acm_certificate.wildcard_cert_us[0].domain_validation_options.0.resource_record_type zone_id = data.aws_route53_zone.primary[0].id @@ -75,7 +75,7 @@ resource "aws_route53_record" "wildcard_cert_validation_us" { resource "aws_acm_certificate_validation" "wildcard_cert_us" { count = local.enable_certificates_count - provider = aws.us + provider = aws.us_east_1 certificate_arn = aws_acm_certificate.wildcard_cert_us[0].arn validation_record_fqdns = [aws_route53_record.wildcard_cert_validation_us[0].fqdn] @@ -89,7 +89,7 @@ resource "aws_acm_certificate_validation" "wildcard_cert_us" { # ######################################### resource "aws_route53_record" "api" { count = local.enable_dns_count - provider = aws.root + provider = aws.dns zone_id = data.aws_route53_zone.primary[0].id name = var.api_dns type = "A" @@ -110,7 +110,7 @@ resource "aws_route53_record" "api" { resource "aws_route53_record" "push" { count = local.enable_dns_count - provider = aws.root + provider = aws.dns zone_id = data.aws_route53_zone.primary[0].id name = var.push_dns type = "A" diff --git a/docs/bastion.md b/docs/bastion.md index 55c7bd2..a61a93f 100644 --- a/docs/bastion.md +++ b/docs/bastion.md @@ -10,7 +10,7 @@ There is a schedule to scale down the bastions at 21:01 UTC each day - so your s - You should then be able to see the instance on the AWS console and can select and hit Connect -> Session Manager -# Postgres client installation +# PostgreSQL client installation The postgresql11 package will be installed using cloud-init, but if you need to install can use ```sudo amazon-linux-extras install postgresql11``` @@ -19,5 +19,5 @@ The postgresql11 package will be installed using cloud-init, but if you need to - Anyone wishing to use the bastion will need to have an AWS account - Will need to connect via the AWS console - If we do an apply if a bastion instance is running it will be terminated as we will reset the desired count to 0 - - Can temp edit the bastion.tf file and set the desired_count to 1 to avoid this + - Can temp edit the bastion_asg_desired_count in the variables.tf file and set the desired_count to 1 to avoid this - Since we use a data source to get the latest AWS Linux 2 AMI this may sometimes appear as change in the plan diff --git a/docs/creating-a-new-project.md b/docs/creating-a-new-project.md index 4e2eb91..81aa0df 100644 --- a/docs/creating-a-new-project.md +++ b/docs/creating-a-new-project.md @@ -1,5 +1,5 @@ # Checklist -Using a project with key **xyz** and a **dev** environment. +Using a project with key **xyz** and a **dev** environment ## Create an AWS profile @@ -7,7 +7,7 @@ Add a xyz-dev profile to ~/.aws/credentials ## Create the infra CI user - admin privs by default -See [../scripts/create-infra-ci-user.sh] script +See [script](../scripts/create-infra-ci-user.sh) ``` # Set your AWS_PROFILE @@ -21,7 +21,7 @@ export AWS_PROFILE=xyz-dev ## Create the Terraform state backend -See [../scripts/create-tf-state-backend.sh] script +See [script](../scripts/create-tf-state-backend.sh) ``` # Set your AWS_PROFILE @@ -31,6 +31,7 @@ export AWS_PROFILE=xyz-dev ./scripts/create-tf-state-backend.sh eu-west-1 xyz-dev-terraform-store xyz-dev-terraform-lock ``` + ## Import TLS certificates In some cases we need to use certificates that are supplied - PENDING Import certicates script @@ -46,13 +47,53 @@ In some cases where we manage the DNS we may need to help with additional DNS re ## Create the AWS SecretsManager secrets We need to create the secrets outside of Terraform, see the [secrets/parameters](./secrets-parameters.md) doc. +Like so - may have to watch for funny chars needing escaping - ignored here +``` +generate-random() { + length=${1:-32} + + LC_ALL=C tr -dc 'A-Za-z0-9'\''()*+,-./:;<=>?@[\]^_{|}~' 0 ? 1 : 0 # This is required as we use the count as toggle: - cloudtrail_log_group_name = "${join(" ", aws_cloudwatch_log_group.cloudtrail.*.name)}" + cloudtrail_log_group_name = join(" ", aws_cloudwatch_log_group.cloudtrail.*.name) # Cloudtrail log stream related: cloudtrail_log_stream_arn_pattern = "arn:aws:logs:${var.aws_region}:${data.aws_caller_identity.current.account_id}:log-group:${local.cloudtrail_log_group_name}:log-stream:${data.aws_caller_identity.current.account_id}_CloudTrail_${var.aws_region}*" } diff --git a/main.tf b/main.tf index 7029f73..512b706 100644 --- a/main.tf +++ b/main.tf @@ -3,63 +3,51 @@ # ######################################### terraform { required_version = ">= 0.12.29" + + # Leaving this, even though we have moved towards using this repo as a module - will ignore in that case + # Also need to cater for git submodule/subtree usage for existing infrastructure backend "s3" {} + + # Providers + required_providers { + archive = "~> 1.3.0" + aws = "~> 2.70" + null = "~> 2.1" + random = "~> 2.0" + } } # ######################################### -# AWS provider +# AWS providers # ######################################### +# Main provider provider "aws" { - version = "2.68.0" region = var.aws_region profile = var.profile } +# Provider based on main but using us_east_1 as region +# Will use this if creating a TLS certificate in us-east-1 region as required by CloudFront Edge used by the APIGateway provider "aws" { - version = "2.68.0" - alias = "us" + alias = "us_east_1" region = "us-east-1" profile = var.profile } +# DNS provider +# Will use this if managing DNS, in some cases the Route53 zones are managed on a different account provider "aws" { - version = "2.68.0" - alias = "root" + alias = "dns" region = var.aws_region - profile = var.root_profile + profile = var.dns_profile } -provider "aws" { - version = "2.68.0" - alias = "root-us" - region = "us-east-1" - profile = var.root_profile -} - -# ######################################### -# Other providers -# ######################################### -provider "null" { - version = "~> 2.1" -} - -provider "template" { - version = "~> 2.1" -} - -provider "random" { - version = "~> 2.0" -} - -provider "archive" { - version = "~> 1.3.0" -} # ######################################### # Data # ######################################### -data "aws_caller_identity" "current" {} - data "aws_availability_zones" "available" { state = "available" } + +data "aws_caller_identity" "current" {} diff --git a/modules/lambda/main.tf b/modules/lambda/main.tf index a259c3d..cb158f5 100644 --- a/modules/lambda/main.tf +++ b/modules/lambda/main.tf @@ -60,7 +60,7 @@ variable "name" { } variable "runtime" { - default = "nodejs10.x" + default = "nodejs12.x" } variable "security_group_ids" { diff --git a/operators.tf b/operators.tf index 30c04d7..2fd5b47 100644 --- a/operators.tf +++ b/operators.tf @@ -14,7 +14,9 @@ data "aws_iam_policy_document" "operators" { aws_lambda_function.settings.arn, aws_lambda_function.token.arn ], - aws_lambda_function.cso.*.arn) + aws_lambda_function.cso.*.arn, + compact([module.daily_registrations_reporter.function_arn, module.download.function_arn, module.upload.function_arn]) + ) } # Explicitly deny anything on authorizer as it contains a secret diff --git a/outputs.tf b/outputs.tf index 61ef6a4..4446cd0 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,15 +1,14 @@ -output "api_aws_dns" { - value = join("", aws_api_gateway_domain_name.main.*.cloudfront_domain_name) -} - - output "admins_role_arn" { description = "ARN of the role used for admins" value = aws_iam_role.admins.arn } +output "api_aws_dns" { + value = join("", aws_api_gateway_domain_name.main.*.cloudfront_domain_name) +} + output "cloudtrail_log_group_name" { - value = "${join(" ", aws_cloudwatch_log_group.cloudtrail.*.name)}" + value = join(" ", aws_cloudwatch_log_group.cloudtrail.*.name) } output "ecs_cluster_api_service_name" { @@ -84,6 +83,14 @@ output "rds_cluster_identifier" { value = module.rds_cluster_aurora_postgres.cluster_identifier } +output "rds_endpoint" { + value = module.rds_cluster_aurora_postgres.endpoint +} + +output "rds_reader_endpoint" { + value = module.rds_cluster_aurora_postgres.reader_endpoint +} + output "secret" { value = aws_iam_access_key.ci_user.secret } @@ -92,10 +99,10 @@ output "vpc_id" { value = module.vpc.vpc_id } -output "waf_acl_name" { - value = aws_wafregional_web_acl.acl.name -} - output "waf_acl_metric_name" { value = aws_wafregional_web_acl.acl.metric_name } + +output "waf_acl_name" { + value = aws_wafregional_web_acl.acl.name +} diff --git a/parameters.tf b/parameters.tf index d043c9d..bd99d27 100644 --- a/parameters.tf +++ b/parameters.tf @@ -113,6 +113,14 @@ resource "aws_ssm_parameter" "enable_check_in" { tags = module.labels.tags } +resource "aws_ssm_parameter" "enable_legacy_settings" { + overwrite = true + name = "${local.config_var_prefix}enable_legacy_settings" + type = "String" + value = var.enable_legacy_settings + tags = module.labels.tags +} + resource "aws_ssm_parameter" "enable_metrics" { overwrite = true name = "${local.config_var_prefix}enable_metrics" @@ -121,6 +129,22 @@ resource "aws_ssm_parameter" "enable_metrics" { tags = module.labels.tags } +resource "aws_ssm_parameter" "certificate_audience" { + overwrite = true + name = "${local.config_var_prefix}certificate_audience" + type = "String" + value = "${module.labels.id}-api" + tags = module.labels.tags +} + +resource "aws_ssm_parameter" "jwt_issuer" { + overwrite = true + name = "${local.config_var_prefix}jwt_issuer" + type = "String" + value = "${module.labels.id}-api" + tags = module.labels.tags +} + resource "aws_ssm_parameter" "log_level" { overwrite = true name = "${local.config_var_prefix}log_level" @@ -193,14 +217,6 @@ resource "aws_ssm_parameter" "security_code_lifetime_mins" { tags = module.labels.tags } -resource "aws_ssm_parameter" "security_exposure_limit" { - overwrite = true - name = "${local.config_var_prefix}security_exposure_limit" - type = "String" - value = var.exposure_limit - tags = module.labels.tags -} - resource "aws_ssm_parameter" "security_refresh_token_expiry" { overwrite = true name = "${local.config_var_prefix}security_refresh_token_expiry" diff --git a/remove-me-later.tf b/remove-me-later.tf new file mode 100644 index 0000000..8965ce7 --- /dev/null +++ b/remove-me-later.tf @@ -0,0 +1,18 @@ +## REMOVE THESE AFTER APPLIES ON ALL ENVS +## We keep this to allow the transition to be made in the state file, if we renamed the plan would balk indicating a missing provider +provider "aws" { + alias = "us" + region = "us-east-1" + profile = var.profile +} + +provider "aws" { + alias = "root" + region = var.aws_region + profile = var.dns_profile +} + +## We keep this till we have applied all the way to prod and state files are updated +provider "template" { + version = "~> 2.1" +} diff --git a/scripts/aws-parameters.sh b/scripts/aws-parameters.sh index c766dc3..ac83fd0 100755 --- a/scripts/aws-parameters.sh +++ b/scripts/aws-parameters.sh @@ -20,7 +20,7 @@ list() { values() { prefix=${1:-} for name in $(list "${prefix}"); do - value=$(aws ssm get-parameter --name ${name} --with-decryption --output json | jq -r .Parameter.Value) + value=$(aws ssm get-parameter --name ${name} --with-decryption --output json | jq -r .Parameter.Value) echo -e "${green_text}${name}${reset_text}\n${value}\n" done } diff --git a/scripts/aws-secrets.sh b/scripts/aws-secrets.sh index 697050a..f3a252a 100755 --- a/scripts/aws-secrets.sh +++ b/scripts/aws-secrets.sh @@ -1,4 +1,5 @@ #!/bin/bash +# Assumes you have set your AWS_PROFILE or credentials - including the region # ./aws-secrets.sh list # ./aws-secrets.sh list dev-xyz- # @@ -33,7 +34,7 @@ list() { values() { prefix=${1:-} for name in $(list "${prefix}"); do - value=$(aws secretsmanager get-secret-value --output json --secret-id ${name} | jq -r .SecretString) + value=$(aws secretsmanager get-secret-value --secret-id ${name} --output json | jq -r .SecretString) echo -e "${green_text}${name}${reset_text}\n${value}\n" done } diff --git a/scripts/create-infra-ci-user.sh b/scripts/create-infra-ci-user.sh index 1643306..4caffc8 100755 --- a/scripts/create-infra-ci-user.sh +++ b/scripts/create-infra-ci-user.sh @@ -7,6 +7,7 @@ # # Usage is # ./create-infra-ci-user.sh dev-xyz +# ./create-infra-ci-user.sh dev-abc # ./create-infra-ci-user.sh prod-xyz # set -eou pipefail diff --git a/scripts/create-tf-state-backend.sh b/scripts/create-tf-state-backend.sh index 4d1fff2..933e642 100755 --- a/scripts/create-tf-state-backend.sh +++ b/scripts/create-tf-state-backend.sh @@ -18,34 +18,36 @@ set -eou pipefail aws_region=${1} s3_bucket_name=${2} dynamodb_table_name=${3} -location_constraint="LocationConstraint=${aws_region}" -if [ "$aws_region" = "us-east-1" ]; then - # us-east-1 is a special case per AWS. - location_constraint="" -fi # S3 bucket # Create bucket -aws s3api create-bucket --bucket ${s3_bucket_name} \ - --region ${aws_region} \ - --create-bucket-configuration $location_constraint +# us-east-1 is a special case where you cannot specify a location contraint, see https://github.com/boto/boto3/issues/125 +if [[ ${aws_region} == "us-east-1" ]]; then + aws s3api create-bucket --bucket ${s3_bucket_name} \ + --region ${aws_region} +else + aws s3api create-bucket --bucket ${s3_bucket_name} \ + --region ${aws_region} \ + --create-bucket-configuration LocationConstraint=${aws_region} +fi # Encrypt bucket aws s3api put-bucket-encryption \ - --bucket ${s3_bucket_name} \ - --server-side-encryption-configuration='{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}' + --bucket ${s3_bucket_name} \ + --server-side-encryption-configuration='{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}' # Apply versioning aws s3api put-bucket-versioning --bucket ${s3_bucket_name} --versioning-configuration Status=Enabled # Apply block public access config aws s3api put-public-access-block \ - --bucket ${s3_bucket_name} \ - --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true" \ + --bucket ${s3_bucket_name} \ + --public-access-block-configuration "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true" \ # DynamoDb table # Create table aws dynamodb create-table \ - --table-name ${dynamodb_table_name} \ - --attribute-definitions AttributeName=LockID,AttributeType=S \ - --key-schema AttributeName=LockID,KeyType=HASH \ - --billing-mode PAY_PER_REQUEST + --table-name ${dynamodb_table_name} \ + --attribute-definitions AttributeName=LockID,AttributeType=S \ + --key-schema AttributeName=LockID,KeyType=HASH \ + --billing-mode PAY_PER_REQUEST \ + --region ${aws_region} diff --git a/scripts/report-rds-cluster-key-attributes.sh b/scripts/report-rds-cluster-key-attributes.sh index 858f62d..7b60ec2 100755 --- a/scripts/report-rds-cluster-key-attributes.sh +++ b/scripts/report-rds-cluster-key-attributes.sh @@ -1,7 +1,7 @@ #!/bin/bash set -eou pipefail -cluster_identifier=${1:-xyz-dev-rds} +cluster_identifier=${1:-fight-together-dev-rds} cluster_attributes='DatabaseName Engine EngineVersion PreferredBackupWindow PreferredMaintenanceWindow' db_instance_attributes='DBInstanceClass PreferredBackupWindow AvailabilityZone PreferredMaintenanceWindow MonitoringInterval Endpoint.Address Endpoint.Port' diff --git a/secrets.tf b/secrets.tf index cc52002..d5aff99 100644 --- a/secrets.tf +++ b/secrets.tf @@ -5,7 +5,7 @@ data "aws_secretsmanager_secret_version" "api_gateway_header" { secret_id = "${local.config_var_prefix}header-x-secret" } -data "aws_secretsmanager_secret_version" "device-check" { +data "aws_secretsmanager_secret_version" "device_check" { secret_id = "${local.config_var_prefix}device-check" } @@ -25,6 +25,18 @@ data "aws_secretsmanager_secret_version" "rds" { secret_id = "${local.config_var_prefix}rds" } +data "aws_secretsmanager_secret_version" "rds_read_only" { + secret_id = "${local.config_var_prefix}rds-read-only" +} + +data "aws_secretsmanager_secret_version" "rds_read_write" { + secret_id = "${local.config_var_prefix}rds-read-write" +} + +data "aws_secretsmanager_secret_version" "rds_read_write_create" { + secret_id = "${local.config_var_prefix}rds-read-write-create" +} + # ######################################### # Optional secrets - These exist for some instances # ######################################### diff --git a/variables.tf b/variables.tf index b25785a..05c0c83 100644 --- a/variables.tf +++ b/variables.tf @@ -1,12 +1,12 @@ # ######################################### # Misc # ######################################### -variable "namespace" {} -variable "full_name" {} +variable "aws_region" {} +variable "dns_profile" {} variable "environment" {} +variable "full_name" {} +variable "namespace" {} variable "profile" {} -variable "root_profile" {} -variable "aws_region" {} # ######################################### @@ -115,6 +115,9 @@ variable "wildcard_domain" {} variable "bastion_enabled" { default = true } +variable "bastion_asg_desired_count" { + default = 0 +} # ######################################### # SMS using AWS - used by the SMS lambda @@ -144,6 +147,30 @@ variable "admins_role_require_mfa" { # ######################################### # API & Lambda - Settings & Env vars # ######################################### +variable "api_image_repo_url" { + description = "ECR image repo to be deployed into ECS for the API container, includes the tag" + default = "" +} +variable "api_image_tag" { + description = "ECR image tag to be deployed into ECS for the API container" + default = "latest" +} +variable "migrations_image_repo_url" { + description = "ECR image repo to be deployed into ECS for the Migration container, includes the tag" + default = "" +} +variable "migrations_image_tag" { + description = "ECR image tag to be deployed into ECS for the Migration container" + default = "latest" +} +variable "push_image_repo_url" { + description = "ECR image repo to be deployed into ECS for the Push API container, includes the tag" + default = "" +} +variable "push_image_tag" { + description = "ECR image tag to be deployed into ECS for the Push API container" + default = "latest" +} variable "api_listening_port" { default = 5000 } @@ -202,9 +229,6 @@ variable "log_level" {} variable "arcgis_url" { default = "" } -variable "exposure_limit" { - default = "10" -} variable "daily_registrations_reporter_email_subject" { default = "" } @@ -234,7 +258,7 @@ variable "upload_token_lifetime_mins" { default = "1440" } variable "metrics_config" { - default = "{ \"CONTACT_UPLOAD\": 60, \"CHECK_IN\": 60, \"FORGET\": 60, \"TOKEN_RENEWAL\": 60, \"CALLBACK_OPTIN\": 60, \"CALLBACK_REQUEST\": 60, \"DAILY_ACTIVE_TRACE\": 60, \"CONTACT_NOTIFICATION\": 60 }" + default = "{ \"CONTACT_UPLOAD\": 60, \"CHECK_IN\": 60, \"FORGET\": 60, \"TOKEN_RENEWAL\": 60, \"CALLBACK_OPTIN\": 60, \"DAILY_ACTIVE_TRACE\": 60, \"CONTACT_NOTIFICATION\": 60 }" } variable "verify_rate_limit_secs" {} variable "push_listening_port" { @@ -282,6 +306,9 @@ variable "enable_callback" { variable "enable_check_in" { default = "true" } +variable "enable_legacy_settings" { + default = "false" +} variable "enable_metrics" { default = "true" } @@ -291,9 +318,75 @@ variable "default_country_code" { variable "default_region" { default = "" } +variable "lambda_authorizer_memory_size" { + default = 512 # Since this is on the hot path and we get faster CPUs with higher memory +} +variable "lambda_authorizer_timeout" { + default = 15 +} +variable "lambda_callback_memory_size" { + default = 128 +} +variable "lambda_callback_timeout" { + default = 15 +} +variable "lambda_cso_memory_size" { + default = 3008 +} +variable "lambda_cso_timeout" { + default = 900 +} +variable "lambda_daily_registrations_reporter_memory_size" { + default = 128 +} +variable "lambda_daily_registrations_reporter_timeout" { + default = 15 +} +variable "lambda_download_memory_size" { + default = 128 +} +variable "lambda_download_timeout" { + default = 15 +} +variable "lambda_exposures_memory_size" { + default = 128 +} +variable "lambda_exposures_timeout" { + default = 15 +} +variable "lambda_settings_memory_size" { + default = 128 +} +variable "lambda_settings_timeout" { + default = 15 +} +variable "lambda_sms_memory_size" { + default = 128 +} variable "lambda_provisioned_concurrencies" { default = {} } +variable "lambda_sms_timeout" { + default = 15 +} +variable "lambda_stats_memory_size" { + default = 256 +} +variable "lambda_stats_timeout" { + default = 120 +} +variable "lambda_token_memory_size" { + default = 128 +} +variable "lambda_token_timeout" { + default = 15 +} +variable "lambda_upload_memory_size" { + default = 128 +} +variable "lambda_upload_timeout" { + default = 15 +} variable "native_regions" { default = "" }