1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
# 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
}
}
}
}
}
|