Setup von Backup-Policies für alle Datenbanken einer Azure SQL Managed Instance mit Terraform

Eins ist klar: Datenbanken sollten immer gesichert werden. Und das Setup dafür gehört in den IaC- bzw. Terraform-Code.
Das was ist, wenn die Verantwortung für die Infrastruktur aufgetrennt ist? In diesem Fall wurde die Azure SQL Managed Instance mit Terraform angelegt, die einzelnen Datenbanken aber manuell von den Applikationsverantwortlichen angelegt und gelöscht.
Eine Möglichkeit, dies in Terraform abzubilden, ist das manuelle Nachpflegen jeder Datenbank im Code. Aber es geht auch einfacher und zuverlässiger.

Beispiel einen Terraform-Blocks für eine Azure SQL Managed Instance:

resource "azurerm_mssql_managed_instance" "sqlmi_gwc" {
  name                = "sqlmi-gwc"
  resource_group_name = azurerm_resource_group.this.name
  location            = azurerm_resource_group.this.location

  license_type       = "BasePrice"
  sku_name           = "GP_Gen5"
  storage_size_in_gb = 32
  subnet_id          = azurerm_subnet.this["snet-db-gwc"].id
  vcores             = 4

  administrator_login          = "admin"
  administrator_login_password = random_password.password.result
}

Ale erstes müssen die aktuellen Datenbanken einer Managed Instance abgefragt werden. Dazu kann die Resource “azurerm_resources” des Terraform-AzureRM-Providers verwenden werden.

Abfrage aller Datenbanken innerhalb einer Resource Group

# Get all current databases
data "azurerm_resources" "databases" {
  resource_group_name = azurerm_resource_group.this.name
  type                = "Microsoft.Sql/managedInstances/databases"
}

Damit die Daten in For_Each-Schleifen weiter verwendet werden können, empfielt sich eine Umwandlung von einer Objektliste nach einer Map.

# transform
locals {
  db_map = {
    for db in data.azurerm_resources.databases.resources : db.name => db
  }
}

Anschließend können die Datenbanken in den Terraform-State importiert und angepasst werden.
Bei den Namen der Datenbanken ist zu beachten, dass der data-Block diesen in der Form “<managed Instace>/<datenbank>” zurückgibt. Deswegen erfolgt eine Trennung nach “/”.
Die jeweilige Backup-Settings können hier nachgelesen werden.
In diesem Fall wurde folgendes als Backup eingestellt:

  • PITR = 30 Tage
  • LTR
    • monatlich 7 Monate
    • jährlich 5 Jahre aus der ersten Woche
# add import with new backup settings
resource "azurerm_mssql_managed_database" "database" {
  for_each = local.db_map

  name                = split("/", each.key)[1]
  managed_instance_id = azurerm_mssql_managed_instance.sqlmi_gwc.id

  short_term_retention_days = 30
  long_term_retention_policy {
    monthly_retention         = "P7M"
    yearly_retention          = "P5Y"
    week_of_year              = 1
    immutable_backups_enabled = false
  }
}
import {
  for_each = local.db_map

  to = azurerm_mssql_managed_database.database[each.key]
  id = each.value.id
}

Um den State und die Backup-Settings aktuell zu halten, sollte die zugehörige Pipeline (Github-Workflow/Action o.ä.) regelmäßig laufen.