Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion examples/source/aws-backups.tf
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ module "source" {
}
]
}
# Note here that we need to explicitly disable DynamoDB and Aurora backups in the source account.
# Note here that we need to explicitly disable DynamoDB, Aurora and RDS backups in the source account.
# The default config in the module enables backups for all resource types.
backup_plan_config_dynamodb = {
"compliance_resource_types": [
Expand Down Expand Up @@ -183,4 +183,14 @@ module "source" {
"selection_tag": "NHSE-Enable-Backup"
}

backup_plan_config_rds = {
"compliance_resource_types": [
"RDS"
],
"rules": [
],
"enable": false,
"selection_tag": "NHSE-Enable-Backup"
}

}
6 changes: 6 additions & 0 deletions modules/aws-backup-source/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,13 @@ No modules.
|------|------|
| [aws_backup_framework.dynamodb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_framework) | resource |
| [aws_backup_framework.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_framework) | resource |
| [aws_backup_framework.rds](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_framework) | resource |
| [aws_backup_plan.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_plan) | resource |
| [aws_backup_plan.dynamodb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_plan) | resource |
| [aws_backup_plan.rds](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_plan) | resource |
| [aws_backup_selection.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_selection) | resource |
| [aws_backup_selection.dynamodb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_selection) | resource |
| [aws_backup_selection.rds](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_selection) | resource |
| [aws_backup_vault.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_vault) | resource |
| [aws_backup_vault_notifications.backup_notification](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_vault_notifications) | resource |
| [aws_backup_vault_policy.vault_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_vault_policy) | resource |
Expand All @@ -61,6 +64,7 @@ No modules.
| [aws_sns_topic_subscription.aws_backup_notifications_email_target](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
| [awscc_backup_restore_testing_plan.backup_restore_testing_plan](https://registry.terraform.io/providers/hashicorp/awscc/latest/docs/resources/backup_restore_testing_plan) | resource |
| [awscc_backup_restore_testing_selection.backup_restore_testing_selection_dynamodb](https://registry.terraform.io/providers/hashicorp/awscc/latest/docs/resources/backup_restore_testing_selection) | resource |
| [awscc_backup_restore_testing_selection.backup_restore_testing_selection_rds](https://registry.terraform.io/providers/hashicorp/awscc/latest/docs/resources/backup_restore_testing_selection) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_iam_policy_document.allow_backup_to_sns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
Expand All @@ -77,6 +81,8 @@ No modules.
| <a name="input_backup_copy_vault_arn"></a> [backup\_copy\_vault\_arn](#input\_backup\_copy\_vault\_arn) | The ARN of the destination backup vault for cross-account backup copies. | `string` | `""` | no |
| <a name="input_backup_plan_config"></a> [backup\_plan\_config](#input\_backup\_plan\_config) | Configuration for backup plans | <pre>object({<br/> selection_tag = string<br/> selection_tag_value = optional(string)<br/> selection_tags = optional(list(object({<br/> key = optional(string)<br/> value = optional(string)<br/> })))<br/> compliance_resource_types = list(string)<br/> rules = list(object({<br/> name = string<br/> schedule = string<br/> enable_continuous_backup = optional(bool)<br/> lifecycle = object({<br/> delete_after = optional(number)<br/> cold_storage_after = optional(number)<br/> })<br/> copy_action = optional(object({<br/> delete_after = optional(number)<br/> }))<br/> }))<br/> })</pre> | <pre>{<br/> "compliance_resource_types": [<br/> "S3"<br/> ],<br/> "rules": [<br/> {<br/> "copy_action": {<br/> "delete_after": 365<br/> },<br/> "lifecycle": {<br/> "delete_after": 35<br/> },<br/> "name": "daily_kept_5_weeks",<br/> "schedule": "cron(0 0 * * ? *)"<br/> },<br/> {<br/> "copy_action": {<br/> "delete_after": 365<br/> },<br/> "lifecycle": {<br/> "delete_after": 90<br/> },<br/> "name": "weekly_kept_3_months",<br/> "schedule": "cron(0 1 ? * SUN *)"<br/> },<br/> {<br/> "copy_action": {<br/> "delete_after": 365<br/> },<br/> "lifecycle": {<br/> "cold_storage_after": 30,<br/> "delete_after": 2555<br/> },<br/> "name": "monthly_kept_7_years",<br/> "schedule": "cron(0 2 1 * ? *)"<br/> },<br/> {<br/> "copy_action": {<br/> "delete_after": 365<br/> },<br/> "enable_continuous_backup": true,<br/> "lifecycle": {<br/> "delete_after": 35<br/> },<br/> "name": "point_in_time_recovery",<br/> "schedule": "cron(0 5 * * ? *)"<br/> }<br/> ],<br/> "selection_tag": "BackupLocal",<br/> "selection_tag_value": "True",<br/> "selection_tags": []<br/>}</pre> | no |
| <a name="input_backup_plan_config_dynamodb"></a> [backup\_plan\_config\_dynamodb](#input\_backup\_plan\_config\_dynamodb) | Configuration for backup plans with dynamodb | <pre>object({<br/> enable = bool<br/> selection_tag = string<br/> selection_tag_value = optional(string)<br/> selection_tags = optional(list(object({<br/> key = optional(string)<br/> value = optional(string)<br/> })))<br/> compliance_resource_types = list(string)<br/> rules = optional(list(object({<br/> name = string<br/> schedule = string<br/> enable_continuous_backup = optional(bool)<br/> lifecycle = object({<br/> delete_after = number<br/> cold_storage_after = optional(number)<br/> })<br/> copy_action = optional(object({<br/> delete_after = optional(number)<br/> }))<br/> })))<br/> })</pre> | <pre>{<br/> "compliance_resource_types": [<br/> "DynamoDB"<br/> ],<br/> "enable": true,<br/> "rules": [<br/> {<br/> "copy_action": {<br/> "delete_after": 365<br/> },<br/> "lifecycle": {<br/> "delete_after": 35<br/> },<br/> "name": "dynamodb_daily_kept_5_weeks",<br/> "schedule": "cron(0 0 * * ? *)"<br/> },<br/> {<br/> "copy_action": {<br/> "delete_after": 365<br/> },<br/> "lifecycle": {<br/> "delete_after": 90<br/> },<br/> "name": "dynamodb_weekly_kept_3_months",<br/> "schedule": "cron(0 1 ? * SUN *)"<br/> },<br/> {<br/> "copy_action": {<br/> "delete_after": 365<br/> },<br/> "lifecycle": {<br/> "cold_storage_after": 30,<br/> "delete_after": 2555<br/> },<br/> "name": "dynamodb_monthly_kept_7_years",<br/> "schedule": "cron(0 2 1 * ? *)"<br/> }<br/> ],<br/> "selection_tag": "BackupDynamoDB",<br/> "selection_tag_value": "True",<br/> "selection_tags": []<br/>}</pre> | no |

| <a name="input_backup_plan_config_rds"></a> [backup\_plan\_config\_rds](#input\_backup\_plan\_config\_rds) | Configuration for backup plans with RDS | <pre>object({<br/> enable = bool<br/> selection_tag = string<br/> selection_tag_value = optional(string)<br/> selection_tags = optional(list(object({<br/> key = optional(string)<br/> value = optional(string)<br/> })))<br/> compliance_resource_types = list(string)<br/> rules = optional(list(object({<br/> name = string<br/> schedule = string<br/> completion_window = optional(number)<br/> enable_continuous_backup = optional(bool)<br/> lifecycle = object({<br/> delete_after = number<br/> cold_storage_after = optional(number)<br/> })<br/> copy_action = optional(object({<br/> delete_after = optional(number)<br/> }))<br/> })))<br/> })</pre> | <pre>{<br/> "compliance_resource_types": [<br/> "RDS"<br/> ],<br/> "enable": true,<br/> "rules": [<br/> {<br/> "copy_action": {<br/> "delete_after": 365<br/> },<br/> "lifecycle": {<br/> "delete_after": 35<br/> },<br/> "completion_window": 24,<br/> "name": "rds_daily_kept_5_weeks",<br/> "schedule": "cron(0 0 * * ? *)"<br/> },<br/> {<br/> "copy_action": {<br/> "delete_after": 365<br/> },<br/> "lifecycle": {<br/> "delete_after": 90<br/> },<br/> "completion_window": 48,<br/> "name": "rds_weekly_kept_3_months",<br/> "schedule": "cron(0 1 ? * SUN *)"<br/> },<br/> {<br/> "copy_action": {<br/> "delete_after": 365<br/> },<br/> "lifecycle": {<br/> "cold_storage_after": 30,<br/> "delete_after": 2555<br/> },<br/> "completion_window": 72,<br/> "name": "rds_monthly_kept_7_years",<br/> "schedule": "cron(0 2 1 * ? *)"<br/> }<br/> ],<br/> "selection_tag": "BackupRDS",<br/> "selection_tag_value": "True",<br/> "selection_tags": []<br/>}</pre> | no |
| <a name="input_backup_plan_config_aurora"></a> [backup_plan_config_aurora](#input_backup_plan_config_aurora) | Configuration for backup plans with aurora | <pre>object({<br> enable = bool<br> selection_tag = string<br> compliance_resource_types = list(string)<br> restore_testing_overrides = optional(string)<br> rules = optional(list(object({<br> name = string<br> schedule = string<br> enable_continuous_backup = optional(bool)<br> lifecycle = object({<br> delete_after = number<br> cold_storage_after = optional(number)<br> })<br> copy_action = optional(object({<br> delete_after = optional(number)<br> }))<br> })))<br> })</pre> | <pre>{<br> "compliance_resource_types": [<br> "Aurora"<br> ],<br> "enable": true,<br> "restore_testing_overrides" : "{\"dbsubnetgroupname\": \"test-subnet\"}",<br> "rules": [<br> {<br> "copy_action": {<br> "delete_after": 365<br> },<br> "lifecycle": {<br> "delete_after": 35<br> },<br> "name": "aurora_daily_kept_5_weeks",<br> "schedule": "cron(0 0 * * ? *)"<br> },<br> {<br> "copy_action": {<br> "delete_after": 365<br> },<br> "lifecycle": {<br> "delete_after": 90<br> },<br> "name": "aurora_weekly_kept_3_months",<br> "schedule": "cron(0 1 ? * SUN *)"<br> },<br> {<br> "copy_action": {<br> "delete_after": 365<br> },<br> "lifecycle": {<br> "cold_storage_after": 30,<br> "delete_after": 2555<br> },<br> "name": "aurora_monthly_kept_7_years",<br> "schedule": "cron(0 2 1 * ? *)"<br> }<br> ],<br> "selection_tag": "BackupAurora"<br>}</pre> | no |
| <a name="input_bootstrap_kms_key_arn"></a> [bootstrap\_kms\_key\_arn](#input\_bootstrap\_kms\_key\_arn) | The ARN of the bootstrap KMS key used for encryption at rest of the SNS topic. | `string` | n/a | yes |
| <a name="input_environment_name"></a> [environment\_name](#input\_environment\_name) | The name of the environment where AWS Backup is configured. | `string` | n/a | yes |
Expand Down
41 changes: 41 additions & 0 deletions modules/aws-backup-source/backup_framework.tf
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,44 @@ resource "aws_backup_framework" "aurora" {
}
}
}

resource "aws_backup_framework" "rds" {
count = var.backup_plan_config_rds.enable ? 1 : 0
# must be underscores instead of dashes
name = replace("${var.name_prefix}-rds-framework", "-", "_")
description = "${var.project_name} RDS Backup Framework"

# Evaluates if resources are protected by a backup plan.
control {
name = "BACKUP_RESOURCES_PROTECTED_BY_BACKUP_PLAN"

scope {
compliance_resource_types = var.backup_plan_config_rds.compliance_resource_types
tags = {
(var.backup_plan_config_rds.selection_tag) = (var.backup_plan_config_rds.selection_tag_value)
}
}
}

# Evaluates if resources have at least one recovery point created within the past 1 day.
control {
name = "BACKUP_LAST_RECOVERY_POINT_CREATED"

input_parameter {
name = "recoveryPointAgeUnit"
value = "days"
}

input_parameter {
name = "recoveryPointAgeValue"
value = "1"
}

scope {
compliance_resource_types = var.backup_plan_config_rds.compliance_resource_types
tags = {
(var.backup_plan_config_rds.selection_tag) = (var.backup_plan_config_rds.selection_tag_value)
}
}
}
}
56 changes: 54 additions & 2 deletions modules/aws-backup-source/backup_plan.tf
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ resource "aws_backup_plan" "default" {
}
}

# this backup plan shouldn't include a continous backup rule as it isn't supported for DynamoDB
# this backup plan shouldn't include a continuous backup rule as it isn't supported for DynamoDB
resource "aws_backup_plan" "dynamodb" {
count = var.backup_plan_config_dynamodb.enable ? 1 : 0
name = "${var.name_prefix}-dynamodb-plan"
Expand Down Expand Up @@ -91,7 +91,7 @@ resource "aws_backup_plan" "ebsvol" {
}
}

# this backup plan shouldn't include a continous backup rule as it isn't supported for Aurora
# this backup plan shouldn't include a continuous backup rule as it isn't supported for Aurora
resource "aws_backup_plan" "aurora" {
count = var.backup_plan_config_aurora.enable ? 1 : 0
name = "${local.resource_name_prefix}-aurora-plan"
Expand Down Expand Up @@ -164,6 +164,58 @@ resource "aws_backup_selection" "dynamodb" {
}
}
}
resource "aws_backup_plan" "rds" {
count = var.backup_plan_config_rds.enable ? 1 : 0
name = "${var.name_prefix}-rds-plan"

dynamic "rule" {
for_each = var.backup_plan_config_rds.rules
content {
recovery_point_tags = {
backup_rule_name = rule.value.name
}
rule_name = rule.value.name
target_vault_name = aws_backup_vault.main.name
schedule = rule.value.schedule
completion_window = rule.value.completion_window
lifecycle {
delete_after = rule.value.lifecycle.delete_after != null ? rule.value.lifecycle.delete_after : null
cold_storage_after = rule.value.lifecycle.cold_storage_after != null ? rule.value.lifecycle.cold_storage_after : null
}
dynamic "copy_action" {
for_each = var.backup_copy_vault_arn != "" && var.backup_copy_vault_account_id != "" && rule.value.copy_action != null ? rule.value.copy_action : {}
content {
lifecycle {
delete_after = copy_action.value
}
destination_vault_arn = var.backup_copy_vault_arn
}
}
}
}
}

resource "aws_backup_selection" "rds" {
count = var.backup_plan_config_rds.enable ? 1 : 0
iam_role_arn = aws_iam_role.backup.arn
name = "${var.name_prefix}-rds-selection"
plan_id = aws_backup_plan.rds[0].id

selection_tag {
key = var.backup_plan_config_rds.selection_tag
type = "STRINGEQUALS"
value = (var.backup_plan_config_rds.selection_tag_value == null) ? "True" : var.backup_plan_config_rds.selection_tag_value
}
condition {
dynamic "string_equals" {
for_each = local.selection_tags_rds_null_checked
content {
key = (try(string_equals.value.key, null) == null) ? null : "aws:ResourceTag/${string_equals.value.key}"
value = try(string_equals.value.value, null)
}
}
}
}

resource "aws_backup_selection" "ebsvol" {
count = var.backup_plan_config_ebsvol.enable ? 1 : 0
Expand Down
15 changes: 15 additions & 0 deletions modules/aws-backup-source/backup_restore_testing.tf
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,18 @@ resource "awscc_backup_restore_testing_selection" "backup_restore_testing_select
}
restore_metadata_overrides = local.aurora_overrides
}

resource "awscc_backup_restore_testing_selection" "backup_restore_testing_selection_rds" {
count = var.backup_plan_config_rds.enable ? 1 : 0
iam_role_arn = aws_iam_role.backup.arn
protected_resource_type = "RDS"
restore_testing_plan_name = awscc_backup_restore_testing_plan.backup_restore_testing_plan.restore_testing_plan_name
restore_testing_selection_name = "backup_restore_testing_selection_rds"
protected_resource_arns = ["*"]
protected_resource_conditions = {
string_equals = [{
key = "aws:ResourceTag/${var.backup_plan_config_rds.selection_tag}"
value = "True"
}]
}
}
8 changes: 7 additions & 1 deletion modules/aws-backup-source/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@ locals {
selection_tag_value_dynamodb_null_checked = (var.backup_plan_config_dynamodb.selection_tag_value == null) ? "True" : var.backup_plan_config_dynamodb.selection_tag_value
selection_tags_null_checked = (var.backup_plan_config.selection_tags == null) ? [{ "key" : var.backup_plan_config.selection_tag, "value" : local.selection_tag_value_null_checked }] : var.backup_plan_config.selection_tags
selection_tags_dynamodb_null_checked = (var.backup_plan_config_dynamodb.selection_tags == null) ? [{ "key" : var.backup_plan_config_dynamodb.selection_tag, "value" : local.selection_tag_value_dynamodb_null_checked }] : var.backup_plan_config_dynamodb.selection_tags

selection_tag_value_ebsvol_null_checked = (var.backup_plan_config_ebsvol.selection_tag_value == null) ? "True" : var.backup_plan_config_ebsvol.selection_tag_value
selection_tags_ebsvol_null_checked = (var.backup_plan_config_ebsvol.selection_tags == null) ? [{ "key" : var.backup_plan_config_ebsvol.selection_tag, "value" : local.selection_tag_value_ebsvol_null_checked }] : var.backup_plan_config_ebsvol.selection_tags

selection_tag_value_rds_null_checked = (var.backup_plan_config_rds.selection_tag_value == null) ? "True" : var.backup_plan_config_rds.selection_tag_value
selection_tags_rds_null_checked = (var.backup_plan_config_rds.selection_tags == null) ? [{ "key" : var.backup_plan_config_rds.selection_tag, "value" : local.selection_tag_value_rds_null_checked }] : var.backup_plan_config_rds.selection_tags

framework_arn_list = flatten(concat(
[aws_backup_framework.main.arn],
var.backup_plan_config_ebsvol.enable ? [aws_backup_framework.ebsvol[0].arn] : [],
var.backup_plan_config_dynamodb.enable ? [aws_backup_framework.dynamodb[0].arn] : [],
var.backup_plan_config_aurora.enable ? [aws_backup_framework.aurora[0].arn] : []
var.backup_plan_config_aurora.enable ? [aws_backup_framework.aurora[0].arn] : [],
var.backup_plan_config_rds.enable ? [aws_backup_framework.rds[0].arn] : []
))
aurora_overrides = jsondecode(var.backup_plan_config_aurora.restore_testing_overrides)
terraform_role_arns = var.terraform_role_arns != null ? var.terraform_role_arns : [var.terraform_role_arn]
Expand Down
Loading