Meet the GrantFlow CLI: a fast, scriptable way to work with GrantFlow from your terminal.
What you can do
- Authenticate with your Entra ID tenant (device code flow)
- List and manage roles, activations, and accounts
- Pipe JSON into your favorite tools for automation
Install
- Build from source (recommended for power users): see the CLI repo
README.mdatcli.grantflow.cloud/README.md. - Optional: Add the compiled
grantflowbinary to your PATH.
Authenticate (device code)
The CLI uses Entra ID device code flow. When you run a command that needs a token, you’ll see a one‑time code and a sign‑in URL.
Steps:
- Copy the provided link or open it directly from your terminal output
- Enter the code shown (or the link will prefill it)
- Select your account in the correct tenant and approve
TIP
If you’re an admin configuring this for a new customer tenant, complete the setup in “Customer tenant Entra setup” first, then return here.
Tokens are cached in ~/.grantflow/token-cache.json and refreshed automatically.
Config
The CLI looks for a YAML config at ~/.grantflow/config.yaml.
Example:
# ~/.grantflow/config.yaml
apiBaseUrl: https://api.grantflow.cloud
# Optional: force tenant authority for auth flows
tenantId: 00000000-0000-0000-0000-000000000000
# Optional: domain hint for the account picker UX
# domainHint: contoso.comapiBaseUrl: Your GrantFlow API base URLtenantId: Forces auth against a specific tenant authority (recommended)domainHint: Hints the account picker to your domain (less chance of picking the wrong account)
Quick start examples
Below are copy‑paste examples you can run right away. Add --output json to pipe into tools like jq.
# Authenticate and cache a token
grantflow login
# List your eligible roles
grantflow roles list --output table
# Get details for a specific role
grantflow roles get <ROLE_ID> --output json
# Request a JIT activation (60 minutes)
grantflow activations request <ROLE_ID> --duration 60 --reason "Routine maintenance"
# Check your activation requests
grantflow activations list --output table
# List privileged accounts you can check out
grantflow accounts list --output json | jq '.[].name'
# Check out an account for 30 minutes (requires a reason)
grantflow accounts checkout <ACCOUNT_ID> --duration 30 --reason "Break-glass access"
# View recent audit events (limit 20)
grantflow audit list --limit 20 --output tableOutput formats
Use --output table|json|yaml at any command level (defaults to table).
Command catalog
High‑level overview of available commands. Use --help on any command for full usage.
Auth and config
grantflow login— Authenticate via device code flow and cache tokengrantflow logout— Clear cached tokensgrantflow config view [--output json]— Show effective configuration
Roles (user scope)
grantflow roles list [--output table|json|yaml]— List roles you can requestgrantflow roles get <ROLE_ID>— Get a role’s details
Activations (user + approver)
grantflow activations list— List your activation requestsgrantflow activations request <ROLE_ID> --duration <MINUTES> --reason <TEXT> [--account <ACCOUNT_ID>]— Request activationgrantflow activations cancel <ACTIVATION_ID>— Cancel a pending requestgrantflow activations approve <ACTIVATION_ID> [--comment <TEXT>]— Approve (requires approver role)grantflow activations deny <ACTIVATION_ID> --reason <TEXT>— Deny (requires approver role)
Accounts (user scope)
grantflow accounts list— List privileged accounts available to yougrantflow accounts get <ACCOUNT_ID>— Get account detailsgrantflow accounts checkout <ACCOUNT_ID> --duration <MINUTES> --reason <TEXT>— Temporary checkout
Audit (read‑only)
grantflow audit list [--limit <N>] [--action <NAME>] [--actor <USER_ID>]— Search audit events
Admin (requires admin role)
grantflow admin connectors list— List identity connectorsgrantflow admin connectors sync <CONNECTOR_ID>— Trigger a syncgrantflow admin agents list— List on‑premises agentsgrantflow admin jobs list [--limit <N>] [--status pending|running|completed|failed]— List background jobsgrantflow admin roles list— List all roles (admin view)grantflow admin roles create --name <NAME> --description <TEXT> --type entraid|activedirectory --connector <CONNECTOR_ID> [--max-duration <MINUTES>]— Create a role
Scripting & automation
JSON processing with jq
List role IDs:
grantflow roles list --output json | jq -r '.[].id'Request activation for the first role returned:
RID=$(grantflow roles list --output json | jq -r '.[0].id')
grantflow activations request "$RID" --duration 45 --reason "Batch job"Filter audit events by action and actor:
grantflow audit list --limit 200 --action ActivationApproved --output json \\
| jq -r '.[] | select(.actor=="alice@contoso.com") | [.timestamp, .action, .resourceId] | @tsv'Pipelines and batching
Process items in a loop (with simple progress):
set -euo pipefail
echo "Listing roles..." >&2
grantflow roles list --output json \\
| jq -r '.[].id' \\
| while read -r RID; do
echo "→ Role: $RID" >&2
grantflow roles get "$RID" --output json | jq '{id, name, maxDuration}'
done | jq -s '.'Parallelize read‑only queries with xargs (tune -P for concurrency):
grantflow roles list --output json \\
| jq -r '.[].id' \\
| xargs -I {} -P 4 sh -c 'grantflow roles get "$1" --output json' -- {}Batch approve activations from a CSV (id,comment):
while IFS=, read -r AID COMMENT; do
grantflow activations approve "$AID" --comment "$COMMENT"
done < approvals.csvError handling & retry
Most commands return non‑zero on error. For transient errors (e.g., HTTP 429), add a small backoff:
retry() {
local tries=\${1:-5} delay=\${2:-2}
shift 2
local n=1
until "$@"; do
if (( n >= tries )); then
echo "Command failed after $tries attempts" >&2
return 1
fi
sleep $((delay * n))
n=$((n+1))
done
}
# Example: list audit events with retry
retry 5 2 grantflow audit list --limit 100 --output json > audit.jsonCheck exit status explicitly and handle gracefully:
if ! grantflow activations request "$RID" --duration 30 --reason "Ops"; then
echo "Activation request failed; check your role eligibility and reason." >&2
exit 1
fiConfig precedence and env vars
The CLI loads config from (lowest → highest precedence):
- Default values
- File:
~/.grantflow/config.yaml - Environment variables (prefix
GRANTFLOW_) - Flags passed on the command line
Environment variables:
export GRANTFLOW_API_URL=https://api.grantflow.cloud
export GRANTFLOW_TENANT_ID=<TENANT_ID>
export GRANTFLOW_CLIENT_ID=211741db-2aac-4f14-8ef4-fb361e386429
export GRANTFLOW_SCOPE="api://01906db8-9d41-46b8-b5d4-a00222f22509/user_impersonation"
export GRANTFLOW_OUTPUT=json
export GRANTFLOW_DEBUG=trueAdvanced topics
Token cache
- Location:
~/.grantflow/token-cache.json - Device code flow tokens refresh automatically when possible
grantflow logoutclears cache and forces re‑auth
Tenant authority and account picker
- Set
tenantIdin config (or--tenant-id) to avoid/commonloops - Optional: add a domain hint to reduce account picker friction (
domainHintin config)
Rate limiting and retries
- The CLI surfaces API rate‑limit responses (HTTP 429). If encountered, wait and retry.
- For scripts, add simple backoff loops around calls that may hit rate limits.
Global flags
These flags can be specified at the root or any subcommand:
| Flag | Description |
|---|---|
--config <path> | Use an alternate config file (default: ~/.grantflow/config.yaml) |
--api-url <url> | Override API base URL |
--tenant-id <uuid> | Force Azure tenant authority |
--client-id <uuid> | Override the Entra ID client ID (rarely needed) |
--scope <scope> | OAuth2 scope (default: api://<client-id>/.default; set it to the GrantFlow API scope below) |
--output table|json|yaml | Output format (default: table) |
--debug | Enable debug logging |
Exit codes & errors
- Success returns exit code
0. - Any error returns a non‑zero exit code and a concise error message.
- Add
--debugto print more detail (request targets, status codes) for troubleshooting.
Planned (subject to change) exit code mapping for future versions:
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Generic error (default) |
| 2 | Validation error (bad flags/input) |
| 3 | Authentication failure (login/consent/tenant) |
| 4 | Not found (resource missing or out of scope) |
| 5 | Forbidden (insufficient privileges) |
| 6 | Rate limited (retry later) |
| 7 | Server/transport error |
INFO
Today the CLI returns 0 on success and non‑zero on error. The specific codes above are reserved for a future release to improve scripting ergonomics.
Common commands
- List roles:
grantflow roles list - List accounts:
grantflow accounts list - Help:
grantflow --helporgrantflow <command> --help
Most list commands support pagination via flags (e.g., --limit, --offset) when available. Output is JSON by default; you can pipe through jq.
FAQ
- How do I switch tenants?
- Set
tenantIdin~/.grantflow/config.yamlor pass--tenant-id <TENANT_ID>.
- Set
- Who approves my activation?
- Approvers are defined by your organization’s approver policies. Use
activations listto see status, or contact your admin.
- Approvers are defined by your organization’s approver policies. Use
- Can I run the CLI non‑interactively?
- Yes, after initial
login, tokens are cached and refreshed silently until they expire or youlogout.
- Yes, after initial
- How do I get raw JSON for scripts?
- Add
--output jsonto any command; combine withjqas needed.
- Add
Troubleshooting
- You finish browser auth but the CLI keeps waiting
- Ensure the tenant is correct and setup is complete (admin consent granted)
- If you see a redirect to “/common”, make sure the CLI is configured with
tenantId
- “Not authorized” errors
- An admin must consent to GrantFlow in your tenant and assign you the User or Admin role on the GrantFlow API application (
pamplus-apiunder Enterprise applications) - See the setup guide below
- An admin must consent to GrantFlow in your tenant and assign you the User or Admin role on the GrantFlow API application (
- Empty lists
- Usually means no data in that scope yet; verify your tenant and filters
Reference IDs
GrantFlow is a multi-tenant application. These IDs are the same for every customer; you do not create any app registration in your tenant.
client_id:211741db-2aac-4f14-8ef4-fb361e386429, the GrantFlow web app, which the portal and the CLI sign in withscope:api://01906db8-9d41-46b8-b5d4-a00222f22509/user_impersonation, the GrantFlow APItenant_id: your own tenant ID
Set scope explicitly: without it the CLI derives one from client_id, which is the wrong application.