Appearance
Provision a cloud Resource
The other tutorials work with tenancy — Domains, Projects, identities — and one works with the mesh. This one works with provisioning: the broker that turns a declared Resource into real cloud substrate. You will hand the platform a single resource create and then watch it do everything else — render the Composite Resource, drive the cloud provider, and report back — until the Resource reaches its terminal Ready phase.
The Resource you provision is an AWS S3 bucket from the aws-s3-bucket Blueprint. It is a standalone (nodeless) resource: a real cloud object with no plexd Node to enrol, served on the lean stack by the floci AWS emulator — so the bucket genuinely materialises, with no real account or cost. Because there is no node, the broker mints no bootstrap token and the lifecycle skips the Enrolling phase: the bucket is Ready the moment its substrate composes. The flow you drive — resource create, the phase machine — is identical to provisioning against a real AWS account; only the endpoint differs.
Provisioning deliberately splits two concerns: the platform operator curates the shared catalog and decides whose credential pays, while the project owner consumes the catalog. You played both sides across the earlier lessons — you built the catalog in the three catalog lessons and bound a credential to your Project under two-party governance in Assign a Cloud Credential. Here you act as the project owner alone, consuming what those lessons set up.
Everything this lesson needs is already in place:
- Register a Blueprint, Create a Cloud, and Issue a Cloud Credential built the catalog — the Cloud, its Credential, and the
aws-s3-bucketBlueprint; - Assign a Cloud Credential bound and approved a credential for your
provision-demoProject.
You will:
- recover the ids you need — your Project, its approved credential, and the
aws-s3-bucketBlueprint version, resource createan S3 bucket and get back202+ aLocation,- poll the Resource through
Pending → Provisioning → Ready, and confirm the bucket really exists in the emulated AWS, - tear it back down.
By the end you will understand the shape of a provisioned Resource and the phases the broker drives a nodeless resource through to make it real.
This lesson takes about ten minutes — most of it spent watching the broker work.
Before you start
This lesson builds onAssign a Cloud Credential, which in turn builds on the three catalog lessons: the demo AWS Cloud, its Credential, the aws-s3-bucket Blueprint, and an approved credential assignment on the provision-demo Project must already exist, because this lesson consumes them. Complete the provisioning track up to and including Assign a Cloud Credential first. Beyond that you need a running, logged-in stack from Set up your local plexsphere, plus jq on your $PATH to read fields out of the JSON responses.
You act as the project owner throughout — admin@example.com, your default profile, the same identity every other tenancy lesson uses. The two-party credential-assignment governance happened in the previous lesson; here the catalog and the approved assignment are already in place, so a single identity drives the whole flow. It signs in with the dev password password; see the tutorials overview for the full cast.
Recreate the shell environment and read the Acme Corp Domain id with make dev-ids:
bash
export PATH="$PWD/bin:$PATH"
export PLEXSPHERE_URL=http://localhost:8080
eval "$(make -s dev-ids)"
echo "domain=$DOMAIN_ID"Step 1 — Recover the ids you need
The catalog and the approved assignment already exist — you built them in the earlier lessons. This lesson consumes them, so begin by recovering the ids resource create needs. You recover all of them as the project owner: the Blueprint catalog is scoped to your Acme Corp Domain, so a domain admin reads the Blueprint and its version directly — there is no operator step in this lesson.
Sign in as the project owner under your default profile, completing sign-in in the browser as admin@example.com with the password password:
bash
plexctl login --domain-id "$DOMAIN_ID"Recover the provision-demo Project you created in the assignment lesson by its slug:
bash
PROJECT_ID=$(plexctl project list --domain "$DOMAIN_ID" --output json \
| jq -r '.items[] | select(.slug == "provision-demo") | .id')
echo "$PROJECT_ID"Read back the approved credential the assignment bound to that Project — resource create names the credential id directly, so capture it from the approved assignment:
bash
CREDENTIAL_ID=$(plexctl credential assignment list --project-id "$PROJECT_ID" --output json \
| jq -r 'first(.items[] | select(.state == "approved") | .cloud_credential_id)')
echo "$CREDENTIAL_ID"resource create takes the BlueprintVersion's id, the broker's internal handle for that revision. Resolve the aws-s3-bucket Blueprint by its slug, then read the version id from the Blueprint's versions array — blueprint get surfaces it on the read API:
bash
BLUEPRINT_ID=$(plexctl blueprint list --all --output json \
| jq -r '.items[] | select(.slug == "aws-s3-bucket") | .id')
BLUEPRINT_VERSION_ID=$(plexctl blueprint get "$BLUEPRINT_ID" --output json \
| jq -r '.versions[] | select(.version == "v1alpha1") | .id')
echo "$BLUEPRINT_VERSION_ID"You now hold everything the broker needs: $PROJECT_ID, $CREDENTIAL_ID, and $BLUEPRINT_VERSION_ID.
Step 2 — Create the provisioned Resource
You have everything the broker needs: a Project, an approved credential, and a blueprint version. Declare the Resource:
bash
RESOURCE_ID=$(plexctl resource create \
--project-id "$PROJECT_ID" \
--kind bucket \
--blueprint-version-id "$BLUEPRINT_VERSION_ID" \
--cloud-credential-id "$CREDENTIAL_ID" \
--parameters '{"region":"us-east-1"}' \
--output json | jq -r '.id')
echo "$RESOURCE_ID"text
019efb32-a148-7c11-a995-3ba6be65aabfThat one line is the captured $RESOURCE_ID. resource create prints the accepted Resource and nothing else, so the capture is the same shape as every other create in these lessons.
The aws-s3-bucket Blueprint declares a required region parameter, so the --parameters object names it — a declaration missing region fails closed before any substrate is touched. --kind is a free-form label on the tenancy Resource; bucket records what this Resource is.
A grant to land. The approval from the previous lesson writes the Project's use of the credential to the authorization mirror a moment later. If
resource createreturnsPermission Denied: project not authorised to use the named credential, wait a second and re-run it — the same eventual-consistency window as a fresh Project grant.
resource create returns 202 Accepted with a Location header pointing at the new Resource — provisioning is asynchronous, so the call accepts your declaration rather than waiting for the substrate. The Resource is created at phase Pending:
bash
plexctl resource get "$RESOURCE_ID" --output json | jq '{kind, origin, phase: .provisioning.phase}'json
{
"kind": "bucket",
"origin": "provisioned",
"phase": "Pending"
}Step 3 — Watch it reach Ready
From here you do nothing — the broker reconcile loop drives the Resource forward on its own ticker. The phase is a single resource get away — run it by hand whenever you want a snapshot:
bash
plexctl resource get "$RESOURCE_ID" --output json | jq -r '.provisioning.phase'text
ProvisioningPoll the same read until it is Ready:
bash
while true; do
PHASE=$(plexctl resource get "$RESOURCE_ID" --output json | jq -r '.provisioning.phase')
echo "$(date +%T) $PHASE"
case "$PHASE" in Ready|Failed) break ;; esac
sleep 10
donetext
14:31:02 Pending
14:31:12 Provisioning
14:33:12 ReadyEach phase is a real milestone the broker observes, never a timer — and a standalone resource has only two transitions, because it skips the node-enrolment phase:
Pending → Provisioning— the Project's management-fleet namespace became ready, so the broker applied the blueprint's Composite Resource (theXAWSS3Bucket). It mints no bootstrap token: there is no node to enrol.Provisioning → Ready— the upjet AWS provider composed the bucket against the emulated AWS endpoint and Crossplane reported the substrateReady. For a standalone resource that is the terminal success — the broker never waits on anEnrollingstep, because nothing registers.
That is a real cloud Resource provisioned end to end. Confirm the bucket actually exists in the emulated AWS by asking floci to list its buckets. floci validates no credentials, so an unsigned ListBuckets is enough — no aws CLI, and no local port forward, because the probe runs inside the cluster where floci is already reachable by service name:
bash
kubectl run floci-probe --rm -i --quiet --restart=Never \
--image=curlimages/curl:8.10.1 -- -sS http://floci:4566/ \
| grep -o '<Name>[^<]*' | cut -d'>' -f2text
pr-019fb9d7-fb6c-7681-a082-07407e041cc0-a2908b4cb3ceThe first run pulls the curl image, so give it a few seconds; --rm deletes the Pod afterwards and --quiet keeps its lifecycle chatter off your terminal.
A bucket the Composition created is there — a real object in the emulated AWS, its name derived from the Resource id with a collision suffix. The same resource create against a real AWS account would have created a real S3 bucket; only the endpoint floci serves differs.
Step 4 — Deprovision the Resource
A provisioned Resource is torn down the way it was built — you declare the intent and the broker reconciles it. Delete it; the --yes gate guards the destructive act:
bash
plexctl resource delete "$RESOURCE_ID" --yesThe command prints nothing — a successful delete is silent, like every other destructive plexctl command. And like create, it is asynchronous: the call returns 202 Accepted and queues the teardown rather than blocking on it. The same single resource get shows the teardown underway:
bash
plexctl resource get "$RESOURCE_ID" --output json | jq -r '.provisioning.phase'text
DeprovisioningPoll the phase the same way you did on the way up, now until it reaches Deleted:
bash
while true; do
PHASE=$(plexctl resource get "$RESOURCE_ID" --output json | jq -r '.provisioning.phase')
echo "$(date +%T) $PHASE"
case "$PHASE" in Deleted|Failed) break ;; esac
sleep 10
donetext
14:40:03 Deprovisioning
14:41:13 DeletedTeardown runs the provisioning arc in reverse. A node resource drains its plexd node out of the mesh first (a Deregistering phase), but a standalone resource has no node, so its teardown opens directly at Deprovisioning:
Ready → Deprovisioning— the broker deletes the substrate (the Crossplane Composite Resource and its ProviderConfig), so the upjet provider removes the bucket from the emulated AWS.Deprovisioning → Deleted— the substrate is gone;Deletedis terminal. Re-running the flociListBucketsprobe from Step 3 now prints nothing for this Resource.
Unlike provisioning, teardown needs no credential assignment and no two-party approval — a single resource delete with the delete relation on the Resource is enough. Separation of duties guards standing spend up, not tearing it down.
Do this in the Console
The provisioning steps above have a browser equivalent in the Console. The credential-assignment governance has its own browser walkthrough in Assign a Cloud Credential; here you create the Resource. Open the Projects page, find your Project, and click Open to reach the Project detail page — the host for the Project-scoped Resources panel:
- Resources lists the Project's Resources and hosts Create Resource. Pick the provisioned origin, enter the
aws-s3-bucketkind, the BlueprintVersion id, and the approved Cloud Credential id, and submit. The new Resource appears with a Status that advances Pending → Provisioning → Ready as the panel polls it — the browser equivalent of re-runningresource getuntil the phase settles. An adopted Resource has no broker phase, so its Status shows an em dash.
What you learned
- Provisioning splits two roles. The platform operator curates the catalog — Clouds, Credentials, Blueprints — and approves which Project may spend a credential; a project owner consumes it. You built the catalog and approved the assignment in the earlier lessons, then consumed them here as the owner alone.
- A Resource is a declaration the broker reconciles.
resource createreturns202and aLocationbecause provisioning is asynchronous; the Resource then advances through observed phases, not a fixed schedule. - A blueprint version is the unit you provision against. The Blueprint is the recipe; the immutable BlueprintVersion id is what
resource createtakes, and its parameter schema tells you what input it needs. - The phases are facts, not a clock.
Provisioningwaits on the substrate being applied, andReadyon Crossplane reporting the bucket composed. A node blueprint adds anEnrollingphase between them, where the broker waits for the node to register; a standalone resource has no node, so it skips that phase and isReadyas soon as the substrate is. - Mesh role shapes the lifecycle. The
aws-s3-bucketBlueprint isstandalone: the broker mints no bootstrap token and runs no enrolment for it, because there is no plexd Node. A node blueprint — the one Reach your Resource provisions — does, which is why only that lesson has something to log in to. - Teardown is the provisioning arc in reverse.
resource deleteis asynchronous likecreate. A node resource deregisters its node from the mesh before it deletes the substrate, so a machine is never stranded; a standalone resource has no node, so it deletes the substrate directly. Either way, tearing down needs no credential assignment and no approval.
Where to go next
- Keep learning by doing — Reach your Resource issues short-lived, mediated sessions — a kubeconfig, a TCP forward, an SSH login — to the Node you just provisioned.
Or pick the quadrant that matches what you need now:
- You have a job to do — the Manage Clouds, Issue a Cloud Credential, Author a Blueprint, and Assign a Cloud Credential how-to guides cover the operator runbooks for building and assigning the catalog.
- You want to understand why provisioning is shaped this way — the Provisioning broker context explains the phase machine, and the bootstrap-token context explains the token's Resource binding.
- You want the exact contract — the
plexctl resource,plexctl blueprint, andplexctl credentialreferences document every flag and output shape.