Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6e63466
docs(azure-sa-backup-docs): update docs
pablosanchezpaz Apr 7, 2026
f178339
docs(azure-sa-backup-docs): update docs
pablosanchezpaz Apr 7, 2026
c8c8d13
Merge branch 'main' into add/azure-sa-backup-docs-v2
pablosanchezpaz Apr 8, 2026
0ee2406
Merge branch 'main' into add/azure-sa-backup-docs-v2
pablosanchezpaz Apr 15, 2026
776ec32
docs(azure-sa-backup): update docs
pablosanchezpaz Apr 21, 2026
aa2b76d
docs(azure-sa-backup-docs) add outputs
pablosanchezpaz Apr 22, 2026
fdf3c98
Merge branch 'main' into add/azure-sa-backup-docs-v2
pablosanchezpaz Apr 23, 2026
06a1e66
Update outputs.tf
pablosanchezpaz Apr 23, 2026
7470527
Apply suggestion from @Copilot
pablosanchezpaz Apr 23, 2026
2800b98
Merge branch 'main' into add/azure-sa-backup-docs-v2
pablosanchezpaz Apr 23, 2026
5296d3a
Merge branch 'main' into add/azure-sa-backup-docs-v2
JorgeIglesiasMosquera Apr 27, 2026
e5d1952
docs(azure-sa-backup-docs) update docs
pablosanchezpaz Apr 30, 2026
998ee7f
Merge branch 'main' into add/azure-sa-backup-docs-v2
pablosanchezpaz Apr 30, 2026
94d6187
Merge branch 'main' into add/azure-sa-backup-docs-v2
JorgeIglesiasMosquera Apr 30, 2026
c1a4abd
Merge branch 'main' into add/azure-sa-backup-docs-v2
JorgeIglesiasMosquera May 4, 2026
3fbc007
Merge branch 'main' into add/azure-sa-backup-docs-v2
pablosanchezpaz May 4, 2026
b604e5c
doc(azure-sa-backup): recreate README and add outputs
JorgeIglesiasMosquera May 4, 2026
85dc111
Merge branch 'add/azure-sa-backup-docs-v2' of https://github.com/pref…
JorgeIglesiasMosquera May 4, 2026
0bd2c40
doc(azure-sa-backup): recreate README
JorgeIglesiasMosquera May 4, 2026
9b64649
docs(azure-sa-backup-docs) update docs
pablosanchezpaz May 5, 2026
1b539cd
Potential fix for pull request finding
JorgeIglesiasMosquera May 5, 2026
205103d
doc(azure-sa-backup): recreate README and fix role assignment logic
JorgeIglesiasMosquera May 5, 2026
4d1772a
docs(azure-mi-docs) update docs
pablosanchezpaz May 5, 2026
7405a2a
docs(azure-sa-backup-docs) update docs
pablosanchezpaz May 5, 2026
c7ee885
docs(azure-sa-backup-docs) update docs
pablosanchezpaz May 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions modules/azure-sa-backup/.terraform-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
formatter: "markdown" # this is required

version: ""

header-from: docs/header.md
footer-from: docs/footer.md

recursive:
enabled: false
path: modules
include-main: true

sections:
hide: []
show: []

content: ""

output:
file: "README.md"
mode: inject
template: |-
<!-- BEGIN_TF_DOCS -->
{{ .Content }}
<!-- END_TF_DOCS -->

output-values:
enabled: false
from: ""

sort:
enabled: true
by: name

settings:
anchor: true
color: true
default: true
description: false
escape: true
hide-empty: false
html: true
indent: 2
lockfile: true
read-comments: true
required: true
sensitive: true
type: true
215 changes: 130 additions & 85 deletions modules/azure-sa-backup/README.md

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions modules/azure-sa-backup/_examples/basic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Basic example

Configures **Azure Files** backup only (`backup_share`). Replace:

- `backup_resource_group_name` — existing resource group for the Recovery Services vault.
- `storage_account_id` — full ID of the storage account to back up.
- Vault name, policy name, and `source_file_share_name` (share must exist).

`backup_blob` is disabled (`null`).

## Usage

```bash
terraform init
terraform plan
```

