Skip to content

Commit 0069ded

Browse files
authored
feat: Add a database for apps (MSSQL only). (#3)
1 parent d622af6 commit 0069ded

10 files changed

Lines changed: 202 additions & 43 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
locals {
22
apps = {
3-
sebt : yamldecode(file("${path.module}/apps/dc-sebt-portal.yaml"))
3+
dc-sebt-portal : yamldecode(file("${path.module}/apps/dc-sebt-portal.yaml"))
44
}
55
}

tofu/config/development/infra/main.tf

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,22 @@ module "vpc" {
4949
}
5050

5151
module "app" {
52-
source = "../../../modules/app"
52+
source = "../../../modules/app"
53+
for_each = local.apps
5354

54-
project = local.apps["sebt"].name
55-
environment = "development"
56-
program = local.apps["sebt"].program
57-
services = local.apps["sebt"].services
58-
domain = try(
59-
local.apps["sebt"].domain,
60-
try(local.apps.internal, true) ? module.hosted_zones.route53_zone_name.internal : module.hosted_zones.route53_zone_name.external
55+
project = each.value.name
56+
environment = "development"
57+
program = each.value.program
58+
services = each.value.services
59+
database_engine = each.value.database.type
60+
database_version = try(each.value.database.version, null)
61+
domain = try(
62+
each.value.domain,
63+
try(each.value.internal, true) ? module.hosted_zones.route53_zone_name.internal : module.hosted_zones.route53_zone_name.external
6164
)
6265

6366
logging_key_arn = module.logging.kms_key_arn
64-
vpc_id = module.vpc.vpc_id
67+
vpc_id = module.vpc.vpc_id
6568
private_subnets = module.vpc.private_subnets
6669
public_subnets = module.vpc.public_subnets
6770
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
output "apps" {
22
description = "Deployed applications."
3-
value = module.app.services
3+
value = module.app
44
}

tofu/modules/app/data.tf

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
data "aws_caller_identity" "identity" {}
2+
3+
data "aws_partition" "current" {}
4+
5+
data "aws_region" "current" {}
6+
7+
data "aws_rds_engine_version" "this" {
8+
engine = local.database_engine
9+
version = var.database_version
10+
latest = true
11+
}

tofu/modules/app/database.tf

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
resource "aws_kms_key" "database" {
2+
description = "Database encryption key for ${var.project} ${var.environment}"
3+
deletion_window_in_days = local.production ? 30 : 7
4+
enable_key_rotation = true
5+
policy = jsonencode(yamldecode(templatefile("${path.module}/templates/key-policy.yaml.tftpl", {
6+
account_id : data.aws_caller_identity.identity.account_id,
7+
partition : data.aws_partition.current.partition,
8+
region : data.aws_region.current.name,
9+
})))
10+
11+
tags = local.tags
12+
}
13+
14+
resource "aws_kms_alias" "database" {
15+
name = "alias/${var.project}/${var.environment}/database"
16+
target_key_id = aws_kms_key.database.id
17+
}

tofu/modules/app/local.tf

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
11
locals {
2-
project_short = var.project_short != null ? var.project_short : var.project
2+
database_engine = var.database_engine == "mssql" ? "sqlserver-web" : var.database_engine
3+
prefix = "${var.project}-${var.environment}"
4+
production = var.environment == "production"
5+
project_short = var.project_short != null ? var.project_short : var.project
6+
tags = {
7+
application = "${var.project}-${var.environment}"
8+
program = var.program
9+
project = var.project
10+
environment = var.environment
11+
}
312
}

tofu/modules/app/main.tf

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1+
module "secrets" {
2+
source = "github.com/codeforamerica/tofu-modules-aws-secrets?ref=1.0.0"
3+
4+
project = var.project
5+
environment = var.environment
6+
}
7+
18
module "service" {
2-
source = "github.com/codeforamerica/tofu-modules-aws-fargate-service?ref=1.2.1"
9+
source = "github.com/codeforamerica/tofu-modules-aws-fargate-service?ref=security-group-outputs"
310
for_each = var.services
411

512
project = var.project
@@ -12,15 +19,78 @@ module "service" {
1219
domain = var.domain
1320
subdomain = try(each.value.subdomain, "www")
1421

15-
vpc_id = var.vpc_id
16-
private_subnets = var.private_subnets
17-
public_subnets = var.public_subnets
18-
logging_key_id = var.logging_key_arn
19-
container_port = try(each.value.expose, 3000)
22+
vpc_id = var.vpc_id
23+
private_subnets = var.private_subnets
24+
public_subnets = var.public_subnets
25+
logging_key_id = var.logging_key_arn
26+
container_port = try(each.value.expose, 3000)
2027
create_version_parameter = true
2128

22-
tags = {
23-
application = "${var.project}-${var.environment}"
24-
program = var.program
29+
environment_variables = {
30+
DATABASE_HOST = module.mssql.db_instance_endpoint
31+
}
32+
33+
environment_secrets = {
34+
DATABASE_USERNAME = "${module.mssql.db_instance_master_user_secret_arn}:usernmae"
35+
DATABASE_PASSWORD = "${module.mssql.db_instance_master_user_secret_arn}:password"
2536
}
37+
38+
tags = local.tags
39+
}
40+
41+
module "mssql" {
42+
source = "terraform-aws-modules/rds/aws"
43+
version = ">= 6.12"
44+
45+
identifier = local.prefix
46+
instance_use_identifier_prefix = true
47+
engine = local.database_engine
48+
engine_version = data.aws_rds_engine_version.this.version
49+
auto_minor_version_upgrade = true
50+
apply_immediately = !local.production
51+
subnet_ids = var.private_subnets
52+
create_db_subnet_group = true
53+
create_db_option_group = false
54+
family = data.aws_rds_engine_version.this.parameter_group_family
55+
instance_class = "db.t3.small"
56+
allocated_storage = 20
57+
max_allocated_storage = 100
58+
username = "root"
59+
storage_type = "gp3"
60+
kms_key_id = aws_kms_key.database.arn
61+
master_user_secret_kms_key_id = module.secrets.kms_key_arn
62+
performance_insights_kms_key_id = var.logging_key_arn
63+
cloudwatch_log_group_kms_key_id = var.logging_key_arn
64+
cloudwatch_log_group_retention_in_days = local.production ? 31 : 7
65+
create_cloudwatch_log_group = true
66+
create_monitoring_role = true
67+
enabled_cloudwatch_logs_exports = data.aws_rds_engine_version.this.exportable_log_types
68+
vpc_security_group_ids = [module.database_security_group.security_group_id]
69+
70+
allow_major_version_upgrade = !local.production
71+
72+
tags = local.tags
73+
}
74+
75+
# Create an empty security group for the database. To avoid a circular
76+
# dependency between the database and the services, we create the security group
77+
# here and then add the ingress rules in a separate resource.
78+
module "database_security_group" {
79+
source = "terraform-aws-modules/security-group/aws"
80+
version = "~> 5.3"
81+
82+
name = "${local.prefix}-database"
83+
vpc_id = var.vpc_id
84+
85+
tags = local.tags
86+
}
87+
88+
resource "aws_vpc_security_group_ingress_rule" "database" {
89+
for_each = module.service
90+
security_group_id = module.database_security_group.security_group_id
91+
92+
ip_protocol = "tcp"
93+
from_port = module.mssql.db_instance_port
94+
to_port = module.mssql.db_instance_port
95+
referenced_security_group_id = each.value.security_group_id
2696
}

tofu/modules/app/outputs.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
output "services" {
22
description = "Deployed services for the application."
3-
value = module.service
3+
value = module.service
44
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
Version: '2012-10-17'
2+
Statement:
3+
- Sid: Enable IAM User Permissions
4+
Effect: Allow
5+
Principal:
6+
AWS: arn:${partition}:iam::${account_id}:root
7+
Action: kms:*
8+
Resource: "*"
9+
- Sid: Allow access through RDS for all principals in the account that are authorized
10+
to use RDS
11+
Effect: Allow
12+
Principal:
13+
AWS: "*"
14+
Action:
15+
- kms:Encrypt
16+
- kms:Decrypt
17+
- kms:ReEncrypt*
18+
- kms:GenerateDataKey*
19+
- kms:CreateGrant
20+
- kms:ListGrants
21+
- kms:DescribeKey
22+
Resource: "*"
23+
Condition:
24+
StringEquals:
25+
kms:ViaService: rds.${region}.amazonaws.com
26+
kms:CallerAccount: '${account_id}'
27+
ForAnyValue:StringEquals:
28+
kms:EncryptionContextKeys: aws:rds:db-id
29+
- Sid: Allow direct access to key metadata to the account
30+
Effect: Allow
31+
Principal:
32+
AWS: arn:${partition}:iam::${account_id}:root
33+
Action:
34+
- kms:Describe*
35+
- kms:Get*
36+
- kms:List*
37+
- kms:RevokeGrant
38+
Resource: "*"

tofu/modules/app/variables.tf

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,62 @@
1+
variable "database_engine" {
2+
description = "The database engine to use for the application."
3+
type = string
4+
}
5+
6+
variable "database_version" {
7+
description = "The version of the database engine to use."
8+
type = string
9+
default = null
10+
}
11+
112
variable "domain" {
2-
description = "The domain for the application."
3-
type = string
13+
description = "The domain for the application."
14+
type = string
415
}
516

617
variable "environment" {
7-
description = "The environment for the application."
8-
type = string
18+
description = "The environment for the application."
19+
type = string
920
}
1021

1122
variable "logging_key_arn" {
12-
description = "The ARN of the KMS key used for logging."
13-
type = string
23+
description = "The ARN of the KMS key used for logging."
24+
type = string
1425
}
1526

1627
variable "private_subnets" {
17-
description = "List of private subnets for the application."
18-
type = list(string)
28+
description = "List of private subnets for the application."
29+
type = list(string)
1930
}
2031

2132
variable "program" {
22-
description = "The program the application is associated with."
23-
type = string
33+
description = "The program the application is associated with."
34+
type = string
2435
}
2536

2637
variable "project" {
27-
description = "The name of the project."
28-
type = string
38+
description = "The name of the project."
39+
type = string
2940
}
3041

3142
variable "project_short" {
32-
description = "Short name for the project, used in resource names."
33-
type = string
43+
description = "Short name for the project, used in resource names."
44+
type = string
3445
default = null
3546
}
3647

3748
variable "public_subnets" {
38-
description = "List of public subnets for the application."
39-
type = list(string)
49+
description = "List of public subnets for the application."
50+
type = list(string)
4051
}
4152

4253
variable "services" {
43-
description = "Services to deploy for the application."
44-
type = map(any)
45-
default = {}
54+
description = "Services to deploy for the application."
55+
type = map(any)
56+
default = {}
4657
}
4758

4859
variable "vpc_id" {
49-
description = "The VPC ID where the application will be deployed."
50-
type = string
60+
description = "The VPC ID where the application will be deployed."
61+
type = string
5162
}

0 commit comments

Comments
 (0)