Skip to content

Latest commit

Β 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

☁️ Azure Recovery Services Vault Terraform Module

Manage a hardened Azure Recovery Services vault together with its VM and file-share backup policies as one unit β€” geo-redundant, soft-delete on, immutable, and private by default β€” on hashicorp/azurerm ~> 4.0.

Terraform azurerm module type resources

🧩 Overview

  • πŸ›‘οΈ Creates one azurerm_recovery_services_vault hardened by default: soft delete on, public network access off, geo-redundant storage, immutability set to Unlocked, and a system-assigned managed identity.
  • πŸ’Ύ Manages azurerm_backup_policy_vm and azurerm_backup_policy_file_share as keyed maps, so a schedule/retention change to one policy never re-indexes the rest.
  • πŸ—“οΈ Full grandfather-father-son retention: daily, weekly, monthly, and yearly rules, plus V2 enhanced hourly VM policies and hourly file-share schedules.
  • πŸ” Customer-managed-key encryption and Azure Monitor alert wiring are exposed but off until you supply them.

πŸ’‘ Why it matters: the vault is the last line of defense for a regulated workload's data. A vault that is soft-delete protected, immutable, geo-redundant, and private on the empty call means the recoverable posture is the default β€” every relaxation is a deliberate, reviewable opt-out, and the irreversible Locked immutability state is never reached by accident.

❀️ Support this project

If these Terraform modules have been helpful to you or your organization, I'd appreciate your support in any of the following ways:

Whether it's a star, a professional connection, or a coffee, every gesture helps keep these modules actively maintained and continually improving. Thank you for being part of the community!

πŸ—ΊοΈ Where this fits in the family

flowchart TD
  RG["terraform-azurerm-resource-group"]
  RSV["terraform-azurerm-recovery-services-vault"]
  VAULT["azurerm_recovery_services_vault"]
  POL["backup policies (for_each)"]
  KV["terraform-azurerm-key-vault (CMK key id)"]
  TARGET["protected VMs / file shares"]
  DIAG["terraform-azurerm-monitor-diagnostic-setting"]

  RG -->|"resource_group_name + location"| RSV
  RSV --> VAULT
  VAULT --> POL
  KV -->|"key id for CMK encryption"| RSV
  POL -->|"policy id protects"| TARGET
  RSV -->|"id"| DIAG

  classDef this fill:#0078D4,color:#ffffff,stroke:#004578,stroke-width:2px;
  classDef key fill:#004578,color:#ffffff,stroke:#004578;
  class RSV this;
  class VAULT key;
Loading

🧬 What this module builds

flowchart LR
  I1["name / resource_group_name / location / sku"]
  I2["storage_mode_type GeoRedundant<br/>soft-delete on, public access off<br/>immutability Unlocked"]
  I3["identity SystemAssigned / encryption CMK"]
  I4["backup_policy_vm / backup_policy_file_share (maps)"]

  RSV["azurerm_recovery_services_vault.this"]
  PVM["azurerm_backup_policy_vm.this (for_each)"]
  PFS["azurerm_backup_policy_file_share.this (for_each)"]

  O1["id / name"]
  O2["identity_principal_id"]
  O3["backup_policy_vm_ids / backup_policy_file_share_ids"]

  I1 --> RSV
  I2 --> RSV
  I3 --> RSV
  I4 --> PVM
  I4 --> PFS
  RSV --> PVM
  RSV --> PFS
  RSV --> O1
  RSV --> O2
  PVM --> O3
  PFS --> O3

  classDef this fill:#0078D4,color:#ffffff,stroke:#004578,stroke-width:2px;
  classDef key fill:#004578,color:#ffffff,stroke:#004578;
  class RSV key;
  class PVM this;
  class PFS this;
Loading

Resource inventory

Resource Cardinality Role
azurerm_recovery_services_vault.this 1 (keystone) The hardened vault.
azurerm_backup_policy_vm.this 0..N (for_each) VM backup schedules and retention.
azurerm_backup_policy_file_share.this 0..N (for_each) Azure File Share backup schedules and retention.

βœ… Provider / Versions

Requirement Value
Terraform >= 1.12.0
Provider hashicorp/azurerm ~> 4.0
Provider block None in this module β€” the caller configures provider "azurerm" { features {} }, auth, and subscription.

