# Backup Spoke ================================================================= # Local vault(s) --------------------------------------------------------------- # Created in this spoke account, alongside the resources it protects. # Most stacks need exactly one vault — see README for the multi-vault case. resource "aws_backup_vault" "this" { for_each = var.vaults name = "${var.name_prefix}-${local.region_abbr}-${each.value.name_suffix}" kms_key_arn = each.value.kms_key_arn tags = merge(var.tags, local.module_tags, { Name = "${var.name_prefix}-${local.region_abbr}-${each.value.name_suffix}" resource-type = "backup-vault" }) } # Backup plans ----------------------------------------------------------------- # One plan per job, one rule per plan. This is a deliberate simplification — # AWS Backup allows multiple rules per plan and multiple selections per plan, # but that flexibility goes unused here in favor of one clearly-named plan # per job. Two jobs with identical schedules are two plans, not a shared one. resource "aws_backup_plan" "this" { for_each = var.jobs name = "${var.name_prefix}-${local.region_abbr}-${each.key}-plan" rule { rule_name = "${var.name_prefix}-${local.region_abbr}-${each.key}-rule" target_vault_name = aws_backup_vault.this[each.value.target_vault_key].name schedule = each.value.schedule start_window = each.value.start_window_minutes completion_window = each.value.completion_window_minutes enable_continuous_backup = each.value.enable_continuous_backup lifecycle { delete_after = each.value.delete_after_days cold_storage_after = each.value.cold_storage_after_days } dynamic "copy_action" { for_each = each.value.copy_action != null ? [each.value.copy_action] : [] content { destination_vault_arn = copy_action.value.destination_vault_arn lifecycle { delete_after = copy_action.value.delete_after_days cold_storage_after = copy_action.value.cold_storage_after_days } } } } tags = merge(var.tags, local.module_tags, { Name = "${var.name_prefix}-${local.region_abbr}-${each.key}-plan" resource-type = "backup-plan" }) } # Backup selections ------------------------------------------------------------ # resource_arns takes precedence over selection_tags when both are set — # the validation on var.jobs only requires at least one to be non-empty, # it does not forbid setting both. resource "aws_backup_selection" "this" { for_each = var.jobs name = "${var.name_prefix}-${local.region_abbr}-${each.key}-selection" plan_id = aws_backup_plan.this[each.key].id iam_role_arn = each.value.iam_role_arn resources = length(each.value.resource_arns) > 0 ? each.value.resource_arns : null not_resources = length(each.value.not_resource_arns) > 0 ? each.value.not_resource_arns : null dynamic "condition" { for_each = length(each.value.resource_arns) == 0 && length(each.value.selection_tags) > 0 ? [each.value.selection_tags] : [] content { dynamic "string_equals" { for_each = condition.value content { key = "aws:ResourceTag/${string_equals.key}" value = string_equals.value } } } } }