Saltar al contenido
← Volver a proyectos

Tsadm

another tailscale API CLI client

#tsadm

tsadm is a proposed CLI tool for the Tailscale API:

https://tailscale.com/api

The goal is to provide a stable, scriptable command line shape for the public Tailscale API while preserving the API's resource model.

#Configuration

The tailnet is selected by --tailnet, then $TAILNET, then the tsadm config default. It can be a profile alias, a tailnet ID such as T1234, a tailnet name such as foo.bar.ts.net or foo.github, or - for the tailnet associated with the authenticated account when the API supports it.

tsadm --tailnet=X policy get

Authentication should use an API access token or OAuth access token accepted by the Tailscale API. Proposed lookup order:

  1. --api-key or --bearer-token
  2. $TAILSCALE_API_KEY or $TAILSCALE_ACCESS_TOKEN
  3. tailnet profile selected by --tailnet, $TAILNET, or the tsadm config default
  4. platform keychain entry for tsadm

Tailnet profiles live in $TSADM_CONFIG_DIR, or ~/.config/tsadm by default. Each alias is a file named after the alias, containing JSON:

{
  "tailnet": "TV9D11111111CNTRL",
  "apiKey": "file:/home/me/keys/foo-tailnet.key"
}

bearerToken may be used instead of apiKey. The older shorter field name key is accepted as an alias for apiKey when reading profiles. The configured default is a symlink named default pointing to the alias file, for example ~/.config/tsadm/default -> foo.

Credentials starting with tskey-client- are OAuth client secrets and are exchanged for bearer access tokens automatically, even if they were entered as an API key.

config add-tailnet consumes positional arguments as alias, tailnet, then credential, and prompts for any omitted suffix. It checks the credentials with a cheap API GET by default; pass --check=false to skip that check.

Useful global flags:

--tailnet=<tailnet>       Tailnet to operate on; defaults to $TAILNET,
                          then the tsadm config default.
--api-key=<key>           Use an API key with HTTP Basic authentication.
                          A value of file:<path> reads the key from a file.
--bearer-token=<token>    Use an OAuth bearer token.
                          A value of file:<path> reads the token from a file.
--base-url=<url>          Override API base URL; defaults to the public API.
--hujson                  Emit HUJSON where supported, currently policy files.
--fields=<list>           Request selected API fields when supported.
--filter=<k=v>            Add a server-side list filter; repeatable.
--from-file=<path>        Read request body or resource spec from a file.
--yes                     Skip confirmation for destructive operations.

Input conventions:

tsadm <resource> set --from-file=file.json
tsadm <resource> set < file.json
tsadm <resource> patch --from-file=patch.json
tsadm <resource> patch key=value key2=value2

Output conventions:

tsadm <resource> list              # concise text table by default
tsadm <resource> list --json       # API-shaped JSON, where supported
tsadm <resource> get --json ID

Policy files are the exception: policy get preserves HUJSON by default, and policy get --json converts the policy file to JSON.

Flag placement is strict. Global flags must appear before the resource name and apply to all commands. Leaf flags are only accepted where each leaf command defines them; for commands with positional operands, put --json before the operand, such as tsadm device get --json <device-id>.

#Implemented Commands

This repository currently implements these commands:

tsadm policy get [--json]
tsadm policy set [--from-file=file] [--etag=etag]
tsadm policy validate [--from-file=file]

tsadm config add-tailnet [--bearer-token] [--check=false] [<alias> [<tailnet> [<credential>]]]
tsadm config list-tailnets [--json]
tsadm config set-default-tailnet <alias>
tsadm config check [<alias>]

tsadm device list [--all] [--json]
tsadm device get [--all] [--json] <device-id>
tsadm device delete <device-id>
tsadm device authorize <device-id>
tsadm device deauthorize <device-id>
tsadm device rename <device-id> <name>
tsadm device reset-name <device-id>
tsadm device expire-key <device-id>
tsadm device tags set <device-id> [tag...]
tsadm device ip set <device-id> <ipv4>
tsadm device routes list [--json] <device-id>
tsadm device routes set <device-id> [route...]
tsadm device routes set --from-file=routes.json <device-id>
tsadm device attrs get [--json] <device-id>
tsadm device attrs set [--expiry=time] [--comment=text] <device-id> <attribute> <value>
tsadm device attrs delete <device-id> <attribute>
tsadm device attrs patch --from-file=attrs.json <device-id>

tsadm key list [--user-only] [--json]
tsadm key get [--json] <key-id>
tsadm key create --from-file=key.json [--json]
tsadm key delete <key-id>

