-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
309 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
98 changes: 98 additions & 0 deletions
98
infrastructure/terraform/aws/modules/core-services/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
# Setup | ||
In a `main.tf` file for a workspace that needs a cluster, | ||
you can use this module like: | ||
``` | ||
module "cluster" { | ||
source = "../path/to/this/directory" | ||
// version is disallowed when using path-based modules | ||
environment = "staging" | ||
region = "us-east-2" | ||
hosted_zone_id = "SOME-ZONE-ID" | ||
// all other variables are optional | ||
} | ||
``` | ||
|
||
then | ||
``` | ||
terraform init | ||
terraform apply | ||
``` | ||
|
||
You will see these resources under `module.cluster.xyz`. | ||
|
||
## Managing the ECS Task Definition | ||
Working with ECS task definitions in Terraform | ||
is kind of awkward. | ||
|
||
This module creates an ECS task definition, | ||
so that it can set up the ECS service that uses that task definition, | ||
but expects that future task revisions will be created by a CI pipeline | ||
as new images are created and pushed. | ||
The `deploy_on_merge.yml` has an example of such a pipeline. | ||
In this pipeline, | ||
we get the ECS task definition from a file, | ||
and interpolate the image, | ||
to create a new revision. | ||
Terraform ignores changes | ||
made by the pipeline, | ||
due to the `lifecycle` setting | ||
on the ECS service resource. | ||
|
||
If you make changes to the task definition resource in this module, | ||
and run `terraform apply` in the `ecs-staging` directory, | ||
Terraform will update the task, | ||
but the next time a new commit is pushed to git, | ||
that change will be overwritten | ||
by the definition in the `ecs-staging` directory, | ||
that's used by the pipeline. | ||
|
||
Ideally these definitions would come from the same source | ||
so if you're reading this | ||
perhaps today is the day | ||
to make that refactor! | ||
|
||
More information about the general wonkiness | ||
of managing ECS with Terraform | ||
can be found in [this Terraform issue.](https://github.com/hashicorp/terraform-provider-aws/issues/632) | ||
|
||
## Rotating the RDS Password | ||
The RDS password is retrieved from AWS Secrets Manager | ||
but that password is managed manually, | ||
and rotating it requires downtime. | ||
|
||
To rotate it, you'll need to perform the following steps: | ||
- Update the value of the Secrets Manager entry through the AWS console | ||
- Update the value in the RDS instance through the AWS console. (At this point, the core container will stop being able to access the database.) | ||
- Recreate the core container's service with `aws update-service cluster $CLUSTER_NAME --service $SERVICE_NAME --force-new-deployment` | ||
|
||
In the future the RDS should probably [manage its own password](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-secrets-manager.html) | ||
which will probably require changing the service's code | ||
to fetch the password from Secrets Manager itself | ||
rather than getting it passed in from an environment variable. | ||
|
||
## Rotating the ACM Certificate | ||
|
||
ACM issues certs that last for 1 year. They send you an email prior to renewing, but will automatically rotate the cert. | ||
|
||
Since we rely on DNS validation, it is necessary that our validation CNAMEs are present in route53, provided by this module. | ||
The required records MAY not change, but if they have changed behind the scenes, this will catch up our terraform state: | ||
|
||
```bash | ||
# from clean state , no changes to code | ||
|
||
# updates our state file's records of the Domain Validation Options on the cert (DVOs). | ||
terraform apply | ||
|
||
# should show that new DNS records need to be created/updated, matching the DVOs. | ||
terraform plan -out TMP.tfplan | ||
terraform apply TMP.tfplan | ||
``` | ||
|
||
For more info: see [AWS Docs](https://docs.aws.amazon.com/acm/latest/userguide/dns-renewal-validation.html). | ||
|
||
## Development | ||
|
||
When you change the resources in this directory, you must run `terraform apply` in the calling workspace to see changes. | ||
|
||
More info on developing [terraform modules](https://developer.hashicorp.com/terraform/language/modules/develop). |
91 changes: 91 additions & 0 deletions
91
infrastructure/terraform/aws/modules/core-services/main.tf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# aws terraform provider config | ||
|
||
terraform { | ||
required_version = ">= 0.12.0" | ||
required_providers { | ||
aws = { | ||
source = "hashicorp/aws" | ||
version = ">= 4.0" | ||
} | ||
} | ||
} | ||
|
||
# Static secrets | ||
resource "random_password" "api_key" { | ||
length = 32 | ||
special = true | ||
override_special = "-_.~!#$&'()*+,/:;=?@[]" | ||
} | ||
|
||
resource "aws_secretsmanager_secret" "api_key" { | ||
name = "api-key-${var.cluster_info.name}-${var.cluster_info.environment}" | ||
} | ||
|
||
resource "aws_secretsmanager_secret_version" "api_key" { | ||
secret_id = aws_secretsmanager_secret.api_key.id | ||
secret_string = random_password.api_key.result | ||
} | ||
|
||
# generate password and make it accessible through aws secrets manager | ||
resource "random_password" "rds_db_password" { | ||
length = 16 | ||
special = true | ||
override_special = "!#$%&*()-_=+[]{}<>:?" | ||
} | ||
|
||
resource "aws_secretsmanager_secret" "rds_db_password" { | ||
name = "rds-db-password-${var.cluster_info.name}-${var.cluster_info.environment}" | ||
} | ||
|
||
resource "aws_secretsmanager_secret_version" "password" { | ||
secret_id = aws_secretsmanager_secret.rds_db_password.id | ||
secret_string = random_password.rds_db_password.result | ||
} | ||
|
||
# network config | ||
resource "aws_db_subnet_group" "ecs_dbs" { | ||
name = "${var.cluster_info.name}_ecs_db_${var.cluster_info.environment}" | ||
subnet_ids = var.cluster_info.private_subnet_ids | ||
|
||
tags = { | ||
Name = "subnet group for ECS RDS instances" | ||
} | ||
} | ||
|
||
resource "aws_security_group" "ecs_tasks_rds_instances" { | ||
name = "${var.cluster_info.name}-sg-rds-${var.cluster_info.environment}" | ||
vpc_id = var.cluster_info.vpc_id | ||
|
||
ingress { | ||
protocol = "tcp" | ||
from_port = 5432 | ||
to_port = 5432 | ||
security_groups = var.cluster_info.container_security_group_ids | ||
} | ||
} | ||
|
||
# the actual database instance | ||
resource "aws_db_instance" "core_postgres" { | ||
identifier = "${var.cluster_info.name}-core-postgres-${var.cluster_info.environment}" | ||
allocated_storage = 20 | ||
db_name = "${var.cluster_info.name}_${var.cluster_info.environment}_core_postgres" | ||
db_subnet_group_name = aws_db_subnet_group.ecs_dbs.name | ||
engine = "postgres" | ||
engine_version = "14" | ||
instance_class = "db.t3.small" | ||
vpc_security_group_ids = [aws_security_group.ecs_tasks_rds_instances.id] | ||
username = var.cluster_info.name | ||
password = random_password.rds_db_password.result | ||
parameter_group_name = "default.postgres14" | ||
skip_final_snapshot = true | ||
|
||
lifecycle { | ||
ignore_changes = [ | ||
password, | ||
] | ||
} | ||
} | ||
|
||
|
||
### TODO : add Sentry? other stuff? | ||
|
18 changes: 18 additions & 0 deletions
18
infrastructure/terraform/aws/modules/core-services/outputs.tf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
locals { | ||
db_user = aws_db_instance.core_postgres.username | ||
db_name = aws_db_instance.core_postgres.db_name | ||
db_host = aws_db_instance.core_postgres.address | ||
db_sslmode = "require" | ||
} | ||
|
||
output "api_key_secret_id" { | ||
value = aws_secretsmanager_secret.api_key.id | ||
} | ||
|
||
output "rds_db_password_id" { | ||
value = aws_secretsmanager_secret.rds_db_password.id | ||
} | ||
|
||
output "rds_connection_string_sans_password" { | ||
value = "postgresql://${local.db_user}@${local.db_host}:5432/${local.db_name}?sslmode=${local.db_sslmode}" | ||
} |
15 changes: 15 additions & 0 deletions
15
infrastructure/terraform/aws/modules/core-services/variables.tf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
variable "cluster_info" { | ||
description = "infrastructure values output from v7-cluster" | ||
|
||
type = object({ | ||
region = string | ||
name = string | ||
vpc_id = string | ||
cluster_arn = string | ||
environment = string | ||
private_subnet_ids = list(string) | ||
container_security_group_ids = list(string) | ||
cloudwatch_log_group_name = string | ||
lb_target_group_arn = string | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 8 additions & 17 deletions
25
infrastructure/terraform/aws/modules/v7-cluster/outputs.tf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.