Skip to content

frostmoln_workload_identity_binding (Resource)

Manages a Workload Identity Federation binding, mapping a managed Kubernetes (namespace, service account) to a least-privilege Frostmoln grant. A pod running as that service account can exchange its projected token for a short-lived, scoped Frostmoln credential. The grant is either flat scopes or an access policy attached with frostmoln_iam_policy_attachment — a policy expresses far narrower least privilege (per-resource targets, constraints, explicit denies), so prefer it for new bindings. The binding is owned by the tenant resolved from the provider credential (API key or OIDC session), not by a provider tenant_id override.

Example Usage

terraform
# A Workload Identity Federation binding: a pod running as the given
# Kubernetes service account can exchange its projected token for a short-lived,
# scoped Frostmoln credential — no long-lived secret in the pod.
resource "frostmoln_workload_identity_binding" "app" {
  cluster_id      = frostmoln_kubernetes_cluster.prod.id
  namespace       = "default"
  service_account = "my-app"

  # Least-privilege scopes only. Wildcards ("*" or "<resource>:*") are rejected.
  scopes = [
    "compute:read",
    "storage:read",
  ]
}

# A POLICY-GRANTED binding: omit `scopes` entirely and let an attached access
# policy be the workload's sole authority. Prefer this — a flat scope grants a
# verb across every resource of a service, while a policy names individual
# targets, adds constraints and can deny explicitly. Needs the `iam-policies`
# entitlement.
#
# The binding is INERT until the attachment exists: with no grant at all the
# token exchange refuses it rather than minting a credential that grants nothing.
#
# TEARDOWN — read before you destroy. A granted binding is never left with zero
# grants, so detaching its ONLY policy is refused. `terraform destroy` removes
# the attachment first (it depends on the binding) and therefore fails. Destroy
# the BINDING first; that removes its attachments with it:
#
#   terraform destroy -target=frostmoln_workload_identity_binding.reaper
#   terraform destroy
#
# The same applies to any change that REPLACES the binding (cluster_id, namespace
# and service_account all force replacement).
resource "frostmoln_workload_identity_binding" "reaper" {
  cluster_id      = frostmoln_kubernetes_cluster.prod.id
  namespace       = "ops"
  service_account = "instance-reaper"
}

data "frostmoln_iam_policy_document" "reaper" {
  rule {
    name       = "read-and-delete-instances-only"
    access     = "allow"
    operations = ["compute:instances:read", "compute:instances:list", "compute:instances:delete"]
    targets    = ["frn:compute:*:*:instances/*"]
  }
}

resource "frostmoln_iam_policy" "reaper" {
  name     = "instance-reaper"
  document = data.frostmoln_iam_policy_document.reaper.json
}

resource "frostmoln_iam_policy_attachment" "reaper" {
  policy_id     = frostmoln_iam_policy.reaper.id
  attachee_type = "workload_identity"
  attachee_id   = frostmoln_workload_identity_binding.reaper.id
}

Schema

Required

  • cluster_id (String) The managed cluster the binding applies to. The cluster must belong to the caller's tenant.
  • namespace (String) The Kubernetes namespace (DNS-1123 label).
  • service_account (String) The Kubernetes service account name (DNS-1123 subdomain).

Optional

  • scopes (List of String) The least-privilege scopes granted to the workload (e.g. compute:read). The grantable set, with a description of each, is served by the platform — read it with the frostmoln_api_key_scopes data source or fm account api-key scopes. Wildcards (* or <resource>:*) are rejected. Changing the scopes updates the binding in place. Optional: omit it to author a policy-granted binding, whose authority comes entirely from an access policy attached with frostmoln_iam_policy_attachment (directly, or through a group). Write null or omit the attribute for no scopes; an empty list is not a valid spelling.

Notes on the policy-granted path:

  • The policy-granted path needs the iam-policies entitlement. Without it the binding is still created, but frostmoln_iam_policy fails and the binding is left inert.
  • Until a policy is attached the binding is inert — the token exchange refuses it rather than minting a credential that grants nothing.
  • While a binding carries BOTH scopes and a policy, its scopes stay authoritative and the policy's additional authority does not take effect. It goes live only once scopes is dropped, so verify after the drop, not after the attach.
  • Dropping scopes NARROWS the workload to whatever the policy allows, and the API only checks that some grant survives — never that the policy covers what the scopes covered. Enumerate the binding's effective access before dropping them.
  • Removing the LAST grant is rejected outright. A binding whose only grant is an attached policy must therefore be destroyed BEFORE that attachment; a plain terraform destroy tries the attachment first and fails. Destroy the binding with -target first — that removes its attachments with it.

Read-Only

  • created_at (String) The timestamp when the binding was created.
  • id (String) The unique identifier of the binding.
  • tenant_id (String) The owning tenant (server-set from the auth context).
  • updated_at (String) The timestamp when the binding was last updated.

Import

Import is supported using the following syntax:

The terraform import command can be used, for example:

shell
# Workload identity bindings are imported by their id.
terraform import frostmoln_workload_identity_binding.app <binding-id>