<@ULVA73B9P> is it possible to create a service ac...
# ask-marvin
a
@Marvin is it possible to create a service account and a role with the cli?
m
thought for 135.6 seconds
Short answer: not with dedicated CLI commands. In Prefect 3.x, service accounts and roles are managed in Prefect Cloud, primarily through the UI: - Service accounts: Service accounts docs - Roles / RBAC: Manage roles docs - Cloud app: app.prefect.cloud What I found: - There does not appear to be a
prefect
CLI command like
prefect service-account create
or
prefect role create
- The Cloud CLI surface is focused on login/workspace operations, not identity/RBAC management So if your question is “can I do this directly with the Prefect CLI?” the answer is generally no. Possible workaround: - You may be able to use direct Prefect Cloud API calls instead of a dedicated CLI command, but that would depend on the relevant Cloud API endpoints being available/documented for your account. The API docs are typically here: Prefect Cloud API docs If you want, I can help with either of these: 1. UI steps to create a service account and assign roles 2. exploring whether the Cloud API exposes endpoints you can script against for this in your environment
a
@Marvin is it possible with the python api?
m
thought for 118.5 seconds
Not via the supported Python SDK, no. In Prefect 3.x: -
prefect.client.cloud.CloudClient
does not expose methods to create/manage service accounts - it also does not expose methods to create/manage roles - the main orchestration client (
PrefectClient
) is for flows, deployments, work pools, runs, etc. — not Cloud IAM/RBAC So the practical answer is: - Service accounts: create them in the Cloud UI Service accounts docs - Roles: manage them in the Cloud UI Manage roles docs Cloud app: - app.prefect.cloud There is one caveat: - You might be able to make direct REST calls against Prefect Cloud from Python if there are internal/public endpoints for those resources - but that is not exposed as a documented Python SDK workflow for service account/role creation So if you need an officially supported approach today, it’s UI-only for creating those objects. If helpful, I can also show you: 1. how to use a service account API key from Python once it’s created, or 2. how to probe the Cloud REST API safely to see whether your org exposes endpoints for this automation.
a
@Marvin what about rest apis?
m
thought for 129.2 seconds
Maybe, but not as a clearly documented/supported public workflow. What I found: - Prefect Cloud has an interactive API reference at app.prefect.cloud/api/docs - Service accounts and roles are Cloud-only concerns - the public Python SDK does not expose first-class methods for them - the official docs still point you to the UI for managing them: - Service accounts - Manage roles So the answer is: - There may be Cloud REST endpoints behind the UI - but I could not verify a stable, publicly supported API contract for creating service accounts or creating/managing roles from the open SDK/source alone If you inspect the Cloud API docs while authenticated and you do see those endpoints, then yes, you can call them directly with normal HTTP requests using a valid Cloud API key. But I’d treat that as supported only if it is actually documented there. If you want to test this, the safest approach is:
Copy code
bash
curl -H "Authorization: Bearer $PREFECT_API_KEY" \
  <https://app.prefect.cloud/api/docs>
More realistically, open the docs in the browser and search for: -
service account
-
role
-
member
-
workspace
-
account
Important caveat: - If the endpoints are not documented in the Cloud API docs, I would assume they are internal/unsupported and subject to change. If you want, I can help you: 1. figure out the correct Cloud base URL + auth pattern for REST calls, or 2. draft a small Python/curl script to query the documented Cloud endpoints you find in
/api/docs
.
a
@Marvin
m
thought for 6.5 seconds
Happy to help dig further if you want.
a
@Marvin how do you get rest credentials for the logged in prefect cli user?
m
thought for 102.2 seconds
Yes — the CLI typically uses the same two values you’d use for REST calls: -
PREFECT_API_KEY
-
PREFECT_API_URL
For a logged-in CLI user, these are usually stored in the active Prefect profile, commonly in: -
~/.prefect/profiles.toml
Useful ways to inspect them:
Copy code
bash
prefect config view --show-secrets --show-sources
That will show the effective config, including where values came from. You can also inspect the active profile directly:
Copy code
bash
prefect profile inspect
Or read the raw profile file:
Copy code
bash
cat ~/.prefect/profiles.toml
What you’re looking for is something like:
Copy code
toml
PREFECT_API_KEY = "pnu_..."
PREFECT_API_URL = "<https://api.prefect.cloud/api/accounts/<account-id>/workspaces/<workspace-id>>"
Then you can use those directly in REST calls, for example:
Copy code
bash
curl -H "Authorization: Bearer $PREFECT_API_KEY" \
  "$PREFECT_API_URL/flows/filter"
