Skip to content

Commit

Permalink
CCAP-295: Added serverless database and secrets modules. (#11)
Browse files Browse the repository at this point in the history
* Allow predefined secrets to be set for fargate containers
* Expose CIDR blocks for subnets
* Updated documentation.
* Updated trivy configuration.
  • Loading branch information
jamesiarmes authored Aug 7, 2024
0 parents commit 9aa4dee
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 0 deletions.
74 changes: 74 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# AWS Serverless Database Module

This module launches an [Aurora Serverless v2][aurora-serverless] database
cluster. Aurora serverless clusters measure capacity in [ACUs] (Aurora Capacity
Units); each unit is approximately 2 GB of memory with corresponding CPU and
networking.

## Usage

Add this module to your `main.tf` (or appropriate) file and configure the inputs
to match your desired configuration. For example:

```hcl
module "database" {
source = "github.com/codeforamerica/tofu-modules/aws/serverless_database"
logging_key_arn = module.logging.kms_key_arn
secrets_key_arn = module.secrets.kms_key_arn
vpc_id = module.vpc.vpc_id
subnets = module.vpc.private_subnets
ingress_cidrs = module.vpc.private_subnets_cidr_blocks
min_capacity = 2
max_capacity = 32
project = "my-project"
environment = "dev"
service = "web"
}
```

Make sure you re-run `tofu init` after adding the module to your configuration.

```bash
tofu init
tofu plan
```

To update the source for this module, pass `-upgrade` to `tofu init`:

```bash
tofu init -upgrade
```

## Inputs

| Name | Description | Type | Default | Required |
|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------|----------|---------|----------|
| logging_key_arn | ARN of the KMS key for logging. | `string` | n/a | yes |
| ingress_cidrs | List of CIDR blocks to allow ingress. This is typically your private subnets. | `list` | n/a | yes |
| project | Name of the project. | `string` | n/a | yes |
| secrets_key_arn | ARN of the KMS key for secrets. This will be used to encrypt database credentials. | `string` | n/a | yes |
| subnets | List of subnet ids the database instances may be placed in. | `list` | n/a | yes |
| vpc_id | Id of the VPC to launch the database cluster into. | `string` | n/a | yes |
| apply_immediately | Whether to apply changes immediately rather than during the next maintenance window. WARNING: This may result in a restart of the cluster! | `bool` | `false` | no |
| environment | Environment for the project. | `string` | `"dev"` | no |
| force_delete | Force deletion of resources. If changing to true, be sure to apply before destroying. | `bool` | `false` | no |
| key_recovery_period | Recovery period for deleted KMS keys in days. Must be between 7 and 30. | `number` | `30` | no |
| min_capacity | Minimum capacity for the serverless cluster in ACUs. | `number` | `2` | no |
| max_capacity | Maximum capacity for the serverless cluster in ACUs. | `number` | `10` | no |
| service | Optional service that these resources are supporting. Example: 'api', 'web', 'worker' | `string` | `""` | no |
| skip_final_snapshot | Whether to skip the final snapshot when destroying the database cluster. | `bool` | `false` | no |
| snapshot_identifier | Optional name or ARN of the snapshot to restore the cluster from. Only applicable on create. | `bool` | `false` | no |


## Outputs

| Name | Description | Type |
|------------------|--------------------------------------------------|----------|
| cluster_endpoint | DNS endpoint to connect to the database cluster. | `string` |
| secret_arn | ARN of the secret holding database credentials. | `string` |

[acus]: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.how-it-works.html#aurora-serverless-v2.how-it-works.capacity
[aurora-serverless]: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/aurora-serverless-v2.html
5 changes: 5 additions & 0 deletions data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
data "aws_caller_identity" "identity" {}

data "aws_partition" "current" {}

data "aws_region" "current" {}
15 changes: 15 additions & 0 deletions kms.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
resource "aws_kms_key" "database" {
description = "Database encryption key for ${var.project} ${var.environment}"
deletion_window_in_days = var.key_recovery_period
enable_key_rotation = true
policy = jsonencode(yamldecode(templatefile("${path.module}/templates/key-policy.yaml.tftpl", {
account_id : data.aws_caller_identity.identity.account_id,
partition : data.aws_partition.current.partition,
region : data.aws_region.current.name,
})))
}

resource "aws_kms_alias" "database" {
name = "alias/${var.project}/${var.environment}/${var.service != "" ? "${var.service}/" : ""}database"
target_key_id = aws_kms_key.database.id
}
3 changes: 3 additions & 0 deletions local.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
locals {
prefix = "${var.project}-${var.environment}${var.service != "" ? "-${var.service}" : ""}"
}
54 changes: 54 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
module "database" {
source = "terraform-aws-modules/rds-aurora/aws"
version = "~> 9.8"

name = local.prefix
create_db_subnet_group = true
db_subnet_group_name = local.prefix
engine = "aurora-postgresql"
engine_mode = "provisioned"
storage_encrypted = true
kms_key_id = aws_kms_key.database.arn
master_username = "root"
subnets = var.subnets
copy_tags_to_snapshot = true
snapshot_identifier = var.snapshot_identifier
deletion_protection = !var.force_delete

vpc_id = var.vpc_id
security_group_rules = {
ingress = {
cidr_blocks = var.ingress_cidrs
}
}

manage_master_user_password = true
manage_master_user_password_rotation = true
master_user_password_rotation_automatically_after_days = 30

cloudwatch_log_group_kms_key_id = var.logging_key_arn
cloudwatch_log_group_retention_in_days = 7
performance_insights_kms_key_id = var.logging_key_arn
performance_insights_enabled = true
performance_insights_retention_period = 7

# TODO: Create a database KMS key
master_user_secret_kms_key_id = var.secrets_key_arn

monitoring_interval = 60

apply_immediately = var.apply_immediately
skip_final_snapshot = var.skip_final_snapshot

serverlessv2_scaling_configuration = {
min_capacity = var.min_capacity
max_capacity = var.max_capacity
}

instance_class = "db.serverless"
# TODO: Make the number of instances configurable.
instances = {
one = {}
two = {}
}
}
7 changes: 7 additions & 0 deletions output.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "cluster_endpoint" {
value = module.database.cluster_endpoint
}

output "secret_arn" {
value = module.database.cluster_master_user_secret[0].secret_arn
}
38 changes: 38 additions & 0 deletions templates/key-policy.yaml.tftpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Version: '2012-10-17'
Statement:
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: arn:${partition}:iam::${account_id}:root
Action: kms:*
Resource: "*"
- Sid: Allow access through RDS for all principals in the account that are authorized
to use RDS
Effect: Allow
Principal:
AWS: "*"
Action:
- kms:Encrypt
- kms:Decrypt
- kms:ReEncrypt*
- kms:GenerateDataKey*
- kms:CreateGrant
- kms:ListGrants
- kms:DescribeKey
Resource: "*"
Condition:
StringEquals:
kms:ViaService: rds.${region}.amazonaws.com
kms:CallerAccount: '${account_id}'
ForAnyValue:StringEquals:
kms:EncryptionContextKeys: aws:rds:db-id
- Sid: Allow direct access to key metadata to the account
Effect: Allow
Principal:
AWS: arn:${partition}:iam::${account_id}:root
Action:
- kms:Describe*
- kms:Get*
- kms:List*
- kms:RevokeGrant
Resource: "*"
88 changes: 88 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
variable "apply_immediately" {
type = bool
description = "Whether to apply changes immediately rather than during the next maintenance window."
default = false
}

variable "environment" {
type = string
description = "Environment for the deployment."
default = "dev"
}

variable "force_delete" {
type = bool
description = "Force deletion of resources. If changing to true, be sure to apply before destroying."
default = false
}

variable "logging_key_arn" {
type = string
description = "ARN of the KMS key for logging."
}

variable "ingress_cidrs" {
type = list(string)
description = "List of CIDR blocks to allow ingress. This is typically your private subnets."
}

variable "key_recovery_period" {
type = number
default = 30
description = "Recovery period for deleted KMS keys in days. Must be between 7 and 30."

validation {
condition = var.key_recovery_period > 6 && var.key_recovery_period < 31
error_message = "Recovery period must be between 7 and 30."
}
}

variable "min_capacity" {
type = number
description = "Minimum capacity for the serverless cluster in ACUs."
default = 2
}

variable "max_capacity" {
type = number
description = "Maximum capacity for the serverless cluster in ACUs."
default = 10
}

variable "project" {
type = string
description = "Project that these resources are supporting."
}

variable "secrets_key_arn" {
type = string
description = "ARN of the KMS key for secrets. This will be used to encrypt database credentials."
}

variable "service" {
type = string
description = "Optional service that these resources are supporting. Example: 'api', 'web', 'worker'"
default = ""
}

variable "skip_final_snapshot" {
type = bool
description = "Whether to skip the final snapshot when destroying the database cluster."
default = false
}

variable "snapshot_identifier" {
type = string
description = "Optional name or ARN of the snapshot to restore the cluster from. Only applicable on create."
default = ""
}

variable "subnets" {
type = list(string)
description = "List of subnet ids the database instances may be placed in"
}

variable "vpc_id" {
type = string
description = "Id of the VPC to launch the database cluster into."
}
10 changes: 10 additions & 0 deletions versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
terraform {
required_version = ">= 1.6"

required_providers {
aws = {
version = ">= 5.44"
source = "hashicorp/aws"
}
}
}

0 comments on commit 9aa4dee

Please sign in to comment.