Skip to content

Terraform Provider

Manage part of HelixIAM as code — version your applications, realm roles, EU Digital Identity Wallet connections and the wallet features built on them in the same workflow as the rest of your infrastructure.

What it is

terraform-provider-helix lets you declare Helix resources in HCL and reconcile them with terraform apply. Configuration becomes reviewable, repeatable, and promotable between environments — no click-ops drift.

The provider ships these resources:

Resource Manages
helix_application An application and its OIDC/SAML client
helix_realm_role A realm role
helix_wallet_identity_provider An EU Digital Identity Wallet (OpenID4VP verifier) identity-provider connection (Beta)
helix_client_verified_claims A client's verified-claims release policy (Preview)
helix_client_verification_api · helix_verification_template A client's verification API settings and templates (Preview)
helix_wallet_profile_settings A verify-only wallet connection's step-up and profile settings (Preview)
helix_credential_issuer · helix_credential_template The realm's credential issuer settings and templates (Preview)

Scope today

Realms, standalone OIDC clients, users, flows, agents, trusted list sources, verifier access keys, credential issuer keys and offers, and identity-provider connections other than the wallet one are not yet Terraform resources. Manage those in the admin console or the admin API, or move them between environments with import / export. The provider is not published to the Terraform Registry; it is available to licensed customers from KubeDNA B.V. ([email protected]), with instructions for a local install.

Authentication

The provider authenticates with OAuth2 client credentials. The realm is set once on the provider, and resources are created within it. Configure it inline or via environment variables (HELIX_ISSUER, HELIX_REALM, HELIX_CLIENT_ID, HELIX_CLIENT_SECRET), which take over when the matching argument is omitted.

terraform {
  required_providers {
    helix = {
      source = "melmarzgioui/helix"
    }
  }
}

provider "helix" {
  issuer        = "https://auth.example.com/realms/acme" # the realm issuer that mints the admin token
  realm         = "acme"
  client_id     = "terraform"
  client_secret = var.helix_client_secret # or HELIX_CLIENT_SECRET
}

Use a dedicated automation client

Create a confidential client for Terraform with just the admin scopes it needs, and supply the secret via HELIX_CLIENT_SECRET so it never lands in state or VCS.

Usage

Declare an application (which owns its OIDC/SAML client) and a realm role in the provider's realm. The resource arguments mirror the admin API bodies documented in the API reference.

# An application — owns its OIDC/SAML client, shared subject claim, and login flow
resource "helix_application" "web" {
  name            = "acme-web"
  display_name    = "Acme Web"
  subject_claim   = "email"
  auth_flow_alias = "browser"
  enabled         = true
}

# A realm role
resource "helix_realm_role" "billing_admin" {
  name        = "billing-admin"
  description = "Manage billing"
}
terraform init
terraform plan
terraform apply

helix_application

Argument Required Description
name yes Application name (also the resource id)
display_name no Human-friendly name shown in the console
description no Free-text description
subject_claim no Claim used as the token subject (e.g. email)
auth_flow_alias no Authentication flow bound to this app
enabled no Whether the application is enabled (default true)

helix_realm_role

Argument Required Description
name yes Role name (also the resource id)
description no Free-text description
role_id computed Server-assigned role id

helix_wallet_identity_provider

An EU Digital Identity Wallet connection: an identity provider with protocol eudi-wallet. Every attribute maps to a key of the connection's config map (configuration keys); an attribute left unset is left out of the write, so the server's own default applies and terraform plan shows no drift for it. Import an existing connection with terraform import helix_wallet_identity_provider.<name> <alias>. A 400 from the admin API becomes one diagnostic per field, on the right attribute where known.

resource "helix_wallet_identity_provider" "eudi_wallet" {
  alias        = "eudi-wallet"
  display_name = "EU Digital Identity Wallet"
  enabled      = true

  access_key_ref     = "software:acme/primary" # an imported verifier key
  trust_anchors      = file("${path.module}/pid-issuer-ca.pem")
  credential_formats = ["dc+sd-jwt", "mso_mdoc"]

  pid_requested_claims = ["given_name", "family_name", "birthdate"]
  minimum_loa          = "http://eidas.europa.eu/LoA/high"
}

Terraform does not manage the verifier access key: import it first (step 1) and reference it with access_key_ref.

