> ## Documentation Index
> Fetch the complete documentation index at: https://docs.formae.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Kubernetes with installation OIDC

> Use your formae installation identity to authenticate to EKS, AKS, GKE, or a Kubernetes API server directly.

An installation can manage Kubernetes using its OIDC identity. The target stores
the cluster endpoint, its public CA certificate, and the public identity
coordinates. You do not put access keys, client secrets, bearer tokens, or an
authenticated kubeconfig in the target.

This requires formae **0.90.2 or newer**, Kubernetes plugin **0.1.13 or newer**,
and a compatible credential broker paired with `K8S`. For a hosted installation,
confirm that a compatible broker is enabled before configuring these targets.
An older self-hosted agent must be upgraded and configured with a broker before
these targets can work. Installing the Kubernetes plugin alone does not establish
an installation identity or grant cluster access.

## How it relates to a cloud connection

Your AWS, Azure, or GCP target continues to provision cloud resources through its
existing cloud plugin. A Kubernetes target is a separate, ordinary target. It
does not inherit a cloud target's credentials or call the cloud plugin for a
token.

You can reuse the cloud connection's public role, client, or workload identity
provider coordinates when that identity also has cluster access. The Kubernetes
plugin asks the installation broker for a short-lived assertion and performs the
appropriate exchange itself:

| Target      | Authentication path                                    | Identity requiring cluster access                                            |
| ----------- | ------------------------------------------------------ | ---------------------------------------------------------------------------- |
| EKS         | Installation assertion → AWS role session → EKS token  | IAM role                                                                     |
| AKS         | Installation assertion → Entra access token for AKS    | Service principal                                                            |
| GKE         | Installation assertion → Google federated access token | Workload identity pool principal, or the explicitly selected service account |
| Direct OIDC | Installation assertion → Kubernetes API server         | The API server's username derived from the installation subject              |

Tokens are cached in memory and refreshed as operations need them. Explicit OIDC
configuration does not fall back to the agent's local cloud credentials if
federation fails. The installation still has its managed identity and signing
infrastructure; the absence of customer credentials in Pkl does not mean there
are no credentials anywhere in the system.

## Prepare access first

1. Create or identify the cluster and obtain its trusted HTTPS endpoint and
   base64-encoded PEM CA certificate.
2. Configure trust for `https://oidc.cloud.formae.ai` and the installation's
   **exact subject from connection setup**. Do not derive the subject from an
   organization name or a guessed installation ID.
3. Grant that authenticated identity the Kubernetes permissions your resources
   need, and allow time for the grants to propagate.
4. Apply a new Kubernetes target and its workloads. The agent must be able to
   reach the cluster and, for cloud authentication, the provider's token service.

Creating a cloud connection does not automatically grant Kubernetes access to
every cluster in that account. Installing cloud provider plugins does not create
cluster access grants either.

## Target configurations

Add these schema dependencies to your `PklProject` and run `pkl project resolve`:

```pkl theme={"languages":{"custom":["/languages/pkl.json"]}}
dependencies {
  ["formae"] {
    uri = "package://hub.platform.engineering/plugins/pkl/schema/pkl/formae/formae@0.90.2"
  }
  ["k8s"] {
    uri = "package://hub.platform.engineering/plugins/k8s/schema/pkl/k8s/k8s@0.1.13"
  }
}
```

Each authentication block below fits inside this target's `config`. Replace the
example coordinates and choose the Kubernetes minor version matching your
cluster. The `1.34` import and `kubernetesVersion` must agree.

```pkl theme={"languages":{"custom":["/languages/pkl.json"]}}
import "@formae/formae.pkl"
import "@k8s/v1.34/k8s.pkl" as k8s

target = new formae.Target {
  label = "application-cluster"
  config = new k8s.Config {
    kubernetesVersion = "1.34"
    // Insert one auth block below.
  }
}
```

### AWS EKS

```pkl theme={"languages":{"custom":["/languages/pkl.json"]}}
auth = new k8s.EKSAuth {
  endpoint = "https://eks.example.invalid"
  certificateAuthority = "REPLACE_WITH_BASE64_PEM_CLUSTER_CA"
  clusterName = "application-cluster"
  region = "us-west-2"
  credentials = new k8s.AwsOidcCredentials {
    roleArn = "arn:aws:iam::123456789012:role/formae-kubernetes"
  }
}
```

The role's federation trust must admit the exact installation subject and
audience `sts.amazonaws.com`. Separately, create an [EKS access entry and
permissions](https://docs.aws.amazon.com/eks/latest/userguide/access-entries.html)
for that role, using an access policy or Kubernetes RBAC groups. The cluster must
have an authentication mode supporting access entries. Role trust alone does
not give the role access to Kubernetes. `region` is required with
`AwsOidcCredentials` and does not default from `AWS_REGION`. This OIDC variant
supports commercial AWS.

### Azure AKS

```pkl theme={"languages":{"custom":["/languages/pkl.json"]}}
auth = new k8s.AKSAuth {
  endpoint = "https://aks.example.invalid"
  certificateAuthority = "REPLACE_WITH_BASE64_PEM_CLUSTER_CA"
  credentials = new k8s.AzureOidcCredentials {
    tenantId = "11111111-1111-4111-8111-111111111111"
    clientId = "22222222-2222-4222-8222-222222222222"
  }
}
```

Configure an Entra federated identity credential for the exact installation
subject and audience `api://AzureADTokenExchange`. The plugin requests an AKS
access token, rather than an Azure Resource Manager token.

Grant access through the cluster's configured [Azure RBAC or Kubernetes RBAC
authorizer](https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization).
Resolve the service principal's object ID when creating its role assignment;
`clientId` in Pkl identifies the application, not that object ID. This variant
supports public Azure.

