Review Identity Access from the CLI
tctl access-review answers who can reach which resources, what grants that
access, and — optionally — whether that access has actually been used. Use it
to review and certify role and access-list grants, answer "who can reach this
database", or find dormant standing privileges.
You scope every review with a --query: a SQL SELECT against the Access Graph
access_path model. The query defines which identities and resources the review
covers; the results describe the access each identity has to each resource
and how that access is granted. It reports access paths only — it does not
enumerate other relationships, such as who can review an Access List rather
than gain access through it.
Prerequisites
See Identity Security from the Command Line for the shared
prerequisites and how the CLI authenticates to Access Graph. This command
requires Access Graph v1.30.1 or later, and tctl access-review requires a
--query.
Identity Activity Center is optional. Without it, the review still reports access paths and their grants, but the activity columns are omitted and the output notes why.
Review access
Scope the review with a --query. The query is a SQL SELECT against
access_path; the WHERE clause is where you narrow it. For example, to review
everything one user can reach:
tctl access-review --query "SELECT * FROM access_path WHERE identity = 'alice'"
identity matches a user or bot by its stored name or alias — for SSO users
this is usually their username or email. resource matches a resource by name,
and identity_group matches what grants access — a role, Access List, or Access
Request (shown as Granted By in the output).
Matches with = and IN are exact and case-sensitive; use ILIKE for
case-insensitive or prefix matching when you are unsure of the exact name.
Common review patterns
| Goal | Query |
|---|---|
| Everything one user can reach | SELECT * FROM access_path WHERE identity = 'alice' |
| Who can reach a resource | SELECT * FROM access_path WHERE resource ILIKE 'prod-db%' |
| What a role grants, and to whom | SELECT * FROM access_path WHERE identity_group IN ('prod-db-admin') |
| What an Access List grants, and to whom | SELECT * FROM access_path WHERE identity_group IN ('Prod Admins') |
| Why a resource is reachable through a role | SELECT * FROM access_path WHERE resource = 'prod-db' AND identity_group IN ('prod-db-admin') |
Roles and Access Lists are both identity_group nodes. The text output does not
mark a grantor's kind; to tell a role from an access_list, use
--format=json (or yaml) and read granted_by[].node.sub_kind. When you scope a
review through a grantor like this, mind what the results do and do not cover —
see Understand query scope.
By default the output summarizes each identity's resources, resolved access
level, the primary grant (the Granted By column), and grant counts. Add
--detailed to break each resource down by each grant and the level it
contributes:
tctl access-review --query "<query>" --detailed
Granted By is the identity group — a role, Access List, or Access Request —
that confers the access; it is what you filter on with identity_group in a
query.
| Flag | Description |
|---|---|
--query | Required. SQL SELECT against access_path scoping the identities to review. |
--from | Show activity at or after this time. RFC3339, YYYY-MM-DD, or durations like 24h, 7d. Defaults to 24 hours ago. |
--to | Upper bound for activity. Defaults to now. |
--no-activity | Skip the activity lookup and hide the activity columns for a faster review. Takes priority over --from/--to. |
--detailed | Show each grant with its individual access level instead of summary counts. |
--limit | Maximum identities to return. Default: 50. |
--format | text, json, yaml. |
In text output, a * marks self-expiring access or a temporary grant.
Understand query scope
Results are derived only from the paths your query produces. When you scope a review through a given node — a role, an Access List, or any other identity group — the results show only the paths that flow through that node, not necessarily every path the matched identities have to the matched resources.
For example, this query answers "what does membership in Prod Admins grant,
and to whom":
tctl access-review --query "SELECT * FROM access_path WHERE identity_group IN ('Prod Admins')"
It does not tell you the full access of each of those members. A member might
also reach the same resource through another role, another Access List, or an
approved Access Request, and those paths are invisible to a query scoped to
Prod Admins.
So when a scoped query shows an identity reaching a resource, it is usually
worth confirming that identity's complete access to the resource with a
follow-up query scoped by identity and resource directly:
tctl access-review --query "SELECT * FROM access_path WHERE identity = 'alice' AND resource = 'prod-server'"
This returns every path from alice to prod-server, with each grant that
backs it — the authoritative view to check before you add or remove access.
Show access activity
If your cluster has
Identity Activity Center enabled,
tctl access-review shows the activity columns by default over the last 24
hours — how often each access was used and when it was last exercised. Widen the
window with --from (and optionally --to):
tctl access-review --query "<query>" --from=30d
To skip the activity lookup entirely — for a faster, structural-only review of
who can reach what — pass --no-activity.
Activity counts come from Teleport session-start events, so they cover only resources reached through a Teleport session (SSH, database, Kubernetes, app, desktop). Resources used outside a session — such as AWS, Okta, or GitLab resources — leave the activity columns blank even when the access is used.
Use tctl access-review with an AI agent
To let an AI agent drive this command, install the matching Agent Skill:
npx skills add https://github.com/gravitational/teleport/tree/master/skills/teleport-access-review
Next steps
Troubleshooting
access-review is unavailable on this cluster
This usually means Identity Security is not enabled, or the access-review endpoint is not yet available on this cluster. Confirm Identity Security is enabled and that your cluster is v18.11.0 or later.
column "..." not found
The WHERE clause references a column access_path does not have. The
queryable columns include identity, resource, identity_group, source,
and id — for example, filter users with identity, not identity_name.
No access found
An empty result is more often a name mismatch than a true "no access". = and
IN match the stored name exactly and case-sensitively, so re-run with ILIKE
(for example, resource ILIKE '%db%'), read the names from the output, then
scope precisely.