setup

Snowflake

Purpose

Synchronises one Snowflake account: its users, roles and grants, warehouses and compute, the database and schema object hierarchy, external and storage integrations, and the network and access policies in force. This makes data-warehouse privilege paths and cross-cloud pivots visible next to the rest of your infrastructure.

tip

Secret fields below accept either an AWS Secrets Manager ARN or a value pasted directly into SubImage's managed vault. See Secrets for details.

Required Fields

Provide one credential: snowflake_private_key or snowflake_pat. Key-pair authentication is the stronger choice, because the private key is not a replayable bearer secret.

Field Secret? Description
snowflake_account No Account identifier, MYORG-MYACCOUNT or MYORG.MYACCOUNT
snowflake_user No Service user SubImage authenticates as, e.g. CARTOGRAPHY_SVC
snowflake_private_key Yes PEM-encoded RSA private key for key-pair (JWT) authentication
snowflake_private_key_passphrase Yes Passphrase protecting the private key, if it is encrypted
snowflake_pat Yes Programmatic access token, as an alternative to the key pair
snowflake_role No Role used to run SQL statements, e.g. CARTOGRAPHY_RO
snowflake_warehouse No Warehouse used to run SQL statements. Defaults to the user's default warehouse
snowflake_databases No Comma-separated databases to sync. Defaults to every readable database

Setup Steps

1. Create the service user, warehouse, and role

TYPE = SERVICE means the user cannot log in interactively and cannot hold a password, which is what you want for a collector.

USE ROLE USERADMIN;
CREATE USER SUBIMAGE_SVC TYPE = SERVICE
  COMMENT = 'SubImage inventory collector';

USE ROLE SYSADMIN;
CREATE WAREHOUSE SUBIMAGE_WH
  WAREHOUSE_SIZE = XSMALL AUTO_SUSPEND = 60 AUTO_RESUME = TRUE;

The object API endpoints take no role parameter: they run as the user's default role. So the role below must be set as the default role, not merely granted.

USE ROLE ACCOUNTADMIN;
CREATE ROLE SUBIMAGE_RO;
GRANT ROLE SUBIMAGE_RO TO USER SUBIMAGE_SVC;
ALTER USER SUBIMAGE_SVC SET DEFAULT_ROLE = SUBIMAGE_RO;

-- Run SQL statements.
GRANT USAGE ON WAREHOUSE SUBIMAGE_WH TO ROLE SUBIMAGE_RO;
-- Read the SNOWFLAKE.ACCOUNT_USAGE views: identities, grants, credential posture
-- and policy attachments. This is a read-only privilege.
GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE TO ROLE SUBIMAGE_RO;
-- Account-level metadata: warehouses, resource monitors, parameters.
GRANT MONITOR ON ACCOUNT TO ROLE SUBIMAGE_RO;
-- Walk the data hierarchy.
GRANT USAGE ON ALL DATABASES IN ACCOUNT TO ROLE SUBIMAGE_RO;
GRANT USAGE ON ALL SCHEMAS IN ACCOUNT TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON ALL TABLES IN ACCOUNT TO ROLE SUBIMAGE_RO;
GRANT USAGE ON FUTURE SCHEMAS IN ACCOUNT TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON FUTURE TABLES IN ACCOUNT TO ROLE SUBIMAGE_RO;

This set is entirely read-only. Do not drop IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE: without the ACCOUNT_USAGE views, SHOW ROLES returns only what the collector's own role can see, and a partial answer is indistinguishable from a complete one. SubImage then keeps what it has and skips role and grant cleanup rather than delete roles it merely could not see.

2. Create the credential

Option A: key pair (recommended). Generate an encrypted RSA key pair and register the public half on the user:

openssl genrsa 2048 | openssl pkcs8 -topk8 -v2 aes-256-cbc -inform PEM -out snowflake_key.p8
openssl rsa -in snowflake_key.p8 -pubout -out snowflake_key.pub
USE ROLE USERADMIN;
ALTER USER SUBIMAGE_SVC SET RSA_PUBLIC_KEY = '<contents of snowflake_key.pub, without the BEGIN/END lines>';

Supply the PEM-encoded private key as snowflake_private_key and its passphrase as snowflake_private_key_passphrase.

Option B: programmatic access token.

USE ROLE USERADMIN;
ALTER USER SUBIMAGE_SVC ADD PROGRAMMATIC ACCESS TOKEN SUBIMAGE_PAT
  ROLE_RESTRICTION = 'SUBIMAGE_RO'
  DAYS_TO_EXPIRY = 30;

Snowflake returns the secret once. Set ROLE_RESTRICTION so the token cannot be used with a more privileged role, keep the expiry short, and supply the value as snowflake_pat.

3. Attach a network policy

Snowflake requires a network policy to be in effect for the user before it accepts a programmatic access token. Key-pair authentication carries no such requirement, but restricting where the collector may connect from is worth doing either way. Attach the policy to the service user rather than the account, so it constrains only the collector.

4. Configure the module

In SubImage, set snowflake_account, snowflake_user, the credential from step 2, and snowflake_role and snowflake_warehouse to match step 1. Save the module.

Notes

  • The sync is read-only and covers one account per run. Each account is a separate SnowflakeAccount tenant, so several accounts can be synced without interfering.
  • On accounts with very many schemas, restrict the walk with snowflake_databases. The SNOWFLAKE and SNOWFLAKE_SAMPLE_DATA databases, and databases created from an inbound share, are skipped automatically.
  • Any surface Snowflake refuses to serve is skipped along with its cleanup, so a missing privilege never deletes previously collected data.

Troubleshooting

390432 Network policy is required. The user has no network policy in effect, which Snowflake requires before accepting a programmatic access token. Attach one, or switch to key-pair authentication. If the sync worked previously and then began failing, a MINS_TO_BYPASS_NETWORK_POLICY_REQUIREMENT exemption on the token has expired.

390144 JWT token is invalid. The registered public key does not match the private key in use, or snowflake_account is wrong.

Empty users, roles, or grants. The role lacks both MANAGE GRANTS and IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE, so neither the real-time nor the ACCOUNT_USAGE path is readable. Grant the latter.

Objects missing from one database only. The role has no USAGE on that database or its schemas. Snowflake reports an unauthorized object identically to a nonexistent one, so the skip is logged rather than guessed at.