Learn how to deploy Azure Recovery Services Vault with Locally Redundant Storage (LRS) by default and the option to use Geo Redundant Storage (GRS) using a Terraform module.

Microsoft Azure Recovery Services Vault is a critical component for managing and protecting your data in the cloud. It provides a robust, scalable, and secure solution for backing up and restoring your infrastructure. In this blog post, we will explore how to deploy an Azure Recovery Services Vault with private endpoints, custom backup policies, and configure locally redundant or geo redundant storage using Terraform.

Why Choose Different SKUs, Private Endpoints, and Zone/Geo Redundancy?

As Azure Recovery Services Vaults are scoped to a subscription, careful consideration is required when selecting the location and access to backup data stored. Microsoft recommends deploying two Recovery Services Vaults in each subscription to allow application teams maximum flexibility while maintaining optimal resilience levels:

  1. Locally Redundant Storage (LRS): This SKU stores your data redundantly within a single data center or availability zone. LRS is the default and most cost-effective option but offers the least amount of resilience in case of a data center or zone-level outage.
  2. Geo Redundant Storage (GRS): This SKU provides more resilience by replicating your data across two geographically separated data centers or regions. GRS ensures higher availability and durability of your backup data but comes with an increased cost.

Private endpoints are essential when you want to secure access to your Recovery Services Vault. They enable you to establish a private connection between your vault and a subnet within a virtual network, ensuring that traffic between the resources stays within the Azure backbone network.

Prerequisites for Azure Recovery Services Vault Teraform Module:

Before we begin, make sure you have the following:

  1. An Azure subscription
  2. Terraform installed on your local machine
  3. Basic knowledge of Terraform and Azure

Step 1: Set Up the Azure Recovery Services Vault Terraform Module Structure

First, create a directory for your Terraform module, and then create three files inside it: main.tf, variables.tf, outputs.tf and data.tf. These files will contain the resources, variables, output values and existing VNet and subnet for private endpoint resources, respectively.

  • main.tf - The main file that contains all of the resource definitions.
  • variables.tf - The file that contains all of the input variables for the module.
  • outputs.tf - The file that contains all of the output variables for the module.
  • data.tf - The file that contains all of the data sources used in the module.

Following this folder structure will help you keep your code organized and maintainable. It will also make it easier to collaborate with others and ensure that everyone is working from the same structure.

Note:  Creating a separate data.tf file for existing virtual network (VNet) and subnet allows you to leverage existing networking infrastructure in your Terraform module. This can help reduce the complexity of your Terraform code and ensure that your infrastructure is consistent across multiple deployments.

By defining azurerm_subnet and azurerm_virtual_network data sources in your data.tf file, you can retrieve information about your existing VNet and subnet to use in your Terraform module. This information can be used to deploy Azure Recovery Services Vault (or, I.E. Azure Storage Accounts or any other services) with Private Endpoints in a specific VNet and subnet that is already available in your environment.

Using existing networking infrastructure can help reduce deployment times and increase efficiency in your cloud infrastructure. Additionally, it can help you maintain compliance and security by leveraging existing policies and configurations.

Overall, using data.tf to retrieve existing VNet and subnet information is a valuable approach that can help streamline your Terraform module and ensure consistent deployment of Azure Recovery Services Vault with Private Endpoints.

More detailes on how to use data sources in Terraform available here: https://developer.hashicorp.com/terraform/language/data-sources

Step 2: Define Resources in main.tf

In the main.tf file, define the Azure Recovery Services Vault, private endpoint, and the three default recovery policies for Azure Files. These policies will have different retention periods and backup frequencies, as follows:

  1. First policy "daily_weekly_monthly_yearly1" - Daily (Incremental - daily): 35 days, Weekly: 90 days, Monthly: 1 year, Yearly: 10 years.
  2. Second policy "daily_weekly_monthly_yearly2" - Daily (Incremental - daily): 14 days, Weekly: 30 days, Monthly: 90 days, Yearly: 7 years
  3. Third policy "daily_weekly_monthly_yearly3" - Daily (Incremental - daily): 7 days, Weekly: 14 days, Monthly: 60 days, Yearly: 5 years

Note: Currently, the Azure Backup service for Azure File Shares (azurerm_backup_policy_file_share) does not support hourly incremental backups. The supported backup frequency for Azure File Shares is daily.

Here's the code for main.tf:


provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "example" {
  name     = var.resource_group_name
  location = var.location
}

resource "azurerm_recovery_services_vault" "example" {
  name                = "example-recovery-vault"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
  sku                 = var.vault_sku
  storage_mode_type   = var.storage_mode_type
}

locals {
  recovery_vault_name = azurerm_recovery_services_vault.example.name
  timezone            = "AUS Eastern Standard Time"
  weekdays            = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]

  backup_policies = {
    daily_weekly_monthly_yearly1 = {
      frequency         = "Daily"
      time              = "23:00"
      retention_daily   = 35
      retention_weekly  = 90
      retention_monthly = 12
      retention_yearly  = 10
    }
    daily_weekly_monthly_yearly2 = {
      frequency         = "Daily"
      time              = "23:00"
      retention_daily   = 14
      retention_weekly  = 30
      retention_monthly = 3
      retention_yearly  = 7
    }
    daily_weekly_monthly_yearly3 = {
      frequency         = "Daily"
      time              = "22:00"
      retention_daily   = 7
      retention_weekly  = 14
      retention_monthly = 2
      retention_yearly  = 5
    }
  }
}

