Skip to content

Terraform Provider for MazeVault

The MazeVault Terraform Provider lets you manage your entire MazeVault infrastructure as code — projects, secrets, certificates, rotations, access control, and integrations with external systems.


Installation

terraform {
  required_version = ">= 1.11"
  required_providers {
    mazevault = {
      source  = "MazeVault/mazevault"
      version = "~> 1.0"
    }
    random = {
      source = "hashicorp/random"
    }
  }
}

Provider Configuration

provider "mazevault" {
  server_url = "https://vault.example.com"
  api_token  = var.mazevault_token   # or env: MAZEVAULT_API_TOKEN
}
Argument Env variable Description
server_url MAZEVAULT_SERVER_URL MazeVault server base URL
api_token MAZEVAULT_API_TOKEN API token (recommended)
client_id MAZEVAULT_CLIENT_ID Service account OAuth2 client ID
client_secret MAZEVAULT_CLIENT_SECRET Service account OAuth2 client secret
timeout Request timeout, e.g. 30s (default)
skip_tls_verify Skip TLS check — development only

Secret handling

Never put credentials directly in .tf files. Use environment variables or a CI/CD secrets store. Never commit .tfvars files containing tokens or passwords.


Authentication

export MAZEVAULT_SERVER_URL="https://vault.example.com"
export MAZEVAULT_API_TOKEN="mv_pat_xxxxx"
terraform plan

Generate tokens in Admin → API Tokens or with mazevault_api_token resource.

Service Account (CI/CD pipelines)

provider "mazevault" {
  server_url    = var.server_url
  client_id     = var.client_id
  client_secret = var.client_secret
}

Example: External CA Account Settings

Use mazevault_ca_account.settings for non-secret CA provider configuration that MazeVault must reuse later during product sync, renewal polling, and DCV checks. Keep API keys, passwords, and EAB HMAC keys in the dedicated sensitive attributes, not in settings. Set provider_type to the documented lowercase value, such as digicert, letsencrypt, or acme; mixed case and surrounding whitespace are rejected before MazeVault receives the request.

resource "mazevault_ca_account" "digicert" {
  organization_id = mazevault_organization.acme.id
  name            = "DigiCert Production"
  provider_type   = "digicert"
  api_key         = var.digicert_api_key
  base_url        = "https://www.digicert.com"

  settings = {
    organization_id       = var.digicert_organization_id
    server_platform_id    = "45"
    dcv_method            = "dns_txt"
    certificate_dcv_scope = "base_domain"
  }
}

Example: Secrets Management with Azure Key Vault Sync

Provision a project, store a database password, and sync it to Azure Key Vault with automatic 30-day rotation.

resource "mazevault_organization" "acme" {
  name = "Acme Corp"
}

resource "mazevault_project" "backend" {
  organization_id = mazevault_organization.acme.id
  name            = "Backend Services"
  type            = "secret"
}

resource "mazevault_integration" "azure_kv" {
  project_id  = mazevault_project.backend.id
  name        = "Production Key Vault"
  type        = "azure_key_vault"
  environment = "production"
  config = {
    vault_url            = var.keyvault_url
    use_managed_identity = "true"
  }
}

ephemeral "random_password" "db" {
  length           = 40
  special          = true
  override_special = "_-+=@#"
}

resource "mazevault_secret" "db_password" {
  project_id  = mazevault_project.backend.id
  key         = "POSTGRES_PASSWORD"
  environment = "production"

  value_wo         = ephemeral.random_password.db.result
  value_wo_version = 1
}

resource "mazevault_secret_link" "db_link" {
  secret_id      = mazevault_secret.db_password.id
  integration_id = mazevault_integration.azure_kv.id
  link_type      = "database"
  metadata = {
    db_user = "app_user"
    db_host = "db.prod.example.com"
    db_name = "appdb"
  }
}

resource "mazevault_rotation_config" "db" {
  secret_id             = mazevault_secret.db_password.id
  rotation_interval_days = 30
  enabled               = true
  notification_emails   = ["ops@example.com"]
}

