Skip to content
14 changes: 13 additions & 1 deletion modules/aws-backup-source/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -91,8 +93,18 @@ No modules.
| <a name="input_restore_testing_plan_selection_window_days"></a> [restore\_testing\_plan\_selection\_window\_days](#input\_restore\_testing\_plan\_selection\_window\_days) | Selection window days | `number` | `7` | no |
| <a name="input_restore_testing_plan_start_window"></a> [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 |
| <a name="input_terraform_role_arn"></a> [terraform\_role\_arn](#input\_terraform\_role\_arn) | ARN of Terraform role used to deploy to account | `string` | n/a | yes |
| <a name="input_enable_logically_air_gapped_vault"></a> [enable_logically_air_gapped_vault](#input\_terraform\_role\_arn) | Enable backing up to Logically Air-gapped Vault for supported resources | `bool` | `false` | no |
| <a name="input_logically_air_gapped_vault_lock_min_retention_days"></a> [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 |
| <a name="input_logically_air_gapped_vault_lock_max_retention_days"></a> [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 |
<!-- END_TF_DOCS -->

13 changes: 13 additions & 0 deletions modules/aws-backup-source/backup_notification.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
]
}
47 changes: 26 additions & 21 deletions modules/aws-backup-source/backup_plan.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -125,19 +129,20 @@ 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
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
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
Expand Down
8 changes: 4 additions & 4 deletions modules/aws-backup-source/backup_restore_testing.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ resource "awscc_backup_restore_testing_plan" "backup_restore_testing_plan" {
start_window_hours = var.restore_testing_plan_start_window
recovery_point_selection = {
algorithm = var.restore_testing_plan_algorithm
include_vaults = [aws_backup_vault.main.arn]
include_vaults = [aws_backup_vault.main.arn, var.enable_logically_air_gapped_vault ? aws_backup_logically_air_gapped_vault.main[0].arn : null]
recovery_point_types = var.restore_testing_plan_recovery_point_types
selection_window_days = var.restore_testing_plan_selection_window_days
}
Expand All @@ -20,7 +20,7 @@ resource "awscc_backup_restore_testing_selection" "backup_restore_testing_select
protected_resource_conditions = {
string_equals = [{
key = "aws:ResourceTag/${var.backup_plan_config_dynamodb.selection_tag}"
value = "True"
value = (var.backup_plan_config_dynamodb.selection_tag_value == null) ? "True" : var.backup_plan_config_dynamodb.selection_tag_value
}]
}
}
Expand All @@ -36,7 +36,7 @@ resource "awscc_backup_restore_testing_selection" "backup_restore_testing_select
protected_resource_conditions = {
string_equals = [{
key = "aws:ResourceTag/${var.backup_plan_config_ebsvol.selection_tag}"
value = "True"
value = (var.backup_plan_config_ebsvol.selection_tag_value == null) ? "True" : var.backup_plan_config_ebsvol.selection_tag_value
}]
}
}
Expand All @@ -51,7 +51,7 @@ resource "awscc_backup_restore_testing_selection" "backup_restore_testing_select
protected_resource_conditions = {
string_equals = [{
key = "aws:ResourceTag/${var.backup_plan_config_aurora.selection_tag}"
value = "True"
value = (var.backup_plan_config_aurora.selection_tag_value == null) ? "True" : var.backup_plan_config_aurora.selection_tag_value
}]
}
restore_metadata_overrides = local.aurora_overrides
Expand Down
7 changes: 7 additions & 0 deletions modules/aws-backup-source/backup_vault.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
33 changes: 18 additions & 15 deletions modules/aws-backup-source/kms.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ["*"]
}
}
14 changes: 12 additions & 2 deletions modules/aws-backup-source/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
24 changes: 24 additions & 0 deletions modules/aws-backup-source/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -520,3 +520,27 @@ 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
}

variable "logically_air_gapped_vault_approval_team_arn" {
description = "The ARN of the Multi-party approval Team to be assigned to the logically air-gapped vault"
type = string
default = null
}