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;

These account-level metadata grants are 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.

5. Grant database metadata access

Grant metadata access separately for each database to inventory. Replace EXAMPLE_DB with its name and repeat this block when onboarding a new database. Ordinary grants do not support ALL DATABASES IN ACCOUNT, or account-scoped ALL / FUTURE schema and table grants.

USE ROLE ACCOUNTADMIN;
GRANT USAGE ON DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT USAGE ON ALL SCHEMAS IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT USAGE ON FUTURE SCHEMAS IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON ALL TABLES IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON FUTURE TABLES IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON ALL EVENT TABLES IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON FUTURE EVENT TABLES IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON ALL VIEWS IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON FUTURE VIEWS IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON ALL MATERIALIZED VIEWS IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON FUTURE MATERIALIZED VIEWS IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON ALL EXTERNAL TABLES IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON FUTURE EXTERNAL TABLES IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON ALL ICEBERG TABLES IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT REFERENCES ON FUTURE ICEBERG TABLES IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT MONITOR ON ALL DYNAMIC TABLES IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;
GRANT MONITOR ON FUTURE DYNAMIC TABLES IN DATABASE EXAMPLE_DB TO ROLE SUBIMAGE_RO;

REFERENCES allows inspecting table and view metadata without granting SELECT on their contents. Dynamic tables instead use MONITOR for read-only metadata access; they do not support REFERENCES. Other object types can require additional privileges. A successful sync does not prove that every object is visible to the collector role.

Schema-level future grants override database-level future grants, even when they target a different role. For each schema with its own future table or view grants, also grant REFERENCES ON FUTURE TABLES IN SCHEMA EXAMPLE_DB.EXAMPLE_SCHEMA or REFERENCES ON FUTURE VIEWS IN SCHEMA EXAMPLE_DB.EXAMPLE_SCHEMA to SUBIMAGE_RO, respectively. Review this when adding schemas or changing future grants. Apply the same rule to event tables, materialized views, external tables, Iceberg tables, and dynamic tables, using the corresponding object type and privilege above.

Native App access

Installed Native Apps expose access through application roles defined by their provider. Database metadata grants do not replace these roles, and account-wide inherited grants do not extend into Native App containers.

For each app whose exposed objects you want to inventory, have the application owner inspect its roles and grant the narrowest suitable one. If EXAMPLE_APP defines a VIEWER role, inspect it before granting it:

SHOW APPLICATION ROLES IN APPLICATION EXAMPLE_APP;
SHOW GRANTS TO APPLICATION ROLE EXAMPLE_APP.VIEWER;

VIEWER is an app-defined name, not a standard metadata-only permission. Review the provider's role documentation, including inherited privileges and whether it allows reading data or executing procedures. If those permissions are appropriate:

GRANT APPLICATION ROLE EXAMPLE_APP.VIEWER TO ROLE SUBIMAGE_RO;

GRANT APPLICATION ROLE has no ALL or FUTURE variant. Repeat this review when installing an app or when its roles change. Keep the IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE grant above for ACCOUNT_USAGE; do not assume a SNOWFLAKE.VIEWER role exists.

SubImage does not currently model application roles or their privilege paths. Granting an app role may improve exposed-object visibility, but does not provide complete application-role coverage in the graph.

Inherited grants

Snowflake's inherited grants are generally available. They support account-wide scope and cover both existing and future objects. For example, GRANT INHERITED REFERENCES ON ALL TABLES IN ACCOUNT TO ROLE SUBIMAGE_RO is different from the unsupported ordinary account-wide grant.

They are not required for this setup. Snowflake documents an account-wide opt-in using ALTER ACCOUNT SET FEATURE_RBAC_INHERITED_GRANTS = 'ENABLED'; this enables inherited grants and container-level grant management for the whole account, not just the collector. Inherited grants apply to each specified object type: a grant on tables does not also cover views or dynamic tables.

SubImage records inherited grants separately from direct privileges, preserving the grantee, object type, privilege, and account, database, or schema scope. These records describe scoped grants rather than expanded effective access: container USAGE, policy restrictions, and object visibility still matter. Use the ordinary grants above for the documented setup.

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.
  • Surfaces with detected permission failures have their cleanup skipped. A successful listing can still omit objects the role cannot see; it is not proof of complete account-wide visibility.

Verify access

Connect as SUBIMAGE_SVC using its credential, or as a user already granted SUBIMAGE_RO. Run these checks with secondary roles disabled so other roles cannot mask missing grants:

USE ROLE SUBIMAGE_RO;
USE SECONDARY ROLES NONE;
USE WAREHOUSE SUBIMAGE_WH;
SELECT COUNT(*) FROM SNOWFLAKE.ACCOUNT_USAGE.GRANTS_TO_ROLES WHERE DELETED_ON IS NULL;
SHOW SCHEMAS IN DATABASE EXAMPLE_DB;
SHOW TABLES IN DATABASE EXAMPLE_DB;
SHOW VIEWS IN DATABASE EXAMPLE_DB;

Compare the listings with objects an administrator knows exist, including newly created objects and schemas with their own future grants. An empty result alone does not distinguish an empty database from insufficient visibility. Finally, run the collector with its own credential and inspect the sync warnings; SQL checks alone do not validate the REST object endpoints.

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.