CloudAccount

Which cloud identity Ringleader uses for one organization and one cloud account: which principal it becomes, and whose trust it presents its assertion to.

A CloudAccount binds one organization to one cloud account, and nothing else. It defines which cloud identity to use: which cloud principal Ringleader should become, and which trust configuration in your cloud will accept its identity assertion.

apiVersion: core.ringleader.dev/v1
kind: CloudAccount

It is cluster-scoped (it has no metadata.namespace) because a cloud account belongs to an organization, which spans namespaces. A CloudIdentity points at one by name (spec.cloudAccountRef) to mint its credentials through it.

What it is, and what it is not

The split is deliberate and worth internalizing:

Lives onExample
Which cloud identity to use — which principal to become, whose trust to present toCloudAccountthe target service account, the workload identity provider, the Entra app, the tenant
The VM’s size and placement — what a workstation VM actually looks likeCloudIdentity’s defaultProviderConfig / overrideProviderConfigproject, zone, machine type, subnet, labels

A CloudAccount is not a placement template. It never decides where a workstation runs or how big it is.

It holds no secret

Every value in a CloudAccount spec is a public identifier: a service-account email, a workload-identity-provider resource name, an Entra application (client) id, a tenant id. Naming a principal grants nothing by itself.

The authority is the short-lived, signed assertion Ringleader presents, whose subject is derived server-side from the workstation’s namespace — never from anything written on this object. That is what makes this object a pointer rather than a capability.

Who can write one

A CloudAccount is org-owned infrastructure, not a tenant object:

  • Organization administrators may create, read, update, and delete the CloudAccounts of their own organization. The control plane enforces that on both the incoming object and the stored one, so an account cannot be re-homed to another organization by editing it.
  • Ringleader operators (global admins) may write any organization’s.
  • A namespace member holds no permission on this kind at all — not even read. This choice of cloud identity is therefore outside the surface a normal user can write, which is the entire point.

How a workstation reaches it

Workstation (labels)
   │  selector match
CloudIdentity  (namespace: dev)   spec.cloudAccountRef: acme-gcp
   │  must be an account of the organization that owns namespace "dev"
CloudAccount   (cluster-scoped)   spec.org: acme
   │  which cloud identity to use
YOUR cloud account — which trusts an assertion whose subject is
organization "acme" and nothing else

The reference must stay inside the identity’s own organization. Pointing a CloudIdentity at another organization’s account is refused when you apply it:

apply cloudidentity/gcp: admission: spec.cloudAccountRef names no CloudAccount
in this namespace's org: CloudIdentity dev/gcp references CloudAccount "other-org-gcp"

An account that belongs to someone else and an account that does not exist give you the same message on purpose, so the error cannot be used to discover which account names other organizations have.

The workstation’s namespace must belong to an organization. If it does not, no assertion can be signed for it and the reference is refused for the same reason.

Examples

Google Cloud

apiVersion: core.ringleader.dev/v1
kind: CloudAccount
metadata:
  name: acme-gcp
spec:
  org: acme
  provider: gcp
  gcp:
    # The service account in YOUR project that Ringleader acts as. It holds the
    # workstation permissions; the federated principal impersonates it directly.
    targetServiceAccount: ringleader-workstations@acme-dev.iam.gserviceaccount.com
    # YOUR workload identity provider's resource name. This is the token-exchange
    # audience — NOT the assertion's `aud` claim (see the note below).
    workloadIdentityProvider: projects/123456789/locations/global/workloadIdentityPools/ringleader/providers/oidc

Apply it before the CloudIdentity that references it (a multi-document apply orders them for you):

rl apply -f acme-gcp-account.yaml
rl cloudaccount get        # alias: rl ca get

Microsoft Azure

apiVersion: core.ringleader.dev/v1
kind: CloudAccount
metadata:
  name: acme-azure
spec:
  org: acme
  provider: azure
  azure:
    # The Entra application (client) id that holds the workstation permissions on
    # your resource group, and whose federated identity credential trusts
    # Ringleader's per-organization issuer.
    targetAppClientId: ec6d0a82-536f-463e-baa5-a10221befa86
    # Your Azure AD tenant.
    tenant: bb90f2d3-842d-41ea-8c0c-0e6671775ef7

