Skip to content

Assign a Cloud Credential to a Project

A Credential Assignment binds a Cloud Credential to a Project. The binding is request-and-approve: the request opens in the requested state, and the binding only becomes live once a second principal approves it. The request, the list, and the revoke are plexctl credential assignment operations; the decision is taken on the approvals queue with plexctl approval.

Prerequisites

  • An authenticated session — see Log in with plexctl.
  • ${PLEXSPHERE_URL}, or a --server flag on each command.
  • A Project UUID. Opening a request needs deploy on that Project (its admin or maintainer, or the Domain admin above it).
  • A Cloud Credential UUID in an assignable lifecycle state. A credential that is not assignable is refused with 422 credential_not_assignable.
  • An approved Cloud Assignment binding that credential's Cloud to the Project. Without it the request is refused with 422 cloud_not_usable_in_project: a credential is only usable where the Cloud under it was assigned too, so an assignment opened without one would be approved and still refused at deploy time.
  • A second principal to decide the request. That principal must hold assign on the Cloud Credential (its owner or a named assigner) and may not be the principal that opened the request — a self-approval is refused with 403 self_approval_denied.

Request the assignment

Name the credential directly:

shell
plexctl credential assignment request \
  --server              "${PLEXSPHERE_URL}" \
  --project-id          <project-uuid> \
  --cloud-credential-id <credential-uuid>

The 201 carries the new assignment in the requested state with materialised: false. Nothing is bound yet. A second open request for the same (Project, Cloud Credential) pair while an earlier one is still live is refused with 409 duplicate_live_assignment.

To let the server pick the credential, name the Cloud instead with --cloud-id. That form is checked against both the Cloud you name and the Cloud the selected credential belongs to — auto-select resolves through the credential-to-Cloud usage join, so those are not necessarily the same Cloud. The two flags are mutually exclusive, and setting both or neither fails locally before the request is sent.

List a Project's assignments

shell
plexctl credential assignment list \
  --server     "${PLEXSPHERE_URL}" \
  --project-id <project-uuid>

The page returns creation-ordered lifecycle metadata for every assignment in the Project. Listing is gated on read on that Project, so this is the requester's view: a credential owner who holds no relation inside the Project cannot run it. Page with --limit (clamped into [1, 200]) and follow next_cursor with --cursor. A cursor minted by one principal cannot be replayed by another — the cross-caller replay surfaces as 403 cursor_binding_mismatch.

Decide the request

The decider works from the approvals queue, the single inbox for everything awaiting a decision. It runs no entry gate and keeps only the rows the caller may decide, which for a Credential Assignment means holding assign on the credential the row spends:

shell
plexctl approval list \
  --server "${PLEXSPHERE_URL}" \
  --kind   credential_assignment \
  --status pending-approval

Each row carries the assignment id under ID and the credential it spends under TARGET_RESOURCE as cloudcredential:<uuid>. A requested assignment shows as pending-approval: that is the queue's word for "awaiting a decision".

Approve by that id:

shell
plexctl approval approve <assignment-uuid> --server "${PLEXSPHERE_URL}"

Approval moves the assignment to approved and materialises the binding. It is legal only from requested — any other source state returns 409 illegal_transition.

Reject instead to close the request without binding anything. The --reason is recorded on the lifecycle event as an audit string; a whitespace-only value is refused with 400 invalid_decision_reason:

shell
plexctl approval reject <assignment-uuid> \
  --server "${PLEXSPHERE_URL}" \
  --reason "Cloud Credential is scheduled for rotation this week"

Revoke the assignment

Revocation tears a materialised binding down and is legal only from approved. It is open to two parties: the server checks assign on the Cloud Credential first and, on that denial, falls back to deploy on the consuming Project. So the credential holder can pull the credential back, and a Project deployer can hand it back, without either needing the other's grant. Only a caller that both checks deny is refused.

shell
plexctl credential assignment revoke <assignment-uuid> \
  --server "${PLEXSPHERE_URL}" \
  --reason "Project decommissioned by the platform on-call" \
  --yes

The --yes flag is required — the call is destructive. The audit row records which of the two relations granted, so the trail names the party that revoked. The cloudcredential#uses tuple is removed the same way either way.

Verification

shell
plexctl credential assignment list \
  --server     "${PLEXSPHERE_URL}" \
  --project-id <project-uuid> \
  --output json | jq '.items[] | {id, cloud_credential_id, state, materialised}'

After approval the row shows state: "approved" and materialised: true; after revocation, state: "revoked" and materialised: false.

See also