CLI
IAM, Members, Teams, and Roles
Use these commands to control what each person and team in your organization can do. An IAM policy lists allowed or denied actions. A role groups policies. You give a role to a user or a team.
| Command group | Manages | API path |
|---|---|---|
verdictan iam policy | IAM policies with one statement each | /v1/policies |
verdictan role | Roles and the policies attached to them | /v1/roles |
verdictan user | Organization members, invitations, and user role assignments | /v1/users, /v1/invitations |
verdictan team | Teams, team members, and team role assignments | /v1/teams |
Give a role to a team when two or more people need the same access. Give a role to a user for individual access. For a complete design example, read Roles and IAM Policies.
Before you start
-
Set
VERDICTAN_API_TOKEN, or sign in withverdictan auth login. -
Get the permissions for the commands you run:
Commands Permissions iam policy list,iam policy getpolicies:readrole list,get,show-actions,show-assignmentsroles:readrole create,update,delete,attach-policy,detach-policyroles:writeuser list,user getusers:readuser inviteusers:inviteuser update,suspend,reactivate,remove-membershipusers:manageuser assign-role,user detach-roleroles:assignandusers:manageteam list,team get,team list-membersteams:readteam create,update,delete,add-member,remove-memberteams:manageteam assign-role,team detach-roleroles:assignandteams:manage -
userandteamchanges need a credential that belongs to a user. A credential without a user gets HTTP403withauth.user_required. -
Policy, role, user, and team IDs are UUIDs. Copy them from the
listcommands.
Each command accepts --json, --config <path>, --api-url <url>, --profile <name> (default default), and --region <region>. The option tables below list only the command-specific flags. Commands that delete or detach do not prompt. They stop with pass --yes to confirm ... and change nothing unless you pass --yes.
verdictan iam policy
verdictan iam policy creates and manages IAM policies. The CLI writes one statement for each policy: an effect, one or more actions, one or more resources, and optional conditions.
Usage
verdictan iam policy list [--json]
verdictan iam policy get --policy-id <id> [--json]
verdictan iam policy create --name <name> --action <action> --resource <resource> [OPTIONS]
verdictan iam policy update --policy-id <id> [OPTIONS]
verdictan iam policy delete --policy-id <id> --yesOptions
| Flag | Applies to | Description |
|---|---|---|
--policy-id <id> | get, update, delete | Required. The policy ID. |
--name <name> | create, update | Policy name. It must be unique in the organization. Required for create. |
--description <text> | create, update | Description. |
--effect <allow|deny> | create, update | Statement effect. The default for create is allow. |
--action <action> | create, update | One action. Repeat the flag for more actions. create needs at least one. For update, the values replace the saved list. |
--resource <resource> | create, update | One resource. Repeat the flag for more resources. create needs at least one. For update, the values replace the saved list. |
--conditions-json <json> | create, update | Conditions as one JSON object. For update, pass {} to remove all conditions. |
--yes | delete | Confirm the deletion. |
The API validates each statement:
- An action is
*, a known action such asevents:read, or a namespace pattern such asevents:*. Find the action names in the Permissions Reference. - A resource is
*or a VDT pattern that starts withvdt:verdictan:. - The condition keys must be operators:
StringEquals,StringLike,StringNotEquals,Bool,Null,NumericLessThanEquals,NumericGreaterThanEquals, orForAnyValue:StringEquals. The snake-case forms such asstring_equalsare also accepted.
A statement that fails validation returns HTTP 422. The CLI prints api validation failed (422).
update without --effect, --action, --resource, or --conditions-json changes only the name or description. With one of those flags, the CLI reads the first saved statement, applies your changes, and sends one statement. If the policy has more than one statement, the update keeps only the first. Each statement change creates a new policy version.
Text output for list prints <policy_id> <name> <statement_count>, system policies first. get prints the ID, name, description, statement count, role count, and timestamps. Use get --json to read the statements and the roles that use the policy.
Examples
# Create a read-only evidence policy
verdictan iam policy create \
--name "Evidence Reader" \
--description "Read governance events and exported evidence" \
--action events:read \
--action exports:read \
--resource '*'
# Examine a policy before you change it
POLICY_ID="replace-with-a-policy-id"
verdictan iam policy get --policy-id "$POLICY_ID" --json
# Replace the action list and keep the resources
verdictan iam policy update --policy-id "$POLICY_ID" --action events:read
# Delete a policy that no role uses
verdictan iam policy delete --policy-id "$POLICY_ID" --yesA * resource is broad. When a narrower scope is available, use a reviewed vdt:verdictan: pattern. System policies are read-only. A change to one returns HTTP 403 with policies.read_only.
verdictan role
verdictan role creates roles, attaches policies to them, and shows the resulting actions and assignments.
Usage
verdictan role list [--json]
verdictan role get --role-id <id> [--json]
verdictan role create --name <name> [--description <text>]
verdictan role update --role-id <id> [--name <name>] [--description <text>]
verdictan role delete --role-id <id> --yes
verdictan role attach-policy --role-id <id> --policy-id <id>
verdictan role detach-policy --role-id <id> --policy-id <id> --yes
verdictan role show-actions --role-id <id> [--json]
verdictan role show-assignments --role-id <id> [--json]Options
| Flag | Applies to | Description |
|---|---|---|
--role-id <id> | All except list and create | Required. The role ID. |
--name <name> | create, update | Role name. It must be unique in the organization. Required for create. |
--description <text> | create, update | Description. |
--policy-id <id> | attach-policy, detach-policy | Required. The IAM policy ID. |
--yes | delete, detach-policy | Confirm the change. |
The subcommands print:
list: one line for each role,<role_id> <name> <description>.get: the ID, name, description, and timestamps. With--json, theroleobject also haspolicies,user_count,team_count, andtoken_count.show-actions: one line for each effective action,<effect> <action> <resource> <source_policy>.show-assignments: theusers,teams, andtokensthat hold the role.
attach-policy succeeds when the policy is already attached. The JSON response is then {"attached": false}. System roles are read-only. A change to one returns HTTP 403 with roles.read_only.
show-actions reads both stored statement lists and policy document objects with a Statement array. It includes actions from policies that you create with verdictan iam policy. The output identifies each source policy.
Examples
# Create a role and attach the evidence policy
verdictan role create \
--name "Evidence Reviewer" \
--description "Reviews events and evidence exports"
ROLE_ID="replace-with-the-role-id"
verdictan role attach-policy --role-id "$ROLE_ID" --policy-id "$POLICY_ID"
# Confirm the attachment and find the holders of the role
verdictan role get --role-id "$ROLE_ID" --json
verdictan role show-assignments --role-id "$ROLE_ID"verdictan user
verdictan user invites people, changes member state, and gives roles to one user.
Usage
verdictan user list [--status <status>] [--json]
verdictan user get --user-id <id> [--json]
verdictan user invite --email <email> --role-id <id> [--assignment-level <org|team>] [--assignment-target-id <id>]
verdictan user update --user-id <id> [--name <name>] [--org-role <role>]
verdictan user suspend --user-id <id> --yes
verdictan user reactivate --user-id <id>
verdictan user remove-membership --user-id <id> --yes
verdictan user assign-role --user-id <id> --role-id <id>
verdictan user detach-role --user-id <id> --role-id <id> --yesOptions
| Flag | Applies to | Description |
|---|---|---|
--user-id <id> | All except list and invite | Required. The user ID. |
--status <status> | list | Print only users with this status, such as active or suspended. The CLI filters the list. |
--email <email> | invite | Required. The email address of the person to invite. |
--role-id <id> | invite, assign-role, detach-role | Required. The role ID. For invite, the role applies when the person accepts. |
--assignment-level <org|team> | invite | Scope of the invitation role. The API default is org. |
--assignment-target-id <id> | invite | Team ID. Required when --assignment-level is team. The team must be in your organization. |
--name <name> | update | New display name. |
--org-role <role> | update | New organization role. The API rejects an empty value. |
--yes | suspend, remove-membership, detach-role | Confirm the change. |
assign-role and detach-role also accept --assignment-level and --assignment-target-id. The API ignores them. assign-role always gives the role to the user at organization level, and detach-role removes that assignment. To give a role at team scope, use verdictan team assign-role, or invite the person with --assignment-level team.
list prints <user_id> <email> <status>. get prints the ID, email, name, status, organization role, and creation time.
When the email belongs to a member of the organization, invite creates no invitation. The API returns {"status": "already_member"} and the text output shows invitation - sent to <email>. Use --json to see the difference.
Examples
# Invite a member with an organization-level role
verdictan user invite \
--email reviewer@example.com \
--role-id "$ROLE_ID"
# Invite a member with a team-level role
TEAM_ID="replace-with-a-team-id"
verdictan user invite \
--email analyst@example.com \
--role-id "$ROLE_ID" \
--assignment-level team \
--assignment-target-id "$TEAM_ID"
# Give a role to an existing member
USER_ID="replace-with-a-user-id"
verdictan user assign-role --user-id "$USER_ID" --role-id "$ROLE_ID"Suspend a member
To remove access for a period, suspend the member. Then confirm the status:
verdictan user suspend --user-id "$USER_ID" --yes
verdictan user list --status suspendedThe suspended member is in the list. Run verdictan user reactivate --user-id "$USER_ID" to restore access. You cannot suspend or remove yourself. The API returns HTTP 422 with users.cannot_suspend_self or users.cannot_remove_self.
remove-membership removes the person from the organization. Before you run it, examine the tokens that the user owns, the team memberships, open work, usage attribution, and evidence ownership.
verdictan team
verdictan team creates teams, manages their members, and gives roles at team scope.
Usage
verdictan team list [--json]
verdictan team get --team-id <id> [--json]
verdictan team create --name <name> [--description <text>]
verdictan team update --team-id <id> [--name <name>] [--description <text>]
verdictan team delete --team-id <id> --yes
verdictan team add-member --team-id <id> --email <email> [--role <role>]
verdictan team remove-member --team-id <id> --user-id <id> --yes
verdictan team assign-role --team-id <id> --role-id <id>
verdictan team detach-role --team-id <id> --role-id <id> --yes
verdictan team list-members --team-id <id> [--json]Options
| Flag | Applies to | Description |
|---|---|---|
--team-id <id> | All except list and create | Required. The team ID. |
--name <name> | create, update | Team name. The API makes a slug from the name, and the slug must be unique. Required for create. |
--description <text> | create, update | Description. |
--email <email> | add-member | Required. The email address of a person who is a member of the organization. |
--role <role> | add-member | Team role for the member. The default is member. The API stores it in lowercase. |
--user-id <id> | remove-member | Required. The user ID to remove. |
--role-id <id> | assign-role, detach-role | Required. The role ID. |
--yes | delete, remove-member, detach-role | Confirm the change. |
list prints <team_id> <name> <description>. get prints the ID, name, slug, description, member count, and role count. list-members prints <user_id> <email> <name>. With --json, get also returns the members with their team_role, the inherited_roles, and the assigned_resources.
add-member returns HTTP 404 with users.not_found when the email does not belong to an organization member. Invite the person first with verdictan user invite.
Examples
# Create a team, add a member, and give the team a role
verdictan team create \
--name "Security Review" \
--description "Governance evidence reviewers"
verdictan team add-member --team-id "$TEAM_ID" --email reviewer@example.com
verdictan team assign-role --team-id "$TEAM_ID" --role-id "$ROLE_ID"
# Confirm the members and the role
verdictan team list-members --team-id "$TEAM_ID"
verdictan team get --team-id "$TEAM_ID" --jsonLimits and failure behavior
- A role that users, teams, tokens, or pending invitations still hold cannot be deleted. The API returns HTTP
409withroles.in_useand the counts in the error details. Userole show-assignmentsto find the holders. - A policy that is attached to a role cannot be deleted. The API returns HTTP
409withpolicies.in_useand the roles in the error details. Detach it withrole detach-policyfirst. - A duplicate policy name returns
409withpolicies.name_conflict. A duplicate role name returnsroles.name_conflict. A team name with a slug that is already in use returnsteams.slug_conflict. The CLI printsapi conflict (409)for each. iam policy updatekeeps only the first statement when it changes statements.user assign-roleanduser detach-rolework only at organization level.- A team deletion removes only the access that comes through that team. Direct user roles and other teams stay.
- To confirm an access change, test it with a non-owner account that has the role. Owner access can hide a missing grant.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
missing api token (set VERDICTAN_API_TOKEN or run `verdictan auth login`) | No token in the environment or the profile. | Set VERDICTAN_API_TOKEN, or run verdictan auth login. |
api authorization failed (403) | The credential does not have the permission for the command, or a user or team change came from a credential without a user (auth.user_required). | Check the permission table in Before you start. Sign in as a user. |
api validation failed (422) | An ID is not a UUID, or a policy statement has an unknown action, a resource that is not * or vdt:verdictan:..., or an unsupported condition operator. | Copy IDs from the list commands. Correct the statement. |
api conflict (409) | A name is already in use, or the role or policy is still in use. | Choose a different name, or remove the assignments first. |
api request failed with status 404 | The policy, role, user, or team does not exist in your organization. | Check the ID and the profile. |
policy creation requires at least one --action and one --resource | iam policy create has no --action or no --resource. | Add both flags. |
nothing to update: provide ... | An update command has no field to change. | Add --name, --description, or another update flag. |
--assignment-target-id is required when --assignment-level team | A team-level invitation has no team ID. | Add --assignment-target-id <team-id>. |
role show-actions prints no effective actions | The role has no attached policy statements with actions. | Check role get --json and iam policy get --json. |
Next steps
- Identity and Access: learn the identity model behind these commands.
- Roles and IAM Policies: design roles before you create them.
- Permissions Reference: find the action names for
--action. - verdictan control: declare the same resources in a manifest and reconcile them.