Both endpoint and CA are mandatory with OIDC. The Kubernetes plugin does not
fetch an admin or user kubeconfig in this mode. If a cloud resource does not
expose the CA, have an administrator supply that public certificate from trusted
cluster metadata. Do not copy the kubeconfig's credentials into Pkl.

### Google GKE

```pkl theme={"languages":{"custom":["/languages/pkl.json"]}}
auth = new k8s.GKEAuth {
  endpoint = "https://gke.example.invalid"
  certificateAuthority = "REPLACE_WITH_BASE64_PEM_CLUSTER_CA"
  projectId = "example-project"
  location = "us-central1"
  clusterName = "application-cluster"
  credentials = new k8s.GcpOidcCredentials {
    workloadIdentityProvider = "//iam.googleapis.com/projects/123456789012/locations/global/workloadIdentityPools/formae/providers/installation"
  }
}
```

Configure the [workload identity
provider](https://docs.cloud.google.com/iam/docs/workload-identity-federation-with-other-providers)
to trust the issuer, map `google.subject` to `assertion.sub`, and admit only the
exact installation subject. Grant the resulting principal the required GKE and
Kubernetes permissions. The provider path uses the **project number**.

Direct federation is the default. To use service-account impersonation, add
`serviceAccountEmail = "formae-kubernetes@example-project.iam.gserviceaccount.com"`
inside `GcpOidcCredentials`. Grant the exact federated principal
`roles/iam.workloadIdentityUser` on that service account, and grant the service
account its own cluster access. Selecting this mode changes the identity seen
by the cluster; it is not an automatic fallback.

### Direct Kubernetes OIDC

This option needs no AWS, Azure, or GCP connection. Configure the Kubernetes API
server to trust the formae issuer and a dedicated, stable audience:

```pkl theme={"languages":{"custom":["/languages/pkl.json"]}}
auth = new k8s.OidcAuth {
  endpoint = "https://kubernetes.example.invalid"
  certificateAuthority = "REPLACE_WITH_BASE64_PEM_CLUSTER_CA"
  audience = "urn:formae:kubernetes:39c24d1d-3815-4817-9242-4032be46601b"
}
```

Generate a new UUID for your cluster and retain it; do not reuse the example.
The audience must be `urn:formae:kubernetes:` followed by a canonical lowercase
RFC 4122 UUID. The broker must allow that audience.

For an API server using [OIDC
flags](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens),
the matching settings are:

```text theme={"languages":{"custom":["/languages/pkl.json"]}}
--oidc-issuer-url=https://oidc.cloud.formae.ai
--oidc-client-id=urn:formae:kubernetes:YOUR-CLUSTER-UUID
--oidc-username-claim=sub
--oidc-username-prefix=formae:
--oidc-signing-algs=RS256
```

The API server must reach the issuer's discovery and JWKS endpoints over trusted
HTTPS. This is separate from the agent reaching the cluster. Bind Kubernetes
RBAC to a `User` named `formae:EXACT_INSTALLATION_SUBJECT`, using the actual
subject and the configured prefix. A valid token authenticates an identity;
RBAC still decides what it can do.

## Ordering and changes to existing targets

A Kubernetes target can reference the host cluster through
[resolvables](/documentation/concepts/resolvable) without being nested under a
cloud target. For example, use `eksCluster.res.endpoint` and
`eksCluster.res.certificateAuthorityData` instead of the EKS literals above.
Those references make the workload depend on the cluster's metadata being
available. They do not order separately configured access grants or guarantee
their propagation. Establish those grants before applying workloads. Remove
workloads while access remains available, before removing grants or the cluster.

<Warning>
  Authentication is a create-only target setting. Changing it on an existing
  target can replace the target and recreate its managed resources across stacks.
  Use a new target for onboarding and review migration plans before changing an
  existing target's authentication.
</Warning>

## Helm permissions and long operations

Helm needs permission to manage its release Secrets and the resources in the
chart. It also needs cluster-scoped permission to `get` the `kube-system`
Namespace to identify the cluster before mutations and recovery. A RoleBinding
limited to the workload namespace cannot grant that read. A narrowly scoped
ClusterRole can use `resources: ["namespaces"]`,
`resourceNames: ["kube-system"]`, and `verbs: ["get"]` in the core API group.
Bind it to the same authenticated identity.

Use a Helm `timeoutSeconds` of at least 300 with the default agent timing.
Credential refresh is serviced during live operations and status polling; it
depends on the agent and broker staying available. If the agent or plugin is
killed, inspect the release and retry through formae's recovery flow.

The plugin's complete [EKS](https://github.com/platform-engineering-labs/formae-plugin-kubernetes/tree/0.1.13/examples/oidc-eks),
[AKS](https://github.com/platform-engineering-labs/formae-plugin-kubernetes/tree/0.1.13/examples/oidc-aks),
[GKE](https://github.com/platform-engineering-labs/formae-plugin-kubernetes/tree/0.1.13/examples/oidc-gke),
and [direct OIDC](https://github.com/platform-engineering-labs/formae-plugin-kubernetes/tree/0.1.13/examples/oidc-direct)
examples include target and workload definitions. These are also the examples
available to AI assistants through the formae MCP.