## Configuration

See [`main.tf`](./main.tf).
46 changes: 46 additions & 0 deletions modules/azure-sa-backup/_examples/basic/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# File-share backup path only. Replace backup RG, storage account ID, vault/share names.

terraform {
required_version = ">= 1.7.0"

required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.6.0"
}
}
}

provider "azurerm" {
features {}
}

module "storage_backup" {
source = "../.."

backup_resource_group_name = "example-backup-rg"
storage_account_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example-rg/providers/Microsoft.Storage/storageAccounts/stexample"

tags_from_rg = false
tags = {
example = "basic"
}

backup_share = {
policy_name = "daily-policy"
recovery_services_vault_name = "example-rsvault"
sku = "Standard"
source_file_share_name = ["example-share"]
timezone = "UTC"
backup = {
frequency = "Daily"
time = "02:00"
}
retention_daily = {
count = 7
}
}

backup_blob = null
lifecycle_policy_rule = null
}
7 changes: 7 additions & 0 deletions modules/azure-sa-backup/_examples/comprehensive/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Comprehensive YAML reference

[`values.reference.yaml`](./values.reference.yaml) shows **illustrative** `backup_share` and `backup_blob` blocks together with `tags_from_rg` and IDs.

It is **not** loaded by this module. Use it as documentation or merge into your root module.

**Convention:** keep large YAML samples under `_examples/**` instead of embedding them in the terraform-docs `README.md`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Reference YAML for module inputs (illustrative — not applied by Terraform on its own).
# Map to module arguments or use with yamldecode() in a root module.
# Do not include fields that are not in variables.tf (e.g. no vault_id under backup_blob.policy; vault is created by the module).

tags_from_rg: true

backup_resource_group_name: "backup-test-rg"

storage_account_id: "/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Storage/storageAccounts/xxx"

backup_share:
policy_name: "daily-backup-policy"
recovery_services_vault_name: "test-vault"
sku: "Standard"
soft_delete_enabled: true
storage_mode_type: "GeoRedundant"
cross_region_restore_enabled: true
source_file_share_name:
- "datadir"
identity:
type: "SystemAssigned"
timezone: "UTC"
backup:
frequency: "Daily"
time: "02:00"
retention_daily:
count: 7
retention_weekly:
count: 4
weekdays:
- "Sunday"
retention_monthly:
count: 12
weekdays:
- "Sunday"
weeks:
- "First"
retention_yearly:
count: 5
weekdays:
- "Sunday"
weeks:
- "First"
months:
- "January"

backup_blob:
vault_name: "test-vault"
datastore_type: "AzureBlob"
redundancy: "GeoRedundant"
identity_type: "SystemAssigned"
instance_blob_name: "datadir"
storage_account_container_names:
- "blob1"
- "blob2"
role_assignment: "Storage Blob Data Contributor"
policy:
name: "daily-blob-backup-policy"
backup_repeating_time_intervals:
- "R/2023-01-01T02:00:00Z/P1D"
operational_default_retention_duration: "P30D"
retention_rule:
- name: "daily-retention"
duration: "P30D"
criteria:
days_of_week:
- "Sunday"
life_cycle:
data_store_type: "VaultStore"
duration: "P30D"
priority: 1
time_zone: "UTC"
vault_default_retention_duration: "P30D"
retention_duration: "P30D"
19 changes: 19 additions & 0 deletions modules/azure-sa-backup/docs/footer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## Examples