Schema notes that bite

  • name, resource_group_name, and location are immutable β€” changing any of them forces replacement of the vault (and, transitively, the policies that reference it). classic_vmware_replication_enabled is force-new too.
  • immutability = "Locked" is irreversible. Once locked, the vault can never return to Unlocked/Disabled, and backup data cannot be shortened or deleted before its retention elapses. The module defaults to the reversible Unlocked state; type Locked only after a reviewed decision.
  • cross_region_restore_enabled = true requires storage_mode_type = "GeoRedundant"; the module enforces this at plan time. Reducing redundancy (for example GeoRedundant β†’ LocallyRedundant) is rejected once protected items exist.
  • storage_mode_type can generally only be changed before backup items are registered in the vault; change it afterward and the service will reject the update.
  • Backup-policy name, resource_group_name, and recovery_vault_name are effectively immutable β€” changing them replaces the policy. Hourly VM schedules require policy_type = "V2" (the enhanced policy).
  • azurerm_backup_policy_vm and azurerm_backup_policy_file_share do not support tags β€” only the vault carries the tags variable.

πŸ”‘ Required Azure RBAC Roles / Permissions

  • Backup Contributor on the target resource group (create/update the vault and its backup policies), or Recovery Services Contributor where broader vault management is required. A custom role covering Microsoft.RecoveryServices/vaults/* and Microsoft.RecoveryServices/vaults/backupPolicies/* at the resource-group scope is the least-privilege equivalent.
  • For customer-managed-key encryption, the vault's identity needs Key Vault crypto access (for example Key Vault Crypto Service Encryption User) at the key's scope β€” granted with the role-assignments module, not here.

Azure Prerequisites

  • An existing resource group in a supported US Azure region.
  • The Microsoft.RecoveryServices resource provider registered on the subscription.
  • For customer-managed-key encryption, an existing Key Vault key (and an identity that can unwrap it).
  • For private data-plane access, a subnet plus a private endpoint and the appropriate private DNS zone.
  • The caller configures the provider "azurerm" { features {} } block, auth, and subscription; the module declares none of these.

πŸ“ Module Structure

terraform-azurerm-recovery-services-vault/
β”œβ”€β”€ providers.tf     # required_version + azurerm ~> 4.0; no provider block
β”œβ”€β”€ variables.tf     # name, rg, location, sku, hardening toggles, identity/encryption/monitoring, policy maps
β”œβ”€β”€ main.tf          # azurerm_recovery_services_vault.this + for_each VM / file-share backup policies
β”œβ”€β”€ outputs.tf       # id, name, identity_principal_id, identity_tenant_id, policy id maps
β”œβ”€β”€ README.md        # this document
β”œβ”€β”€ SCOPE.md         # cross-module contract
β”œβ”€β”€ LICENSE          # MIT
└── .gitignore       # canonical Terraform ignore set

βš™οΈ Quick Start

provider "azurerm" {
  features {}
}

module "rsv" {
  source              = "git::https://github.com/microsoftexpert/terraform-azurerm-recovery-services-vault.git?ref=v1.0.0"
  name                = "rsv-platform-prod-eus2"
  resource_group_name = "rg-backup-prod-eastus2"
  location            = "eastus2"
}

ℹ️ The empty call is soft-delete protected, geo-redundant, private, immutable (Unlocked), with a system-assigned identity. Pin the module by tag (?ref=v1.0.0), never a branch.

πŸ”Œ Cross-Module Contract

Consumes

Input Type From
resource_group_name string terraform-azurerm-resource-group (name)
location string caller / resource group (location)
encryption.key_id string terraform-azurerm-key-vault (key_versionless_ids)
identity.identity_ids list(string) terraform-azurerm-user-assigned-identity (id)

Emits

Output Description Consumed by
id Vault Resource ID backup protection, recovery-services-vault-resource-guard-association, terraform-azurerm-site-recovery-services-vault-hyperv-site, diagnostics, private endpoints, role assignments
name Vault name terraform-azurerm-site-recovery-fabric (as recovery_vault_name)
identity_principal_id System-assigned identity principal ID Key Vault crypto role assignment (CMK)
backup_policy_vm_ids Map key β†’ VM policy ID azurerm_backup_protected_vm
backup_policy_file_share_ids Map key β†’ file-share policy ID azurerm_backup_protected_file_share

πŸ“š Example Library

1 Β· Minimal (hardened)
module "rsv" {
  source              = "git::https://github.com/microsoftexpert/terraform-azurerm-recovery-services-vault.git?ref=v1.0.0"
  name                = "rsv-min-eus2"
  resource_group_name = "rg-backup-eastus2"
  location            = "eastus2"
}

πŸ”’ Soft delete on, geo-redundant, public access off, immutability Unlocked, system-assigned identity β€” all by default.

2 Β· VM backup policy (daily)
module "rsv" {
  source              = "git::https://github.com/microsoftexpert/terraform-azurerm-recovery-services-vault.git?ref=v1.0.0"
  name                = "rsv-vm-eus2"
  resource_group_name = "rg-backup-eastus2"
  location            = "eastus2"

  backup_policy_vm = {
    daily = {
      backup          = { frequency = "Daily", time = "23:00" }
      retention_daily = { count = 30 }
    }
  }
}

ℹ️ time is "HH:mm" in the policy's timezone (default UTC). Daily retention is required for a valid VM policy.

3 Β· VM policy with GFS retention (weekly / monthly / yearly)
backup_policy_vm = {
  gold = {
    timezone          = "Eastern Standard Time"
    backup            = { frequency = "Daily", time = "23:00" }
    retention_daily   = { count = 14 }
    retention_weekly  = { count = 12, weekdays = ["Sunday"] }
    retention_monthly = { count = 12, weekdays = ["Sunday"], weeks = ["First"] }
    retention_yearly  = { count = 7, weekdays = ["Sunday"], weeks = ["First"], months = ["January"] }
  }
}

πŸ’‘ Grandfather-father-son: short daily rotation plus long-horizon weekly/monthly/yearly restore points.

4 Β· Enhanced V2 VM policy (hourly)
backup_policy_vm = {
  enhanced = {
    policy_type                    = "V2"
    instant_restore_retention_days = 7
    backup = {
      frequency     = "Hourly"
      time          = "08:00"
      hour_interval = 4
      hour_duration = 12
    }
    retention_daily = { count = 30 }
  }
}

⚠️ Hourly schedules require the enhanced policy (policy_type = "V2"); hour_duration must be a multiple of hour_interval.

5 Β· File-share backup policy (daily)
backup_policy_file_share = {
  shares = {
    backup          = { frequency = "Daily", time = "22:00" }
    retention_daily = { count = 30 }
  }
}

ℹ️ File-share policies always require retention_daily; the module types it as required.

6 Β· File-share policy (hourly)
backup_policy_file_share = {
  hourly = {
    backup = {
      frequency = "Hourly"
      hourly    = { interval = 4, start_time = "08:00", window_duration = 12 }
    }
    retention_daily = { count = 30 }
  }
}
7 Β· Geo-redundant with cross-region restore
module "rsv" {
  source                       = "git::https://github.com/microsoftexpert/terraform-azurerm-recovery-services-vault.git?ref=v1.0.0"
  name                         = "rsv-grs-eus2"
  resource_group_name          = "rg-backup-eastus2"
  location                     = "eastus2"
  storage_mode_type            = "GeoRedundant"
  cross_region_restore_enabled = true
}

πŸ”’ Cross-region restore is only valid with GeoRedundant (the default). The module rejects the combination otherwise at plan time.

8 Β· Immutability β€” Locked (irreversible)
module "rsv" {
  source              = "git::https://github.com/microsoftexpert/terraform-azurerm-recovery-services-vault.git?ref=v1.0.0"
  name                = "rsv-locked-eus2"
  resource_group_name = "rg-backup-eastus2"
  location            = "eastus2"
  immutability        = "Locked"
}

⚠️ Locked cannot be undone. The vault can never leave the immutable state and retention can never be shortened. Use only after a reviewed, deliberate decision β€” the default Unlocked gives the same protection but stays reversible.

9 Β· Customer-managed-key encryption
module "rsv" {
  source              = "git::https://github.com/microsoftexpert/terraform-azurerm-recovery-services-vault.git?ref=v1.0.0"
  name                = "rsv-cmk-eus2"
  resource_group_name = "rg-backup-eastus2"
  location            = "eastus2"

  identity = { type = "SystemAssigned" }

  encryption = {
    key_id                       = var.cmk_key_versionless_id # from a Key Vault key output
    use_system_assigned_identity = true
  }
}

πŸ”’ Provide the key by reference (a Key Vault key output), never inline material. infrastructure_encryption_enabled defaults to true. The vault's identity must hold crypto access on the key.

10 Β· User-assigned identity
module "rsv" {
  source              = "git::https://github.com/microsoftexpert/terraform-azurerm-recovery-services-vault.git?ref=v1.0.0"
  name                = "rsv-uai-eus2"
  resource_group_name = "rg-backup-eastus2"
  location            = "eastus2"

  identity = {
    type         = "UserAssigned"
    identity_ids = [var.user_assigned_identity_id]
  }
}
11 Β· Azure Monitor alerts
module "rsv" {
  source              = "git::https://github.com/microsoftexpert/terraform-azurerm-recovery-services-vault.git?ref=v1.0.0"
  name                = "rsv-mon-eus2"
  resource_group_name = "rg-backup-eastus2"
  location            = "eastus2"

  monitoring = {
    alerts_for_all_job_failures_enabled            = true
    alerts_for_critical_operation_failures_enabled = true
  }
}
12 Β· Public network access (opt-in)
module "rsv" {
  source                        = "git::https://github.com/microsoftexpert/terraform-azurerm-recovery-services-vault.git?ref=v1.0.0"
  name                          = "rsv-public-eus2"
  resource_group_name           = "rg-backup-eastus2"
  location                      = "eastus2"
  public_network_access_enabled = true
}

⚠️ Opening public access removes the private guardrail β€” use only with justification and prefer a private endpoint instead.

13 Β· for_each β€” a vault per environment
module "rsv" {
  source              = "git::https://github.com/microsoftexpert/terraform-azurerm-recovery-services-vault.git?ref=v1.0.0"
  for_each            = toset(["dev", "prod"])
  name                = "rsv-${each.key}-eus2"
  resource_group_name = "rg-backup-eastus2"
  location            = "eastus2"

  backup_policy_vm = {
    daily = {
      backup          = { frequency = "Daily", time = "23:00" }
      retention_daily = { count = each.key == "prod" ? 30 : 7 }
    }
  }
}
14 Β· Compose diagnostics and RBAC
module "rsv" { # ... }

module "rsv_diag" {
  source                     = "git::https://github.com/microsoftexpert/terraform-azurerm-monitor-diagnostic-setting.git?ref=v1.0.0"
  name                       = "diag-rsv"
  target_resource_id         = module.rsv.id
  log_analytics_workspace_id = var.law_id
}

module "rsv_roles" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-role-assignments.git?ref=v1.0.0"
  scope  = module.rsv.id
  role_assignments = {
    backup-operator = {
      role_definition_name = "Backup Operator"
      principal_id         = var.ops_principal_id
    }
  }
}

πŸ’‘ Diagnostics and RBAC are deliberately sibling concerns, wired to the vault's id β€” this module stays composable.

15 Β· πŸ—οΈ End-to-end composition

A resource group, a Key Vault key for CMK, a hardened vault encrypted with that key, and a daily VM backup policy.

provider "azurerm" {
  features {}
}

module "rg" {
  source   = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-group.git?ref=v1.0.0"
  name     = "rg-backup-prod-eastus2"
  location = "eastus2"
}

module "kv" {
  source              = "git::https://github.com/microsoftexpert/terraform-azurerm-key-vault.git?ref=v1.0.0"
  name                = "kv-backup-prod-eus2"
  resource_group_name = module.rg.name
  location            = module.rg.location
  tenant_id           = var.tenant_id

  keys = {
    rsv-cmk = { key_type = "RSA", key_size = 3072, key_opts = ["wrapKey", "unwrapKey"] }
  }
}

module "rsv" {
  source              = "git::https://github.com/microsoftexpert/terraform-azurerm-recovery-services-vault.git?ref=v1.0.0"
  name                = "rsv-plat-prod-eus2"
  resource_group_name = module.rg.name
  location            = module.rg.location

  identity = { type = "SystemAssigned" }

  encryption = {
    key_id                       = module.kv.key_versionless_ids["rsv-cmk"]
    use_system_assigned_identity = true
  }

  backup_policy_vm = {
    daily = {
      backup            = { frequency = "Daily", time = "23:00" }
      retention_daily   = { count = 30 }
      retention_weekly  = { count = 12, weekdays = ["Sunday"] }
    }
  }
}

# module.rsv.identity_principal_id is granted "Key Vault Crypto Service Encryption User" on the key,
# and module.rsv.backup_policy_vm_ids["daily"] feeds azurerm_backup_protected_vm to protect a VM.

πŸ’‘ The vault's system-assigned identity unwraps the Key Vault key for CMK encryption, and the daily policy's ID drives VM protection β€” hardened and least-privilege, end to end.

πŸ“₯ Inputs

Name Type Required Default Description
name string βœ… β€” Vault name (2–50). Immutable.
resource_group_name string βœ… β€” Containing resource group. Immutable.
location string βœ… β€” Azure region. Immutable.
sku string β€” "Standard" Standard / RS0.
storage_mode_type string β€” "GeoRedundant" GeoRedundant / LocallyRedundant / ZoneRedundant.
cross_region_restore_enabled bool β€” false Requires GeoRedundant.
soft_delete_enabled bool β€” true Soft-delete protection.
public_network_access_enabled bool β€” false Public reachability.
immutability string β€” "Unlocked" Disabled / Unlocked / Locked (Locked is irreversible).
classic_vmware_replication_enabled bool β€” false Classic VMware replication. Force-new.
identity object β€” { type = "SystemAssigned" } Managed identity; null for none.
encryption object β€” null CMK encryption (key + identity).
monitoring object β€” null Azure Monitor alert settings.
backup_policy_vm map(object) β€” {} VM backup policies.
backup_policy_file_share map(object) β€” {} File-share backup policies.
tags map(string) β€” {} Tags (vault only; policies do not support tags).
timeouts object β€” null Optional timeouts.
Full variable schemas (children)
backup_policy_vm = map(object({
  name                           = optional(string)          # defaults to map key
  timezone                       = optional(string, "UTC")
  policy_type                    = optional(string, "V1")    # V1 | V2 (validated)
  instant_restore_retention_days = optional(number)
  consistency_type               = optional(string)          # ShutdownFileConsistency | FileConsistency (V2)
  backup = object({
    frequency     = string                                   # Daily | Weekly | Hourly (validated)
    time          = string                                   # "HH:mm"
    hour_interval = optional(number)
    hour_duration = optional(number)
    weekdays      = optional(set(string))
  })
  instant_restore_resource_group = optional(object({ prefix = string, suffix = optional(string) }))
  retention_daily   = optional(object({ count = number }))
  retention_weekly  = optional(object({ count = number, weekdays = set(string) }))
  retention_monthly = optional(object({ count = number, days = optional(set(number)), include_last_days = optional(bool), weekdays = optional(set(string)), weeks = optional(set(string)) }))
  retention_yearly  = optional(object({ count = number, months = set(string), days = optional(set(number)), include_last_days = optional(bool), weekdays = optional(set(string)), weeks = optional(set(string)) }))
  tiering_policy    = optional(object({ archived_restore_point = object({ mode = string, duration = optional(number), duration_type = optional(string) }) }))
}))   # this resource does not support tags

backup_policy_file_share = map(object({
  name                       = optional(string)              # defaults to map key
  timezone                   = optional(string, "UTC")
  backup_tier                = optional(string)
  snapshot_retention_in_days = optional(number)
  backup = object({
    frequency = string                                       # Daily | Hourly (validated)
    time      = optional(string)
    hourly    = optional(object({ interval = number, start_time = string, window_duration = number }))
  })
  retention_daily   = object({ count = number })             # required
  retention_weekly  = optional(object({ count = number, weekdays = set(string) }))
  retention_monthly = optional(object({ count = number, days = optional(set(number)), include_last_days = optional(bool), weekdays = optional(set(string)), weeks = optional(set(string)) }))
  retention_yearly  = optional(object({ count = number, months = set(string), days = optional(set(number)), include_last_days = optional(bool), weekdays = optional(set(string)), weeks = optional(set(string)) }))
}))   # this resource does not support tags

🧾 Outputs

Output Description Notes
id Vault Resource ID Emitted first.
name Vault name β€”
location Azure region, in the canonical form Azure uses. Read from the resource, not var.location.
identity_principal_id System-assigned identity principal ID null when no system-assigned identity.
identity_tenant_id System-assigned identity tenant ID null when no system-assigned identity.
backup_policy_vm_ids Map key β†’ VM policy ID Feeds azurerm_backup_protected_vm.
backup_policy_file_share_ids Map key β†’ file-share policy ID Feeds azurerm_backup_protected_file_share.

🧠 Architecture Notes

  • Hardened empty call. Soft delete, geo-redundant storage, no public access, immutability Unlocked, and a system-assigned identity are the defaults; each is a documented opt-out.
  • Immutability is a one-way door when Locked. The module defaults to Unlocked, which delivers immutability protection while remaining reversible. Locked is exposed but requires the caller to type it, because it can never be undone.
  • Children reference the keystone by name. azurerm_backup_policy_vm and azurerm_backup_policy_file_share set recovery_vault_name = azurerm_recovery_services_vault.this.name, so the vault is always created first β€” no depends_on is needed. Children use for_each over keyed maps, so a key rename never re-creates an unrelated policy.
  • Policies do not carry tags. The universal tags variable applies to the vault only; the backup-policy resources have no tags argument, confirmed against the live provider schema.
  • CMK is caller-supplied. Encryption is off (Microsoft-managed keys) unless an encryption object with a Key Vault key_id is provided; the key is passed by reference and the vault's identity unwraps it.
  • features {} dependence. No provider {} block here; the caller configures provider "azurerm" { features {} }. Vault soft-delete and purge behavior on destroy is governed by the caller's features configuration.

🧱 Design Principles

Concern Secure default (empty call) Opt-out
Soft delete soft_delete_enabled = true set false
Public network access public_network_access_enabled = false set true
Storage redundancy storage_mode_type = "GeoRedundant" LocallyRedundant / ZoneRedundant
Immutability immutability = "Unlocked" (reversible) Disabled, or Locked (irreversible)
Identity system-assigned identity set identity = null
Encryption Microsoft-managed keys supply encryption for CMK

πŸš€ Runbook

cd terraform-azurerm-recovery-services-vault
terraform init -backend=false
terraform validate
terraform fmt -check
Remove-Item -Recurse -Force .terraform -ErrorAction SilentlyContinue

Pin the module by tag (?ref=v1.0.0), never a branch. Plan-only during authoring; a human runs plan/apply from CI.

πŸ§ͺ Testing

The offline proof gate β€” terraform init -backend=false, terraform validate, terraform fmt -check β€” proves the configuration is type-correct against the pinned azurerm ~> 4.0 schema (including the sku, storage_mode_type, immutability, and backup-frequency validations, plus the cross-region-restore/GeoRedundant guard) and canonically formatted, with no cloud calls. What it does not exercise: global name uniqueness, whether the deploying identity holds Backup Contributor, service-side retention rule combinations, and CMK key access β€” those surface only under terraform plan/apply against real credentials from CI.

πŸ’¬ Example Output

$ terraform output
id   = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-backup-prod-eastus2/providers/Microsoft.RecoveryServices/vaults/rsv-plat-prod-eus2"
name = "rsv-plat-prod-eus2"
identity_principal_id = "11111111-2222-3333-4444-555555555555"
backup_policy_vm_ids = {
  "daily" = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-backup-prod-eastus2/providers/Microsoft.RecoveryServices/vaults/rsv-plat-prod-eus2/backupPolicies/daily"
}
backup_policy_file_share_ids = {}

πŸ” Troubleshooting

Symptom Cause Fix
cross_region_restore_enabled requires storage_mode_type = "GeoRedundant" Cross-region restore set with non-geo redundancy Set storage_mode_type = "GeoRedundant" or disable cross-region restore.
Cannot reduce storage redundancy Protected items already exist in the vault Redundancy is fixed once items are registered; plan it before onboarding backups.
Cannot revert immutability immutability was set to Locked Locked is irreversible by design; a new vault is the only path back.
Hourly VM schedule rejected policy_type is V1 Set policy_type = "V2" for hourly (enhanced) VM backups.
VM policy apply error on retention Missing retention_daily Provide retention_daily; it is required for a valid VM policy.
Vault will not delete Soft delete / registered backup items Stop protection and clear soft-deleted items, or wait out the window, before destroy.
Tag argument rejected on a policy Backup policies do not support tags Apply tags on the vault only.

πŸ”— Related Docs


πŸ’™ "Infrastructure as Code should be standardized, consistent, and secure."