tsadm dns nameservers list [--json]
tsadm dns nameservers set [addr...]
tsadm dns nameservers set --from-file=nameservers.json
tsadm dns preferences get [--json]
tsadm dns preferences set --magic-dns=true|false
tsadm dns preferences set --from-file=preferences.json
tsadm dns searchpaths list [--json]
tsadm dns searchpaths set [domain...]
tsadm dns searchpaths set --from-file=searchpaths.json
tsadm dns split get [--json]
tsadm dns split set --from-file=split-dns.json
tsadm dns split update --from-file=split-dns.json [--json]
tsadm dns split patch --domain=corp.example.com --nameserver=10.0.0.53
tsadm dns split delete-domain corp.example.com
tsadm dns config get [--json]
tsadm dns config set --from-file=dns.json

tsadm user list [--json]
tsadm user get [--json] <user-id>
tsadm user role set <user-id> owner|admin|it-admin|network-admin|billing-admin|auditor|member
tsadm user approve <user-id>
tsadm user suspend <user-id>
tsadm user restore <user-id>
tsadm user delete <user-id>

tsadm user-invite list [--json]
tsadm user-invite create [--email=email] [--role=role] [--from-file=file] [--json]
tsadm user-invite get [--json] <invite-id>
tsadm user-invite resend <invite-id>
tsadm user-invite delete <invite-id>

tsadm device invites list [--json] <device-id>
tsadm device invite create [--email=email] [--multi-use] [--allow-exit-node] [--from-file=file] [--json] <device-id>
tsadm device-invite get [--json] <invite-id>
tsadm device-invite resend <invite-id>
tsadm device-invite delete <invite-id>
tsadm device-invite accept [--from-file=file] [--json] <invite-url-or-code>

tsadm setting get [--json]
tsadm setting patch [setting flags]
tsadm setting patch --from-file=settings.json

tsadm contact get [--json]
tsadm contact set account|support|security user@example.com
tsadm contact resend-verification account|support|security

tsadm webhook list [--json]
tsadm webhook create [--json] --url=https://example.com/hook --subscription=nodeCreated
tsadm webhook create [--json] --from-file=webhook.json
tsadm webhook get [--json] <endpoint-id>
tsadm webhook patch [--json] --subscription=nodeCreated <endpoint-id>
tsadm webhook patch [--json] --from-file=patch.json <endpoint-id>
tsadm webhook delete <endpoint-id>
tsadm webhook test <endpoint-id>
tsadm webhook rotate-secret [--json] <endpoint-id>

tsadm service list [--json]
tsadm service get [--json] <service-name>
tsadm service set --from-file=service.json <service-name>
tsadm service delete <service-name>
tsadm service devices list [--json] <service-name>
tsadm service device approval get [--json] <service-name> <device-id>
tsadm service device approval set --approved=true|false <service-name> <device-id>

tsadm posture integrations list [--json]
tsadm posture integrations get [--json] <integration-id>

tsadm logs stream get [--json] configuration|network
tsadm logs aws-external-id get-or-create [--reusable] [--json]

Singular resource names are preferred. Existing plural aliases, such as devices list and users list, continue to work.

The sections below remain the broader proposed command surface.

#Policy File

The Tailscale API exposes the policy file at /tailnet/{tailnet}/acl.

tsadm policy get
tsadm policy get --json | jq .
tsadm policy set < policy.hujson
tsadm policy set --from-file=policy.hujson
tsadm policy validate < policy.hujson
tsadm policy validate --from-file=policy.hujson
tsadm policy preview --src=<user-or-tag> --dst=<host:port>
tsadm policy preview --from-file=request.json

policy get should preserve HUJSON by default. --json converts HUJSON to JSON.

#Devices

Device list operations are tailnet-scoped. Device mutation operations use the stable device ID returned by the API.

tsadm device list
tsadm device list --fields=id,name,hostname,tags
tsadm device list --filter=isEphemeral=true --filter=tags=tag:prod
tsadm device attrs patch --from-file=attrs.json <device-id>

tsadm device get <device-id>
tsadm device delete <device-id>
tsadm device expire-key <device-id>
tsadm device authorize <device-id>
tsadm device deauthorize <device-id>
tsadm device rename <device-id> <name>
tsadm device reset-name <device-id>
tsadm device tags set <device-id> tag:foo tag:bar
tsadm device key set --key-expiry-disabled=true <device-id>
tsadm device ip set <device-id> <ipv4>

tsadm device routes list <device-id>
tsadm device routes set <device-id> 10.0.0.0/16 192.168.1.0/24
tsadm device routes set --from-file=routes.json <device-id>

tsadm device attrs get <device-id>
tsadm device attrs set <device-id> custom:asset-tag value
tsadm device attrs set --expiry=2026-12-31T00:00:00Z <device-id> custom:asset-tag value
tsadm device attrs delete <device-id> custom:asset-tag