resource "azurerm_backup_policy_file_share" "example" {
  for_each = local.backup_policies

  name                = each.key
  resource_group_name = azurerm_resource_group.example.name
  recovery_vault_name = local.recovery_vault_name
  timezone            = local.timezone

  backup {
    frequency = each.value.frequency
    time      = each.value.time
  }

  retention_daily {
    count = each.value.retention_daily
  }

  retention_weekly {
    weekdays = ["Sunday"]
    count    = each.value.retention_weekly
  }

  retention_monthly {
    weekdays = ["Sunday"]
    weeks    = ["First"]
    count    = each.value.retention_monthly
  }

  retention_yearly {
    count    = each.value.retention_yearly
    weekdays = ["Sunday"]
    weeks    = ["First"]
    months   = toset(["January"])
  }
}

resource "azurerm_private_endpoint" "example" {
  name                = "example-private-endpoint"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
  subnet_id           = data.azurerm_subnet.example.id

  private_service_connection {
    name                           = "example-private-connection"
    is_manual_connection           = false
    private_connection_resource_id = azurerm_recovery_services_vault.example.id
    subresource_names              = ["AzureBackup"]
  }
}

Note: Using months like ["January"] in the retention_yearly block of the custom backup policy configuration provides you with granular control over when backups are retained for long-term retention. By specifying a specific month, you can ensure that backups for a particular month are retained for the duration specified in the policy. This can be useful if you have regulatory or compliance requirements that dictate how long certain types of data must be retained.

For example, if you need to retain backups of financial data for a period of 7 years, you could specify months = toset(["January"]) in the retention_yearly block of your custom backup policy. This would ensure that backups for the month of January are retained for 7 years, while backups for other months are retained for the durations specified in their respective retention blocks.

Using months like this can help you ensure compliance with regulatory requirements and provide you with peace of mind that your critical data is backed up and recoverable for the necessary period.

variables.tf:

variable "vault_name" {
  description = "The name of the Recovery Services Vault"
  type        = string
  default     = "myrsv-01"
}

variable "location" {
  description = "The location of the Recovery Services Vault"
  type        = string
  default     = "Australia East"
}

variable "resource_group_name" {
  description = "The name of the resource group"
  type        = string
  default     = "myTFResourceGroup1"
}

variable "vault_sku" {
  description = "The SKU of the Recovery Services Vault. Possible values are Standard and Premium."
  type        = string
  default     = "Standard"
}

variable "private_endpoint_name" {
  description = "The name of the private endpoint"
  type        = string
  default     = "private-endpoint"
}

variable "storage_mode_type" {
  description = "The storage type of the Recovery Services Vault."
  type        = string
  default     = "ZoneRedundant"
}

data.tf:

 data "azurerm_virtual_network" "example" {
  name                = "example-vnet"
  resource_group_name = "myTFResourceGroup"
}

data "azurerm_subnet" "example" {
  name                 = "example-subnet"
  virtual_network_name = data.azurerm_virtual_network.example.name
  resource_group_name  = "myTFResourceGroup"
}

outputs.tf:

output "recovery_services_vault_id" {
  value       = azurerm_recovery_services_vault.vault.id

Here are some additional output values that can be useful for Azure governance:

  • Private endpoint FQDN: The fully qualified domain name of the private endpoint can be used for DNS resolution and connectivity purposes.
  • Private endpoint IP address: The private IP address assigned to the private endpoint can be helpful for network troubleshooting and management.
  • Recovery Services Vault properties: This can include various properties such as soft delete, enabled features, and minimum TLS version, which can be important for security and compliance.

updated outputs.tf:

output "recovery_services_vault_id" {
  value       = azurerm_recovery_services_vault.vault.id
  description = "The ID of the Recovery Services Vault"
}

output "private_endpoint_id" {
  value       = azurerm_private_endpoint.pe.id
  description = "The ID of the private endpoint"
}

output "private_endpoint_fqdn" {
  value       = azurerm_private_endpoint.pe.private_service_connection[0].private_endpoint_fqdn
  description = "The FQDN of the private endpoint"
}

output "private_endpoint_ip_address" {
  value       = azurerm_private_endpoint.pe.private_service_connection[0].private_ip_address
  description = "The private IP address of the private endpoint"
}

output "vault_soft_delete_enabled" {
  value       = azurerm_recovery_services_vault.vault.soft_delete_enabled
  description = "Indicates if soft delete is enabled for the Recovery Services Vault"
}

output "vault_enabled_features" {
  value       = azurerm_recovery_services_vault.vault.enabled_features
  description = "A list of enabled features for the Recovery Services Vault"
}

output "vault_minimum_tls_version" {
  value       = azurerm_recovery_services_vault.vault.minimum_tls_version
  description = "The minimum TLS version required for the Recovery Services Vault"
}

These output values provide better visibility into the deployed infrastructure and can help in Azure governance, monitoring, and management activities.

Final result after deploying Azure Recovery Services Vault

Once the Terraform configuration is applied, the Azure Recovery Services Vault will be deployed along with the custom backup policies specified in the configuration. These policies will ensure that your data is backed up at the specified intervals, providing you with peace of mind that your critical data is protected in the event of a disaster or data loss. With this solution, you can easily scale your backup solution and ensure that your data is safe and recoverable.

Recovery Services Vault Backup Policies

Conclusion:

By following these steps, you have successfully created a reusable Terraform module that deploys Azure Recovery Services Vault with private endpoints and three default recovery policies. This module uses Locally Redundant Storage (LRS) by default but allows you to switch to Geo Redundant Storage (GRS) if necessary. With this module, you can easily protect your data in Azure and ensure business continuity.