IaC Bazaar
AzureStatic-verified

Azure Key Vault

RBAC-mode Key Vault with private endpoint, diagnostics, and managed keys/secrets/certificates scaffolding.

terraformAzure#azure

Compare Secrets & Key Management across clouds →

azure-key-vaultterraform v1.7

Verification

Static-verified

Passed: validated and lint-clean (provider-schema-validated for AWS/Azure/GCP; Terraform-language lint elsewhere).

Conformance

  • Static validation (fmt · validate · tflint)
  • Security scan: findings disclosed (Checkov)
  • Plan tests (mocked: validation rules · outputs)

Provenance

  • SHA-256 checksum
  • Signature (pending)

Functional

  • Live test pending (no cloud run yet)

Last verified 2026-06-28 · how we verify

Documentation

azure-key-vault

RBAC-mode Key Vault with private endpoint, diagnostics, and managed keys/secrets/certificates scaffolding. Opinionated and secure by default: Azure RBAC authorization only (no legacy access policies), purge protection on, 90-day soft delete, network default-deny with trusted-services bypass. Role assignments, rotation-enabled keys, secrets and self-signed certificate scaffolding all hang off one module call — with data-plane objects sequenced after RBAC grants so fresh deployments don't race role propagation. Works with Terraform and OpenTofu (>= 1.6), azurerm provider >= 4.0, < 5.0.

Usage

module "key_vault" {
  source              = "./azure-key-vault"
  name                = "kv-app-prod-001"
  resource_group_name = "rg-security-prod"
  location            = "westeurope"

  role_assignments = {
    admins = {
      principal_id         = data.azurerm_client_config.current.object_id
      role_definition_name = "Key Vault Administrator"
    }
  }

  keys = {
    "cmk-storage" = { rotation_policy = {} } # 90-day auto-rotation
  }

  network_acls = {
    ip_rules = ["203.0.113.0/24"]
  }
}

Inputs

NameTypeDefaultDescription
namestring3-24 chars, globally unique
resource_group_namestringExisting resource group
locationstringAzure region
tenant_idstringnullDefaults to the deployer's tenant
sku_namestringstandardstandard or premium (HSM keys)
purge_protection_enabledbooltrueIrreversible once on (see notes)
soft_delete_retention_daysnumber907-90
public_network_access_enabledbooltrueStill ACL-filtered; false for PE-only
enabled_for_deploymentboolfalseVM certificate retrieval
enabled_for_disk_encryptionboolfalseADE access
enabled_for_template_deploymentboolfalseARM template access
network_aclsobjectDeny + AzureServices{ default_action, bypass, ip_rules, virtual_network_subnet_ids }
role_assignmentsmap(object){}{ principal_id, role_definition_name, principal_type?, description? }
keysmap(object){}RSA/EC keys with optional rotation_policy
secretsmap(object){}Sensitive; { value, content_type?, expiration_date? }
self_signed_certificatesmap(object){}Self-issued certs with AutoRenew
diagnostic_settingsobjectdisabledAuditEvent logs to LA/storage/Event Hub
private_endpointobjectnull{ subnet_id, private_dns_zone_ids, name? }
tagsmap(string){}Tags for all resources

Outputs

id, name, vault_uri, tenant_id, key_ids, key_versionless_ids (use these for CMK so rotation is picked up), secret_ids, certificate_ids, certificate_secret_ids, role_assignment_ids, private_endpoint_id, private_endpoint_ip_address.

Notes

  • Soft delete + purge protection: deleting the vault keeps the name reserved for soft_delete_retention_days; with purge protection on you cannot purge early. For ephemeral environments set purge_protection_enabled = false and randomize vault names so re-runs don't collide with soft-deleted vaults.
  • The deployer needs a data-plane role (e.g. Key Vault Administrator) to create keys/secrets/certificates — grant it via role_assignments in the same apply; the module orders data-plane objects after the grants.
  • Network firewall vs. seeding data-plane objects: keys/secrets/certificates are written over the vault data plane (<name>.vault.azure.net), which network_acls filters. The bypass = "AzureServices" trusted-services exception does not cover a Terraform runner. So with default_action = "Deny" you must add the runner's public IP to network_acls.ip_rules (or its subnet to virtual_network_subnet_ids) before seeding keys/secrets/certs, or the apply fails with a 403 on the first data-plane write. The module enforces this at plan time: it errors if you set default_action = "Deny" and seed any data-plane object without an ip_rules / subnet entry. For a quick bootstrap you can set default_action = "Allow" (as the examples/basic does), then tighten to Deny once the runner's IP is allow-listed.
  • Secret values transit Terraform state. Treat the state as secret material, or seed placeholders and rotate out-of-band.

Requirements

RequirementVersion
Terraform / OpenTofu>= 1.6
hashicorp/azurerm>= 4.0, < 5.0

Verification

Static-validated (fmt, validate, tflint). Live apply/destroy testing pending cloud sandbox availability — see catalog status.

License

Commercial — IaC Bazaar EULA. © IaC Bazaar. Original work (not derived from a third-party module).