diff --git a/docs/usage.md b/docs/usage.md index bfc3b8a..c05fad3 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -60,6 +60,7 @@ module "my_backup" { backup_vault_immutability = "Unlocked" log_analytics_workspace_id = azurerm_log_analytics_workspace.my_workspace.id use_extended_retention = true + soft_delete = "Off" tags = { tagOne = "tagOneValue" @@ -76,29 +77,33 @@ module "my_backup" { storage_account_containers = ["container1", "container2"] } backup2 = { - backup_name = "storage2" - retention_period = "P30D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P2D"] - storage_account_id = azurerm_storage_account.my_storage_account_2.id - storage_account_containers = ["container1", "container2"] + backup_name = "storage2" + retention_period = "P30D" + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P2D"] + storage_account_id = azurerm_storage_account.my_storage_account_2.id + storage_account_containers = ["container1", "container2"] + backup_policy_naming_template = "nhsuk-{resource_abbreviation}-{resource_type}-{backup_name}" + backup_instance_naming_template = "nhsuk-{resource_abbreviation}-{resource_type}-{backup_name}" } } managed_disk_backups = { backup1 = { - backup_name = "disk1" - retention_period = "P7D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1D"] - managed_disk_id = azurerm_managed_disk.my_managed_disk_1.id + backup_name = "disk1" + retention_period = "P7D" + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1D"] + managed_disk_id = azurerm_managed_disk.my_managed_disk_1.id managed_disk_resource_group = { id = azurerm_resource_group.my_resource_group.id name = azurerm_resource_group.my_resource_group.name } } backup2 = { - backup_name = "disk2" - retention_period = "P30D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P2D"] - managed_disk_id = azurerm_managed_disk.my_managed_disk_2.id + backup_name = "disk2" + retention_period = "P30D" + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P2D"] + managed_disk_id = azurerm_managed_disk.my_managed_disk_2.id + backup_policy_naming_template = "nhsuk-{resource_abbreviation}-{resource_type}-{backup_name}" + backup_instance_naming_template = "nhsuk-{resource_abbreviation}-{resource_type}-{backup_name}" managed_disk_resource_group = { id = azurerm_resource_group.my_resource_group.id name = azurerm_resource_group.my_resource_group.name @@ -107,18 +112,20 @@ module "my_backup" { } postgresql_flexible_server_backups = { backup1 = { - backup_name = "server1" - retention_period = "P7D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1D"] - server_id = azurerm_postgresql_flexible_server.my_server_1.id + backup_name = "server1" + retention_period = "P7D" + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1D"] + server_id = azurerm_postgresql_flexible_server.my_server_1.id server_resource_group_id = azurerm_resource_group.my_resource_group.id } backup2 = { - backup_name = "server2" - retention_period = "P30D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P2D"] - server_id = azurerm_postgresql_flexible_server.my_server_2.id - server_resource_group_id = azurerm_resource_group.my_resource_group.id + backup_name = "server2" + retention_period = "P30D" + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P2D"] + server_id = azurerm_postgresql_flexible_server.my_server_2.id + server_resource_group_id = azurerm_resource_group.my_resource_group.id + backup_policy_naming_template = "nhsuk-{resource_abbreviation}-{resource_type}-{backup_name}" + backup_instance_naming_template = "nhsuk-{resource_abbreviation}-{resource_type}-{backup_name}" } } } @@ -133,6 +140,7 @@ module "my_backup" { | `create_resource_group` | States whether a resource group should be created. Setting this to `false` means the vault will be deployed into an externally managed resource group, the name of which is defined in `resource_group_name`. | No | `true` | | `backup_vault_name` | The name of the backup vault. The value supplied will be automatically prefixed with `rg-nhsbackup-`. If more than one az-backup module is created, this value must be unique across them. | Yes | n/a | | `backup_vault_redundancy` | The redundancy of the vault, e.g. `GeoRedundant`. [See the following link for the possible values.](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/data_protection_backup_vault#redundancy) | No | `LocallyRedundant` | +| `soft_delete` | The state of soft delete for this Backup Vault, e.g. `On`. [See the following link for the possible values.](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/data_protection_backup_vault#soft_delete) | No | `Off` | | `backup_vault_immutability` | The immutability of the vault, e.g. `Locked`. [See the following link for the possible values.](https://learn.microsoft.com/en-us/azure/templates/microsoft.dataprotection/backupvaults?pivots=deployment-language-terraform#immutabilitysettings-2) | No | `Disabled` | | `log_analytics_workspace_id` | The id of the log analytics workspace that backup telemetry and diagnostics should be sent to. **NOTE** this variable was made mandatory in v2 of the module. | Yes | n/a | | `tags` | A map of tags which will be applied to the resource group and backup vault. When no tags are specified then no tags are added. NOTE when using an externally managed resource group the tags will not be applied to it (they will still be applied to the backup vault). | No | n/a | @@ -143,14 +151,22 @@ module "my_backup" { | `blob_storage_backups.backup_name` | The name of the backup, which must be unique across blob storage backups. | Yes | n/a | | `blob_storage_backups.retention_period` | How long the backed up data will be retained for, which should be in `ISO 8601` duration format. This must be specified in days, and can be up to 7 days unless `use_extended_retention` is on. [See the following link for more information about the format](https://en.wikipedia.org/wiki/ISO_8601#Durations). | Yes | n/a | | `blob_storage_backups.backup_intervals` | A list of intervals at which backups should be taken, which should be in `ISO 8601` duration format. [See the following link for the possible values](https://en.wikipedia.org/wiki/ISO_8601#Time_intervals). | Yes | n/a | +| `blob_storage_backups.backup_policy_naming_template` | Naming template used to construct the blob backup instance name. The following placeholders are supported and will be replaced by the module: `{resource_abbreviation}` → `bkpol`, `{resource_type}` → `blob`, `{backup_name}` → value of `blob_storage_backups.backup_name` | No | {resource_abbreviation}-{resource_type}-{backup_name} | +| `blob_storage_backups.backup_instance_naming_template` | Naming template used to construct the blob backup instance name. The following placeholders are supported and will be replaced by the module: `{resource_abbreviation}` → `bkinst`, `{resource_type}` → `blob`, `{backup_name}` → value of `blob_storage_backups.backup_name` | No | {resource_abbreviation}-{resource_type}-{backup_name} | +| `blob_storage_backups.time_zone` | The time zone to apply to the backup policy schedule (eg. Europe/London). If not specified, Azure’s default time zone behaviour is used. | No | n/a | +| `blob_storage_backups.enable_daily_retention_rule` | Enables an additional daily retention rule on the backup policy. This is optional and intended for scenarios that require explicit daily retention behaviour beyond the default policy configuration. | No | false | | `managed_disk_backups` | A map of managed disk backups that should be created. For each backup the following values should be provided: `managed_disk_id`, `backup_name` and `retention_period`. When no value is provided then no backups are created. | No | n/a | | `managed_disk_backups.managed_disk_id` | The id of the managed disk that should be backed up. | Yes | n/a | | `managed_disk_backups.backup_name` | The name of the backup, which must be unique across managed disk backups. | Yes | n/a | | `managed_disk_backups.retention_period` | How long the backed up data will be retained for, which should be in `ISO 8601` duration format. This must be specified in days, and can be up to 7 days unless `use_extended_retention` is on. [See the following link for more information about the format](https://en.wikipedia.org/wiki/ISO_8601#Durations). | Yes | n/a | | `managed_disk_backups.backup_intervals` | A list of intervals at which backups should be taken, which should be in `ISO 8601` duration format. [See the following link for the possible values](https://en.wikipedia.org/wiki/ISO_8601#Time_intervals). | Yes | n/a | +| `managed_disk_backup.backup_policy_naming_template` | Naming template used to construct the disk backup instance name. The following placeholders are supported and will be replaced by the module: `{resource_abbreviation}` → `bkpol`, `{resource_type}` → `disk`, `{backup_name}` → value of `managed_disk_backup.backup_name` | No | {resource_abbreviation}-{resource_type}-{backup_name} | +| `managed_disk_backup.backup_instance_naming_template` | Naming template used to construct the disk backup instance name. The following placeholders are supported and will be replaced by the module: `{resource_abbreviation}` → `bkinst`, `{resource_type}` → `disk`, `{backup_name}` → value of `managed_disk_backup.backup_name` | No | {resource_abbreviation}-{resource_type}-{backup_name} | | `postgresql_flexible_server_backups` | A map of postgresql flexible server backups that should be created. For each backup the following values should be provided: `backup_name`, `server_id`, `server_resource_group_id`, `retention_period` and `backup_intervals`. When no value is provided then no backups are created. | No | n/a | | `postgresql_flexible_server_backups.backup_name` | The name of the backup, which must be unique across postgresql flexible server backups. | Yes | n/a | | `postgresql_flexible_server_backups.server_id` | The id of the postgresql flexible server that should be backed up. | Yes | n/a | | `postgresql_flexible_server_backups.server_resource_group_id` | The id of the resource group which the postgresql flexible server resides in. | Yes | n/a | | `postgresql_flexible_server_backups.retention_period` | How long the backed up data will be retained for, which should be in `ISO 8601` duration format. This must be specified in days, and can be up to 7 days unless `use_extended_retention` is on. [See the following link for more information about the format](https://en.wikipedia.org/wiki/ISO_8601#Durations). | Yes | n/a | | `postgresql_flexible_server_backups.backup_intervals` | A list of intervals at which backups should be taken, which should be in `ISO 8601` duration format. [See the following link for the possible values](https://en.wikipedia.org/wiki/ISO_8601#Time_intervals). | Yes | n/a | +| `postgresql_flexible_server_backup.backup_policy_naming_template` | Naming template used to construct the pgflex server backup instance name. The following placeholders are supported and will be replaced by the module: `{resource_abbreviation}` → `bkpol`, `{resource_type}` → `pgflex`, `{backup_name}` → value of `postgresql_flexible_server_backup.backup_name` | No | {resource_abbreviation}-{resource_type}-{backup_name} | +| `postgresql_flexible_server_backup.backup_instance_naming_template` | Naming template used to construct the pgflex server backup instance name. The following placeholders are supported and will be replaced by the module: `{resource_abbreviation}` → `bkinst`, `{resource_type}` → `pgflex`, `{backup_name}` → value of `postgresql_flexible_server_backup.backup_name` | No | {resource_abbreviation}-{resource_type}-{backup_name} | diff --git a/infrastructure/backup_modules.tf b/infrastructure/backup_modules.tf index 6ada0cb..7d9d1a5 100644 --- a/infrastructure/backup_modules.tf +++ b/infrastructure/backup_modules.tf @@ -1,12 +1,16 @@ module "blob_storage_backup" { - for_each = var.blob_storage_backups - source = "./modules/backup/blob_storage" - vault = azurerm_data_protection_backup_vault.backup_vault - backup_name = each.value.backup_name - retention_period = each.value.retention_period - backup_intervals = each.value.backup_intervals - storage_account_id = each.value.storage_account_id - storage_account_containers = each.value.storage_account_containers + for_each = var.blob_storage_backups + source = "./modules/backup/blob_storage" + vault = azurerm_data_protection_backup_vault.backup_vault + backup_name = each.value.backup_name + retention_period = each.value.retention_period + backup_intervals = each.value.backup_intervals + storage_account_id = each.value.storage_account_id + storage_account_containers = each.value.storage_account_containers + backup_policy_naming_template = each.value.backup_policy_naming_template + backup_instance_naming_template = each.value.backup_instance_naming_template + time_zone = try(each.value.time_zone, null) + enable_daily_retention_rule = try(each.value.enable_daily_retention_rule, false) } @@ -20,6 +24,8 @@ module "managed_disk_backup" { managed_disk_id = each.value.managed_disk_id managed_disk_resource_group = each.value.managed_disk_resource_group assign_resource_group_level_roles = each.key == keys(var.managed_disk_backups)[0] ? true : false + backup_policy_naming_template = each.value.backup_policy_naming_template + backup_instance_naming_template = each.value.backup_instance_naming_template } @@ -33,5 +39,7 @@ module "postgresql_flexible_server_backup" { server_id = each.value.server_id server_resource_group_id = each.value.server_resource_group_id assign_resource_group_level_roles = each.key == keys(var.postgresql_flexible_server_backups)[0] ? true : false + backup_policy_naming_template = each.value.backup_policy_naming_template + backup_instance_naming_template = each.value.backup_instance_naming_template } diff --git a/infrastructure/backup_vault.tf b/infrastructure/backup_vault.tf index 538a5af..d5924d2 100644 --- a/infrastructure/backup_vault.tf +++ b/infrastructure/backup_vault.tf @@ -4,7 +4,7 @@ resource "azurerm_data_protection_backup_vault" "backup_vault" { location = local.resource_group.location datastore_type = "VaultStore" redundancy = var.backup_vault_redundancy - soft_delete = "Off" + soft_delete = var.backup_vault_soft_delete immutability = var.backup_vault_immutability tags = var.tags identity { diff --git a/infrastructure/modules/backup/blob_storage/backup_instance.tf b/infrastructure/modules/backup/blob_storage/backup_instance.tf index e7671f9..f143d75 100644 --- a/infrastructure/modules/backup/blob_storage/backup_instance.tf +++ b/infrastructure/modules/backup/blob_storage/backup_instance.tf @@ -6,7 +6,7 @@ resource "azurerm_role_assignment" "role_assignment" { } resource "azurerm_data_protection_backup_instance_blob_storage" "backup_instance" { - name = "bkinst-blob-${var.backup_name}" + name = local.backup_instance_name vault_id = var.vault.id location = var.vault.location storage_account_id = var.storage_account_id diff --git a/infrastructure/modules/backup/blob_storage/backup_policy.tf b/infrastructure/modules/backup/blob_storage/backup_policy.tf index 815f5eb..e11ba50 100644 --- a/infrastructure/modules/backup/blob_storage/backup_policy.tf +++ b/infrastructure/modules/backup/blob_storage/backup_policy.tf @@ -1,6 +1,24 @@ resource "azurerm_data_protection_backup_policy_blob_storage" "backup_policy" { - name = "bkpol-blob-${var.backup_name}" + name = local.backup_policy_name vault_id = var.vault.id vault_default_retention_duration = var.retention_period backup_repeating_time_intervals = var.backup_intervals + time_zone = var.time_zone + + dynamic "retention_rule" { + for_each = coalesce(var.enable_daily_retention_rule, false) ? [1] : [] + content { + name = "daily-retention" + priority = 9 + + criteria { + absolute_criteria = "AllBackup" + } + + life_cycle { + data_store_type = "VaultStore" + duration = var.retention_period + } + } + } } diff --git a/infrastructure/modules/backup/blob_storage/locals.tf b/infrastructure/modules/backup/blob_storage/locals.tf new file mode 100644 index 0000000..4734508 --- /dev/null +++ b/infrastructure/modules/backup/blob_storage/locals.tf @@ -0,0 +1,22 @@ +locals { + + resource_type = "blob" + + # Render names using templates + backup_policy_name = replace( + replace( + replace(var.backup_policy_naming_template, "{resource_abbreviation}", "bkpol"), + "{resource_type}", local.resource_type + ), + "{backup_name}", var.backup_name + ) + + backup_instance_name = replace( + replace( + replace(var.backup_instance_naming_template, "{resource_abbreviation}", "bkinst"), + "{resource_type}", local.resource_type + ), + "{backup_name}", var.backup_name + ) + +} diff --git a/infrastructure/modules/backup/blob_storage/variables.tf b/infrastructure/modules/backup/blob_storage/variables.tf index 6a22a36..5649224 100644 --- a/infrastructure/modules/backup/blob_storage/variables.tf +++ b/infrastructure/modules/backup/blob_storage/variables.tf @@ -21,3 +21,23 @@ variable "storage_account_id" { variable "storage_account_containers" { type = list(string) } + +variable "backup_policy_naming_template" { + type = string + default = "{resource_abbreviation}-{resource_type}-{backup_name}" +} + +variable "backup_instance_naming_template" { + type = string + default = "{resource_abbreviation}-{resource_type}-{backup_name}" +} + +variable "time_zone" { + type = string + default = null +} + +variable "enable_daily_retention_rule" { + type = bool + default = false +} diff --git a/infrastructure/modules/backup/managed_disk/backup_instance.tf b/infrastructure/modules/backup/managed_disk/backup_instance.tf index ecb12a7..6aebaa3 100644 --- a/infrastructure/modules/backup/managed_disk/backup_instance.tf +++ b/infrastructure/modules/backup/managed_disk/backup_instance.tf @@ -14,7 +14,7 @@ resource "azurerm_role_assignment" "role_assignment_backup_reader" { } resource "azurerm_data_protection_backup_instance_disk" "backup_instance" { - name = "bkinst-disk-${var.backup_name}" + name = local.backup_instance_name vault_id = var.vault.id location = var.vault.location disk_id = var.managed_disk_id diff --git a/infrastructure/modules/backup/managed_disk/backup_policy.tf b/infrastructure/modules/backup/managed_disk/backup_policy.tf index f6a12e1..fd9d5a2 100644 --- a/infrastructure/modules/backup/managed_disk/backup_policy.tf +++ b/infrastructure/modules/backup/managed_disk/backup_policy.tf @@ -1,5 +1,5 @@ resource "azurerm_data_protection_backup_policy_disk" "backup_policy" { - name = "bkpol-disk-${var.backup_name}" + name = local.backup_policy_name vault_id = var.vault.id default_retention_duration = var.retention_period backup_repeating_time_intervals = var.backup_intervals diff --git a/infrastructure/modules/backup/managed_disk/locals.tf b/infrastructure/modules/backup/managed_disk/locals.tf new file mode 100644 index 0000000..1f5474b --- /dev/null +++ b/infrastructure/modules/backup/managed_disk/locals.tf @@ -0,0 +1,22 @@ +locals { + + resource_type = "disk" + + # Render names using templates + backup_policy_name = replace( + replace( + replace(var.backup_policy_naming_template, "{resource_abbreviation}", "bkpol"), + "{resource_type}", local.resource_type + ), + "{backup_name}", var.backup_name + ) + + backup_instance_name = replace( + replace( + replace(var.backup_instance_naming_template, "{resource_abbreviation}", "bkinst"), + "{resource_type}", local.resource_type + ), + "{backup_name}", var.backup_name + ) + +} diff --git a/infrastructure/modules/backup/managed_disk/variables.tf b/infrastructure/modules/backup/managed_disk/variables.tf index f3b92da..25893e2 100644 --- a/infrastructure/modules/backup/managed_disk/variables.tf +++ b/infrastructure/modules/backup/managed_disk/variables.tf @@ -28,3 +28,13 @@ variable "managed_disk_resource_group" { variable "assign_resource_group_level_roles" { type = bool } + +variable "backup_policy_naming_template" { + type = string + default = "{resource_abbreviation}-{resource_type}-{backup_name}" +} + +variable "backup_instance_naming_template" { + type = string + default = "{resource_abbreviation}-{resource_type}-{backup_name}" +} diff --git a/infrastructure/modules/backup/postgresql_flexible_server/backup_instance.tf b/infrastructure/modules/backup/postgresql_flexible_server/backup_instance.tf index fe1f5d0..a7b96d6 100644 --- a/infrastructure/modules/backup/postgresql_flexible_server/backup_instance.tf +++ b/infrastructure/modules/backup/postgresql_flexible_server/backup_instance.tf @@ -14,7 +14,7 @@ resource "azurerm_role_assignment" "role_assignment_long_term_retention_backup_r } resource "azurerm_data_protection_backup_instance_postgresql_flexible_server" "backup_instance" { - name = "bkinst-pgflex-${var.backup_name}" + name = local.backup_instance_name vault_id = var.vault.id location = var.vault.location server_id = var.server_id diff --git a/infrastructure/modules/backup/postgresql_flexible_server/backup_policy.tf b/infrastructure/modules/backup/postgresql_flexible_server/backup_policy.tf index cf8eab3..3d15dee 100644 --- a/infrastructure/modules/backup/postgresql_flexible_server/backup_policy.tf +++ b/infrastructure/modules/backup/postgresql_flexible_server/backup_policy.tf @@ -1,5 +1,5 @@ resource "azurerm_data_protection_backup_policy_postgresql_flexible_server" "backup_policy" { - name = "bkpol-pgflex-${var.backup_name}" + name = local.backup_policy_name vault_id = var.vault.id backup_repeating_time_intervals = var.backup_intervals diff --git a/infrastructure/modules/backup/postgresql_flexible_server/locals.tf b/infrastructure/modules/backup/postgresql_flexible_server/locals.tf new file mode 100644 index 0000000..9cbcb7d --- /dev/null +++ b/infrastructure/modules/backup/postgresql_flexible_server/locals.tf @@ -0,0 +1,22 @@ +locals { + + resource_type = "pgflex" + + # Render names using templates + backup_policy_name = replace( + replace( + replace(var.backup_policy_naming_template, "{resource_abbreviation}", "bkpol"), + "{resource_type}", local.resource_type + ), + "{backup_name}", var.backup_name + ) + + backup_instance_name = replace( + replace( + replace(var.backup_instance_naming_template, "{resource_abbreviation}", "bkinst"), + "{resource_type}", local.resource_type + ), + "{backup_name}", var.backup_name + ) + +} diff --git a/infrastructure/modules/backup/postgresql_flexible_server/variables.tf b/infrastructure/modules/backup/postgresql_flexible_server/variables.tf index c889fd5..dd5bd7f 100644 --- a/infrastructure/modules/backup/postgresql_flexible_server/variables.tf +++ b/infrastructure/modules/backup/postgresql_flexible_server/variables.tf @@ -25,3 +25,13 @@ variable "server_resource_group_id" { variable "assign_resource_group_level_roles" { type = bool } + +variable "backup_policy_naming_template" { + type = string + default = "{resource_abbreviation}-{resource_type}-{backup_name}" +} + +variable "backup_instance_naming_template" { + type = string + default = "{resource_abbreviation}-{resource_type}-{backup_name}" +} diff --git a/infrastructure/variables.tf b/infrastructure/variables.tf index 30ac2bc..1fbe598 100644 --- a/infrastructure/variables.tf +++ b/infrastructure/variables.tf @@ -57,11 +57,15 @@ variable "use_extended_retention" { variable "blob_storage_backups" { description = "A map of blob storage backups to create" type = map(object({ - backup_name = string - retention_period = string - backup_intervals = list(string) - storage_account_id = string - storage_account_containers = list(string) + backup_name = string + retention_period = string + backup_intervals = list(string) + storage_account_id = string + storage_account_containers = list(string) + backup_policy_naming_template = optional(string, "{resource_abbreviation}-{resource_type}-{backup_name}") + backup_instance_naming_template = optional(string, "{resource_abbreviation}-{resource_type}-{backup_name}") + time_zone = optional(string) + enable_daily_retention_rule = optional(bool) })) default = {} @@ -93,6 +97,8 @@ variable "managed_disk_backups" { id = string name = string }) + backup_policy_naming_template = optional(string, "{resource_abbreviation}-{resource_type}-{backup_name}") + backup_instance_naming_template = optional(string, "{resource_abbreviation}-{resource_type}-{backup_name}") })) default = {} @@ -111,11 +117,13 @@ variable "managed_disk_backups" { variable "postgresql_flexible_server_backups" { description = "A map of postgresql flexible server backups to create" type = map(object({ - backup_name = string - retention_period = string - backup_intervals = list(string) - server_id = string - server_resource_group_id = string + backup_name = string + retention_period = string + backup_intervals = list(string) + server_id = string + server_resource_group_id = string + backup_policy_naming_template = optional(string, "{resource_abbreviation}-{resource_type}-{backup_name}") + backup_instance_naming_template = optional(string, "{resource_abbreviation}-{resource_type}-{backup_name}") })) default = {} @@ -130,3 +138,8 @@ variable "postgresql_flexible_server_backups" { error_message = "Invalid retention period: valid periods are up to 7 days. If you require a longer retention period then please set use_extended_retention to true." } } + +variable "backup_vault_soft_delete" { + type = string + default = "Off" +}