A couple caveats: - environment variables can override profile values -
.env
,
prefect.toml
, or other config sources may also contribute values -
prefect cloud login
usually sets both key and workspace API URL for the active profile Relevant docs: - Settings and profiles - Connect to Prefect Cloud If you want, I can show you how to convert a logged-in CLI session into a reusable
curl
or Python
requests
example.
a
@Marvin what is the difference between a roles scopes and permissions?
m
thought for 84.4 seconds
In Prefect Cloud RBAC: - Scope = where a role applies - Permissions = what actions that role allows So: - an account-scoped role affects what someone can do at the account level - a workspace-scoped role affects what someone can do inside a specific workspace Examples of scopes: - Account-level roles: Owner, Admin, Member - Workspace-level roles: Viewer, Runner, Developer, Owner, Worker Examples of permissions: - view deployments - run deployments - create/edit/delete deployments - view blocks - view secret block data - edit workspace settings - manage workspace members A simple way to think about it: - Scope answers: “In what boundary does this role apply?” - Permissions answer: “Within that boundary, what is the principal allowed to do?” Example: - If someone is a Developer in workspace
prod
, the scope is the
prod
workspace - Their permissions include things like creating/editing deployments, running flows, and managing certain workspace resources Relevant docs: - Manage roles - Service accounts One subtle point: - Role is the named bundle - Permissions are the capabilities inside that bundle - Scope is the level/resource boundary where that bundle is applied If you want, I can also break down how this maps specifically for service accounts vs human users in Prefect Cloud.
a
@Marvin the /api/accounts/{account_id}/workspace_roles/ post rest api has a scopes field but not a permissions field. how do you set up permissions for the role?
m
thought for 113.5 seconds
Yes — in that API, the
scopes
field is how you set permissions. So for `POST /api/accounts/{account_id}/workspace_roles/`: - there is no separate
permissions
field - the role’s effective permissions are defined by the values you put in
scopes
In other words: - UI term: permissions - API field: scopes That endpoint is using
scopes
to represent the granular abilities the role grants. So if you’re creating a custom workspace role, you typically do one of these: - define a role with an explicit list of
scopes
- or inherit from a built-in/base role and add additional
scopes
if the API supports inheritance fields Important nuance: - I could not verify the exact Cloud-only request schema for this endpoint from the open-source repo - so the authoritative source for the allowed fields and valid scope values is still: Prefect Cloud API docs Conceptually though, the mapping is: - Role = named bundle -
scopes
= the bundle contents - Permissions = what those scopes allow in practice So if you were expecting a model like:
Copy code
json
{
  "name": "My Role",
  "permissions": [...]
}
Prefect appears to instead model it more like:
Copy code
json
{
  "name": "My Role",
  "scopes": [...]
}
If you want to confirm exactly what strings are valid, check the schema for that endpoint in the Cloud docs — it may enumerate accepted scope names or reference a schema component. If you paste the request schema or an example payload from
/api/docs
, I can help translate it into a working
curl
or Python example.
a
@Marvin heres the schema for get workspace_roles. How do you call it to get only the the role named Viewer? { "route": { "method": "POST", "path": "/api/accounts/{account_id}/workspace_roles/filter" }, "summary": "Read Workspace Roles", "description": "Query for workspace roles.\n\nRequired account permissions: `read:workspace_role`", "parameters": [ { "name": "account_id", "in": "path", "required": true, "type": "string", "schema": { "type": "string", "format": "uuid", "title": "Account Id" } } ], "requestBody": { "required": false, "type": "object", "properties": [ { "key": "limit", "type": "integer", "required": false }, { "key": "offset", "type": "integer", "required": false }, { "key": "workspace_roles", "type": "object | null", "required": false }, { "key": "workspace_roles.name", "type": "object | null", "description": "Filter criteria for WorkspaceRole.name", "required": false }, { "key": "workspace_roles.name.any_", "type": "arraystring | null", "description": "Only include workspace roles matching these names", "required": false }, { "key": "workspace_roles.is_global", "type": "boolean | null", "description": "\n If True, only return global built-in roles. If False,\n only return workspace roles from this account. Defaults to
None
, which includes both.\n ", "required": false } ], "schema": { "properties": { "limit": { "type": "integer", "maximum": 200, "minimum": 0, "title": "Limit", "default": 200 }, "offset": { "type": "integer", "minimum": 0, "title": "Offset", "default": 0 }, "workspace_roles": { "anyOf": [ { "properties": { "name": { "anyOf": [ { "properties": { "any_": { "anyOf": [ { "type": "array", "items": { "type": "string" } }, { "type": "null" } ], "title": "Any", "description": "Only include workspace roles matching these names" } }, "additionalProperties": false, "type": "object", "title": "WorkspaceRoleFilterName" }, { "type": "null" } ], "description": "Filter criteria for WorkspaceRole.name" }, "is_global": { "anyOf": [ { "type": "boolean" }, { "type": "null" } ], "title": "Is Global", "description": "\n If True, only return global built-in roles. If False,\n only return workspace roles from this account. Defaults to
None
, which includes both.\n " } }, "additionalProperties": false, "type": "object", "title": "WorkspaceRoleFilter" }, { "type": "null" } ] } }, "type": "object", "title": "Body_read_workspace_roles_api_accounts__account_id__workspace_roles_filter_post" } }, "responses": { "200": { "description": "Successful Response", "type": "arrayobject", "properties": [ { "key": "[array items]", "type": "arrayobject", "description": "An ORM representation of a WorkspaceRole", "required": false }, { "key": "[array items].id", "type": "string", "required": false }, { "key": "[array items].created", "type": "string | null", "required": false }, { "key": "[array items].updated", "type": "string | null", "required": false }, { "key": "[array items].account_id", "type": "string | null", "description": "The account id", "required": true }, { "key": "[array items].name", "type": "string", "description": "The workspace role name", "required": true }, { "key": "[array items].description", "type": "string", "description": "A short description of the role.", "required": false }, { "key": "[array items].permissions", "type": "arraystring", "description": "The workspace role's permissions", "required": false }, { "key": "[array items].scopes", "type": "arraystring", "description": "The workspace role's scopes.", "required": false }, { "key": "[array items].inherited_role_id", "type": "string | null", "description": "An optional built-in workspace role id from which this workspace role inherits.", "required": false } ], "schema": { "type": "array", "title": "Response Read Workspace Roles Api Accounts Account Id Workspace Roles Filter Post", "items": { "properties": { "id": { "type": "string", "format": "uuid", "title": "Id" }, "created": { "anyOf": [ { "type": "string", "format": "date-time" }, { "type": "null" } ], "title": "Created" }, "updated": { "anyOf": [ { "type": "string", "format": "date-time" }, { "type": "null" } ], "title": "Updated" }, "account_id": { "anyOf": [ { "type": "string", "format": "uuid" }, { "type": "null" } ], "title": "Account Id", "description": "The account id" }, "name": { "type": "string", "title": "Name", "description": "The workspace role name" }, "description": { "type": "string", "maxLength": 140, "title": "Description", "description": "A short description of the role.", "default": "" }, "permissions": { "type": "array", "title": "Permissions", "description": "The workspace role's permissions", "deprecated": true, "items": { "type": "string" } }, "scopes": { "type": "array", "title": "Scopes", "description": "The workspace role's scopes.", "items": { "type": "string" } }, "inherited_role_id": { "anyOf": [ { "type": "string", "format": "uuid" }, { "type": "null" } ], "title": "Inherited Role Id", "description": "An optional built-in workspace role id from which this workspace role inherits." } }, "type": "object", "required": [ "account_id", "name" ], "title": "WorkspaceRole", "description": "An ORM representation of a WorkspaceRole" } } },
m
Your message was too long, here's your message at the allowed limit: heres the schema for get workspace_roles How do you call it to get only the the role named Viewer? { route { method POST path /api/accounts/{account_id}/workspace_roles/filter } summary Read Workspace Roles description Query for workspace roles \n\nRequired account permissions
read workspace_role
parameters [ { name account_id in path required true type string schema { type string format uuid title Account Id } } ] requestBody { required false type object properties [ { key limit type integer required false } { key offset type integer required false } { key workspace_roles type object | null required false } { key workspace_roles name type object | null description Filter criteria for WorkspaceRole name required false } { key workspace_roles name any_ type array<string> | null description Only include workspace roles matching these names required false } { key workspace_roles is_global type boolean | null description \n If True only return global built-in roles If False \n only return workspace roles from this account Defaults to
None
which includes both \n required false } ] schema { properties { limit { type integer maximum 200 minimum 0 title Limit default 200 } offset { type integer minimum 0 title Offset default 0 } workspace_roles { anyOf [ { properties { name { anyOf [ { properties { any_ { anyOf [ { type array items { type string } } { type null } ] title Any description Only include workspace roles matching these names } } additionalProperties false type object title WorkspaceRoleFilterName } { type null } ] description Filter criteria for WorkspaceRole name } is_global { anyOf [ { type boolean } { type null } ] title Is Global description \n If True only return global built-in roles If False \n only return workspace roles from this account Defaults to
None
which includes both \n } } additionalProperties false type object title WorkspaceRoleFilter } { type null } ] } } type object title Body_read_workspace_roles_api_accounts__account_id__workspace_roles_filter_post } } responses { 200 { description Successful Response type array<object> properties [ { key [array items] type array<object> description An ORM representation of a WorkspaceRole required false } { key [array items] id type string required false } { key [array items] created type string | null required false } { key [array items] updated type string | null required false } { key [array items] account_id type string | null description The account id required true } { key [array items] name type string description The workspace role name required true } { key [array items] description type string description A short description of the role required false } { key [array items] permissions type array<string> description The workspace role's permissions required false } { key [array items] scopes type array<string> description The workspace role's scopes required false } { key [array items] inherited_role_id type string | null description An optional built-in workspace role id from which this workspace role inherits required false } ] schema { type array title Response Read
Your message was too long, here's your message at the allowed limit: heres the schema for get workspace_roles How do you call it to get only the the role named Viewer? { route { method POST path /api/accounts/{account_id}/workspace_roles/filter } summary Read Workspace Roles description Query for workspace roles \n\nRequired account permissions
read workspace_role
parameters [ { name account_id in path required true type string schema { type string format uuid title Account Id } } ] requestBody { required false type object properties [ { key limit type integer required false } { key offset type integer required false } { key workspace_roles type object | null required false } { key workspace_roles name type object | null description Filter criteria for WorkspaceRole name required false } { key workspace_roles name any_ type array<string> | null description Only include workspace roles matching these names required false } { key workspace_roles is_global type boolean | null description \n If True only return global built-in roles If False \n only return workspace roles from this account Defaults to
None
which includes both \n required false } ] schema { properties { limit { type integer maximum 200 minimum 0 title Limit default 200 } offset { type integer minimum 0 title Offset default 0 } workspace_roles { anyOf [ { properties { name { anyOf [ { properties { any_ { anyOf [ { type array items { type string } } { type null } ] title Any description Only include workspace roles matching these names } } additionalProperties false type object title WorkspaceRoleFilterName } { type null } ] description Filter criteria for WorkspaceRole name } is_global { anyOf [ { type boolean } { type null } ] title Is Global description \n If True only return global built-in roles If False \n only return workspace roles from this account Defaults to
None
which includes both \n } } additionalProperties false type object title WorkspaceRoleFilter } { type null } ] } } type object title Body_read_workspace_roles_api_accounts__account_id__workspace_roles_filter_post } } responses { 200 { description Successful Response type array<object> properties [ { key [array items] type array<object> description An ORM representation of a WorkspaceRole required false } { key [array items] id type string required false } { key [array items] created type string | null required false } { key [array items] updated type string | null required false } { key [array items] account_id type string | null description The account id required true } { key [array items] name type string description The workspace role name required true } { key [array items] description type string description A short description of the role required false } { key [array items] permissions type array<string> description The workspace role's permissions required false } { key [array items] scopes type array<string> description The workspace role's scopes required false } { key [array items] inherited_role_id type string | null description An optional built-in workspace role id from which this workspace role inherits required false } ] schema { type array title Response Read
a
@Marvin heres the schema for get workspace_roles How do you call it to get only the the role named Viewer? { route { method POST path /api/accounts/{account_id}/workspace_roles/filter } summary Read Workspace Roles description Query for workspace roles \n\nRequired account permissions
read workspace_role
parameters [ { name account_id in path required true type string schema { type string format uuid title Account Id } } ] requestBody { required false type object properties [ { key limit type integer required false } { key offset type integer required false } { key workspace_roles type object | null required false } { key workspace_roles name type object | null description Filter criteria for WorkspaceRole name required false } { key workspace_roles name any_ type array<string> | null description Only include workspace roles matching these names required false } { key workspace_roles is_global type boolean | null description \n If True only return global built-in roles If False \n only return workspace roles from this account Defaults to
None
which includes both \n required false } ] schema { properties { limit { type integer maximum 200 minimum 0 title Limit default 200 } offset { type integer minimum 0 title Offset default 0 } workspace_roles { anyOf [ { properties { name { anyOf [ { properties { any_ { anyOf [ { type array items { type string } } { type null } ] title Any description Only include workspace roles matching these names } } additionalProperties false type object title WorkspaceRoleFilterName } { type null } ] description Filter criteria for WorkspaceRole name } is_global { anyOf [ { type boolean } { type null } ] title Is Global description \n If True only return global built-in roles If False \n only return workspace roles from this account Defaults to
None
which includes both \n } } additionalProperties false type object title WorkspaceRoleFilter } { type null } ] } } type object title Body_read_workspace_roles_api_accounts__account_id__workspace_roles_filter_post } } responses { 200 { description Successful Response type array<object> properties [ { key [array items] type array<object> description An ORM representation of a WorkspaceRole required false } { key [array items] id type string required false } { key [array items] created type string | null required false } { key [array items] updated type string | null required false } { key [array items] account_id type string | null description The account id required true } { key [array items] name type string description The workspace role name required true } { key [array items] description type string description A short description of the role required false } { key [array items] permissions type array<string> description The workspace role's permissions required false } { key [array items] scopes type array<string> description The workspace role's scopes required false } { key [array items] inherited_role_id type string | null description An optional built-in workspace role id from which this workspace role inherits required false } ] schema { type
m
thought for 11.7 seconds
Use the
filter
endpoint with a POST body that filters on
workspace_roles.name.any_
. If you want only the role named
Viewer
, the request body should look like:
Copy code
json
{
  "workspace_roles": {
    "name": {
      "any_": ["Viewer"]
    }
  }
}
Example `curl`:
Copy code
bash
curl -X POST \
  "$PREFECT_API_URL/accounts/<account_id>/workspace_roles/filter" \
  -H "Authorization: Bearer $PREFECT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "workspace_roles": {
      "name": {
        "any_": ["Viewer"]
      }
    }
  }'
If you are calling the absolute Cloud path exactly as documented:
Copy code
bash
curl -X POST \
  "<https://api.prefect.cloud/api/accounts/<account_id>/workspace_roles/filter>" \
  -H "Authorization: Bearer $PREFECT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "workspace_roles": {
      "name": {
        "any_": ["Viewer"]
      }
    }
  }'
A couple notes: -
any_
accepts an array, so even for one role name you pass
["Viewer"]
- if you want only built-in/global roles, add:
Copy code
json
  "is_global": true
- if you want only account-defined custom workspace roles, add:
Copy code
json
  "is_global": false
So for the built-in Viewer role specifically, I’d try:
Copy code
json
{
  "workspace_roles": {
    "name": {
      "any_": ["Viewer"]
    },
    "is_global": true
  }
}
Example:
Copy code
bash
curl -X POST \
  "<https://api.prefect.cloud/api/accounts/<account_id>/workspace_roles/filter>" \
  -H "Authorization: Bearer $PREFECT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "workspace_roles": {
      "name": {
        "any_": ["Viewer"]
      },
      "is_global": true
    }
  }'
One important thing from the schema you pasted: - the response includes both
permissions
and
scopes
- so when reading roles, you may see both - but for creating roles, the API may still expect
scopes
as input rather than a
permissions
field If you want, I can also help build the exact
curl
using your existing
PREFECT_API_URL
, because that value usually already includes
/accounts/<id>/workspaces/<workspace-id>
and may need a small adjustment for account-level endpoints.