AWS

apiVersion: core.ringleader.dev/v1
kind: CloudAccount
metadata:
  name: acme-aws
spec:
  org: acme
  provider: aws
  aws:
    # The IAM role in YOUR account that Ringleader assumes via
    # AssumeRoleWithWebIdentity. Its trust policy admits only your organization's
    # subject and audience. The whole AWS edge is this one value — AWS resolves the
    # OIDC provider and the audience from the signed assertion itself.
    targetRoleArn: arn:aws:iam::123456789012:role/ringleader-workstations

Spec fields

FieldTypeDescription
orgstringRequired. The organization that owns this cloud account. It must be the organization the referencing CloudIdentity’s namespace belongs to.
providerstringRequired. gcp, azure or aws.
gcp.targetServiceAccountstringRequired for gcp. The service account in your project that Ringleader acts as. A bare email, or a fully qualified projects/…/serviceAccounts/… resource name.
gcp.workloadIdentityProviderstringRequired for gcp. Your workload identity provider’s resource name, projects/<number>/locations/global/workloadIdentityPools/<pool>/providers/<provider>. An //iam.googleapis.com/ prefix is accepted and optional.
gcp.targetScopestringOptional OAuth scope override for the minted token (default: cloud-platform).
azure.targetAppClientIdstringRequired for azure. The Entra application (client) id Ringleader authenticates as.
azure.tenantstringRequired for azure. Your Azure AD tenant id.
aws.targetRoleArnstringRequired for aws. The IAM role Ringleader assumes via AssumeRoleWithWebIdentity. The whole AWS edge: AWS resolves the identity provider from the assertion’s iss and checks aud against that provider’s client-id list, so neither is named here. There is no aws.targetScope.
azure.targetScopestringOptional OAuth scope override (default: the Azure Resource Manager .default scope).

A gcp account may not carry an azure block, and vice versa. An incomplete configuration is refused when you apply it, not when a workstation later fails to boot:

apply cloudaccount/acme-gcp: validation failed:
spec.gcp.workloadIdentityProvider: is required

Status fields

The system writes these. They answer one question: can a workstation governed by this account actually mint a credential?

FieldTypeDescription
messagestringThe headline: the account is ready: its org's OIDC issuer documents are published, or the account is not ready, so a workstation governed by it cannot mint a cloud credential.
oidc.readyboolWhether the federation is usable end to end.
oidc.messagestringThe specific reason when it is not — this is the one to read.
rl cloudaccount get acme-gcp -o jsonpath='{.status.oidc.message}{"\n"}'

oidc.ready is derived from the account’s own validity and its org’s publish state, so a healthy account whose org has not published yet reports not-ready and says which of the two is the problem, e.g.:

org acme's OIDC issuer documents are not published yet, so a cloud cannot validate
an assertion for it

The account’s own failures read the same way — the account names no org, so no assertion can be signed for it, org acme does not exist, or the validator’s own text when the spec is invalid.

Why the AWS edge is a single field

The AWS edge is deliberately one field. Unlike the other two clouds, sts:AssumeRoleWithWebIdentity names neither an audience nor an identity provider in the request: AWS resolves the provider from the assertion’s own iss, and checks the aud claim against that provider’s client-id list. Both pins therefore live inside the signed token and in your own IAM configuration, so the only thing the account carries is the role to assume.

See onboarding AWS for the runbook.

The two audience values (Google Cloud)

The single most common mistake when configuring the Google Cloud trust is conflating two different “audiences”:

  • spec.gcp.workloadIdentityProvider is the token-exchange audience — the resource name of your workload identity provider. It is per-customer, and it belongs on this object.
  • The assertion’s aud claim is a per-organization value Ringleader derives itself and pins in your provider’s allowed_audiences. You never write it here.

They are two independent pins on the same exchange. See per-organization federation for the exact values to configure in your cloud.

See also

  • CloudIdentity: the namespaced object that references a CloudAccount and shapes the workstations it brokers.
  • Per-organization federation: the end-to-end onboarding runbook, and the trust configuration to create in your cloud account.