# Deploy the rotated value to a Kubernetes secret after each rotation.
resource "mazevault_secret_rotation_target" "k8s" {
  secret_id   = mazevault_secret.db_password.id
  target_type = "kubernetes_secret"
  priority    = 10
  enabled     = true
  config_json = jsonencode({
    namespace   = "production"
    secret_name = "app-db-credentials"
    secret_key  = "POSTGRES_PASSWORD"
    agent_id    = "your-agent-uuid"   # agent with K8s cluster access
  })
}

# Also rotate the password on the database itself.
resource "mazevault_secret_rotation_target" "postgres" {
  secret_id   = mazevault_secret.db_password.id
  target_type = "database_password"
  priority    = 20
  enabled     = true
  config_json = jsonencode({
    db_provider                = "postgres"
    host                       = "db.prod.example.com"
    port                       = 5432
    database                   = "appdb"
    username                   = "app_user"
    admin_credential_secret_id = "your-admin-secret-uuid"
  })
}

State-safe secret values

For Terraform-managed secret material, prefer value_wo with Terraform 1.11+ ephemeral resources. The provider reads the value from configuration during apply, sends it to MazeVault, and does not store it in Terraform plan or state. Increment value_wo_version when a new write-only value should be sent.

ephemeral "random_password" "api" {
  length           = 48
  special          = true
  override_special = "_-+=@#"
}

resource "mazevault_secret" "api_password" {
  project_id       = mazevault_project.backend.id
  key              = "API_PASSWORD"
  environment      = "production"
  value_wo         = ephemeral.random_password.api.result
  value_wo_version = 1
}

The legacy value argument remains supported for existing configurations, but Terraform stores sensitive values in state. Avoid reading secret material back with the mazevault_secret data source when state must remain free of plaintext.

Server-side secret generation

Instead of supplying value, add a generate { ... } block and MazeVault creates the value for you using cryptographically secure, DB/URL-safe characters. Exactly one of value, value_wo, or generate must be set. The generated plaintext is never returned to Terraform (it never enters state); in Orchestrator Mode it is offloaded directly to the external secret manager.

resource "mazevault_secret" "api_key" {
  project_id  = mazevault_project.backend.id
  key         = "API_KEY"
  environment = "production"

  generate {
    length          = 48
    include_symbols = true
  }
}
generate field Default Description
length 40 Length of the generated value
include_upper true Include uppercase letters
include_lower true Include lowercase letters
include_digits true Include digits
include_symbols true Include DB/URL-safe symbols
excluded_chars Characters to exclude from the result

Azure Key Vault and Entra integrations

Beyond the typed Azure DevOps azure_* fields, mazevault_integration also accepts a generic non-secret config map and a write-only sensitive_config map (secret values, never read back from the server), plus interactive_auth_method. This enables Azure Key Vault and Microsoft Entra integrations.

resource "mazevault_integration" "kv" {
  project_id    = mazevault_project.backend.id
  name          = "prod-keyvault"
  type          = "azure_key_vault"
  provider_name = "azure_keyvault"
  environment   = "production"

  config = {
    vault_url   = "https://myvault.vault.azure.net/"
    auth_method = "client_secret"
    tenant_id   = var.tenant_id
    client_id   = var.client_id
  }

  sensitive_config = {
    client_secret = var.client_secret
  }
}

Entra ID credential rotation

post_rotation_actions is the single source of truth for post-rotation delivery. If you define no action blocks, nothing is delivered. Key Vault write-back and Spring Boot refresh are expressed as actions here — not as separate top-level fields. The kv_integration_ids, secret_name, spring_endpoints, and webhook_urls attributes are now read-only computed mirrors of these actions.

resource "mazevault_entra_rotation_config" "app" {
  credential_id               = var.entra_credential_id
  rotation_days_before_expiry = 30

  post_rotation_actions {
    type = "azure_keyvault"
    config = {
      integration_id = mazevault_integration.kv.id
      secret_name    = "app-client-secret"
    }
  }

  post_rotation_actions {
    type   = "spring_actuator_refresh"
    config = { actuator_url = "https://app.example.com/actuator/refresh" }
  }
}

