Managing Azure Key Vault with Terraform

Learn how to set up and manage Azure Key Vault for secure secrets, keys, and certificates management using Terraform

Managing Azure Key Vault with Terraform

Azure Key Vault is a cloud service for securely storing and accessing secrets, keys, and certificates. This guide demonstrates how to manage Azure Key Vault using Terraform.

Video Tutorial

Learn more about managing Azure Key Vault with Terraform in this comprehensive video tutorial:

View Source Code

Prerequisites

  • Azure CLI configured with appropriate permissions
  • Terraform installed (version 1.0.0 or later)
  • Azure subscription with Key Vault access
  • Resource group created

Project Structure

terraform-azure-keyvault/
├── main.tf
├── variables.tf
├── outputs.tf
├── modules/
│   └── keyvault/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── policies/
    └── key_vault_policy.json

Key Vault Configuration

Create modules/keyvault/main.tf:

# Key Vault
resource "azurerm_key_vault" "main" {
  name                        = "${var.project_name}-kv"
  location                    = var.location
  resource_group_name         = var.resource_group_name
  enabled_for_disk_encryption = true
  tenant_id                   = data.azurerm_client_config.current.tenant_id
  soft_delete_retention_days  = 7
  purge_protection_enabled    = true

  sku_name = "standard"

  network_acls {
    default_action = "Deny"
    bypass         = "AzureServices"
    ip_rules       = var.allowed_ip_ranges
    virtual_network_subnet_ids = var.subnet_ids
  }

  tags = var.tags
}

# Access Policy
resource "azurerm_key_vault_access_policy" "example" {
  key_vault_id = azurerm_key_vault.main.id
  tenant_id    = data.azurerm_client_config.current.tenant_id
  object_id    = data.azurerm_client_config.current.object_id

  key_permissions = [
    "Get",
    "List",
    "Create",
    "Delete",
    "Update",
    "Import",
    "Backup",
    "Restore",
    "Recover"
  ]

  secret_permissions = [
    "Get",
    "List",
    "Set",
    "Delete",
    "Backup",
    "Restore",
    "Recover"
  ]

  certificate_permissions = [
    "Get",
    "List",
    "Create",
    "Import",
    "Update",
    "Delete",
    "Backup",
    "Restore",
    "Recover"
  ]
}

# Secrets
resource "azurerm_key_vault_secret" "example" {
  name         = "example-secret"
  value        = var.secret_value
  key_vault_id = azurerm_key_vault.main.id

  tags = var.tags

  depends_on = [
    azurerm_key_vault_access_policy.example
  ]
}

# Keys
resource "azurerm_key_vault_key" "example" {
  name         = "example-key"
  key_vault_id = azurerm_key_vault.main.id
  key_type     = "RSA"
  key_size     = 2048

  key_opts = [
    "decrypt",
    "encrypt",
    "sign",
    "unwrapKey",
    "verify",
    "wrapKey",
  ]

  depends_on = [
    azurerm_key_vault_access_policy.example
  ]
}

# Certificates
resource "azurerm_key_vault_certificate" "example" {
  name         = "example-cert"
  key_vault_id = azurerm_key_vault.main.id

  certificate_policy {
    issuer_parameters {
      name = "Self"
    }

    key_properties {
      exportable = true
      key_size   = 2048
      key_type   = "RSA"
      reuse_key  = true
    }

    lifetime_action {
      action {
        action_type = "AutoRenew"
      }

      trigger {
        days_before_expiry = 30
      }
    }

    secret_properties {
      content_type = "application/x-pkcs12"
    }

    x509_certificate_properties {
      # Server Authentication = 1.3.6.1.5.5.7.3.1
      # Client Authentication = 1.3.6.1.5.5.7.3.2
      extended_key_usage = ["1.3.6.1.5.5.7.3.1"]

      key_usage = [
        "cRLSign",
        "dataEncipherment",
        "digitalSignature",
        "keyAgreement",
        "keyCertSign",
        "keyEncipherment",
      ]

      subject_alternative_names {
        dns_names = ["example.com", "www.example.com"]
      }

      subject            = "CN=example.com"
      validity_in_months = 12
    }
  }

  depends_on = [
    azurerm_key_vault_access_policy.example
  ]
}

## Private Endpoint Configuration

