Azure Key Vault
RBAC-mode Key Vault with private endpoint, diagnostics, and managed keys/secrets/certificates scaffolding.
Verification
Static-verifiedPassed: 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
| Name | Type | Default | Description |
|---|---|---|---|
name | string | — | 3-24 chars, globally unique |
resource_group_name | string | — | Existing resource group |
location | string | — | Azure region |
tenant_id | string | null | Defaults to the deployer's tenant |
sku_name | string | standard | standard or premium (HSM keys) |
purge_protection_enabled | bool | true | Irreversible once on (see notes) |
soft_delete_retention_days | number | 90 | 7-90 |
public_network_access_enabled | bool | true | Still ACL-filtered; false for PE-only |
enabled_for_deployment | bool | false | VM certificate retrieval |
enabled_for_disk_encryption | bool | false | ADE access |
enabled_for_template_deployment | bool | false | ARM template access |
network_acls | object | Deny + AzureServices | { default_action, bypass, ip_rules, virtual_network_subnet_ids } |
role_assignments | map(object) | {} | { principal_id, role_definition_name, principal_type?, description? } |
keys | map(object) | {} | RSA/EC keys with optional rotation_policy |
secrets | map(object) | {} | Sensitive; { value, content_type?, expiration_date? } |
self_signed_certificates | map(object) | {} | Self-issued certs with AutoRenew |
diagnostic_settings | object | disabled | AuditEvent logs to LA/storage/Event Hub |
private_endpoint | object | null | { subnet_id, private_dns_zone_ids, name? } |
tags | map(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 setpurge_protection_enabled = falseand 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_assignmentsin 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), whichnetwork_aclsfilters. Thebypass = "AzureServices"trusted-services exception does not cover a Terraform runner. So withdefault_action = "Deny"you must add the runner's public IP tonetwork_acls.ip_rules(or its subnet tovirtual_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 setdefault_action = "Deny"and seed any data-plane object without anip_rules/ subnet entry. For a quick bootstrap you can setdefault_action = "Allow"(as theexamples/basicdoes), then tighten toDenyonce 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
| Requirement | Version |
|---|---|
| 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).