diff --git a/modules/aws-backup-source/README.md b/modules/aws-backup-source/README.md index 2bcc0ec..5469fc1 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 | @@ -91,8 +93,18 @@ No modules. | [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 -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/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..aa24ca4 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.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/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 = ["*"] } } diff --git a/modules/aws-backup-source/outputs.tf b/modules/aws-backup-source/outputs.tf index 96ab936..0fc5ff6 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 "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 "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" } diff --git a/modules/aws-backup-source/variables.tf b/modules/aws-backup-source/variables.tf index 6ca9252..8c98c6f 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 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" + 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" + type = number + default = 365 +}