From 063965fc82e55b397d71994444c960e2d1cf5e96 Mon Sep 17 00:00:00 2001 From: Michel Fasen Date: Wed, 11 Feb 2026 13:20:30 +0000 Subject: [PATCH 1/7] LZVS-2638: Add support for Logically Air-Gapped Vault --- .../aws-backup-source/backup_notification.tf | 13 +++++ modules/aws-backup-source/backup_plan.tf | 47 ++++++++++--------- modules/aws-backup-source/backup_vault.tf | 7 +++ modules/aws-backup-source/outputs.tf | 14 +++++- modules/aws-backup-source/variables.tf | 18 +++++++ 5 files changed, 76 insertions(+), 23 deletions(-) diff --git a/modules/aws-backup-source/backup_notification.tf b/modules/aws-backup-source/backup_notification.tf index cb71232..f4f1e08 100644 --- a/modules/aws-backup-source/backup_notification.tf +++ b/modules/aws-backup-source/backup_notification.tf @@ -10,3 +10,16 @@ resource "aws_backup_vault_notifications" "backup_notification" { "COPY_JOB_FAILED" ] } + +resource "aws_backup_vault_notifications" "backup_notification_lag" { + count = var.enable_logically_air_gapped_vault && var.notifications_target_email_address != "" ? 1 : 0 + backup_vault_name = aws_backup_logically_air_gapped_vault.main[0].name + sns_topic_arn = aws_sns_topic.backup[0].arn + backup_vault_events = [ + "BACKUP_JOB_COMPLETED", + "RESTORE_JOB_COMPLETED", + "S3_BACKUP_OBJECT_FAILED", + "S3_RESTORE_OBJECT_FAILED", + "COPY_JOB_FAILED" + ] +} diff --git a/modules/aws-backup-source/backup_plan.tf b/modules/aws-backup-source/backup_plan.tf index f4e783d..877d365 100644 --- a/modules/aws-backup-source/backup_plan.tf +++ b/modules/aws-backup-source/backup_plan.tf @@ -7,11 +7,12 @@ resource "aws_backup_plan" "default" { 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 - enable_continuous_backup = rule.value.enable_continuous_backup != null ? rule.value.enable_continuous_backup : null + rule_name = rule.value.name + target_vault_name = aws_backup_vault.main.name + target_logically_air_gapped_backup_vault_arn = var.enable_logically_air_gapped_vault ? aws_backup_logically_air_gapped_vault.main[0].arn : null + schedule = rule.value.schedule + completion_window = rule.value.completion_window + enable_continuous_backup = rule.value.enable_continuous_backup != null ? rule.value.enable_continuous_backup : null 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 @@ -40,10 +41,11 @@ resource "aws_backup_plan" "dynamodb" { 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 + rule_name = rule.value.name + target_vault_name = aws_backup_vault.main.name + target_logically_air_gapped_backup_vault_arn = var.enable_logically_air_gapped_vault ? aws_backup_logically_air_gapped_vault.main[0].arn : null + 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 @@ -71,9 +73,10 @@ resource "aws_backup_plan" "ebsvol" { 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 + rule_name = rule.value.name + target_vault_name = aws_backup_vault.main.name + target_logically_air_gapped_backup_vault_arn = var.enable_logically_air_gapped_vault ? aws_backup_logically_air_gapped_vault.main[0].arn : null + schedule = rule.value.schedule 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 @@ -102,9 +105,10 @@ resource "aws_backup_plan" "aurora" { 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 + rule_name = rule.value.name + target_vault_name = aws_backup_vault.main.name + target_logically_air_gapped_backup_vault_arn = var.enable_logically_air_gapped_vault ? aws_backup_logically_air_gapped_vault.main[0].arn : null + schedule = rule.value.schedule 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 @@ -125,7 +129,7 @@ resource "aws_backup_plan" "aurora" { resource "aws_backup_plan" "parameter_store" { count = var.backup_plan_config_parameter_store.enable ? 1 : 0 - name = "${local.resource_name_prefix}-ps-plan" + name = "${local.resource_name_prefix}-ps-plan" dynamic "rule" { for_each = var.backup_plan_config_parameter_store.rules @@ -133,11 +137,12 @@ resource "aws_backup_plan" "parameter_store" { 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 - enable_continuous_backup = rule.value.enable_continuous_backup != null ? rule.value.enable_continuous_backup : null + rule_name = rule.value.name + target_vault_name = aws_backup_vault.main.name + target_logically_air_gapped_backup_vault_arn = var.enable_logically_air_gapped_vault ? aws_backup_logically_air_gapped_vault.main[0].arn : null + schedule = rule.value.schedule + completion_window = rule.value.completion_window + enable_continuous_backup = rule.value.enable_continuous_backup != null ? rule.value.enable_continuous_backup : null lifecycle { delete_after = rule.value.lifecycle.delete_after cold_storage_after = rule.value.lifecycle.cold_storage_after diff --git a/modules/aws-backup-source/backup_vault.tf b/modules/aws-backup-source/backup_vault.tf index 49f79ca..a1bfb4c 100644 --- a/modules/aws-backup-source/backup_vault.tf +++ b/modules/aws-backup-source/backup_vault.tf @@ -2,3 +2,10 @@ resource "aws_backup_vault" "main" { name = "${local.resource_name_prefix}-vault" kms_key_arn = aws_kms_key.aws_backup_key.arn } + +resource "aws_backup_logically_air_gapped_vault" "main" { + count = var.enable_logically_air_gapped_vault ? 1 : 0 + name = "${local.resource_name_prefix}-lag-vault" + min_retention_days = var.vault_lock_min_retention_days + max_retention_days = var.vault_lock_max_retention_days +} diff --git a/modules/aws-backup-source/outputs.tf b/modules/aws-backup-source/outputs.tf index 96ab936..8dbdd04 100644 --- a/modules/aws-backup-source/outputs.tf +++ b/modules/aws-backup-source/outputs.tf @@ -5,10 +5,20 @@ output "backup_role_arn" { output "backup_vault_arn" { value = aws_backup_vault.main.arn - description = "ARN of the of the vault" + description = "ARN of the of the Backup Vault" } output "backup_vault_name" { value = aws_backup_vault.main.name - description = "Name of the of the vault" + description = "Name of the of the Backup Vault" +} + +output "backup_vault_lag_arn" { + value = var.enable_logically_air_gapped_vault ? aws_backup_logically_air_gapped_vault.main[0].arn : null + description = "ARN of the of the Logically air-gapped Vault" +} + +output "backup_vault_lag_name" { + value = var.enable_logically_air_gapped_vault ? aws_backup_logically_air_gapped_vault.main[0].name : null + description = "Name of the of the Logically air-gapped Vault" } diff --git a/modules/aws-backup-source/variables.tf b/modules/aws-backup-source/variables.tf index 6ca9252..e0a16d9 100644 --- a/modules/aws-backup-source/variables.tf +++ b/modules/aws-backup-source/variables.tf @@ -520,3 +520,21 @@ variable "lambda_restore_to_s3_max_wait_minutes" { type = number default = 5 } + +variable "enable_logically_air_gapped_vault" { + description = "Enable backing up to Logically Air-Gapped Backup Vault for supported resources" + type = bool + default = false +} + +variable "logically_air_gapped_vault_lock_min_retention_days" { + description = "The minimum retention period that the Logically Air-Gapped Backup Vault retains its recovery points" + type = number + default = 35 +} + +variable "logically_air_gapped_vault_lock_max_retention_days" { + description = "The maximum retention period that the Logically Air-Gapped Backup Vault retains its recovery points" + type = number + default = 365 +} From b93ebefe4c6629aceec88be32926493df80af747 Mon Sep 17 00:00:00 2001 From: Michel Fasen Date: Wed, 11 Feb 2026 13:39:07 +0000 Subject: [PATCH 2/7] Fix rename of retention variables --- modules/aws-backup-source/backup_vault.tf | 4 ++-- modules/aws-backup-source/variables.tf | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/aws-backup-source/backup_vault.tf b/modules/aws-backup-source/backup_vault.tf index a1bfb4c..aa24ca4 100644 --- a/modules/aws-backup-source/backup_vault.tf +++ b/modules/aws-backup-source/backup_vault.tf @@ -6,6 +6,6 @@ resource "aws_backup_vault" "main" { resource "aws_backup_logically_air_gapped_vault" "main" { count = var.enable_logically_air_gapped_vault ? 1 : 0 name = "${local.resource_name_prefix}-lag-vault" - min_retention_days = var.vault_lock_min_retention_days - max_retention_days = var.vault_lock_max_retention_days + min_retention_days = var.logically_air_gapped_vault_lock_min_retention_days + max_retention_days = var.logically_air_gapped_vault_lock_max_retention_days } diff --git a/modules/aws-backup-source/variables.tf b/modules/aws-backup-source/variables.tf index e0a16d9..385426d 100644 --- a/modules/aws-backup-source/variables.tf +++ b/modules/aws-backup-source/variables.tf @@ -522,19 +522,19 @@ variable "lambda_restore_to_s3_max_wait_minutes" { } variable "enable_logically_air_gapped_vault" { - description = "Enable backing up to Logically Air-Gapped Backup Vault for supported resources" + description = "Enable backing up to Logically Air-Gapped Vault for supported resources" type = bool default = false } variable "logically_air_gapped_vault_lock_min_retention_days" { - description = "The minimum retention period that the Logically Air-Gapped Backup Vault retains its recovery points" + description = "The minimum retention period that the Logically Air-Gapped Vault retains its recovery points" type = number default = 35 } variable "logically_air_gapped_vault_lock_max_retention_days" { - description = "The maximum retention period that the Logically Air-Gapped Backup Vault retains its recovery points" + description = "The maximum retention period that the Logically Air-Gapped Vault retains its recovery points" type = number default = 365 } From ec3fe94f9db0a2773d19c9039358b8538c31deca Mon Sep 17 00:00:00 2001 From: Michel Fasen Date: Wed, 11 Feb 2026 13:43:08 +0000 Subject: [PATCH 3/7] Correct capitalisation of Air-gapped Vault --- modules/aws-backup-source/outputs.tf | 4 ++-- modules/aws-backup-source/variables.tf | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/aws-backup-source/outputs.tf b/modules/aws-backup-source/outputs.tf index 8dbdd04..c385171 100644 --- a/modules/aws-backup-source/outputs.tf +++ b/modules/aws-backup-source/outputs.tf @@ -15,10 +15,10 @@ output "backup_vault_name" { output "backup_vault_lag_arn" { value = var.enable_logically_air_gapped_vault ? aws_backup_logically_air_gapped_vault.main[0].arn : null - description = "ARN of the of the Logically air-gapped Vault" + description = "ARN of the of the Logically Air-gapped Vault" } output "backup_vault_lag_name" { value = var.enable_logically_air_gapped_vault ? aws_backup_logically_air_gapped_vault.main[0].name : null - description = "Name of the of the Logically air-gapped Vault" + description = "Name of the of the Logically Air-gapped Vault" } diff --git a/modules/aws-backup-source/variables.tf b/modules/aws-backup-source/variables.tf index 385426d..8c98c6f 100644 --- a/modules/aws-backup-source/variables.tf +++ b/modules/aws-backup-source/variables.tf @@ -522,19 +522,19 @@ variable "lambda_restore_to_s3_max_wait_minutes" { } variable "enable_logically_air_gapped_vault" { - description = "Enable backing up to Logically Air-Gapped Vault for supported resources" + description = "Enable backing up to Logically Air-gapped Vault for supported resources" type = bool default = false } variable "logically_air_gapped_vault_lock_min_retention_days" { - description = "The minimum retention period that the Logically Air-Gapped Vault retains its recovery points" + description = "The minimum retention period that the Logically Air-gapped Vault retains its recovery points" type = number default = 35 } variable "logically_air_gapped_vault_lock_max_retention_days" { - description = "The maximum retention period that the Logically Air-Gapped Vault retains its recovery points" + description = "The maximum retention period that the Logically Air-gapped Vault retains its recovery points" type = number default = 365 } From 3540f05a04b054e692a09dd0862431bdd68f851d Mon Sep 17 00:00:00 2001 From: Michel Fasen Date: Thu, 12 Feb 2026 10:58:14 +0000 Subject: [PATCH 4/7] Updated readme file --- modules/aws-backup-source/README.md | 45 ++++++++++++++++------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/modules/aws-backup-source/README.md b/modules/aws-backup-source/README.md index 2bcc0ec..0326967 100644 --- a/modules/aws-backup-source/README.md +++ b/modules/aws-backup-source/README.md @@ -48,7 +48,9 @@ No modules. | [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_vault.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_vault) | resource | +| [aws_backup_logically_air_gapped_vault.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/backup_logically_air_gapped_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_notifications.backup_notification_lag](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 | | [aws_iam_role.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy_attachment.backup](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource | @@ -71,26 +73,29 @@ No modules. ## Inputs -| Name | Description | Type | Default | Required | -|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|------|---------|:--------:| -| [backup\_copy\_vault\_account\_id](#input\_backup\_copy\_vault\_account\_id) | The account id of the destination backup vault for allowing restores back into the source account. | `string` | `""` | no | -| [backup\_copy\_vault\_arn](#input\_backup\_copy\_vault\_arn) | The ARN of the destination backup vault for cross-account backup copies. | `string` | `""` | no | -| [backup\_plan\_config](#input\_backup\_plan\_config) | Configuration for backup plans |
object({
selection_tag = string
selection_tag_value = optional(string)
selection_tags = optional(list(object({
key = optional(string)
value = optional(string)
})))
compliance_resource_types = list(string)
rules = list(object({
name = string
schedule = string
enable_continuous_backup = optional(bool)
lifecycle = object({
delete_after = optional(number)
cold_storage_after = optional(number)
})
copy_action = optional(object({
delete_after = optional(number)
}))
}))
})
|
{
"compliance_resource_types": [
"S3"
],
"rules": [
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 35
},
"name": "daily_kept_5_weeks",
"schedule": "cron(0 0 * * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 90
},
"name": "weekly_kept_3_months",
"schedule": "cron(0 1 ? * SUN *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"cold_storage_after": 30,
"delete_after": 2555
},
"name": "monthly_kept_7_years",
"schedule": "cron(0 2 1 * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"enable_continuous_backup": true,
"lifecycle": {
"delete_after": 35
},
"name": "point_in_time_recovery",
"schedule": "cron(0 5 * * ? *)"
}
],
"selection_tag": "BackupLocal",
"selection_tag_value": "True",
"selection_tags": []
}
| no | -| [backup\_plan\_config\_dynamodb](#input\_backup\_plan\_config\_dynamodb) | Configuration for backup plans with dynamodb |
object({
enable = bool
selection_tag = string
selection_tag_value = optional(string)
selection_tags = optional(list(object({
key = optional(string)
value = optional(string)
})))
compliance_resource_types = list(string)
rules = optional(list(object({
name = string
schedule = string
enable_continuous_backup = optional(bool)
lifecycle = object({
delete_after = number
cold_storage_after = optional(number)
})
copy_action = optional(object({
delete_after = optional(number)
}))
})))
})
|
{
"compliance_resource_types": [
"DynamoDB"
],
"enable": true,
"rules": [
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 35
},
"name": "dynamodb_daily_kept_5_weeks",
"schedule": "cron(0 0 * * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 90
},
"name": "dynamodb_weekly_kept_3_months",
"schedule": "cron(0 1 ? * SUN *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"cold_storage_after": 30,
"delete_after": 2555
},
"name": "dynamodb_monthly_kept_7_years",
"schedule": "cron(0 2 1 * ? *)"
}
],
"selection_tag": "BackupDynamoDB",
"selection_tag_value": "True",
"selection_tags": []
}
| no | -| [backup_plan_config_aurora](#input_backup_plan_config_aurora) | Configuration for backup plans with aurora |
object({
enable = bool
selection_tag = string
compliance_resource_types = list(string)
restore_testing_overrides = optional(string)
rules = optional(list(object({
name = string
schedule = string
enable_continuous_backup = optional(bool)
lifecycle = object({
delete_after = number
cold_storage_after = optional(number)
})
copy_action = optional(object({
delete_after = optional(number)
}))
})))
})
|
{
"compliance_resource_types": [
"Aurora"
],
"enable": true,
"restore_testing_overrides" : "{\"dbsubnetgroupname\": \"test-subnet\"}",
"rules": [
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 35
},
"name": "aurora_daily_kept_5_weeks",
"schedule": "cron(0 0 * * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 90
},
"name": "aurora_weekly_kept_3_months",
"schedule": "cron(0 1 ? * SUN *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"cold_storage_after": 30,
"delete_after": 2555
},
"name": "aurora_monthly_kept_7_years",
"schedule": "cron(0 2 1 * ? *)"
}
],
"selection_tag": "BackupAurora"
}
| no | -| [parameter_store_backup_config](#input_backup_plan_config_parameter_store) | Configuration for the scheduled Lambda function to backup tagged Parameter Store parameters to S3. |
object({ enable = bool selection_tag = string selection_tag_value = optional(string) selection_tags = optional(list(object({ key = optional(string) value = optional(string) }))) lambda_backup_cron = optional(string) lambda_timeout_seconds = optional(number) air_gapped_kms_key_arn = optional(string) s3_bucket_name = optional(string) rules = optional(list(object({ name = string schedule = string completion_window = optional(number) enable_continuous_backup = optional(bool) lifecycle = object({ delete_after = number cold_storage_after = optional(number) }) copy_action = optional(object({ delete_after = optional(number) })) }))) })
|
{ "air_gapped_kms_key_arn": "", "enable": true, "lambda_backup_cron": "cron(0 6 * * ? *)", "lambda_timeout_seconds": 300, "rules": [ { "copy_action": { "delete_after": 365 }, "lifecycle": { "delete_after": 35 }, "name": "daily_kept_5_weeks", "schedule": "cron(0 0 * * ? *)" }, { "copy_action": { "delete_after": 365 }, "lifecycle": { "delete_after": 90 }, "name": "weekly_kept_3_months", "schedule": "cron(0 1 ? * SUN *)" }, { "copy_action": { "delete_after": 365 }, "lifecycle": { "cold_storage_after": 30, "delete_after": 2555 }, "name": "monthly_kept_7_years", "schedule": "cron(0 2 1 * ? *)" }, { "copy_action": { "delete_after": 365 }, "enable_continuous_backup": true, "lifecycle": { "delete_after": 35 }, "name": "point_in_time_recovery", "schedule": "cron(0 5 * * ? *)" } ], "s3_bucket_name": "", "selection_tag": "BackupParameterStore", "selection_tag_value": "True", "selection_tags": [] }
| no | -| [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 | -| [environment\_name](#input\_environment\_name) | The name of the environment where AWS Backup is configured. | `string` | n/a | yes | -| [name\_prefix](#input\_name\_prefix) | Optional name prefix for vault resources | `string` | `null` | no | -| [notifications\_target\_email\_address](#input\_notifications\_target\_email\_address) | The email address to which backup notifications will be sent via SNS. | `string` | `""` | no | -| [project\_name](#input\_project\_name) | The name of the project this relates to. | `string` | n/a | yes | -| [reports\_bucket](#input\_reports\_bucket) | Bucket to drop backup reports into | `string` | n/a | yes | -| [restore\_testing\_plan\_algorithm](#input\_restore\_testing\_plan\_algorithm) | Algorithm of the Recovery Selection Point | `string` | `"LATEST_WITHIN_WINDOW"` | no | -| [restore\_testing\_plan\_recovery\_point\_types](#input\_restore\_testing\_plan\_recovery\_point\_types) | Recovery Point Types | `list(string)` |
[
"SNAPSHOT"
]
| no | -| [restore\_testing\_plan\_scheduled\_expression](#input\_restore\_testing\_plan\_scheduled\_expression) | Scheduled Expression of Recovery Selection Point | `string` | `"cron(0 1 ? * SUN *)"` | no | -| [restore\_testing\_plan\_selection\_window\_days](#input\_restore\_testing\_plan\_selection\_window\_days) | Selection window days | `number` | `7` | no | -| [restore\_testing\_plan\_start\_window](#input\_restore\_testing\_plan\_start\_window) | Start window from the scheduled time during which the test should start | `number` | `1` | no | -| [terraform\_role\_arn](#input\_terraform\_role\_arn) | ARN of Terraform role used to deploy to account | `string` | n/a | yes | +| Name | Description | Type | Default | Required | +|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:| +| [backup\_copy\_vault\_account\_id](#input\_backup\_copy\_vault\_account\_id) | The account id of the destination backup vault for allowing restores back into the source account. | `string` | `""` | no | +| [backup\_copy\_vault\_arn](#input\_backup\_copy\_vault\_arn) | The ARN of the destination backup vault for cross-account backup copies. | `string` | `""` | no | +| [backup\_plan\_config](#input\_backup\_plan\_config) | Configuration for backup plans |
object({
selection_tag = string
selection_tag_value = optional(string)
selection_tags = optional(list(object({
key = optional(string)
value = optional(string)
})))
compliance_resource_types = list(string)
rules = list(object({
name = string
schedule = string
enable_continuous_backup = optional(bool)
lifecycle = object({
delete_after = optional(number)
cold_storage_after = optional(number)
})
copy_action = optional(object({
delete_after = optional(number)
}))
}))
})
|
{
"compliance_resource_types": [
"S3"
],
"rules": [
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 35
},
"name": "daily_kept_5_weeks",
"schedule": "cron(0 0 * * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 90
},
"name": "weekly_kept_3_months",
"schedule": "cron(0 1 ? * SUN *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"cold_storage_after": 30,
"delete_after": 2555
},
"name": "monthly_kept_7_years",
"schedule": "cron(0 2 1 * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"enable_continuous_backup": true,
"lifecycle": {
"delete_after": 35
},
"name": "point_in_time_recovery",
"schedule": "cron(0 5 * * ? *)"
}
],
"selection_tag": "BackupLocal",
"selection_tag_value": "True",
"selection_tags": []
}
| no | +| [backup\_plan\_config\_dynamodb](#input\_backup\_plan\_config\_dynamodb) | Configuration for backup plans with dynamodb |
object({
enable = bool
selection_tag = string
selection_tag_value = optional(string)
selection_tags = optional(list(object({
key = optional(string)
value = optional(string)
})))
compliance_resource_types = list(string)
rules = optional(list(object({
name = string
schedule = string
enable_continuous_backup = optional(bool)
lifecycle = object({
delete_after = number
cold_storage_after = optional(number)
})
copy_action = optional(object({
delete_after = optional(number)
}))
})))
})
|
{
"compliance_resource_types": [
"DynamoDB"
],
"enable": true,
"rules": [
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 35
},
"name": "dynamodb_daily_kept_5_weeks",
"schedule": "cron(0 0 * * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 90
},
"name": "dynamodb_weekly_kept_3_months",
"schedule": "cron(0 1 ? * SUN *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"cold_storage_after": 30,
"delete_after": 2555
},
"name": "dynamodb_monthly_kept_7_years",
"schedule": "cron(0 2 1 * ? *)"
}
],
"selection_tag": "BackupDynamoDB",
"selection_tag_value": "True",
"selection_tags": []
}
| no | +| [backup_plan_config_aurora](#input_backup_plan_config_aurora) | Configuration for backup plans with aurora |
object({
enable = bool
selection_tag = string
compliance_resource_types = list(string)
restore_testing_overrides = optional(string)
rules = optional(list(object({
name = string
schedule = string
enable_continuous_backup = optional(bool)
lifecycle = object({
delete_after = number
cold_storage_after = optional(number)
})
copy_action = optional(object({
delete_after = optional(number)
}))
})))
})
|
{
"compliance_resource_types": [
"Aurora"
],
"enable": true,
"restore_testing_overrides" : "{\"dbsubnetgroupname\": \"test-subnet\"}",
"rules": [
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 35
},
"name": "aurora_daily_kept_5_weeks",
"schedule": "cron(0 0 * * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 90
},
"name": "aurora_weekly_kept_3_months",
"schedule": "cron(0 1 ? * SUN *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"cold_storage_after": 30,
"delete_after": 2555
},
"name": "aurora_monthly_kept_7_years",
"schedule": "cron(0 2 1 * ? *)"
}
],
"selection_tag": "BackupAurora"
}
| no | +| [parameter_store_backup_config](#input_backup_plan_config_parameter_store) | Configuration for the scheduled Lambda function to backup tagged Parameter Store parameters to S3. |
object({ enable = bool selection_tag = string selection_tag_value = optional(string) selection_tags = optional(list(object({ key = optional(string) value = optional(string) }))) lambda_backup_cron = optional(string) lambda_timeout_seconds = optional(number) air_gapped_kms_key_arn = optional(string) s3_bucket_name = optional(string) rules = optional(list(object({ name = string schedule = string completion_window = optional(number) enable_continuous_backup = optional(bool) lifecycle = object({ delete_after = number cold_storage_after = optional(number) }) copy_action = optional(object({ delete_after = optional(number) })) }))) })
|
{ "air_gapped_kms_key_arn": "", "enable": true, "lambda_backup_cron": "cron(0 6 * * ? *)", "lambda_timeout_seconds": 300, "rules": [ { "copy_action": { "delete_after": 365 }, "lifecycle": { "delete_after": 35 }, "name": "daily_kept_5_weeks", "schedule": "cron(0 0 * * ? *)" }, { "copy_action": { "delete_after": 365 }, "lifecycle": { "delete_after": 90 }, "name": "weekly_kept_3_months", "schedule": "cron(0 1 ? * SUN *)" }, { "copy_action": { "delete_after": 365 }, "lifecycle": { "cold_storage_after": 30, "delete_after": 2555 }, "name": "monthly_kept_7_years", "schedule": "cron(0 2 1 * ? *)" }, { "copy_action": { "delete_after": 365 }, "enable_continuous_backup": true, "lifecycle": { "delete_after": 35 }, "name": "point_in_time_recovery", "schedule": "cron(0 5 * * ? *)" } ], "s3_bucket_name": "", "selection_tag": "BackupParameterStore", "selection_tag_value": "True", "selection_tags": [] }
| no | +| [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 | +| [environment\_name](#input\_environment\_name) | The name of the environment where AWS Backup is configured. | `string` | n/a | yes | +| [name\_prefix](#input\_name\_prefix) | Optional name prefix for vault resources | `string` | `null` | no | +| [notifications\_target\_email\_address](#input\_notifications\_target\_email\_address) | The email address to which backup notifications will be sent via SNS. | `string` | `""` | no | +| [project\_name](#input\_project\_name) | The name of the project this relates to. | `string` | n/a | yes | +| [reports\_bucket](#input\_reports\_bucket) | Bucket to drop backup reports into | `string` | n/a | yes | +| [restore\_testing\_plan\_algorithm](#input\_restore\_testing\_plan\_algorithm) | Algorithm of the Recovery Selection Point | `string` | `"LATEST_WITHIN_WINDOW"` | no | +| [restore\_testing\_plan\_recovery\_point\_types](#input\_restore\_testing\_plan\_recovery\_point\_types) | Recovery Point Types | `list(string)` |
[
"SNAPSHOT"
]
| no | +| [restore\_testing\_plan\_scheduled\_expression](#input\_restore\_testing\_plan\_scheduled\_expression) | Scheduled Expression of Recovery Selection Point | `string` | `"cron(0 1 ? * SUN *)"` | no | +| [restore\_testing\_plan\_selection\_window\_days](#input\_restore\_testing\_plan\_selection\_window\_days) | Selection window days | `number` | `7` | no | +| [restore\_testing\_plan\_start\_window](#input\_restore\_testing\_plan\_start\_window) | Start window from the scheduled time during which the test should start | `number` | `1` | no | +| [terraform\_role\_arn](#input\_terraform\_role\_arn) | ARN of Terraform role used to deploy to account | `string` | n/a | yes | +| [enable_logically_air_gapped_vault](#input\_terraform\_role\_arn) | Enable backing up to Logically Air-gapped Vault for supported resources | `string` | `false` | no | +| [logically_air_gapped_vault_lock_min_retention_days](#input\_terraform\_role\_arn) | The minimum retention period that the Logically Air-gapped Vault retains its recovery points | `number` | `35` | no | +| [logically_air_gapped_vault_lock_max_retention_days](#input\_terraform\_role\_arn) | The maximum retention period that the Logically Air-gapped Vault retains its recovery points | `number` | `365` | no | ## Outputs From 5954ab865b2d2cc96f5921b71dae5ad45f07b4f3 Mon Sep 17 00:00:00 2001 From: Michel Fasen Date: Thu, 12 Feb 2026 11:05:55 +0000 Subject: [PATCH 5/7] Updated readme file --- modules/aws-backup-source/README.md | 8 +++++++- modules/aws-backup-source/outputs.tf | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/aws-backup-source/README.md b/modules/aws-backup-source/README.md index 0326967..efe098e 100644 --- a/modules/aws-backup-source/README.md +++ b/modules/aws-backup-source/README.md @@ -99,5 +99,11 @@ No modules. ## Outputs -No outputs. +| Name | Description | +|---------------------------------| -------------------------| +| backup_role_arn | ARN of the of the backup role | +| backup_vault_arn | ARN of the of the Backup Vault | +| backup_vault_name | Name of the of the Backup Vault | +| logically_air_gapped_vault_arn | ARN of the of the Logically Air-gapped Vault | +| logically_air_gapped_vault_name | Name of the of the Logically Air-gapped Vault | diff --git a/modules/aws-backup-source/outputs.tf b/modules/aws-backup-source/outputs.tf index c385171..0fc5ff6 100644 --- a/modules/aws-backup-source/outputs.tf +++ b/modules/aws-backup-source/outputs.tf @@ -13,12 +13,12 @@ output "backup_vault_name" { description = "Name of the of the Backup Vault" } -output "backup_vault_lag_arn" { +output "logically_air_gapped_vault_arn" { value = var.enable_logically_air_gapped_vault ? aws_backup_logically_air_gapped_vault.main[0].arn : null description = "ARN of the of the Logically Air-gapped Vault" } -output "backup_vault_lag_name" { +output "logically_air_gapped_vault_name" { value = var.enable_logically_air_gapped_vault ? aws_backup_logically_air_gapped_vault.main[0].name : null description = "Name of the of the Logically Air-gapped Vault" } From 07390cb676e4d9c5aa2d4026a29e389fa01811f5 Mon Sep 17 00:00:00 2001 From: Michel Fasen Date: Thu, 12 Feb 2026 14:21:28 +0000 Subject: [PATCH 6/7] Updated readme file --- modules/aws-backup-source/README.md | 47 +++++++++++++++-------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/modules/aws-backup-source/README.md b/modules/aws-backup-source/README.md index efe098e..5469fc1 100644 --- a/modules/aws-backup-source/README.md +++ b/modules/aws-backup-source/README.md @@ -73,29 +73,29 @@ No modules. ## Inputs -| Name | Description | Type | Default | Required | -|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------:| -| [backup\_copy\_vault\_account\_id](#input\_backup\_copy\_vault\_account\_id) | The account id of the destination backup vault for allowing restores back into the source account. | `string` | `""` | no | -| [backup\_copy\_vault\_arn](#input\_backup\_copy\_vault\_arn) | The ARN of the destination backup vault for cross-account backup copies. | `string` | `""` | no | -| [backup\_plan\_config](#input\_backup\_plan\_config) | Configuration for backup plans |
object({
selection_tag = string
selection_tag_value = optional(string)
selection_tags = optional(list(object({
key = optional(string)
value = optional(string)
})))
compliance_resource_types = list(string)
rules = list(object({
name = string
schedule = string
enable_continuous_backup = optional(bool)
lifecycle = object({
delete_after = optional(number)
cold_storage_after = optional(number)
})
copy_action = optional(object({
delete_after = optional(number)
}))
}))
})
|
{
"compliance_resource_types": [
"S3"
],
"rules": [
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 35
},
"name": "daily_kept_5_weeks",
"schedule": "cron(0 0 * * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 90
},
"name": "weekly_kept_3_months",
"schedule": "cron(0 1 ? * SUN *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"cold_storage_after": 30,
"delete_after": 2555
},
"name": "monthly_kept_7_years",
"schedule": "cron(0 2 1 * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"enable_continuous_backup": true,
"lifecycle": {
"delete_after": 35
},
"name": "point_in_time_recovery",
"schedule": "cron(0 5 * * ? *)"
}
],
"selection_tag": "BackupLocal",
"selection_tag_value": "True",
"selection_tags": []
}
| no | -| [backup\_plan\_config\_dynamodb](#input\_backup\_plan\_config\_dynamodb) | Configuration for backup plans with dynamodb |
object({
enable = bool
selection_tag = string
selection_tag_value = optional(string)
selection_tags = optional(list(object({
key = optional(string)
value = optional(string)
})))
compliance_resource_types = list(string)
rules = optional(list(object({
name = string
schedule = string
enable_continuous_backup = optional(bool)
lifecycle = object({
delete_after = number
cold_storage_after = optional(number)
})
copy_action = optional(object({
delete_after = optional(number)
}))
})))
})
|
{
"compliance_resource_types": [
"DynamoDB"
],
"enable": true,
"rules": [
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 35
},
"name": "dynamodb_daily_kept_5_weeks",
"schedule": "cron(0 0 * * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 90
},
"name": "dynamodb_weekly_kept_3_months",
"schedule": "cron(0 1 ? * SUN *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"cold_storage_after": 30,
"delete_after": 2555
},
"name": "dynamodb_monthly_kept_7_years",
"schedule": "cron(0 2 1 * ? *)"
}
],
"selection_tag": "BackupDynamoDB",
"selection_tag_value": "True",
"selection_tags": []
}
| no | -| [backup_plan_config_aurora](#input_backup_plan_config_aurora) | Configuration for backup plans with aurora |
object({
enable = bool
selection_tag = string
compliance_resource_types = list(string)
restore_testing_overrides = optional(string)
rules = optional(list(object({
name = string
schedule = string
enable_continuous_backup = optional(bool)
lifecycle = object({
delete_after = number
cold_storage_after = optional(number)
})
copy_action = optional(object({
delete_after = optional(number)
}))
})))
})
|
{
"compliance_resource_types": [
"Aurora"
],
"enable": true,
"restore_testing_overrides" : "{\"dbsubnetgroupname\": \"test-subnet\"}",
"rules": [
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 35
},
"name": "aurora_daily_kept_5_weeks",
"schedule": "cron(0 0 * * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 90
},
"name": "aurora_weekly_kept_3_months",
"schedule": "cron(0 1 ? * SUN *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"cold_storage_after": 30,
"delete_after": 2555
},
"name": "aurora_monthly_kept_7_years",
"schedule": "cron(0 2 1 * ? *)"
}
],
"selection_tag": "BackupAurora"
}
| no | -| [parameter_store_backup_config](#input_backup_plan_config_parameter_store) | Configuration for the scheduled Lambda function to backup tagged Parameter Store parameters to S3. |
object({ enable = bool selection_tag = string selection_tag_value = optional(string) selection_tags = optional(list(object({ key = optional(string) value = optional(string) }))) lambda_backup_cron = optional(string) lambda_timeout_seconds = optional(number) air_gapped_kms_key_arn = optional(string) s3_bucket_name = optional(string) rules = optional(list(object({ name = string schedule = string completion_window = optional(number) enable_continuous_backup = optional(bool) lifecycle = object({ delete_after = number cold_storage_after = optional(number) }) copy_action = optional(object({ delete_after = optional(number) })) }))) })
|
{ "air_gapped_kms_key_arn": "", "enable": true, "lambda_backup_cron": "cron(0 6 * * ? *)", "lambda_timeout_seconds": 300, "rules": [ { "copy_action": { "delete_after": 365 }, "lifecycle": { "delete_after": 35 }, "name": "daily_kept_5_weeks", "schedule": "cron(0 0 * * ? *)" }, { "copy_action": { "delete_after": 365 }, "lifecycle": { "delete_after": 90 }, "name": "weekly_kept_3_months", "schedule": "cron(0 1 ? * SUN *)" }, { "copy_action": { "delete_after": 365 }, "lifecycle": { "cold_storage_after": 30, "delete_after": 2555 }, "name": "monthly_kept_7_years", "schedule": "cron(0 2 1 * ? *)" }, { "copy_action": { "delete_after": 365 }, "enable_continuous_backup": true, "lifecycle": { "delete_after": 35 }, "name": "point_in_time_recovery", "schedule": "cron(0 5 * * ? *)" } ], "s3_bucket_name": "", "selection_tag": "BackupParameterStore", "selection_tag_value": "True", "selection_tags": [] }
| no | -| [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 | -| [environment\_name](#input\_environment\_name) | The name of the environment where AWS Backup is configured. | `string` | n/a | yes | -| [name\_prefix](#input\_name\_prefix) | Optional name prefix for vault resources | `string` | `null` | no | -| [notifications\_target\_email\_address](#input\_notifications\_target\_email\_address) | The email address to which backup notifications will be sent via SNS. | `string` | `""` | no | -| [project\_name](#input\_project\_name) | The name of the project this relates to. | `string` | n/a | yes | -| [reports\_bucket](#input\_reports\_bucket) | Bucket to drop backup reports into | `string` | n/a | yes | -| [restore\_testing\_plan\_algorithm](#input\_restore\_testing\_plan\_algorithm) | Algorithm of the Recovery Selection Point | `string` | `"LATEST_WITHIN_WINDOW"` | no | -| [restore\_testing\_plan\_recovery\_point\_types](#input\_restore\_testing\_plan\_recovery\_point\_types) | Recovery Point Types | `list(string)` |
[
"SNAPSHOT"
]
| no | -| [restore\_testing\_plan\_scheduled\_expression](#input\_restore\_testing\_plan\_scheduled\_expression) | Scheduled Expression of Recovery Selection Point | `string` | `"cron(0 1 ? * SUN *)"` | no | -| [restore\_testing\_plan\_selection\_window\_days](#input\_restore\_testing\_plan\_selection\_window\_days) | Selection window days | `number` | `7` | no | -| [restore\_testing\_plan\_start\_window](#input\_restore\_testing\_plan\_start\_window) | Start window from the scheduled time during which the test should start | `number` | `1` | no | -| [terraform\_role\_arn](#input\_terraform\_role\_arn) | ARN of Terraform role used to deploy to account | `string` | n/a | yes | -| [enable_logically_air_gapped_vault](#input\_terraform\_role\_arn) | Enable backing up to Logically Air-gapped Vault for supported resources | `string` | `false` | no | -| [logically_air_gapped_vault_lock_min_retention_days](#input\_terraform\_role\_arn) | The minimum retention period that the Logically Air-gapped Vault retains its recovery points | `number` | `35` | no | -| [logically_air_gapped_vault_lock_max_retention_days](#input\_terraform\_role\_arn) | The maximum retention period that the Logically Air-gapped Vault retains its recovery points | `number` | `365` | no | +| Name | Description | Type | Default | Required | +|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|------|---------|:--------:| +| [backup\_copy\_vault\_account\_id](#input\_backup\_copy\_vault\_account\_id) | The account id of the destination backup vault for allowing restores back into the source account. | `string` | `""` | no | +| [backup\_copy\_vault\_arn](#input\_backup\_copy\_vault\_arn) | The ARN of the destination backup vault for cross-account backup copies. | `string` | `""` | no | +| [backup\_plan\_config](#input\_backup\_plan\_config) | Configuration for backup plans |
object({
selection_tag = string
selection_tag_value = optional(string)
selection_tags = optional(list(object({
key = optional(string)
value = optional(string)
})))
compliance_resource_types = list(string)
rules = list(object({
name = string
schedule = string
enable_continuous_backup = optional(bool)
lifecycle = object({
delete_after = optional(number)
cold_storage_after = optional(number)
})
copy_action = optional(object({
delete_after = optional(number)
}))
}))
})
|
{
"compliance_resource_types": [
"S3"
],
"rules": [
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 35
},
"name": "daily_kept_5_weeks",
"schedule": "cron(0 0 * * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 90
},
"name": "weekly_kept_3_months",
"schedule": "cron(0 1 ? * SUN *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"cold_storage_after": 30,
"delete_after": 2555
},
"name": "monthly_kept_7_years",
"schedule": "cron(0 2 1 * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"enable_continuous_backup": true,
"lifecycle": {
"delete_after": 35
},
"name": "point_in_time_recovery",
"schedule": "cron(0 5 * * ? *)"
}
],
"selection_tag": "BackupLocal",
"selection_tag_value": "True",
"selection_tags": []
}
| no | +| [backup\_plan\_config\_dynamodb](#input\_backup\_plan\_config\_dynamodb) | Configuration for backup plans with dynamodb |
object({
enable = bool
selection_tag = string
selection_tag_value = optional(string)
selection_tags = optional(list(object({
key = optional(string)
value = optional(string)
})))
compliance_resource_types = list(string)
rules = optional(list(object({
name = string
schedule = string
enable_continuous_backup = optional(bool)
lifecycle = object({
delete_after = number
cold_storage_after = optional(number)
})
copy_action = optional(object({
delete_after = optional(number)
}))
})))
})
|
{
"compliance_resource_types": [
"DynamoDB"
],
"enable": true,
"rules": [
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 35
},
"name": "dynamodb_daily_kept_5_weeks",
"schedule": "cron(0 0 * * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 90
},
"name": "dynamodb_weekly_kept_3_months",
"schedule": "cron(0 1 ? * SUN *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"cold_storage_after": 30,
"delete_after": 2555
},
"name": "dynamodb_monthly_kept_7_years",
"schedule": "cron(0 2 1 * ? *)"
}
],
"selection_tag": "BackupDynamoDB",
"selection_tag_value": "True",
"selection_tags": []
}
| no | +| [backup_plan_config_aurora](#input_backup_plan_config_aurora) | Configuration for backup plans with aurora |
object({
enable = bool
selection_tag = string
compliance_resource_types = list(string)
restore_testing_overrides = optional(string)
rules = optional(list(object({
name = string
schedule = string
enable_continuous_backup = optional(bool)
lifecycle = object({
delete_after = number
cold_storage_after = optional(number)
})
copy_action = optional(object({
delete_after = optional(number)
}))
})))
})
|
{
"compliance_resource_types": [
"Aurora"
],
"enable": true,
"restore_testing_overrides" : "{\"dbsubnetgroupname\": \"test-subnet\"}",
"rules": [
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 35
},
"name": "aurora_daily_kept_5_weeks",
"schedule": "cron(0 0 * * ? *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"delete_after": 90
},
"name": "aurora_weekly_kept_3_months",
"schedule": "cron(0 1 ? * SUN *)"
},
{
"copy_action": {
"delete_after": 365
},
"lifecycle": {
"cold_storage_after": 30,
"delete_after": 2555
},
"name": "aurora_monthly_kept_7_years",
"schedule": "cron(0 2 1 * ? *)"
}
],
"selection_tag": "BackupAurora"
}
| no | +| [parameter_store_backup_config](#input_backup_plan_config_parameter_store) | Configuration for the scheduled Lambda function to backup tagged Parameter Store parameters to S3. |
object({ enable = bool selection_tag = string selection_tag_value = optional(string) selection_tags = optional(list(object({ key = optional(string) value = optional(string) }))) lambda_backup_cron = optional(string) lambda_timeout_seconds = optional(number) air_gapped_kms_key_arn = optional(string) s3_bucket_name = optional(string) rules = optional(list(object({ name = string schedule = string completion_window = optional(number) enable_continuous_backup = optional(bool) lifecycle = object({ delete_after = number cold_storage_after = optional(number) }) copy_action = optional(object({ delete_after = optional(number) })) }))) })
|
{ "air_gapped_kms_key_arn": "", "enable": true, "lambda_backup_cron": "cron(0 6 * * ? *)", "lambda_timeout_seconds": 300, "rules": [ { "copy_action": { "delete_after": 365 }, "lifecycle": { "delete_after": 35 }, "name": "daily_kept_5_weeks", "schedule": "cron(0 0 * * ? *)" }, { "copy_action": { "delete_after": 365 }, "lifecycle": { "delete_after": 90 }, "name": "weekly_kept_3_months", "schedule": "cron(0 1 ? * SUN *)" }, { "copy_action": { "delete_after": 365 }, "lifecycle": { "cold_storage_after": 30, "delete_after": 2555 }, "name": "monthly_kept_7_years", "schedule": "cron(0 2 1 * ? *)" }, { "copy_action": { "delete_after": 365 }, "enable_continuous_backup": true, "lifecycle": { "delete_after": 35 }, "name": "point_in_time_recovery", "schedule": "cron(0 5 * * ? *)" } ], "s3_bucket_name": "", "selection_tag": "BackupParameterStore", "selection_tag_value": "True", "selection_tags": [] }
| no | +| [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 | +| [environment\_name](#input\_environment\_name) | The name of the environment where AWS Backup is configured. | `string` | n/a | yes | +| [name\_prefix](#input\_name\_prefix) | Optional name prefix for vault resources | `string` | `null` | no | +| [notifications\_target\_email\_address](#input\_notifications\_target\_email\_address) | The email address to which backup notifications will be sent via SNS. | `string` | `""` | no | +| [project\_name](#input\_project\_name) | The name of the project this relates to. | `string` | n/a | yes | +| [reports\_bucket](#input\_reports\_bucket) | Bucket to drop backup reports into | `string` | n/a | yes | +| [restore\_testing\_plan\_algorithm](#input\_restore\_testing\_plan\_algorithm) | Algorithm of the Recovery Selection Point | `string` | `"LATEST_WITHIN_WINDOW"` | no | +| [restore\_testing\_plan\_recovery\_point\_types](#input\_restore\_testing\_plan\_recovery\_point\_types) | Recovery Point Types | `list(string)` |
[
"SNAPSHOT"
]
| no | +| [restore\_testing\_plan\_scheduled\_expression](#input\_restore\_testing\_plan\_scheduled\_expression) | Scheduled Expression of Recovery Selection Point | `string` | `"cron(0 1 ? * SUN *)"` | no | +| [restore\_testing\_plan\_selection\_window\_days](#input\_restore\_testing\_plan\_selection\_window\_days) | Selection window days | `number` | `7` | no | +| [restore\_testing\_plan\_start\_window](#input\_restore\_testing\_plan\_start\_window) | Start window from the scheduled time during which the test should start | `number` | `1` | no | +| [terraform\_role\_arn](#input\_terraform\_role\_arn) | ARN of Terraform role used to deploy to account | `string` | n/a | yes | +| [enable_logically_air_gapped_vault](#input\_terraform\_role\_arn) | Enable backing up to Logically Air-gapped Vault for supported resources | `bool` | `false` | no | +| [logically_air_gapped_vault_lock_min_retention_days](#input\_terraform\_role\_arn) | The minimum retention period that the Logically Air-gapped Vault retains its recovery points | `number` | `35` | no | +| [logically_air_gapped_vault_lock_max_retention_days](#input\_terraform\_role\_arn) | The maximum retention period that the Logically Air-gapped Vault retains its recovery points | `number` | `365` | no | ## Outputs @@ -107,3 +107,4 @@ No modules. | logically_air_gapped_vault_arn | ARN of the of the Logically Air-gapped Vault | | logically_air_gapped_vault_name | Name of the of the Logically Air-gapped Vault | + From d3d48094f909dce25798171b5c10069ec5fee07f Mon Sep 17 00:00:00 2001 From: Michel Fasen Date: Mon, 16 Feb 2026 15:39:42 +0000 Subject: [PATCH 7/7] Make KMS statement for backup account conditional if not provided --- modules/aws-backup-source/kms.tf | 33 +++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/modules/aws-backup-source/kms.tf b/modules/aws-backup-source/kms.tf index e8a07a2..ae5d543 100644 --- a/modules/aws-backup-source/kms.tf +++ b/modules/aws-backup-source/kms.tf @@ -36,21 +36,24 @@ data "aws_iam_policy_document" "backup_key_policy" { actions = ["kms:*"] resources = ["*"] } - statement { - sid = "Allow attachment of persistent resources" - principals { - type = "AWS" - identifiers = ["arn:aws:iam::${var.backup_copy_vault_account_id}:root"] + dynamic "statement" { + for_each = var.backup_copy_vault_arn != "" && var.backup_copy_vault_account_id != "" ? [1] : [] + content { + sid = "Allow attachment of persistent resources" + principals { + type = "AWS" + identifiers = ["arn:aws:iam::${var.backup_copy_vault_account_id}:root"] + } + actions = [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:CreateGrant", + "kms:ListGrants", + "kms:DescribeKey" + ] + resources = ["*"] } - actions = [ - "kms:Encrypt", - "kms:Decrypt", - "kms:ReEncrypt*", - "kms:GenerateDataKey*", - "kms:CreateGrant", - "kms:ListGrants", - "kms:DescribeKey" - ] - resources = ["*"] } }