Skip to content

Assign a Cloud to a Project

A Cloud must be assigned to a Project before it can be used there. This guide walks the two directions the Cloud Assignment context supports: the operator push (you grant a Cloud directly) and the project request (a Project asks, you approve or reject). It assumes you already know the Cloud Assignment context and have a Cloud and a Project to work with.

Before you start

You need:

  • the Cloud ID and the Project ID you are binding;
  • an authenticated session — see Log in with plexctl — and ${PLEXSPHERE_URL}, or a --server flag on each command;
  • the Cloud's assign permission (held by its owner, cloud_admin, or a named assigner) for the grant and the decision;
  • the Project's deploy permission (a Project admin or maintainer, or the Domain admin above it) for the request.

Revocation accepts either of the last two, so you do not need both.

Grant a Cloud to a Project (operator push)

When you already know a Project should be able to use a Cloud, grant it directly. The grant is a single authoritative action: the assignment is created immediately approved and materialised, with no separate decision step.

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

The 201 carries the assignment in state approved with materialised: true, and the cloud:<id>#uses@project:<id> tuple is written so the Project can use the Cloud at once. A grant for a (project, cloud) pair that already has a live assignment is refused with 409 duplicate_live_cloud_assignment — revoke the existing assignment first if you need to re-grant.

Request usage of a Cloud (project side)

When a Project wants to use a Cloud it is not yet assigned, request it as a principal holding the Project's deploy permission:

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

The assignment is persisted in state requested and is now waiting for a decision. A second live request for the same (project, cloud) pair is refused with 409 duplicate_live_cloud_assignment — there is at most one live (requested or approved) assignment per pair.

List what a Project holds at any point. Listing is gated on read on the Project, so this is the requester's view:

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

Decide a request (operator side)

Decisions are taken on the approvals queue, the single inbox for everything awaiting one. The queue runs no entry gate and keeps only the rows the caller may decide, which for a Cloud Assignment means holding assign on the Cloud the row spends. Narrow it to one Cloud with --cloud-id:

shell
plexctl approval list \
  --server   "${PLEXSPHERE_URL}" \
  --kind     cloud_assignment \
  --cloud-id <cloud-uuid> \
  --status   pending-approval

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

Approve to advance the assignment to approved and materialise it. The cloud#uses tuple is written and the Project gains the Cloud's use permission. You cannot approve a request you filed yourself — that is refused with 403 self_approval_denied, so a request is always decided by a second party:

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

Reject to close the request as rejected (terminal) with an audit-recorded reason. No tuple is written and the Project gains no access; it may request the same Cloud again later:

shell
plexctl approval reject <assignment-uuid> \
  --server "${PLEXSPHERE_URL}" \
  --reason "this Cloud is reserved for the platform fleet"

A decision on a row that is already terminal is refused with 409 illegal_transition. Break-glass does not apply to an assignment row and is refused the same way — an assignment carries no quorum to short-circuit.

Revoke an assignment

To tear a Cloud's usage in a Project down, revoke its approved (or granted) assignment with a reason:

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

The assignment becomes revoked (terminal) and the cloud:<id>#uses@project:<id> tuple is narrow-deleted — only this Project's edge is removed, so a sibling Project assigned the same Cloud keeps its access.

Either party may revoke: the server checks assign on the Cloud first and, on that denial, falls back to deploy on the consuming Project. Only a caller that both checks deny is refused. The audit row records which of the two relations granted, so the trail names the party that revoked, and the tuple is removed the same way either way.

After a revoke the (project, cloud) pair is free again: the Project may request the Cloud anew, or you may grant it again.

Verify the result

  • The assignment row's state reflects the last decision (approved / rejected / revoked), and materialised is true only while the assignment is approved and wired.
  • A permission check for use on the Cloud, with the Project as subject, resolves only while a live materialised assignment exists.
  • Each step emits exactly one names-only audit row recording the acting principal, the relation, and the Cloud object.

Do this in the Console

The same flows have a browser equivalent in the Console:

  • Request (project side) — open the Project detail page and use the Cloud assignments panel's Request Cloud action, naming the Cloud ID. The request appears in the requested state.
  • Grant (operator push) — open the Cloud detail page and use Grant to Project, naming the Project ID. The assignment lands immediately approved with materialised = true.
  • Decide (operator side) — open Governance → Approvals for the queue across every Domain you may read, or the Cloud detail page's Assignments tab for the rows spending that one Cloud. A pending-approval row carries Approve and Reject (reason required). Approving a request you filed yourself is refused inline with "you cannot approve your own request", the browser face of the server's self-approval guard.
  • Revoke — back on the Project detail page, each approved-state row in the Cloud assignments panel carries a Revoke action with a destructive confirm and a required reason; only this Project's edge is removed, so a sibling Project keeps its access.

A duplicate live request or grant for the same (project, cloud) pair is refused inline as a conflict rather than thrown.

See also