Skip to content

Commit

Permalink
RDS backups to S3
Browse files Browse the repository at this point in the history
* Conditonally create a scheduled Fargate task to backup RDS databases
  to S3
  • Loading branch information
Stretch96 committed Jul 29, 2024
1 parent 82977fa commit 7d8993d
Show file tree
Hide file tree
Showing 12 changed files with 516 additions and 2 deletions.
35 changes: 35 additions & 0 deletions README.md

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions kms-infrastructure.tf
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ resource "aws_kms_key" "infrastructure" {
for k, v in local.infrastructure_ecs_cluster_services : aws_iam_role.infrastructure_ecs_cluster_service_task_execution[k].arn if v["enable_execute_command"] == true && local.infrastructure_ecs_cluster_enable_execute_command_logging
])
}
)}${length(local.infrastructure_rds) > 0 && local.infrastructure_kms_encryption && local.enable_infrastructure_rds_backup_to_s3 ? "," : ""}
${templatefile("${path.root}/policies/kms-key-policy-statements/role-allow-encrypt.json.tpl",
{
role_arns = jsonencode([
for k, v in local.infrastructure_rds : aws_iam_role.infrastructure_rds_s3_backups_task[k].arn if local.enable_infrastructure_rds_backup_to_s3
])
}
)}${length(local.infrastructure_rds) > 0 && local.infrastructure_kms_encryption && local.enable_infrastructure_rds_backup_to_s3 ? "," : ""}
${templatefile("${path.root}/policies/kms-key-policy-statements/cloudwatch-logs-allow.json.tpl",
{
log_group_arn = length(local.infrastructure_rds) > 0 && local.infrastructure_kms_encryption && local.enable_infrastructure_rds_backup_to_s3 ? "arn:aws:logs:${local.aws_region}:${local.aws_account_id}:log-group:${local.resource_prefix}-infrastructure-rds-s3-backups-*" : ""
}
)}${contains([for k, v in local.custom_s3_buckets : (v["cloudfront_dedicated_distribution"] == true || v["cloudfront_infrastructure_ecs_cluster_service"] != null) && v["create_dedicated_kms_key"] == false ? true : false], true) && local.infrastructure_kms_encryption ? "," : ""}
${templatefile("${path.root}/policies/kms-key-policy-statements/cloudfront-distribution-allow.json.tpl",
{
Expand Down
17 changes: 15 additions & 2 deletions locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ locals {
length(local.custom_s3_buckets) != 0 ||
local.enable_cloudformatian_s3_template_store ||
local.enable_infrastructure_vpc_transfer_s3_bucket ||
local.infrastructure_ecs_cluster_enable_execute_command_logging
local.infrastructure_ecs_cluster_enable_execute_command_logging ||
local.enable_infrastructure_rds_backup_to_s3
)
logs_bucket_s3_source_arns = concat(
length(local.infrastructure_ecs_cluster_services) != 0 ? [aws_s3_bucket.infrastructure_ecs_cluster_service_build_pipeline_artifact_store[0].arn] : [],
local.enable_infrastructure_vpc_transfer_s3_bucket ? [aws_s3_bucket.infrastructure_vpc_transfer[0].arn] : [],
[for k, v in local.custom_s3_buckets : aws_s3_bucket.custom[k].arn]
[for k, v in local.custom_s3_buckets : aws_s3_bucket.custom[k].arn],
local.enable_infrastructure_rds_backup_to_s3 ? [aws_s3_bucket.infrastructure_rds_s3_backups[0].arn] : [],
)
logs_bucket_logs_source_arns = concat(
local.infrastructure_vpc_flow_logs_s3_with_athena ? ["arn:aws:logs:${local.aws_region}:${local.aws_account_id}:*"] : []
Expand Down Expand Up @@ -232,6 +234,17 @@ locals {
"mysql" = 3306
"postgres" = 5432
}
rds_s3_backups_container_entrypoint = {
"mysql" = ""
"postgres" = ""
}
enable_infrastructure_rds_backup_to_s3 = var.enable_infrastructure_rds_backup_to_s3
infrastructure_rds_backup_to_s3_cron_expression = var.infrastructure_rds_backup_to_s3_cron_expression
infrastructure_rds_backup_to_s3_retention = var.infrastructure_rds_backup_to_s3_retention
enable_infrastructure_rds_tooling_ecs_cluster = anytrue([
local.enable_infrastructure_rds_backup_to_s3,
])
infrastructure_rds_tooling_ecs_cluster_name = "${local.resource_prefix}-infrastructure-rds-tooling"

infrastructure_elasticache_defaults = var.infrastructure_elasticache_defaults
infrastructure_elasticache_keys = length(var.infrastructure_elasticache) > 0 ? keys(values(var.infrastructure_elasticache)[0]) : []
Expand Down
12 changes: 12 additions & 0 deletions policies/secrets-manager-get-secret-value.json.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": ${secret_name_arns}
}
]
}
9 changes: 9 additions & 0 deletions rds-infrastructure-cloudwatch-logs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,12 @@ resource "aws_cloudwatch_log_group" "infrastructure_rds_exports" {
retention_in_days = 30
kms_key_id = local.infrastructure_kms_encryption ? aws_kms_key.infrastructure[0].id : null
}

resource "aws_cloudwatch_log_group" "infrastructure_rds_s3_backups" {
for_each = local.enable_infrastructure_rds_backup_to_s3 ? local.infrastructure_rds : {}

name = "${local.resource_prefix}-infrastructure-rds-s3-backups-${each.key}"
retention_in_days = local.infrastructure_rds_backup_to_s3_retention
kms_key_id = local.infrastructure_kms_encryption ? aws_kms_key.infrastructure[0].arn : null
skip_destroy = true
}
17 changes: 17 additions & 0 deletions rds-infrastructure-s3-backups-ecr.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
resource "aws_ecr_repository" "infrastructure_rds_s3_backups" {
count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0

name = "${local.resource_prefix}-rds-s3-backups"

#tfsec:ignore:aws-ecr-enforce-immutable-repository
image_tag_mutability = "MUTABLE"

encryption_configuration {
encryption_type = local.infrastructure_kms_encryption ? "KMS" : "AES256"
kms_key = local.infrastructure_kms_encryption ? aws_kms_key.infrastructure[0].arn : null
}

image_scanning_configuration {
scan_on_push = true
}
}
87 changes: 87 additions & 0 deletions rds-infrastructure-s3-backups-s3.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
resource "aws_s3_bucket" "infrastructure_rds_s3_backups" {
count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0

bucket = "${local.resource_prefix_hash}-infrastructure-rds-s3-backups"
}

resource "aws_s3_bucket_policy" "infrastructure_rds_s3_backups" {
count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0

bucket = aws_s3_bucket.infrastructure_rds_s3_backups[0].id
policy = templatefile(
"${path.module}/policies/s3-bucket-policy.json.tpl",
{
statement = <<EOT
[
${templatefile("${path.root}/policies/s3-bucket-policy-statements/enforce-tls.json.tpl",
{
bucket_arn = aws_s3_bucket.infrastructure_rds_s3_backups[0].arn
}
)}
]
EOT
}
)
}

resource "aws_s3_bucket_public_access_block" "infrastructure_rds_s3_backups" {
count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0

bucket = aws_s3_bucket.infrastructure_rds_s3_backups[0].id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

resource "aws_s3_bucket_versioning" "infrastructure_rds_s3_backups" {
count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0

bucket = aws_s3_bucket.infrastructure_rds_s3_backups[0].id

versioning_configuration {
status = "Enabled"
}
}

resource "aws_s3_bucket_logging" "infrastructure_rds_s3_backups" {
count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0

bucket = aws_s3_bucket.infrastructure_rds_s3_backups[0].id

target_bucket = aws_s3_bucket.infrastructure_logs[0].id
target_prefix = "s3/infrastructure-rds-s3-backups"
}

resource "aws_s3_bucket_server_side_encryption_configuration" "infrastructure_rds_s3_backups" {
count = local.enable_infrastructure_rds_backup_to_s3 ? 1 : 0

bucket = aws_s3_bucket.infrastructure_rds_s3_backups[0].id

rule {
apply_server_side_encryption_by_default {
kms_master_key_id = local.infrastructure_kms_encryption ? aws_kms_key.infrastructure[0].arn : null
sse_algorithm = local.infrastructure_kms_encryption ? "aws:kms" : "AES256"
}
}
}

resource "aws_s3_bucket_lifecycle_configuration" "infrastructure_rds_s3_backups" {
count = local.enable_infrastructure_logs_bucket && local.infrastructure_rds_backup_to_s3_retention != 0 ? 1 : 0

bucket = aws_s3_bucket.infrastructure_rds_s3_backups[0].id

rule {
id = "all_expire"

filter {
prefix = ""
}

expiration {
days = local.infrastructure_rds_backup_to_s3_retention
}

status = "Enabled"
}
}
77 changes: 77 additions & 0 deletions rds-infrastructure-s3-backups-security-group.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
resource "aws_security_group" "infrastructure_rds_s3_backups_scheduled_task" {
for_each = local.enable_infrastructure_rds_backup_to_s3 ? local.infrastructure_rds : {}

name = "${local.resource_prefix}-infrastructure-rds-s3-backups-scheduled-task"
description = "Infrastructure RDS S3 backups scheduled task"
vpc_id = aws_vpc.infrastructure[0].id
}

resource "aws_security_group_rule" "infrastructure_rds_s3_backups_scheduled_task_egress_https_tcp" {
for_each = local.enable_infrastructure_rds_backup_to_s3 ? local.infrastructure_rds : {}

description = "Allow HTTPS tcp outbound"
type = "egress"
from_port = 443
to_port = 443
protocol = "tcp"
# tfsec:ignore:aws-ec2-no-public-egress-sgr
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.infrastructure_rds_s3_backups_scheduled_task[each.key].id
}

resource "aws_security_group_rule" "infrastructure_rds_s3_backups_scheduled_task_egress_https_udp" {
for_each = local.enable_infrastructure_rds_backup_to_s3 ? local.infrastructure_rds : {}

description = "Allow HTTPS udp outbound"
type = "egress"
from_port = 443
to_port = 443
protocol = "udp"
# tfsec:ignore:aws-ec2-no-public-egress-sgr
cidr_blocks = ["0.0.0.0/0"]
security_group_id = aws_security_group.infrastructure_rds_s3_backups_scheduled_task[0].id
}

resource "aws_security_group_rule" "infrastructure_rds_s3_backups_scheduled_task_egress_dns_tcp" {
for_each = local.enable_infrastructure_rds_backup_to_s3 ? local.infrastructure_rds : {}

description = "Allow DNS tcp outbound to AWS"
type = "egress"
from_port = 53
to_port = 53
protocol = "tcp"
cidr_blocks = local.infrastructure_ecs_cluster_publicly_avaialble ? [
for subnet in aws_subnet.infrastructure_public : subnet.cidr_block
] : [
for subnet in aws_subnet.infrastructure_private : subnet.cidr_block
]
security_group_id = aws_security_group.infrastructure_rds_s3_backups_scheduled_task[0].id
}

resource "aws_security_group_rule" "infrastructure_rds_s3_backups_scheduled_task_egress_dns_udp" {
for_each = local.enable_infrastructure_rds_backup_to_s3 ? local.infrastructure_rds : {}

description = "Allow DNS udp outbound to AWS"
type = "egress"
from_port = 53
to_port = 53
protocol = "udp"
cidr_blocks = local.infrastructure_ecs_cluster_publicly_avaialble ? [
for subnet in aws_subnet.infrastructure_public : subnet.cidr_block
] : [
for subnet in aws_subnet.infrastructure_private : subnet.cidr_block
]
security_group_id = aws_security_group.infrastructure_rds_s3_backups_scheduled_task[0].id
}

resource "aws_security_group_rule" "infrastructure_rds_s3_backups_scheduled_task_egress_rds" {
for_each = local.enable_infrastructure_rds_backup_to_s3 ? local.infrastructure_rds : {}

description = "Allow ${each.value["engine"]} tcp outbound to RDS security group"
type = "egress"
from_port = local.rds_ports[each.value["engine"]]
to_port = local.rds_ports[each.value["engine"]]
protocol = "tcp"
source_security_group_id = aws_security_group.infrastructure_rds[each.key].id
security_group_id = aws_security_group.infrastructure_rds_s3_backups_scheduled_task[0].id
}
Loading

0 comments on commit 7d8993d

Please sign in to comment.