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
+}