November 16, 2025

Using Azure Kubernetes Workload Identity for Humanitec Terraform Container Runner

Following recommended practice, Kubernets workloads should authenticate to the surrounding cloud infrastructure using workload identities. In Azure and for Azure Kubernetes Service (AKS), this is supported by Azure Managed Identities being projected to Kubernetes service accounts.

The Humanitec Plattform Orchestrator provides the Terraform and OpenTofu Container Runner Drivers that allow to apply Terraform and OpenTofu code from within your infrastructure, running for instance on a AKS cluster.

This post shows the configuration necessary for configuring the Terraform Container Runner to use Azure Workload Identity.

All IDs have been redacted.

First, create the service account and roles and role bindings (see Configure AKS for the Container runner). Create the namespace humanitec-runner if it doesn’t already exist. Its name is predetermined and it is created automatically by the Humanitec orchestrator.

For the service account:

apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    azure.workload.identity/client-id: "00000000-0000-0000-0000-000000000000"
  name: humanitec-runner
  namespace: humanitec-runner
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: humanitec-runner
  name: humanitec-runner
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["create"]
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["create", "get", "delete", "list", "update", "deletecollection"]
  - apiGroups: ["coordination.k8s.io"]
    resources: ["leases"]
    verbs: ["create", "get", "list", "update", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: humanitec-runner
  namespace: humanitec-runner
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: humanitec-runner
subjects:
  - kind: ServiceAccount
    name: humanitec-runner
    namespace: humanitec-runner

Similarly, for the Azure managed identity to be used by the driver:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: container-driver-role
  namespace: humanitec-runner
rules:
  - apiGroups: ["batch"]
    resources: ["jobs"]
    verbs    : ["create", "delete"]
  - apiGroups: [""]
    resources: ["configmaps","secrets"]
    verbs    : ["create", "delete", "deletecollection","get"]
  - apiGroups: [""]
    resources: ["pods","events"]
    verbs    : ["list"]
  - apiGroups: [""]
    resources: ["pods/log"]
    verbs    : ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: container-driver-deploy-access
  namespace: humanitec-runner
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: container-driver-role
subjects:
  - kind: User
    name: 00000000-0000-0000-0000-000000000000 # The object ID of the Humanitec driver's Azure managed identity
    apiGroup: rbac.authorization.k8s.io

For each Humanitec resource type, a resource definition is needed, specifying which driver type should handle that resource type.

In the context of this post, this is the humanitec/terraform-container-runner driver type. In each resource definition, the runner’s pod specification needs to be extended to specify that Azure Workload Identity should be used:

  driver_inputs = {
      runner = {
        pod_template = <<EOT
metadata:
  labels:
    azure.workload.identity/use: "true"
EOT
      }

      credentials_config = {
        "variables" = {
          "oidc_token" = "oidc_token"
        }
      }

Finally, make sure that the configuration of the azurerm Terraform provider used in the root module referenced by the Humanitec resource definition specifies that AKS workload identity is to be used:

provider "azurerm" {
  features {}
  use_aks_workload_identity = true
  use_cli                   = false
  subscription_id           = var.subscription_id
}

Note that the Humanitec Terraform container runner does not include the az command line interface. Also, in general, the Azure subscription needs to specified in the provider configuration, unless it is set magically as an environment variable.

Happy platforming!

Tags: AKS Azure Humanitec IaC Platform Engineering