Find your backend details
A backend record needs four facts: the bucket, the prefix (if any), the region, and for Pulumi the layout. Getting the layout or prefix wrong is the most common reason a backend reports a healthy diagnostic but shows no stacks. This page shows how to read each fact off the tools you already use.
Step 1: Find the backend URL
Pulumi stores its backend as a URL of the form s3://<bucket>/<prefix>?<options>. Find it in
any of these places:
From a machine that already uses the backend:
pulumi whoami --verbose
The Backend URL line is what you need.
From the project file, if the team pins it there:
# Pulumi.yaml
backend:
url: s3://acme-pulumi-state/team/platform?region=us-east-1
From your CI pipeline, in the pulumi login step or the PULUMI_BACKEND_URL variable.
Step 2: Read the URL
| URL | bucket |
prefix |
region |
endpoint |
forcePathStyle |
|---|---|---|---|---|---|
s3://acme-pulumi-state |
acme-pulumi-state |
omit | see below | omit | false |
s3://acme-pulumi-state/team/platform?region=eu-west-1 |
acme-pulumi-state |
team/platform |
eu-west-1 |
omit | false |
s3://state?endpoint=s3.internal.example&s3ForcePathStyle=true®ion=local |
state |
omit | local |
https://s3.internal.example |
true |
- The host part of the URL is the bucket; the path part is the prefix. No leading or trailing slash in the prefix.
endpointin the URL may omit the scheme; inbackends.jsonit must be a fullhttps://origin with no path.s3ForcePathStyle=truein the URL is"forcePathStyle": truein the record.
If the URL has no region, ask the bucket:
aws s3api get-bucket-location --bucket acme-pulumi-state
A null LocationConstraint means us-east-1.
Step 3: Find the layout
Pulumi has two ways of arranging stack files under the prefix, and Lumaft never guesses between them. List the stacks directory:
aws s3 ls s3://acme-pulumi-state/team/platform/.pulumi/stacks/
(Drop team/platform/ if you have no prefix.)
| You see | Layout | Record value |
|---|---|---|
PRE platform/, PRE networking/ — directories |
Project-scoped | "layout": "project-scoped" |
prod.json, dev.json — files directly here |
Legacy | "layout": "legacy" |
| Both directories and files | Both layouts in one prefix | One record per layout, with distinct ids |
Nothing, or NoSuchKey |
Wrong prefix | Recheck Step 2 |
Project-scoped is the default for backends created with recent Pulumi versions. Legacy is deprecated by Pulumi and slated for removal; Lumaft still reads it, but treat a legacy backend as something to migrate.
Step 4: Check for KMS encryption
If the bucket encrypts objects with a customer-managed KMS key, the Lumaft identity also needs
kms:Decrypt on that key. Check one state object:
aws s3api head-object --bucket acme-pulumi-state \
--key team/platform/.pulumi/stacks/platform/prod.json \
--query '{sse: ServerSideEncryption, key: SSEKMSKeyId}'
"sse": "aws:kms" with a key ARN means you need the KMS grant from
IAM setup on AWS. "AES256" or null
needs nothing extra.
Step 5: Write the record
{
"kind": "s3",
"id": "platform",
"displayName": "Platform (production account)",
"bucket": "acme-pulumi-state",
"prefix": "team/platform",
"layout": "project-scoped",
"region": "us-east-1",
"forcePathStyle": false,
"enabled": true
}
idis yours to choose: lowercase, unique, stable. It appears in URLs, filters, and integration-token scopes, so pick something you will not want to rename.displayNameis what people see.
Put the record in the backends array of the file described in
Connect a backend. Several backends — one per
account, per prefix, or per layout — go in the same file.
Terraform and OpenTofu backends
For these engines the facts come from the backend block, and Lumaft expects the state key to
be <project>/terraform.tfstate under the default workspace prefix:
backend "s3" {
bucket = "acme-tf-state" # bucket
key = "platform/terraform.tfstate" # <project>/terraform.tfstate
region = "us-east-1" # region
# workspace_key_prefix left at its default of "env:"
}
Set the record's prefix to the namespace above the project directories (here there is none,
so add one if your keys are nested, for example infra/platform/terraform.tfstate → prefix: "infra"). A custom workspace_key_prefix or a key with extra path segments is not observed.
The record shape is in
Connect a backend.
Engine support bounds. State observation is verified against Terraform 1.15.9 and OpenTofu 1.12.6; other builds are unverified.
Terraform and OpenTofu can write native S3 lock files when locking is enabled, but Lumaft does not currently observe them.
Backend-native update history remains unavailable for those engines, so lock and update-history observation are Pulumi-only and these backends answer engine-unsupported for both rather than showing empty results.
Verify before you deploy
The identity Lumaft will run as should be able to do exactly this and nothing more:
aws s3 ls s3://acme-pulumi-state/team/platform/.pulumi/stacks/ --recursive | head
aws s3 cp s3://acme-pulumi-state/team/platform/.pulumi/stacks/platform/prod.json - | head -c 200
If both work from the credentials you will give Lumaft, the backend record will work too.