Attribute Required Description
alias yes, forces replace Connection alias, unique in the realm (also the resource id)
display_name / enabled no Label on the console and login page; enabled (default true)
access_key_ref recommended Verifier key reference (e.g. software:acme/primary); never written to state
access_certificate_chain no PEM chain, leaf first, no root
access_certificate_private_key no, sensitive A raw PEM key instead of access_key_ref. The server accepts it only in the dev profile. Write-only against the API but stored in plaintext in Terraform state; removing it from the configuration clears it on the server
client_id_scheme / client_id_host no x509_hash (default) or x509_san_dns with its DNS host
registration_certificate no PEM bundle for OpenID Federation registration
invocation_scheme / response_mode / request_uri_method / transaction_ttl_seconds no Request settings (defaults haip-vp, direct_post.jwt, get, 300)
credential_formats, pid_sd_jwt_vcts, pid_mdoc_doctype, pid_requested_claims no What the wallet is asked for
custom_credential_queries no Not supported yet (must stay empty)
trust_anchors no PEM bundle of trusted PID-issuer CAs
trusted_lists / trusted_list_service_types no Names of the realm's trusted list sources whose anchors are trusted in addition to trust_anchors (each must exist in the realm), optionally narrowed to service type URIs
dcql_trusted_authorities no HAIP trusted_authorities in the DCQL query. Unset: on for x509_hash + direct_post.jwt, off otherwise. true requires every anchor to carry a SubjectKeyIdentifier
dc_api_enabled / dc_api_request / dc_api_expected_origins no The W3C Digital Credentials API (server default off; signed, unsigned or multisigned; expected origins)
dc_api_additional_key_ref no The second realm key of a multi-signed request (Preview)
verifier_info_source / verifier_info_format / verifier_info_json no OpenID4VP verifier_info (Preview)
transaction_data_types no Verify-only: the transaction types the connection may confirm (Preview)
credentials / credential_sets no JSON strings (jsonencode([...])): credentials beyond the PID and how they combine; read back without drift when the server's canonical JSON means the same (Preview)
holder_binding_same_key no Refuse presentations bound to different holder keys (server default false)
did_issuers no PID issuers identified by a DID (full DIDs, or a bare domain accepting only did:web:<domain>); per credential: didIssuers inside credentials (Preview)
revocation_checking no Issuer certificate revocation, default HARD_FAIL
status_list_required / status_list_on_unavailable / status_list_max_cache_seconds / status_list_allowed_hosts no Token Status List policy
subject_strategy / subject_acknowledge_weak_subject no ADMINISTRATIVE_NUMBER (default), ATTRIBUTE_TUPLE (needs the acknowledgement) or NONE (verify-only)
verify_linked_provider / verify_released_claims with NONE The wallet sign-in connection whose links prove whose wallet it is (required with NONE), and the claims released as verified_claims
minimum_loa / clock_skew_seconds / allow_legacy_vc_sd_jwt_typ no Defaults http://eidas.europa.eu/LoA/high, 60, false
limits_max_jwt_bytes / limits_cbor_max_depth / limits_cbor_max_items / limits_cbor_max_bytes no Parser bounds
id computed The connection alias

Keep state encrypted if you use access_certificate_private_key

Sensitive only redacts the value from CLI output and logs; the state file holds it in plaintext. Prefer access_key_ref, which never reaches state. If you do set the key, keep state in an encrypted remote backend.

helix_client_verified_claims

Preview. Which wallet-verified claims an OIDC client receives as verified_claims, and where (Verified data for applications). The admin API refuses it unless the provider's service account is a realm admin.

resource "helix_client_verified_claims" "portal" {
  client_id        = "citizen-portal"
  allowed_claims   = ["given_name", "age_over_18", "qualification.profession"]
  deliver_userinfo = true
  max_age_seconds  = 900
}
Attribute Required Description
client_id yes, forces replace The OIDC client's client_id
allowed_claims yes PID claims, age_over_NN, a credential id or <credential>.<claim>
enabled no The opt-in (default true)
deliver_userinfo / deliver_id_token / deliver_access_token no Channels (defaults true, false, false)
max_age_seconds no 60–2592000
api_verification / api_predicate_claims no Allow the verification API, and the claims usable only in yes/no checks
use_stored_attributes no Also release verified attributes kept on the profile (default false)

helix_client_verification_api and helix_verification_template

Preview. A client's verification API settings and templates. Realm admin only.

resource "helix_client_verification_api" "shop" {
  client_id         = "shop"
  wallet_connection = "eudi-wallet"
  webhook_url       = "https://shop.example/hooks/wallet"
  webhook_secret    = var.webhook_secret # write-only; plaintext in state
  max_open          = 50
}

resource "helix_verification_template" "age" {
  client_id   = "shop"
  template_id = "age-check"
  name        = "Age check"
  purpose     = "To check you may rent a car"
  claims      = ["age_over_18"]
  predicates  = [{ id = "resident_utrecht", claim = "address.locality", values = ["Utrecht"] }]
}

webhook_secret is sensitive and write-only (16–256 characters); webhook_secret_set (computed) says whether one is stored. max_open is 1–1000, default 50.

helix_credential_issuer and helix_credential_template

Preview. The realm's credential issuer. helix_credential_issuer holds the issuer settings (enabled, display_name, logo_uri, locale, offer_ttl_seconds, wallet_provider_anchors, wallet_clients, publish_did_web); helix_credential_template holds one template (configuration_id, format, vct or doctype / namespace, claims with name, source, disclosable, mandatory, display, validity_days, max_batch, required_role, scope, key_attestation, wallet_attestation, signing_method, enabled). The issuer key is not a Terraform resource: create it in the console or the admin API.

See also