For detailed examples, refer to the [module examples](https://github.com/prefapp/tfm/tree/main/modules/azure-sa-backup/_examples):

- [basic](https://github.com/prefapp/tfm/tree/main/modules/azure-sa-backup/_examples/basic) — Azure Files backup via Recovery Services vault (`backup_share`); set RG, storage account ID, vault/policy/share names (see folder README).
- [comprehensive](https://github.com/prefapp/tfm/tree/main/modules/azure-sa-backup/_examples/comprehensive) — Reference YAML illustrating `backup_share`, `backup_blob`, and tags (documentation-oriented; see folder README).

## External Resources

Terraform resource docs use **4.6.0** as a baseline aligned with the `azurerm` constraint in `versions.tf` (`~> 4.6.0`).

- **Azure Backup**: [https://learn.microsoft.com/azure/backup/](https://learn.microsoft.com/azure/backup/)
- **azurerm_recovery_services_vault**: [https://registry.terraform.io/providers/hashicorp/azurerm/4.6.0/docs/resources/recovery_services_vault](https://registry.terraform.io/providers/hashicorp/azurerm/4.6.0/docs/resources/recovery_services_vault)
- **azurerm_data_protection_backup_vault**: [https://registry.terraform.io/providers/hashicorp/azurerm/4.6.0/docs/resources/data_protection_backup_vault](https://registry.terraform.io/providers/hashicorp/azurerm/4.6.0/docs/resources/data_protection_backup_vault)
- **Terraform AzureRM provider**: [https://registry.terraform.io/providers/hashicorp/azurerm/4.6.0](https://registry.terraform.io/providers/hashicorp/azurerm/4.6.0)

## Support

For issues, questions, or contributions related to this module, please visit the [repository's issue tracker](https://github.com/prefapp/tfm/issues).
88 changes: 88 additions & 0 deletions modules/azure-sa-backup/docs/header.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Azure Storage Account backup Terraform module (`azure-sa-backup`)

## Overview

This module configures **Azure Backup** for an existing **storage account**, in two optional paths:

- **Azure Files**: Recovery Services vault, registration of the storage account as a backup container, a **single file share backup policy**, and **one protected file share**.

> ⚠️ **Known limitation**: Only a single value is supported in `backup_share.source_file_share_name`. Providing multiple entries will result in a validation error until multi-share support is implemented.
- **Blobs (Data Protection)**: Backup vault, **blob backup policy**, optional **managed identity**, **role assignment** on the storage account for the vault identity, and a **blob backup instance**.
Comment thread
JorgeIglesiasMosquera marked this conversation as resolved.

Comment thread
pablosanchezpaz marked this conversation as resolved.
> ⚠️ **Known caveat**: Always set `backup_blob.identity_type` (for example, `SystemAssigned`) when enabling blob backup. The role assignment count uses `can(var.backup_blob.identity_type)`, which can evaluate to `true` even when the value is `null`; in that case plan/apply can fail when resolving `identity[0]`.

You can enable **only shares**, **only blobs**, or **both**. The module reads an existing **resource group** (`backup_resource_group_name`) for location and optional tag merge; it does **not** create that resource group or the storage account.

## Key features

- **Tags**: `tags` plus optional merge from the backup resource group when `tags_from_rg = true` (default `false`).
- **Conditional resources**: `backup_share` and `backup_blob` are each optional (`null` disables that path).
- **Outputs**: vault and instance IDs for the blob path; Recovery Services vault ID and a map of protected file share item IDs for the share path (see `outputs.tf`).
- **Known limitation (file shares)**: Only one value in `backup_share.source_file_share_name` is supported; multiple entries will result in a validation error.
- **Known caveat (blobs)**: Omitting `backup_blob.identity_type` is unsafe with the current role assignment logic; set it explicitly for blob backup.

## Prerequisites

- Existing **resource group** for backup resources (`backup_resource_group_name`).
- Existing **storage account** and, for file share backup, a single existing **file share** name in `backup_share.source_file_share_name` (one element only).
- Appropriate **permissions** for Terraform in the subscription (Backup Contributor / relevant roles as required by your org).

## Basic usage

Provide `backup_resource_group_name`, `storage_account_id`, and at least one of `backup_share` or `backup_blob`. See the **Inputs** table for the full object shapes.

### Example (file share backup only)

```hcl
module "storage_backup" {
source = "git::https://github.com/prefapp/tfm.git//modules/azure-sa-backup?ref=<version>"

backup_resource_group_name = "my-backup-rg"
storage_account_id = "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/sa-rg/providers/Microsoft.Storage/storageAccounts/mystorage"

tags_from_rg = false
tags = {
environment = "dev"
}

backup_share = {
policy_name = "daily-backup-policy"
recovery_services_vault_name = "my-rsvault"
sku = "Standard"
source_file_share_name = ["myshare"]
timezone = "UTC"
backup = {
frequency = "Daily"
time = "02:00"
}
retention_daily = {
count = 7
}
}

backup_blob = null
lifecycle_policy_rule = null
}
```

## File structure

```
.
├── CHANGELOG.md
├── blobs.tf
├── locals.tf
├── main.tf
├── outputs.tf
├── shares.tf
├── variables.tf
├── versions.tf
├── docs
│ ├── footer.md
│ └── header.md
├── _examples
│ ├── basic
│ └── comprehensive
├── README.md
└── .terraform-docs.yml
```
42 changes: 42 additions & 0 deletions modules/azure-sa-backup/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
output "blob_data_protection_vault_id" {
description = "Resource ID of the Data Protection backup vault for blob backup; null if `backup_blob` is not configured."
value = var.backup_blob != null ? azurerm_data_protection_backup_vault.this[0].id : null
}
Comment thread
JorgeIglesiasMosquera marked this conversation as resolved.

output "blob_backup_instance_id" {
description = "Resource ID of the blob backup instance; null if `backup_blob` is not configured."
value = var.backup_blob != null ? azurerm_data_protection_backup_instance_blob_storage.this[0].id : null
}

output "file_share_recovery_services_vault_id" {
description = "Resource ID of the Recovery Services vault for file share backup; null if `backup_share` is not configured."
value = var.backup_share != null ? azurerm_recovery_services_vault.this[0].id : null
}

output "file_share_protected_item_ids" {
description = "Map containing the protected item ID for the configured file share (only one supported); empty if `backup_share` is not configured."
value = var.backup_share == null ? {} : {
for idx, name in var.backup_share.source_file_share_name :
name => azurerm_backup_protected_file_share.this[idx].id
}
Comment thread
JorgeIglesiasMosquera marked this conversation as resolved.
}

output "blob_backup_policy_id" {
description = "Resource ID of the blob backup policy; null if `backup_blob` is not configured."
value = var.backup_blob != null ? azurerm_data_protection_backup_policy_blob_storage.this[0].id : null
}

output "blob_backup_vault_principal_id" {
description = "Principal ID of the backup vault managed identity; null if `backup_blob` is not configured or no identity is defined."
value = var.backup_blob != null && var.backup_blob.identity_type != null ? azurerm_data_protection_backup_vault.this[0].identity[0].principal_id : null
}

output "file_share_backup_policy_id" {
description = "Resource ID of the file share backup policy; null if `backup_share` is not configured."
value = var.backup_share != null ? azurerm_backup_policy_file_share.this[0].id : null
}

output "blob_backup_role_assignment_id" {
description = "Role assignment ID granting the backup vault access to the storage account; null if `backup_blob` is not configured or no identity is defined."
value = var.backup_blob != null && var.backup_blob.identity_type != null ? azurerm_role_assignment.this[0].id : null
}
Comment thread
pablosanchezpaz marked this conversation as resolved.
Comment on lines +29 to +42
9 changes: 7 additions & 2 deletions modules/azure-sa-backup/variables.tf
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# VARIABLES SECTION
## General
variable "backup_resource_group_name" {
description = "The name for the resource group for the backups"
description = "Name of the existing resource group where backup vaults and policies are created (also used for the data source and optional tag merge)."
type = string
}

variable "storage_account_id" {
description = "The ID of the storage account"
description = "Full Azure resource ID of the storage account to protect (file shares and/or blob backup)."
type = string
}

Expand Down Expand Up @@ -70,6 +70,10 @@ variable "backup_share" {
}))
})
default = null
validation {
condition = var.backup_share == null || length(var.backup_share.source_file_share_name) == 1
error_message = "When backup_share is set, exactly one value must be provided in backup_share.source_file_share_name."
}
}

## Backup blobs variables
Expand Down Expand Up @@ -113,6 +117,7 @@ variable "backup_blob" {
}

variable "lifecycle_policy_rule" {
description = "DEPRECATED: Currently not used by any resource. Setting this variable has no effect."
type = list(object({
name = string
Comment thread
JorgeIglesiasMosquera marked this conversation as resolved.
enabled = bool
Expand Down