diff --git a/.github/workflows/ci-pipeline.yaml b/.github/workflows/ci-pipeline.yaml index 2736c96..7b50c2a 100644 --- a/.github/workflows/ci-pipeline.yaml +++ b/.github/workflows/ci-pipeline.yaml @@ -12,6 +12,15 @@ on: - main pull_request: workflow_dispatch: + inputs: + grype_version: + description: "Grype version to install" + default: "v0.110.0" + type: string + grype_commit_sha: + description: "Grype commit SHA for install script" + default: "dee8de483dfba5b4e0bc0aa8e4ab2ce52137e490" + type: string jobs: build-verification: @@ -27,7 +36,7 @@ jobs: with: terraform_version: 1.9.3 terraform_wrapper: false - + - name: Install Go uses: actions/setup-go@v6 with: @@ -97,17 +106,12 @@ jobs: - name: Run GitLeaks Scan run: gitleaks detect --source . --config .gitleaks.toml - - name: Install Trivy + - name: Install Grype run: | - sudo apt-get update - sudo apt-get install -y wget apt-transport-https gnupg lsb-release - wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add - - echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list - sudo apt-get update - sudo apt-get install -y trivy + curl -sSfL https://raw.githubusercontent.com/anchore/grype/${{ inputs.grype_commit_sha || 'dee8de483dfba5b4e0bc0aa8e4ab2ce52137e490' }}/install.sh | bash -s -- -b /usr/local/bin ${{ inputs.grype_version || 'v0.110.0' }} - - name: Run Trivy Scan - run: trivy filesystem --security-checks vuln,config --exit-code 1 --severity HIGH,CRITICAL --ignore-unfixed . + - name: Run Grype Filesystem Scan + run: grype "dir:./" --fail-on high publishing: name: Publish Release @@ -118,7 +122,7 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - persist-credentials: false # Disable default GITHUB_TOKEN persistence + persist-credentials: false # Disable default GITHUB_TOKEN persistence - name: Create Semantic Release uses: cycjimmy/semantic-release-action@v4.1.0 @@ -132,4 +136,4 @@ jobs: echo ${{ steps.semantic-release.outputs.new_release_version }} echo ${{ steps.semantic-release.outputs.new_release_major_version }} echo ${{ steps.semantic-release.outputs.new_release_minor_version }} - echo ${{ steps.semantic-release.outputs.new_release_patch_version }} \ No newline at end of file + echo ${{ steps.semantic-release.outputs.new_release_patch_version }} diff --git a/docs/usage.md b/docs/usage.md index c05fad3..90bfdfe 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -79,7 +79,7 @@ module "my_backup" { backup2 = { backup_name = "storage2" retention_period = "P30D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P2D"] + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1W"] 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}" @@ -100,7 +100,7 @@ module "my_backup" { backup2 = { backup_name = "disk2" retention_period = "P30D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P2D"] + backup_intervals = ["R/2024-01-01T00:00:00+00:00/PT12H"] 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}" @@ -114,14 +114,14 @@ module "my_backup" { backup1 = { backup_name = "server1" retention_period = "P7D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1D"] + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1W"] 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"] + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1W"] 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}" @@ -150,7 +150,7 @@ module "my_backup" { | `blob_storage_backups.storage_account_containers` | A list of containers in the storage account that should be backed up. | Yes | n/a | | `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_intervals` | A list of intervals at which backups should be taken, in `ISO 8601` repeating interval format. The frequency (duration) part must be `P1D` (daily) or `P1W` (weekly). [See the Azure Blob backup documentation for supported schedules](https://learn.microsoft.com/en-us/azure/backup/blob-backup-configure-manage). | 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 | @@ -159,7 +159,7 @@ module "my_backup" { | `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_backups.backup_intervals` | A list of intervals at which backups should be taken, in `ISO 8601` repeating interval format. The frequency (duration) part must be one of `PT1H`, `PT2H`, `PT4H`, `PT6H`, `PT8H`, `PT12H` (hourly) or `P1D` (daily). [See the Azure Disk backup support matrix for supported schedules](https://learn.microsoft.com/en-us/azure/backup/disk-backup-support-matrix). | 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 | @@ -167,6 +167,6 @@ module "my_backup" { | `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_backups.backup_intervals` | A list of intervals at which backups should be taken, in `ISO 8601` repeating interval format. Only `P1W` (weekly) is supported. [See the Azure PostgreSQL Flexible Server backup support matrix for supported schedules](https://learn.microsoft.com/en-us/azure/backup/backup-azure-database-postgresql-flex-support-matrix). | 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/variables.tf b/infrastructure/variables.tf index 1fbe598..89d8de0 100644 --- a/infrastructure/variables.tf +++ b/infrastructure/variables.tf @@ -1,6 +1,17 @@ locals { # The valid backup retention period - up to 7 days, which can be bypassed when use_extended_retention is set to true valid_retention_periods = [for days in range(1, 8) : "P${days}D"] + + # Valid backup interval frequencies per resource type + valid_blob_storage_intervals = ["P1D", "P1W"] + valid_managed_disk_intervals = ["PT1H", "PT2H", "PT4H", "PT6H", "PT8H", "PT12H", "P1D"] + valid_postgresql_flexible_server_intervals = ["P1W"] + + # Repeating interval format: R// + backup_interval_timestamp_pattern = "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(Z|[+-][0-9]{2}:[0-9]{2})" + blob_storage_interval_pattern = "^R/${local.backup_interval_timestamp_pattern}/(${join("|", local.valid_blob_storage_intervals)})$" + managed_disk_interval_pattern = "^R/${local.backup_interval_timestamp_pattern}/(${join("|", local.valid_managed_disk_intervals)})$" + postgresql_interval_pattern = "^R/${local.backup_interval_timestamp_pattern}/(${join("|", local.valid_postgresql_flexible_server_intervals)})$" } variable "resource_group_name" { @@ -75,6 +86,15 @@ variable "blob_storage_backups" { error_message = "At least one backup interval must be provided." } + validation { + condition = alltrue([ + for k, v in var.blob_storage_backups : alltrue([ + for interval in v.backup_intervals : can(regex(local.blob_storage_interval_pattern, interval)) + ]) + ]) + error_message = "Invalid backup interval for blob storage: allowed frequencies are P1D (daily) or P1W (weekly). See https://learn.microsoft.com/en-us/azure/backup/blob-backup-configure-manage for details." + } + validation { condition = alltrue([for k, v in var.blob_storage_backups : length(v.storage_account_containers) > 0]) error_message = "At least one storage account container must be provided." @@ -108,6 +128,15 @@ variable "managed_disk_backups" { error_message = "At least one backup interval must be provided." } + validation { + condition = alltrue([ + for k, v in var.managed_disk_backups : alltrue([ + for interval in v.backup_intervals : can(regex(local.managed_disk_interval_pattern, interval)) + ]) + ]) + error_message = "Invalid backup interval for managed disk: allowed frequencies are PT1H, PT2H, PT4H, PT6H, PT8H, PT12H (hourly) or P1D (daily). See https://learn.microsoft.com/en-us/azure/backup/disk-backup-support-matrix for details." + } + validation { condition = var.use_extended_retention ? true : alltrue([for k, v in var.managed_disk_backups : contains(local.valid_retention_periods, v.retention_period)]) 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." @@ -133,6 +162,15 @@ variable "postgresql_flexible_server_backups" { error_message = "At least one backup interval must be provided." } + validation { + condition = alltrue([ + for k, v in var.postgresql_flexible_server_backups : alltrue([ + for interval in v.backup_intervals : can(regex(local.postgresql_interval_pattern, interval)) + ]) + ]) + error_message = "Invalid backup interval for PostgreSQL flexible server: only P1W (weekly) is allowed. See https://learn.microsoft.com/en-us/azure/backup/backup-azure-database-postgresql-flex-support-matrix for details." + } + validation { condition = var.use_extended_retention ? true : alltrue([for k, v in var.postgresql_flexible_server_backups : contains(local.valid_retention_periods, v.retention_period)]) 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." diff --git a/tests/end-to-end-tests/blob_storage_backup_test.go b/tests/end-to-end-tests/blob_storage_backup_test.go index c56d657..7d523f0 100644 --- a/tests/end-to-end-tests/blob_storage_backup_test.go +++ b/tests/end-to-end-tests/blob_storage_backup_test.go @@ -77,7 +77,7 @@ func TestBlobStorageBackup(t *testing.T) { blobStorageBackups := map[string]map[string]interface{}{ "backup1": { "backup_name": "blob1", - "retention_period": "P1D", + "retention_period": "P6D", "backup_intervals": []string{"R/2024-01-01T00:00:00+00:00/P1D"}, "storage_account_id": *externalResources.StorageAccountOne.ID, "storage_account_containers": []string{*externalResources.StorageAccountOneContainer.Name}, @@ -85,7 +85,7 @@ func TestBlobStorageBackup(t *testing.T) { "backup2": { "backup_name": "blob2", "retention_period": "P7D", - "backup_intervals": []string{"R/2024-01-01T00:00:00+00:00/P2D"}, + "backup_intervals": []string{"R/2024-01-01T00:00:00+00:00/P1W"}, "storage_account_id": *externalResources.StorageAccountTwo.ID, "storage_account_containers": []string{*externalResources.StorageAccountTwoContainer.Name}, }, diff --git a/tests/end-to-end-tests/managed_disk_backup_test.go b/tests/end-to-end-tests/managed_disk_backup_test.go index 17bcf82..2d0a6aa 100644 --- a/tests/end-to-end-tests/managed_disk_backup_test.go +++ b/tests/end-to-end-tests/managed_disk_backup_test.go @@ -71,8 +71,8 @@ func TestManagedDiskBackup(t *testing.T) { managedDiskBackups := map[string]map[string]interface{}{ "backup1": { "backup_name": "disk1", - "retention_period": "P1D", - "backup_intervals": []string{"R/2024-01-01T00:00:00+00:00/P1D"}, + "retention_period": "P6D", + "backup_intervals": []string{"R/2024-01-01T00:00:00+00:00/PT6H"}, "managed_disk_id": *externalResources.ManagedDiskOne.ID, "managed_disk_resource_group": map[string]interface{}{ "id": *externalResources.ResourceGroup.ID, @@ -82,7 +82,7 @@ func TestManagedDiskBackup(t *testing.T) { "backup2": { "backup_name": "disk2", "retention_period": "P7D", - "backup_intervals": []string{"R/2024-01-01T00:00:00+00:00/P2D"}, + "backup_intervals": []string{"R/2024-01-01T00:00:00+00:00/P1D"}, "managed_disk_id": *externalResources.ManagedDiskTwo.ID, "managed_disk_resource_group": map[string]interface{}{ "id": *externalResources.ResourceGroup.ID, diff --git a/tests/end-to-end-tests/postgresql_flexible_server_backup_test.go b/tests/end-to-end-tests/postgresql_flexible_server_backup_test.go index d57c1a8..e9881ad 100644 --- a/tests/end-to-end-tests/postgresql_flexible_server_backup_test.go +++ b/tests/end-to-end-tests/postgresql_flexible_server_backup_test.go @@ -71,15 +71,15 @@ func TestPostgresqlFlexibleServerBackup(t *testing.T) { PostgresqlFlexibleServerBackups := map[string]map[string]interface{}{ "backup1": { "backup_name": "server1", - "retention_period": "P1D", - "backup_intervals": []string{"R/2024-01-01T00:00:00+00:00/P1D"}, + "retention_period": "P6D", + "backup_intervals": []string{"R/2024-01-01T00:00:00+00:00/P1W"}, "server_id": *externalResources.PostgresqlFlexibleServerOne.ID, "server_resource_group_id": *externalResources.ResourceGroup.ID, }, "backup2": { "backup_name": "server2", "retention_period": "P7D", - "backup_intervals": []string{"R/2024-01-01T00:00:00+00:00/P2D"}, + "backup_intervals": []string{"R/2024-01-01T00:00:00+00:00/P1W"}, "server_id": *externalResources.PostgresqlFlexibleServerTwo.ID, "server_resource_group_id": *externalResources.ResourceGroup.ID, }, diff --git a/tests/integration-tests/.terraform.lock.hcl b/tests/integration-tests/.terraform.lock.hcl index 279276e..b9b7719 100644 --- a/tests/integration-tests/.terraform.lock.hcl +++ b/tests/integration-tests/.terraform.lock.hcl @@ -6,6 +6,7 @@ provider "registry.terraform.io/hashicorp/azurerm" { constraints = ">= 4.18.0, < 5.0.0" hashes = [ "h1:/Mwep7xvjuaQvT0cSmCJRwn4bJhPQRFYN/a9Jq+nWDs=", + "h1:ct8QFejdwiSb0+Q0DyuvPdVjlSZ8lOOKz/NXuegQ/dE=", "zh:3504c2142661ecb0dae71e38b2eb8e1766f7abd08e1979b599d5c52961e84f3c", "zh:49ad233a9506ca6815b014b5c7eb0b68c152dd1deac5763e519cebadcbad5259", "zh:58daec6f8ab1ebf60755585084a3390a25fbae2f11b39ea925037b0d510faf9b", @@ -26,6 +27,7 @@ provider "registry.terraform.io/hashicorp/random" { constraints = "3.7.2" hashes = [ "h1:0hcNr59VEJbhZYwuDE/ysmyTS0evkfcLarlni+zATPM=", + "h1:Def/iHM4HihJCIxQ8AYoxtoVL5lVlYx0V7bX91pxwgM=", "zh:14829603a32e4bc4d05062f059e545a91e27ff033756b48afbae6b3c835f508f", "zh:1527fb07d9fea400d70e9e6eb4a2b918d5060d604749b6f1c361518e7da546dc", "zh:1e86bcd7ebec85ba336b423ba1db046aeaa3c0e5f921039b3f1a6fc2f978feab", diff --git a/tests/integration-tests/backup_modules_blob_storage.tftest.hcl b/tests/integration-tests/backup_modules_blob_storage.tftest.hcl index 0344149..b149edd 100644 --- a/tests/integration-tests/backup_modules_blob_storage.tftest.hcl +++ b/tests/integration-tests/backup_modules_blob_storage.tftest.hcl @@ -32,7 +32,7 @@ run "create_blob_storage_backup" { backup2 = { backup_name = "storage2" retention_period = "P7D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P2D"] + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1W"] storage_account_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Storage/storageAccounts/sastorage2" storage_account_containers = ["container2"] } @@ -125,7 +125,7 @@ run "create_blob_storage_backup" { } assert { - condition = module.blob_storage_backup["backup2"].backup_policy.backup_repeating_time_intervals[0] == "R/2024-01-01T00:00:00+00:00/P2D" + condition = module.blob_storage_backup["backup2"].backup_policy.backup_repeating_time_intervals[0] == "R/2024-01-01T00:00:00+00:00/P1W" error_message = "Blob storage backup policy backup intervals not as expected." } @@ -254,6 +254,64 @@ run "validate_backup_intervals" { ] } +run "validate_backup_intervals_invalid_frequency" { + command = plan + + module { + source = "../../infrastructure" + } + + variables { + resource_group_name = run.setup_tests.resource_group_name + resource_group_location = "uksouth" + backup_vault_name = run.setup_tests.backup_vault_name + log_analytics_workspace_id = run.setup_tests.log_analytics_workspace_id + tags = run.setup_tests.tags + blob_storage_backups = { + backup1 = { + backup_name = "storage1" + retention_period = "P7D" + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P2D"] + storage_account_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Storage/storageAccounts/sastorage1" + storage_account_containers = ["container1"] + } + } + } + + expect_failures = [ + var.blob_storage_backups, + ] +} + +run "validate_backup_intervals_invalid_structure" { + command = plan + + module { + source = "../../infrastructure" + } + + variables { + resource_group_name = run.setup_tests.resource_group_name + resource_group_location = "uksouth" + backup_vault_name = run.setup_tests.backup_vault_name + log_analytics_workspace_id = run.setup_tests.log_analytics_workspace_id + tags = run.setup_tests.tags + blob_storage_backups = { + backup1 = { + backup_name = "storage1" + retention_period = "P7D" + backup_intervals = ["P1D", "R/bad-date/P1D", "R/2024-01-01T00:00:00+00:00/PT10H"] + storage_account_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Storage/storageAccounts/sastorage1" + storage_account_containers = ["container1"] + } + } + } + + expect_failures = [ + var.blob_storage_backups, + ] +} + run "validate_storage_account_containers" { command = plan diff --git a/tests/integration-tests/backup_modules_managed_disk.tftest.hcl b/tests/integration-tests/backup_modules_managed_disk.tftest.hcl index e6cee41..a18e334 100644 --- a/tests/integration-tests/backup_modules_managed_disk.tftest.hcl +++ b/tests/integration-tests/backup_modules_managed_disk.tftest.hcl @@ -35,7 +35,7 @@ run "create_managed_disk_backup" { backup2 = { backup_name = "disk2" retention_period = "P7D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P2D"] + backup_intervals = ["R/2024-01-01T00:00:00+00:00/PT12H"] managed_disk_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Compute/disks/disk-2" managed_disk_resource_group = { id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group2" @@ -131,7 +131,7 @@ run "create_managed_disk_backup" { } assert { - condition = module.managed_disk_backup["backup2"].backup_policy.backup_repeating_time_intervals[0] == "R/2024-01-01T00:00:00+00:00/P2D" + condition = module.managed_disk_backup["backup2"].backup_policy.backup_repeating_time_intervals[0] == "R/2024-01-01T00:00:00+00:00/PT12H" error_message = "Managed disk backup policy backup intervals not as expected." } @@ -268,3 +268,67 @@ run "validate_backup_intervals" { var.managed_disk_backups, ] } + +run "validate_backup_intervals_invalid_frequency" { + command = plan + + module { + source = "../../infrastructure" + } + + variables { + resource_group_name = run.setup_tests.resource_group_name + resource_group_location = "uksouth" + backup_vault_name = run.setup_tests.backup_vault_name + log_analytics_workspace_id = run.setup_tests.log_analytics_workspace_id + tags = run.setup_tests.tags + managed_disk_backups = { + backup1 = { + backup_name = "disk1" + retention_period = "P7D" + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P2D"] + managed_disk_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Compute/disks/disk-1" + managed_disk_resource_group = { + id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group1" + name = "example-resource-group1" + } + } + } + } + + expect_failures = [ + var.managed_disk_backups, + ] +} + +run "validate_backup_intervals_invalid_structure" { + command = plan + + module { + source = "../../infrastructure" + } + + variables { + resource_group_name = run.setup_tests.resource_group_name + resource_group_location = "uksouth" + backup_vault_name = run.setup_tests.backup_vault_name + log_analytics_workspace_id = run.setup_tests.log_analytics_workspace_id + tags = run.setup_tests.tags + managed_disk_backups = { + backup1 = { + backup_name = "disk1" + retention_period = "P7D" + backup_intervals = ["P1D", "R/bad-date/P1D", "R/2024-01-01T00:00:00+00:00/PT10H"] + managed_disk_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.Compute/disks/disk-1" + managed_disk_resource_group = { + id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group1" + name = "example-resource-group1" + } + } + } + } + + expect_failures = [ + var.managed_disk_backups, + ] +} diff --git a/tests/integration-tests/backup_modules_postgresql_flexible_server.tftest.hcl b/tests/integration-tests/backup_modules_postgresql_flexible_server.tftest.hcl index c33bd39..27754ac 100644 --- a/tests/integration-tests/backup_modules_postgresql_flexible_server.tftest.hcl +++ b/tests/integration-tests/backup_modules_postgresql_flexible_server.tftest.hcl @@ -25,14 +25,14 @@ run "create_postgresql_flexible_server_backup" { backup1 = { backup_name = "server1" retention_period = "P1D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1D"] + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1W"] server_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.DBforPostgreSQL/flexibleServers/server-1" server_resource_group_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group1" } backup2 = { backup_name = "server2" retention_period = "P7D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P2D"] + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1W"] server_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.DBforPostgreSQL/flexibleServers/server-2" server_resource_group_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group2" } @@ -65,7 +65,7 @@ run "create_postgresql_flexible_server_backup" { } assert { - condition = module.postgresql_flexible_server_backup["backup1"].backup_policy.backup_repeating_time_intervals[0] == "R/2024-01-01T00:00:00+00:00/P1D" + condition = module.postgresql_flexible_server_backup["backup1"].backup_policy.backup_repeating_time_intervals[0] == "R/2024-01-01T00:00:00+00:00/P1W" error_message = "Postgresql flexible server backup policy backup intervals not as expected." } @@ -120,7 +120,7 @@ run "create_postgresql_flexible_server_backup" { } assert { - condition = module.postgresql_flexible_server_backup["backup2"].backup_policy.backup_repeating_time_intervals[0] == "R/2024-01-01T00:00:00+00:00/P2D" + condition = module.postgresql_flexible_server_backup["backup2"].backup_policy.backup_repeating_time_intervals[0] == "R/2024-01-01T00:00:00+00:00/P1W" error_message = "Postgresql flexible server backup policy backup intervals not as expected." } @@ -172,7 +172,7 @@ run "validate_retention_period" { backup1 = { backup_name = "server1" retention_period = "P30D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1D"] + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1W"] server_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.DBforPostgreSQL/flexibleServers/server-1" server_resource_group_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group1" } @@ -202,7 +202,7 @@ run "validate_retention_period_with_extended_retention" { backup1 = { backup_name = "server1" retention_period = "P30D" - backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1D"] + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1W"] server_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.DBforPostgreSQL/flexibleServers/server-1" server_resource_group_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group1" } @@ -243,3 +243,61 @@ run "validate_backup_intervals" { var.postgresql_flexible_server_backups, ] } + +run "validate_backup_intervals_invalid_frequency" { + command = plan + + module { + source = "../../infrastructure" + } + + variables { + resource_group_name = run.setup_tests.resource_group_name + resource_group_location = "uksouth" + backup_vault_name = run.setup_tests.backup_vault_name + log_analytics_workspace_id = run.setup_tests.log_analytics_workspace_id + tags = run.setup_tests.tags + postgresql_flexible_server_backups = { + backup1 = { + backup_name = "server1" + retention_period = "P7D" + backup_intervals = ["R/2024-01-01T00:00:00+00:00/P1D"] + server_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.DBforPostgreSQL/flexibleServers/server-1" + server_resource_group_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group1" + } + } + } + + expect_failures = [ + var.postgresql_flexible_server_backups, + ] +} + +run "validate_backup_intervals_invalid_structure" { + command = plan + + module { + source = "../../infrastructure" + } + + variables { + resource_group_name = run.setup_tests.resource_group_name + resource_group_location = "uksouth" + backup_vault_name = run.setup_tests.backup_vault_name + log_analytics_workspace_id = run.setup_tests.log_analytics_workspace_id + tags = run.setup_tests.tags + postgresql_flexible_server_backups = { + backup1 = { + backup_name = "server1" + retention_period = "P7D" + backup_intervals = ["P1D", "R/bad-date/P1D", "R/2024-01-01T00:00:00+00:00/PT10H"] + server_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group/providers/Microsoft.DBforPostgreSQL/flexibleServers/server-1" + server_resource_group_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/example-resource-group1" + } + } + } + + expect_failures = [ + var.postgresql_flexible_server_backups, + ] +}