Summary
terraform import cloudstack_project.<name> <uuid> fails with a not-found error whenever the
account behind the provider credentials is not a member of the project — even when that
account is a Root Admin or Domain Admin that can see the project via listProjects with
listall=true.
The root cause is that the provider's project lookup never sets the listall parameter, and the
CloudStack API default (listall=false) makes listProjects participant-scoped: it only
returns projects the calling account owns or is a member of, regardless of the caller's role.
Steps to reproduce
- Have a project
P owned by account team-a in domain D.
- Configure the provider with credentials of a Root Admin (or Domain Admin over
D) whose
account is not a member of P.
- Run
terraform import cloudstack_project.p <uuid-of-P>.
The API-level asymmetry is easy to demonstrate with CloudMonkey using the same key pair:
$ cmk list projects id=<uuid-of-P> # count: 0 (participant-scoped default)
$ cmk list projects id=<uuid-of-P> listall=true # returns the project
Expected behavior
The import succeeds: an admin whose role entitles it to see the project via listall=true
should be able to import it.
Actual behavior
The import fails with a not-found error ("No match found for " from the lookup /
"Cannot import non-existent remote object"), because the read path queries with the
participant-scoped default.
Root cause
resourceCloudStackProjectRead (cloudstack/resource_cloudstack_project.go) resolves the project
via the getProjectByID helper, which builds the params with only the id (and optionally the
domain):
p := cs.Project.NewListProjectsParams()
p.SetId(id)
...
l, err := cs.Project.ListProjects(p)
listall is never set, and neither the helper nor the SDK's NewListProjectsParams applies a
default, so the request goes out with the API default listall=false.
Proposed fix
Set listall=true on the lookup — the SDK already exposes it:
p := cs.Project.NewListProjectsParams()
p.SetId(id)
p.SetListall(true)
This does not widen anyone's privileges: listall=true only disables the "own/participating
projects only" default filter, and the management server still enforces the caller's role and
domain-subtree entitlements. Callers who could not see the project before will still get
count: 0.
Workaround and why it is insufficient
The current workaround is to add the provider's account as a member of every project
(addAccountToProject). This is impractical at scale (we hit this with ~90 projects) and,
more importantly, impossible across domains: CloudStack requires a project member account
to belong to the project's exact domain (ProjectManagerImpl looks the account up with
project.getDomainId()), and an account cannot exist in more than one domain. A Root Admin
managing projects across several domains therefore has no workaround at all.
Environment
- Provider version: latest
- CloudStack version: latest
- Terraform version: 1.5.7
Summary
terraform import cloudstack_project.<name> <uuid>fails with a not-found error whenever theaccount behind the provider credentials is not a member of the project — even when that
account is a Root Admin or Domain Admin that can see the project via
listProjectswithlistall=true.The root cause is that the provider's project lookup never sets the
listallparameter, and theCloudStack API default (
listall=false) makeslistProjectsparticipant-scoped: it onlyreturns projects the calling account owns or is a member of, regardless of the caller's role.
Steps to reproduce
Powned by accountteam-ain domainD.D) whoseaccount is not a member of
P.terraform import cloudstack_project.p <uuid-of-P>.The API-level asymmetry is easy to demonstrate with CloudMonkey using the same key pair:
Expected behavior
The import succeeds: an admin whose role entitles it to see the project via
listall=trueshould be able to import it.
Actual behavior
The import fails with a not-found error ("No match found for " from the lookup /
"Cannot import non-existent remote object"), because the read path queries with the
participant-scoped default.
Root cause
resourceCloudStackProjectRead(cloudstack/resource_cloudstack_project.go) resolves the projectvia the
getProjectByIDhelper, which builds the params with only the id (and optionally thedomain):
listallis never set, and neither the helper nor the SDK'sNewListProjectsParamsapplies adefault, so the request goes out with the API default
listall=false.Proposed fix
Set
listall=trueon the lookup — the SDK already exposes it:This does not widen anyone's privileges:
listall=trueonly disables the "own/participatingprojects only" default filter, and the management server still enforces the caller's role and
domain-subtree entitlements. Callers who could not see the project before will still get
count: 0.Workaround and why it is insufficient
The current workaround is to add the provider's account as a member of every project
(
addAccountToProject). This is impractical at scale (we hit this with ~90 projects) and,more importantly, impossible across domains: CloudStack requires a project member account
to belong to the project's exact domain (
ProjectManagerImpllooks the account up withproject.getDomainId()), and an account cannot exist in more than one domain. A Root Adminmanaging projects across several domains therefore has no workaround at all.
Environment