1. **Private Endpoint Setup**
```hcl
resource "azurerm_private_endpoint" "keyvault" {
  name                = "${var.project_name}-pe"
  location            = var.location
  resource_group_name = var.resource_group_name
  subnet_id           = var.subnet_id

  private_service_connection {
    name                           = "${var.project_name}-psc"
    private_connection_resource_id = azurerm_key_vault.main.id
    is_manual_connection          = false
    subresource_names            = ["vault"]
  }

  private_dns_zone_group {
    name                 = "default"
    private_dns_zone_ids = [azurerm_private_dns_zone.keyvault.id]
  }
}

resource "azurerm_private_dns_zone" "keyvault" {
  name                = "privatelink.vaultcore.azure.net"
  resource_group_name = var.resource_group_name
}

resource "azurerm_private_dns_zone_virtual_network_link" "keyvault" {
  name                  = "${var.project_name}-vnet-link"
  resource_group_name   = var.resource_group_name
  private_dns_zone_name = azurerm_private_dns_zone.keyvault.name
  virtual_network_id    = var.vnet_id
}

Monitoring Configuration

  1. Diagnostic Settings
resource "azurerm_monitor_diagnostic_setting" "keyvault" {
  name                       = "${var.project_name}-diag"
  target_resource_id        = azurerm_key_vault.main.id
  log_analytics_workspace_id = var.log_analytics_workspace_id

  log {
    category = "AuditEvent"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }

  metric {
    category = "AllMetrics"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }
}
  1. Alert Rules
resource "azurerm_monitor_metric_alert" "keyvault" {
  name                = "${var.project_name}-alert"
  resource_group_name = var.resource_group_name
  scopes              = [azurerm_key_vault.main.id]
  description         = "Alert when total API hits exceeds threshold"

  criteria {
    metric_namespace = "Microsoft.KeyVault/vaults"
    metric_name      = "ServiceApiHit"
    aggregation      = "Total"
    operator         = "GreaterThan"
    threshold        = 1000
  }

  action {
    action_group_id = var.action_group_id
  }
}

Backup Configuration

  1. Automated Backup
resource "azurerm_key_vault_backup" "example" {
  key_vault_id = azurerm_key_vault.main.id
  storage_account_id = var.backup_storage_account_id
  
  backup_container_name = "keyvault-backups"
  backup_folder_path    = "backups"
}

resource "azurerm_automation_schedule" "backup" {
  name                    = "${var.project_name}-backup-schedule"
  resource_group_name     = var.resource_group_name
  automation_account_name = var.automation_account_name
  frequency              = "Day"
  interval               = 1
  timezone               = "UTC"
  start_time             = "2025-01-01T00:00:00Z"
  description            = "Schedule for Key Vault backup"
}

Best Practices

  1. Security

    • Use Private Endpoints
    • Enable Soft Delete
    • Enable Purge Protection
    • Regular access reviews
  2. Access Control

    • Use Managed Identities
    • Implement least privilege
    • Regular rotation
    • Monitor access
  3. Monitoring

    • Enable diagnostics
    • Configure alerts
    • Regular audits
    • Monitor performance
  4. Backup and Recovery

    • Regular backups
    • Test recovery
    • Document procedures
    • Maintain compliance

Advanced Features

  1. Managed HSM
resource "azurerm_key_vault_managed_hardware_security_module" "example" {
  name                     = "${var.project_name}-hsm"
  resource_group_name      = var.resource_group_name
  location                 = var.location
  sku_name                 = "Standard_B1"
  purge_protection_enabled = true
  tenant_id               = data.azurerm_client_config.current.tenant_id
  
  admin_object_ids = [
    data.azurerm_client_config.current.object_id
  ]

  network_acls {
    bypass         = "AzureServices"
    default_action = "Deny"
    ip_rules       = var.allowed_ip_ranges
  }
}
  1. Role-Based Access Control
resource "azurerm_role_assignment" "keyvault" {
  scope                = azurerm_key_vault.main.id
  role_definition_name = "Key Vault Administrator"
  principal_id         = var.admin_group_id
}

Conclusion

You’ve learned how to manage Azure Key Vault using Terraform. This setup provides:

  • Secure secret management
  • Access control
  • Monitoring and alerts
  • Backup and recovery

Remember to:

  • Monitor access
  • Rotate secrets regularly
  • Maintain backups
  • Review security settings