Example: Certificate Lifecycle Automation

Issue a TLS certificate via a CA account and set up automatic renewal. A mazevault_certificate_template is a certificate-category Project Template: it is created at the organization level (resolved from project_id) and appears in the frontend Project Templates view for certificate-management projects (type = "certificate").

resource "mazevault_project" "pki" {
  organization_id = mazevault_organization.acme.id
  name            = "PKI"
  type            = "certificate"
}

resource "mazevault_ca_account" "digicert" {
  organization_id = mazevault_organization.acme.id
  name            = "DigiCert Production"
  provider_type   = "digicert"
  api_key         = var.digicert_api_key
  base_url        = "https://www.digicert.com"
}

Certificate templates are certificate-category Project Templates

mazevault_certificate_template creates a Project Template with category certificates at the organization level (resolved from project_id), so it shows up in the Project Templates view. MazeVault automatically creates the underlying certificate template, exposed as the computed certificate_template_id. validity_period (e.g. 8760h) is converted to whole days; type is the certificate subtype (e.g. ssl_tls).

ACME CA accounts (Let's Encrypt / ZeroSSL / custom)

ACME providers use email and directory_url (and, when the CA uses External Account Binding, eab_kid / eab_hmac_key) instead of api_key. Every non-internal provider must supply credentials — the provider validates this client-side and returns a clear error before calling the server if they are missing. directory_url is required for a custom acme provider and is defaulted automatically for letsencrypt and zerossl. The ACME challenge policy is set on the certificate template via challenge_type.

resource "mazevault_ca_account" "letsencrypt" {
  organization_id = mazevault_organization.acme.id
  name            = "Let's Encrypt"
  provider_type   = "letsencrypt"
  email           = "pki@example.com"
}

resource "mazevault_ca_account" "zerossl" {
  organization_id = mazevault_organization.acme.id
  name            = "ZeroSSL"
  provider_type   = "zerossl"
  email           = "pki@example.com"
  eab_kid         = var.zerossl_eab_kid
  eab_hmac_key    = var.zerossl_eab_hmac_key
}

resource "mazevault_ca_account" "internal_acme" {
  organization_id = mazevault_organization.acme.id
  name            = "Internal ACME"
  provider_type   = "acme"
  email           = "pki@example.com"
  directory_url   = "https://ca.internal.example.com/acme/acme/directory"
}

resource "mazevault_certificate_template" "acme_web" {
  project_id      = mazevault_project.pki.id
  name            = "ACME Web TLS"
  type            = "tls_server"
  validity_period = "2160h"
  # auto (default) | dns-01 | http-01 | tls-alpn-01
  challenge_type  = "dns-01"
}

For external commercial CAs that require domain control validation (for example SSLMarket), the per-template validation method is set via dcv_method (email, dns, or file). An empty value uses the provider/account default. dcv_method is ignored by internal and ACME issuers.

resource "mazevault_certificate_template" "sslmarket_dv" {
  project_id      = mazevault_project.pki.id
  name            = "SSLMarket DV"
  type            = "ssl_tls"
  validity_period = "8760h"
  # email | dns | file (empty = provider/account default)
  dcv_method      = "dns"
}

The organization's internal root CA can be managed with the mazevault_ca resource. Each organization has at most one internal CA; its parameters are immutable (changing them replaces the CA), and destroying the resource soft-deletes it — the delete is rejected while active certificates were issued by it.

resource "mazevault_ca" "org_root" {
  organization_id = mazevault_organization.acme.id
  name            = "ACME Internal CA"
  valid_years     = 10
  key_size        = 4096
}
resource "mazevault_renewal_policy" "default" {
  organization_id  = mazevault_organization.acme.id
  name             = "30-day lead"
  lead_days        = 30
  auto_approve     = true
  notify_emails    = "ops@example.com"
}

resource "mazevault_certificate_template" "web_tls" {
  project_id      = mazevault_project.pki.id
  name            = "Web TLS 1Y"
  type            = "ssl_tls"
  validity_period = "8760h"
  key_usage       = ["digitalSignature", "keyEncipherment"]
}

resource "mazevault_certificate" "api_tls" {
  common_name                = "api.example.com"
  ttl                        = "8760h"
  key_size                   = 2048
  organization_ca_account_id = mazevault_ca_account.digicert.id
}

output "api_cert_pem" {
  value     = mazevault_certificate.api_tls.certificate_pem
  sensitive = false
}

output "api_key_pem" {
  value     = mazevault_certificate.api_tls.private_key_pem
  sensitive = true
}

Issuing from the organization internal CA and deploying to Key Vault: a certificate can be issued by the organization internal CA (mazevault_ca), assigned to a project, and delivered into an Azure Key Vault on renewal via a post_rotation_actions entry. For the complete arguments, attributes and worked examples, see the provider resource docs: mazevault_ca, mazevault_certificate and mazevault_certificate_rotation_config.


Example: RBAC and Service Account Setup

Create a CI/CD service account with read-only access to a specific project.

resource "mazevault_role" "ci_readonly" {
  name        = "ci-readonly"
  description = "Read-only access for deployment pipelines"
  permissions = ["secrets:read", "certificates:read", "projects:read"]
}

resource "mazevault_service_identity" "github_actions" {
  display_name = "GitHub Actions CI"
  description  = "Read-only service account for production deployments"
  owner_email  = "platform@example.com"
}

resource "mazevault_api_token" "ci_token" {
  name   = "github-actions-prod"
  scopes = ["secrets:read", "certificates:read"]
}

output "ci_client_id" {
  value = mazevault_service_identity.github_actions.client_id
}

output "ci_client_secret" {
  value     = mazevault_service_identity.github_actions.client_secret
  sensitive = true
}

Example: Sync Rules and Rotation Templates

Continuously pull secrets from Azure Key Vault into a MazeVault project and apply a shared rotation policy template.

# Reusable rotation policy template
resource "mazevault_rotation_template" "standard_90d" {
  name                  = "standard-90-day"
  description           = "Standard 90-day rotation with 14-day certificate lead time"
  rotation_interval_days = 90
  lead_time_days        = 14
  grace_period_days     = 3
  max_retry_attempts    = 3
  timeout_minutes       = 30
  is_default            = true
}

# Pull secrets from Azure KV into production environment
resource "mazevault_sync_rule" "azure_kv_pull" {
  name               = "azure-kv-production-pull"
  project_id         = mazevault_project.backend.id
  integration_id     = mazevault_integration.azure_kv.id
  target_environment = "production"
  source_path        = "secrets/app/"
  sync_direction     = "pull"
  sync_mode          = "incremental"
  conflict_strategy  = "external_wins"
}

# Post-rotation action that targets a specific gateway
resource "mazevault_rotation_workflow" "db_with_gateway" {
  secret_id             = mazevault_secret.db_password.id
  environment           = "production"
  rotation_interval_days = 90

  post_rotation_actions {
    type       = "azure_keyvault"
    order      = 1
    on_failure = "rollback"
    gateway_id = "gateway-eu-west-01"
    config = {
      vault_url   = "https://myvault.vault.azure.net"
      secret_name = "db-password"
    }
  }

  post_rotation_actions {
    type              = "spring_actuator_refresh"
    order             = 2
    on_failure        = "continue"
    target_environment = "production"
    config = {
      url = "https://api.example.com/actuator/refresh"
    }
  }
}

Example: Environment Tiers

Define environment tiers for an organization. name and slug are immutable — changing either forces the environment to be recreated. Only is_production and incident_auto_escalation can be updated in place.

resource "mazevault_environment" "production" {
  organization_id          = mazevault_organization.acme.id
  name                     = "production"
  slug                     = "prod"
  is_production            = true
  incident_auto_escalation = true
}

Example: External API Token Management

Many external systems (Jira, Signi, …) issue long-lived API tokens that can only be renewed manually in their own UI. mazevault_token lets you register such a token with a mandatory expiry date so MazeVault raises an incident before it lapses. The value is stored encrypted (or offloaded to your secret manager in Orchestrator Mode). This feature requires a PoC or Enterprise license.

resource "mazevault_token" "jira" {
  name             = "jira-automation-token"
  token_provider   = "jira"                 # free-text label; jira/signi get dedicated icons in the UI
  description      = "CI automation token for Jira Cloud"
  value_wo         = var.jira_token          # Terraform 1.11+; absent from plan and state
  value_wo_version = 1                       # incrementing this replaces the token
  expires_at       = "2027-01-01T00:00:00Z" # mandatory
  lead_time_days   = 30                      # alert 30 days before expiry
}

# Surface tokens that have entered their alert window.
data "mazevault_tokens" "all" {}

output "tokens_needing_renewal" {
  value = [for t in data.mazevault_tokens.all.tokens : t.name if t.days_until_expiry <= t.lead_time_days]
}

When the token is renewed in the external system, update its value through the MazeVault UI/API renew flow (which also re-deploys any configured agent targets). Changing value in Terraform forces the resource to be recreated.

The deprecated value argument remains available for existing configurations, but Terraform stores it in state. Do not transparently replace value with value_wo on an existing resource: the change recreates the token, changes its ID, and deletes its MazeVault deployment targets. Use value_wo for new tokens. For an intentional migration, inventory and recreate all targets in a maintenance window. Editing HCL does not remove plaintext from historical remote-state versions; rotate the external token and follow the state backend's retention and purge procedure.


Resources Reference

Full attribute documentation for each resource is available on the Terraform Registry.

Resource Description
mazevault_organization Top-level organization
mazevault_project Project (RBAC boundary, groups secrets/certs)
mazevault_project_settings Per-project operational settings (retention, sync, notifications)
mazevault_secret Encrypted secret with optional rotation
mazevault_secret_link Links a secret to an external integration for write-back
mazevault_shared_secret One-time share link for a secret
mazevault_rotation_config Full rotation pipeline configuration for a secret (v2: uses rotation_interval_days instead of legacy ttl_hours/rotation_strategy)
mazevault_rotation_workflow Simple rotation schedule for a secret
mazevault_rotation_template Reusable rotation policy template
mazevault_secret_rotation_target Structured deployment target in a secret's rotation pipeline (K8s, DB, Agent, DevOps, cloud vault)
mazevault_certificate_rotation_config Automatic renewal and rotation configuration for a specific certificate
mazevault_entra_rotation_config Entra ID credential rotation scheduling (days-before-expiry, staged rotation)
mazevault_sync_rule Synchronization rule between MazeVault and an external KV/secrets provider
mazevault_certificate Certificate issued by or tracked in MazeVault
mazevault_certificate_template Reusable certificate issuance policy
mazevault_ca Organization internal root Certificate Authority (singleton)
mazevault_ca_account CA account integration or reference for external providers, self-hosted providers, and internal CA account references
mazevault_renewal_policy Automatic certificate renewal timing policy
mazevault_integration External secrets store / deployment target (Azure KV, AWS SM, K8s, …)
mazevault_integration_group Multi-environment integration routing
mazevault_consistency_group Cross-environment secret parity monitoring
mazevault_role Custom RBAC role
mazevault_group_mapping Maps external IdP group to a MazeVault role
mazevault_user User account
mazevault_user_role Assigns a role to a user in a project
mazevault_service_identity Machine identity (OAuth2 client)
mazevault_api_token Scoped API token
mazevault_identity_provider SAML / OIDC / LDAP SSO provider
mazevault_environment Environment definition
mazevault_approval_policy Approval workflow for sensitive operations
mazevault_keytab Kerberos keytab for agent authentication
mazevault_token External API token (Jira, Signi, …) with mandatory expiry tracking
mazevault_deployment Agent deployment bootstrap package
mazevault_config_template Configuration template for secret injection

Data Sources Reference

Data Source Description
mazevault_organization Look up an organization by ID
mazevault_project Look up a project by ID
mazevault_secret Read a secret value (sensitive)
mazevault_certificate Read a certificate by ID
mazevault_project_certificates List certificates in a project
mazevault_project_cas List CA accounts in a project
mazevault_project_certificate_templates List certificate templates in a project
mazevault_project_csrs List pending CSRs in a project
mazevault_environments List environments in an organization
mazevault_ca_accounts List CA accounts in an organization
mazevault_users List all users
mazevault_roles List all RBAC roles
mazevault_integrations List integrations in a project
mazevault_audit_logs Read audit log entries
mazevault_rotation_executions List rotation execution records
mazevault_renewal_queue List certificates pending renewal
mazevault_consistency_status Cross-environment consistency check result
mazevault_rotation_resources List all rotation-managed resources with their status
mazevault_rotation_resource_history Read rotation execution history for a specific resource
mazevault_project_rotation_configs List all rotation configs within a project
mazevault_tokens List managed external API tokens with their expiry status

Importing Existing Resources

Bring existing MazeVault resources under Terraform management with terraform import. Resource UUIDs are visible in the UI URL or via GET /api/v1/....

terraform import mazevault_project.backend <project-uuid>
terraform import mazevault_secret.db_password <secret-uuid>
terraform import mazevault_certificate.tls <certificate-uuid>
terraform import mazevault_integration.azure_kv <integration-uuid>

Known Limitations

  • mazevault_role — Roles cannot be deleted via the MazeVault API. A terraform destroy removes the resource from Terraform state only; the role remains in MazeVault.
  • mazevault_ca — Manages the organization's single internal root CA (organization_id). All parameters are immutable; changing any of them replaces the CA. Destroying the resource soft-deletes the internal CA, but the delete is rejected (HTTP 409) while active (non-revoked, non-expired) certificates were issued by it. The ocsp_url / crl_url attributes are read-only and reflect the URLs published for issued certificates.
  • mazevault_integration — The environment argument is required and forces resource replacement on change. The backend does not accept environment changes via update; always recreate when changing environment.
  • mazevault_secret_rotation_target — The config_json argument must be a valid JSON object. Use jsonencode() in Terraform. Supplying malformed JSON produces a diagnostic error at apply time. Read operations use a list-scan (no direct GET-by-ID endpoint). Destroying a target that has already been deleted externally is idempotent and succeeds silently.
  • mazevault_rotation_config (v2.0 BREAKING) — The environment, rotation_strategy, workflow_steps_json, scope, and grace_period_minutes arguments have been removed. Replace ttl_hours with rotation_interval_days. Run terraform state rm and re-import when upgrading from v1.x.
  • mazevault_certificate_rotation_config — No hard-delete endpoint exists. Destroying sets enabled = false and removes from state only.
  • mazevault_entra_rotation_config — No hard-delete endpoint exists. Destroying sets rotation_enabled = false and removes from state only.
  • mazevault_token — Requires a PoC or Enterprise license (feature token_management); the API returns HTTP 403 otherwise. Use token_provider for the provider label. New resources should use value_wo plus value_wo_version; legacy value is sensitive but remains in Terraform state. Import manages metadata only. In-place value renewal is done through the MazeVault UI/API. project_id is immutable, and any Terraform replacement also deletes the token's MazeVault deployment targets.

Best Practices

  • Remote state — use Terraform Cloud, Azure Blob Storage, or S3 with state locking.
  • Sensitive outputs — always set sensitive = true on outputs exposing secret values or private keys.
  • Inject credentials via env vars — use MAZEVAULT_API_TOKEN or TF_VAR_ prefixed variables; never commit tokens in .tfvars.
  • Pin provider version~> 1.0 allows patch updates while blocking unexpected major changes.
  • Consistency groups — add mazevault_consistency_group after provisioning to detect cross-environment drift before it causes incidents.
  • Rotation scheduling — use rotation_interval_days and schedule (cron) to define rotation cadence (v2: ttl_hours/rotation_strategy removed). Post-rotation deployment targets are configured via mazevault_rotation_workflow or the more granular mazevault_secret_rotation_target (supports K8s, database password rotation, agent sync, DevOps variables, and cloud vaults). For certificates use mazevault_certificate_rotation_config; for Entra ID credentials use mazevault_entra_rotation_config.
  • Separate state per environment — use workspaces or separate state backends per environment to prevent accidental cross-environment writes.