#Auth Keys And Trust Credentials

The API's keys resource covers auth keys and trust credentials.

tsadm key list
tsadm key list --user-only
tsadm key create --from-file=key.json
tsadm key create --ephemeral --reusable --preauthorized --tag=tag:ci
tsadm key get <key-id>
tsadm key set <key-id> --from-file=key.json
tsadm key delete <key-id>

#DNS

DNS commands mirror the API's nameserver, preference, search path, split DNS, and whole-configuration endpoints.

tsadm dns nameservers list
tsadm dns nameservers set 1.1.1.1 8.8.8.8
tsadm dns nameservers set --from-file=nameservers.json

tsadm dns preferences get
tsadm dns preferences set --magic-dns=true
tsadm dns preferences set --from-file=preferences.json

tsadm dns searchpaths list
tsadm dns searchpaths set corp.example.com svc.example.com
tsadm dns searchpaths set --from-file=searchpaths.json

tsadm dns split get
tsadm dns split set --from-file=split-dns.json
tsadm dns split patch --domain=corp.example.com --nameserver=10.0.0.53
tsadm dns split delete-domain corp.example.com

tsadm dns config get
tsadm dns config set --from-file=dns.json

#Users

tsadm user list
tsadm user get <user-id>
tsadm user role set <user-id> owner|admin|it-admin|network-admin|billing-admin|auditor|member
tsadm user approve <user-id>
tsadm user suspend <user-id>
tsadm user restore <user-id>
tsadm user delete <user-id>

#User Invites

tsadm user-invites list
tsadm user-invite create --email=user@example.com --role=member
tsadm user-invite create --from-file=invite.json
tsadm user-invite get <invite-id>
tsadm user-invite resend <invite-id>
tsadm user-invite delete <invite-id>

#Device Invites

tsadm device invites list <device-id>
tsadm device invite create <device-id> --email=user@example.com
tsadm device invite create <device-id> --from-file=invite.json
tsadm device-invite get <invite-id>
tsadm device-invite resend <invite-id>
tsadm device-invite delete <invite-id>
tsadm device-invite accept --from-file=invite.json

#Logging

tsadm logs configuration list
tsadm logs network list

tsadm logs stream status configuration|network
tsadm logs stream get configuration|network
tsadm logs stream set configuration|network --from-file=stream.json
tsadm logs stream disable configuration|network

tsadm logs aws-external-id get-or-create
tsadm logs aws-external-id validate <id> --role-arn=<arn>
tsadm logs aws-external-id validate <id> --from-file=request.json

#Device Posture Integrations

tsadm posture integrations list
tsadm posture integration create --from-file=integration.json
tsadm posture integration get <integration-id>
tsadm posture integration patch <integration-id> --from-file=patch.json
tsadm posture integration delete <integration-id>

Custom device posture attributes are under the device attrs command.

#Contacts

Contact types are the API's contact preference types: account, support, and security.

tsadm contact get
tsadm contact set account|support|security user@example.com
tsadm contact resend-verification account|support|security

#Webhooks

tsadm webhook list
tsadm webhook create --url=https://example.com/hook --subscription=nodeCreated
tsadm webhook create --from-file=webhook.json
tsadm webhook get <endpoint-id>
tsadm webhook patch --subscription=nodeCreated <endpoint-id>
tsadm webhook patch --from-file=patch.json <endpoint-id>
tsadm webhook delete <endpoint-id>
tsadm webhook test <endpoint-id>
tsadm webhook rotate-secret <endpoint-id>

#Tailnet Settings

tsadm setting get
tsadm setting patch --devices-approval-on=true
tsadm setting patch --users-approval-on=true
tsadm setting patch --acls-externally-managed-on=true --acls-external-link=https://example.com/policy
tsadm setting patch --from-file=settings.json

#Services

Tailscale Services are tailnet-scoped resources named like svc:example.

tsadm service list
tsadm service get <service-name>
tsadm service set --from-file=service.json <service-name>
tsadm service delete <service-name>

tsadm service devices list <service-name>
tsadm service device approval get <service-name> <device-id>
tsadm service device approval set --approved=true <service-name> <device-id>

#Raw Escape Hatch

For endpoints that are new or not yet wrapped by a first-class command:

tsadm api get /tailnet/{tailnet}/devices
tsadm api post /tailnet/{tailnet}/acl/validate --from-file=policy.hujson
tsadm api patch /tailnet/{tailnet}/settings --from-file=settings.json
tsadm api delete /device/{deviceId}

{tailnet} should be expanded from --tailnet or $TAILNET. Other path parameters must be supplied literally or with --param name=value.

Nueva versión disponible.