Deploy Azure Recovery Services Vault with LRS and GRS Using Terraform

Deploying Azure Recovery Services Vault with LRS and GRS Using Terraform

Introduction

Microsoft Azure Recovery Services Vault is a vital component for managing and safeguarding data in the cloud, offering robust, scalable, and secure solutions for backing up and restoring infrastructure. In this guide, we'll explore how to deploy an Azure Recovery Services Vault using Terraform, configure private endpoints, customize backup policies, and choose between Locally Redundant Storage (LRS) and Geo-Redundant Storage (GRS).

Why Choose Different SKUs, Private Endpoints, and Redundancy Options?

When deploying Azure Recovery Services Vault, selecting the right configuration is essential for data resilience and access control. Here’s a brief overview:

  • Locally Redundant Storage (LRS): Stores data within a single data center or availability zone. It's the default, cost-effective option but provides minimal resilience during a data center outage.
  • Geo Redundant Storage (GRS): Replicates data across geographically separated data centers, ensuring high availability and durability at a higher cost.

Private Endpoints: Establish a private connection between the vault and a subnet within a virtual network to secure traffic within the Azure backbone.

Prerequisites

Before proceeding, ensure the following:

  • An active Azure subscription.
  • Terraform installed on your local machine.
  • Basic knowledge of Terraform and Azure.

Step 1: Set Up the Terraform Module Structure

Create a directory for the module and include these files:

  • main.tf: Defines the resources.
  • variables.tf: Holds input variables.
  • outputs.tf: Manages output variables.
  • data.tf: Retrieves existing networking resources (e.g., VNet and subnet).

Tip: A separate data.tf file helps leverage existing networking infrastructure, simplifying the code and maintaining consistency across deployments.

Using Data Sources in Terraform: Utilizing azurerm_subnet and azurerm_virtual_network data sources in data.tf ensures efficient use of existing VNets and subnets, streamlining deployments and maintaining compliance.

More details on using data sources in Terraform can be found here.

Step 2: Define Resources in main.tf

Here's a breakdown of what main.tf should include:

  1. Provider Configuration:

    provider "azurerm" {
      features {}
    }
    
  2. Resource Group and Recovery Services Vault:

    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
    }
    
  3. Custom Backup Policies:

    locals {
      recovery_vault_name = azurerm_recovery_services_vault.example.name
      timezone            = "AUS Eastern Standard Time"
    
      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"])
      }
    }
    

Important Note: The retention_yearly block with months = ["January"] ensures specific backups are retained for long-term retention, ideal for compliance with regulatory requirements.

4. Define the Private Endpoint:


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"]
  }
}
```

Step 3: Define Variables and Data Sources

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 "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"
}
```

Step 4: Define Outputs for Enhanced Visibility

outputs.tf:


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

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

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

output "private_endpoint_ip_address" {
  value       = azurerm_private_endpoint.example.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.example.soft_delete_enabled
  description = "Indicates if soft delete is enabled for the Recovery Services Vault"
}

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

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

Conclusion

By following these steps, you have created a reusable Terraform module that deploys an Azure Recovery Services Vault with private endpoints and custom backup policies. The module supports LRS by default but allows switching to GRS when needed, ensuring data protection and business continuity.