Create and manage Snowflake Programmatic Access Tokens (PATs) for service users. Handles the full lifecycle: service user creation, authentication policies, network policies, PAT generation, rotation, and local configuration.
PAT secrets are stored only in the OS keychain (via Python keyring), using a service string aligned with snowflake-connector-python conventions. The tool does not write PAT material to .env or other files. It may update .env with SA_USER and SA_ROLE only.
8+ manual steps → single command.
- Snowflake CLI (
snow) installed and configured - Python 3.12+
- Task (optional, for task-based workflow)
uv sync # or: pip install .For running tests: uv sync --extra dev
# Create PAT with local IP network policy (most secure)
sfutils-pat create --user my_sa --role demo_role --db my_db
# Allow GitHub Actions (hybrid policy: custom CIDRs + Snowflake-managed rule; see Network Policy)
sfutils-pat create --user ci_sa --role ci_role --db my_db --allow-gh
# Skip network setup (if managed separately by sfutils-networks)
sfutils-pat create --user my_sa --role demo_role --db my_db --skip-network
# Preview all SQL without executing
sfutils-pat create --user my_sa --role demo_role --db my_db --dry-run
# Rotate an existing PAT (keeps all policies)
sfutils-pat rotate --user my_sa --role demo_role
# Verify PAT works (loads token from keyring only)
sfutils-pat verify --user my_sa --role demo_role
# Print PAT to stdout once (insecure; appears in logs) — optional
sfutils-pat create --user my_sa --role demo_role --db my_db --print
# Remove PAT and all associated objects (per-step prompts; use --yes to skip)
sfutils-pat remove --user my_sa --db my_db
sfutils-pat remove --user my_sa --db my_db --yesThe PAT is stored with:
keyringservice (first argument):
{HOST}:{ACCOUNT}:{SA_USER}:SFUTILS-PAT:{PAT_NAME}
All segments are uppercased.HOSTcomes fromsnow connection test --format jsonwhen present; otherwise the first segment isNA.ACCOUNTis the connection account identifier from the same JSON. Legacy installs may still have secrets underSNOW-UTILS-PAT; the CLI reads that label as a fallback and deletes both onremove.keyringusername (second argument):SA_USERuppercased (same pattern as the Snowflake Python connector for cached tokens).
The same tuple is used for create, rotate, verify, show-pat, and keyring cleanup on remove.
- Creates a service user (if not exists)
- Creates a network rule and policy with your allowed CIDRs (unless
--skip-network). With--allow-gh, the policy also references Snowflake’s managed GitHub Actions rule (Snowflake-managed network rules); GitHub IPs are no longer copied from the GitHub meta API into your rule. - Assigns the network policy to the user
- Creates an authentication policy (PAT-only, with network policy enforcement). Default expiry profile is 7 days default / 30 days max (hardened baseline). For Snowflake’s platform-style limits, pass
--default-expiry-days 15 --max-expiry-days 365. - Generates or rotates the PAT in Snowflake
- Stores the PAT in the OS keyring (not on disk)
- Updates
.envwithSA_USERandSA_ROLEonly (strips obsolete raw-PAT lines from older layouts) - Verifies the connection by loading the PAT from the keyring and running a test query (unless
--skip-verify)
JSON output (-o json) never includes the raw token; it includes redaction and keyring metadata (e.g. keyring_service, account, host).
--print may print the PAT to stdout after storage (default: off). It cannot be combined with -o json.
verifyreads the PAT only from the keyring. It does not accept--password, PAT-in-env, or.envsecrets for the token.show-patasks for confirmation (with a warning) before printing the PAT to stdout; use--yesonly when you must script it and accept log exposure.
task create SA_USER=my_sa SA_ROLE=demo_role SF_UTILS_DB=my_db
task create SA_USER=ci_sa SA_ROLE=ci_role SF_UTILS_DB=my_db -- --allow-gh
task no-rotate SA_USER=my_sa SA_ROLE=demo_role SF_UTILS_DB=my_db
task remove SA_USER=my_sa SF_UTILS_DB=my_dbOpt-in checks against a real Snowflake account and the OS keychain are documented in docs/it/README.md. task it runs unit tests (pytest) then shell smoke (scripts/it/smoke.sh). For manual full lifecycle IT, use the same .env keys as daily CLI use (SA_USER, SA_ROLE, SF_UTILS_DB; legacy SNOW_UTILS_DB is still accepted); see the runbook.
| Command | Description |
|---|---|
create |
Create/rotate PAT for a service user (full setup) |
rotate |
Rotate an existing PAT (keeps policies intact) |
verify |
Test PAT connection; PAT loaded from keyring only |
show-pat |
Print PAT from keyring to stdout after confirm (or --yes; insecure) |
remove |
Remove PAT and associated objects (your account network policy and {db}.NETWORKS.{user} rule; auth policy); does not drop SNOWFLAKE.NETWORK_SECURITY managed rules; per-step confirmation unless --yes; non-interactive runs require --yes; clears keyring entry when connection metadata is available |
| Variable | Description |
|---|---|
SA_USER |
Service account username |
SA_ROLE |
Role restriction for the PAT |
SA_ADMIN_ROLE |
Admin role for creating policies (default: ACCOUNTADMIN) |
SF_UTILS_DB |
Database for PAT objects (network rules, auth policies); legacy SNOW_UTILS_DB still read by CLI and check-setup |
PAT_NAME |
Snowflake PAT object name (default: {USER}_PAT) |
DOT_ENV_FILE |
Used by Taskfile examples; CLI uses --env-path / --dot-env-file |
The PAT is not supplied through any environment variable; only the keychain entry is used for auth in this tool.
Older releases may have stored the raw PAT in .env. After upgrading:
- Run
createorrotateagain so the PAT is stored in the keychain. - When this tool updates
.env, it removes obsolete file-stored PAT lines automatically; delete any leftover secret manually if you still see one. - Use
verify(keyring) or export secrets for other tools outside this CLI, at your own risk.
PATs require a network policy for security (Snowflake best practice). By default, create sets up:
- A network rule
{USER}_NETWORK_RULEin your utils database’sNETWORKSschema with CIDRs from--allow-local(default),--allow-google, and--extra-cidrs(no GitHub IPs in this rule). - A network policy
{USER}_NETWORK_POLICY. With--allow-gh, this is a hybrid policy: it lists your custom rule andSNOWFLAKE.NETWORK_SECURITY.GITHUB_ACTIONSso GitHub egress is maintained by Snowflake (not available in government regions; confirm names withSHOW NETWORK RULES IN SNOWFLAKE.NETWORK_SECURITY). - With
--allow-ghand no custom CIDRs (e.g.--no-local --allow-ghonly), the policy may reference only the managed GitHub rule—no custom network rule is created. - Assigns the policy to the service user.
Use --skip-network if you manage network rules separately with sfutils-networks (or the sf-utils-networks Cortex skill).
For security principles, residual risks, and an informal compliance-oriented review, see docs/SECURITY.md.
- sf-utils-skills (Cortex Code skills; use path
sf-utils-patafter repo rename fromsnow-utils-skills)
Apache 2.0