Skip to content

Create a Cloud

Before plexsphere can provision anything, the platform needs a catalog to provision against: the Clouds it can reach, the Credentials that pay for them, and the Blueprints that describe what to build. The catalog starts empty on a fresh stack — nothing is pre-seeded. You registered the first Blueprint into your Domain in Register a Blueprint; building the catalog-global half — the Clouds and the Credentials that pay for them — is the platform operator's job.

This lesson is the second of three that build that catalog. Here you create a Cloud: a provider account plexsphere can provision against. The next lesson issues a Credential under it; then Provision a cloud Resource consumes the whole catalog to stand up a real S3 bucket.

The Cloud you create names an AWS account. On the lean dev stack that account is served by floci, an in-cluster AWS emulator: the same plexctl cloud create you run here works unchanged against a real AWS account, but locally there is no real account, no token, and no cost. The account id 000000000000 below is floci's well-known dummy account.

This lesson takes about five minutes.

Before you start

This lesson is standalone: it does not build on the explore, build, or Group lessons. You only need a running, logged-in stack from Set up your local plexsphere, plus jq on your $PATH to read the new Cloud's id out of the JSON response.

You act as the platform operator throughout — operator@example.com, the seeded identity whose platform-login row holds the platform:plexsphere#admin authoring grant, so it is the one allowed to build the platform catalog. The operator signs in without selecting a Domain: platform authority is platform-wide, not tenant-scoped, and the grant seed attaches it to the Domain-less platform sign-in. The dev password is password. See the tutorials overview for the full cast.

Recreate the shell environment:

bash
export PATH="$PWD/bin:$PATH"
export PLEXSPHERE_URL=http://localhost:8080

Step 1 — Create the demo Cloud

Sign in as the platform operator and save the session under a named operator profile — they curate the catalog, so they are the identity allowed to build it. The --platform flag runs the Domain-independent platform sign-in (the dev stack opts its device-code surface into it). Run the login below, then complete sign-in in the browser as operator@example.com with the password password:

bash
plexctl login --profile-name operator --platform

Check what that sign-in produced:

bash
plexctl whoami --profile operator
text
PRINCIPAL  SCOPE     SUBJECT                               DOMAIN  ACR  AMR
user       platform  019100c0-0d0c-7000-8000-000000000002  -       -    -

Two columns carry the whole difference from every earlier lesson. SCOPE reads platform rather than domain, and DOMAIN is - rather than a UUID: this session belongs to no tenant Domain at all, and everything it may do flows from the platform grant instead of from a relation inside a tenant. Compare it with the whoami from Set up your local plexsphere, where the same command named Acme Corp in that column.

The subject is a different principal, too. operator@example.com also has an Acme Corp row, seen in the identity listing in Explore your first Domain, but that is its tenant hat and it is not the identity signing in here.

A Cloud is a provider account plexsphere can provision against; the Credential issued under it (the next lesson) is the vaulted secret that pays for it. Create the demo Cloud — the platform:plexsphere#admin grant the operator holds folds into the platform manage permission Cloud creation gates on:

bash
CLOUD_ID=$(plexctl cloud create \
  --slug demo-cloud \
  --display-name "Demo Cloud" \
  --provider aws \
  --external-id 000000000000 \
  --endpoint '{"region":"us-east-1","partition":"aws"}' \
  --region-defaults '{"default_region":"us-east-1"}' \
  --provider-package xpkg.upbound.io/upbound/provider-aws-s3@v2.6.1@sha256:7e00c910bdbf39b2dcc8d7632153b6be1c31d202ede0f2d12ade053eb77aae96 \
  --provider-config-api-version aws.m.upbound.io/v1beta1 \
  --profile operator --output json | jq -r '.id')
echo "$CLOUD_ID"

--provider-package declares one Crossplane provider package for this Cloud as <source>@<version>: the OCI repository it is pulled from, then the version it is pinned at. The flag is repeatable, so a Cloud that needs several providers names them one occurrence each; at least one is required. --provider-config-api-version is a single Cloud-level value, the apiVersion every declared package serves its ProviderConfig under. The values above name the exact package the local stack runs, so the record matches what is really installed.

Creating a Cloud grants its creator manage on it — the relation issuing a credential authorises against — and manage folds into the observe relation reads gate on, so the creator can read the Cloud straight back, as the next step shows.

Step 2 — Read the Cloud back

List the catalog you have started. cloud list filters each row through the per-Cloud observe relation, and the demo Cloud you just created is the only entry:

bash
plexctl cloud list --all --profile operator --output json \
  | jq '.items[] | {id, slug, provider, external_id}'
json
{
  "id": "019fb9c7-97ba-7d4a-8867-29e77d21c5e6",
  "slug": "demo-cloud",
  "provider": "aws",
  "external_id": "000000000000"
}

Then fetch that one Cloud by the id you captured in Step 1 — cloud get runs the same observe check before it reads:

bash
plexctl cloud get "$CLOUD_ID" --profile operator --output json
json
{
  "created_at": "2026-06-24T17:16:24.417317Z",
  "display_name": "Demo Cloud",
  "endpoint": {
    "partition": "aws",
    "region": "us-east-1"
  },
  "external_id": "000000000000",
  "id": "019fb9c7-97ba-7d4a-8867-29e77d21c5e6",
  "provider": "aws",
  "provider_config_api_version": "aws.m.upbound.io/v1beta1",
  "provider_packages": [
    {
      "source": "xpkg.upbound.io/upbound/provider-aws-s3",
      "version": "v2.6.1@sha256:7e00c910bdbf39b2dcc8d7632153b6be1c31d202ede0f2d12ade053eb77aae96"
    }
  ],
  "region_defaults": {
    "default_region": "us-east-1"
  },
  "slug": "demo-cloud",
  "updated_at": "2026-06-24T17:16:24.417317Z"
}

Both reads gate on the per-Cloud observe relation. The grant from creating the Cloud reaches the authorization mirror a moment after creation, so if cloud list comes back empty or cloud get returns Permission Denied, wait a second and re-run it — the same lag applies to the next lesson's credential issue.

Do this in the Console

Every step above has a browser equivalent in the Console. Open the Clouds page under the Cloud group in the sidebar: Create Cloud opens a dialog with the same fields you passed to cloud create — a Display name, a kebab-case Slug, a Provider select over the supported providers, an External ID, and JSON textareas for the Endpoint and Region defaults blobs. On success the Cloud appears in the list; clicking its name opens the Cloud detail page — the browser equivalent of cloud get, showing the same metadata and the read-only endpoint and region-defaults JSON.

What you learned

  • A Cloud is a provider account plexsphere provisions against. It names the upstream account and its region defaults; the secret that pays for it is a separate Credential, issued under the Cloud in the next lesson.
  • The catalog starts empty. Nothing is pre-seeded on a fresh stack, so the operator builds it — beginning with the Cloud, the residency pivot the credential and assignment gates later authorise against.
  • Creating a Cloud grants the creator manage on it. That relation is what the credential-issue gate checks; it also folds into observe, so the creator can cloud list and cloud get the Cloud it just created. The grant reaches the authorization mirror a moment after creation.

Where to go next

Or pick the quadrant that matches what you need now:

  • You have a job to do — the Manage Clouds how-to covers the operator runbook for creating, updating, and deleting Clouds.
  • You want the exact contract — the plexctl cloud reference documents every flag and output shape.
  • You want to understand why the catalog is shaped this way — the Cloud Inventory context explains the Cloud aggregate and its invariants.