Minimum Permissions for Scan Accounts

Forcepoint DSPM scans Snowflake data sources for cataloging and classification. This topic describes the minimum Snowflake privileges required for a scan account, so customers can provision a least-privilege role instead of using ACCOUNTADMIN or a full IMPORTED PRIVILEGES grant on the SNOWFLAKE share.

Connection prerequisites
Requirement Reason
Network reachability to <account>.snowflakecomputing.com (443) Establish the JDBC connection
A Snowflake user for the scanner (a TYPE = SERVICE user is recommended) SERVICE users authenticate with a PAT or key-pair only—never a password - so they bypass MFA, which suits an unattended scanner
A Programmatic Access Token (PAT) or key-pair for that user The connector passes the PAT as the JDBC password
A network policy bound to the user Snowflake requires a network policy on the user for PAT authentication
The target database name Required - the connector rejects a connection with no database

The connector uses the official Snowflake JDBC driver. No ACCOUNTADMIN role is required.

How Snowflake handles catalog visibility

Snowflake's per-database INFORMATION_SCHEMA shows only objects the active role has at least one privilege on. USAGE on a schema does not cascade to its tables - the role needs both schema USAGE and explicit SELECT on the tables/views for them to appear in INFORMATION_SCHEMA.TABLES and to be sampled. Account-wide identity metadata (users, roles, grants) lives in the SNOWFLAKE.ACCOUNT_USAGE share, which is reachable through the granular SNOWFLAKE.OBJECT_VIEWER + SNOWFLAKE.SECURITY_VIEWER database roles.

Operations and required permissions
  1. Connection test & database metadata
    Operation Objects read Minimum grant
    Test connection / run queries USAGE on warehouse + USAGE on database
    Schema enumeration (getSchemas) Database schemas USAGE on schema
    Table / view listing SHOW TABLES IN SCHEMA, SHOW VIEWS IN SCHEMA USAGE on schema
    Column / PK / FK metadata SHOW COLUMNS / PRIMARY KEYS / IMPORTED KEYS IN SCHEMA USAGE on schema
    Approximate row count & table size INFORMATION_SCHEMA.TABLES (row_count, bytes) USAGE on schema + SELECT on the tables (INFORMATION_SCHEMA is privilege-filtered)
  2. Data sampling
    Operation Objects read Minimum grant
    SAMPLE BERNOULLI row sample Target user table/view USAGE on schema + SELECT on table/view
    Column payload sampling Target user table/view Same as above
  3. Trustee (users & roles) extraction
    Operation Objects read Minimum grant
    List users SNOWFLAKE.ACCOUNT_USAGE.USERS SNOWFLAKE.OBJECT_VIEWER + SNOWFLAKE.SECURITY_VIEWER database roles
    List roles (groups) SNOWFLAKE.ACCOUNT_USAGE.ROLES Same as above
    Recursive role membership SNOWFLAKE.ACCOUNT_USAGE.GRANTS_TO_USERS, GRANTS_TO_ROLES Same as above

    Users are classified as users; roles are classified as groups. ACCOUNTADMIN, SECURITYADMIN, SYSADMIN, and USERADMIN are flagged as top-level admins.

    The two viewer database roles are a tighter substitute for GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE - they give the scanner the same read access to the ACCOUNT_USAGE views it needs but keep SHOW GRANTS on the scanner role at a couple of rows instead of expanding to hundreds. Note ACCOUNT_USAGE views have a 45 - 90 minute data latency.

  4. Permissions extraction
    Operation Objects read Minimum grant
    Database-level grants SHOW GRANTS ON DATABASE USAGE on database
    Schema-level grants SHOW GRANTS ON SCHEMA / ACCOUNT_USAGE.GRANTS_TO_ROLES USAGE on schema (+ viewer roles for the ACCOUNT_USAGE fast path)
    Table-level grants SHOW GRANTS ON TABLE USAGE on schema + SELECT on table
    Top-level admin holders ACCOUNT_USAGE.ROLES viewer roles
    Column-level grants Not supported by the Snowflake connector

Granting scan access

Attach all grants to a custom role, not directly to the user - that keeps the permission set auditable and revocable as a unit. USAGE on a schema does not reach its tables, so the role needs schema USAGE plus SELECT on current and future tables/views.

Consolidated grant script
-- Run as ACCOUNTADMIN. Edit the SET values first.
SET USER_NAME           = 'FP_SCANNER';
SET ROLE_NAME           = 'FP_SCANNER_ROLE';
SET NETWORK_POLICY_NAME = 'FP_SCANNER_NETWORK_POLICY';
SET PAT_NAME            = 'FP_SCANNER_PAT';
SET WAREHOUSE_NAME      = 'COMPUTE_WH';
SET DATABASE_NAME       = '<TARGET_DATABASE>';
SET SCHEMAS             = '<SCHEMA_1>,<SCHEMA_2>';   -- comma-separated

USE ROLE ACCOUNTADMIN;

-- Network policy (required for PAT auth) — replace with the scanner's CIDRs
CREATE NETWORK POLICY IF NOT EXISTS IDENTIFIER($NETWORK_POLICY_NAME)
    ALLOWED_IP_LIST = ('0.0.0.0/0');

-- Custom role
CREATE ROLE IF NOT EXISTS IDENTIFIER($ROLE_NAME);

-- Service user (no password; PAT only)
CREATE USER IF NOT EXISTS IDENTIFIER($USER_NAME)
    TYPE = SERVICE
    DEFAULT_ROLE = $ROLE_NAME
    DEFAULT_WAREHOUSE = $WAREHOUSE_NAME;
GRANT ROLE IDENTIFIER($ROLE_NAME) TO USER IDENTIFIER($USER_NAME);
ALTER USER IDENTIFIER($USER_NAME) SET NETWORK_POLICY = $NETWORK_POLICY_NAME;

-- Account- and database-level grants
GRANT USAGE ON WAREHOUSE IDENTIFIER($WAREHOUSE_NAME) TO ROLE IDENTIFIER($ROLE_NAME);
GRANT DATABASE ROLE SNOWFLAKE.OBJECT_VIEWER          TO ROLE IDENTIFIER($ROLE_NAME);
GRANT DATABASE ROLE SNOWFLAKE.SECURITY_VIEWER        TO ROLE IDENTIFIER($ROLE_NAME);
GRANT USAGE ON DATABASE IDENTIFIER($DATABASE_NAME)   TO ROLE IDENTIFIER($ROLE_NAME);

-- Per-schema grants (loops over $SCHEMAS)
EXECUTE IMMEDIATE $$
BEGIN
    LET schemas ARRAY := SPLIT($SCHEMAS, ',');
    FOR i IN 0 TO ARRAY_SIZE(schemas) - 1 DO
        LET qualified VARCHAR := '"' || $DATABASE_NAME || '"."' || TRIM(GET(schemas, i)::VARCHAR) || '"';
        EXECUTE IMMEDIATE 'GRANT USAGE  ON SCHEMA '            || qualified || ' TO ROLE "' || $ROLE_NAME || '"';
        EXECUTE IMMEDIATE 'GRANT SELECT ON ALL TABLES IN SCHEMA '    || qualified || ' TO ROLE "' || $ROLE_NAME || '"';
        EXECUTE IMMEDIATE 'GRANT SELECT ON ALL VIEWS  IN SCHEMA '    || qualified || ' TO ROLE "' || $ROLE_NAME || '"';
        EXECUTE IMMEDIATE 'GRANT SELECT ON FUTURE TABLES IN SCHEMA ' || qualified || ' TO ROLE "' || $ROLE_NAME || '"';
        EXECUTE IMMEDIATE 'GRANT SELECT ON FUTURE VIEWS  IN SCHEMA ' || qualified || ' TO ROLE "' || $ROLE_NAME || '"';
    END FOR;
    RETURN 'done';
END;
$$;

-- Generate the PAT — token_secret is shown ONCE; copy it into your secret manager
ALTER USER IDENTIFIER($USER_NAME) ADD PROGRAMMATIC ACCESS TOKEN IDENTIFIER($PAT_NAME)
    ROLE_RESTRICTION = $ROLE_NAME
    DAYS_TO_EXPIRY = 90;

Scope variants: to scan the whole datasource, list every user schema in $SCHEMAS; to scan a single schema or table, grant USAGE/SELECT only on that schema (and, for a single table, SELECT on just that table).

Verification

Connect as the scanner user (role FP_SCANNER_ROLE) and run:
SELECT CURRENT_USER(), CURRENT_ROLE(), CURRENT_DATABASE();
SHOW TABLES IN SCHEMA "<DATABASE>"."<SCHEMA>";
SELECT * FROM "<DATABASE>"."<SCHEMA>"."<TABLE>" SAMPLE BERNOULLI (1) LIMIT 10;
SELECT COUNT(*) FROM SNOWFLAKE.ACCOUNT_USAGE.USERS WHERE DELETED_ON IS NULL;
SELECT COUNT(*) FROM SNOWFLAKE.ACCOUNT_USAGE.ROLES WHERE DELETED_ON IS NULL;
SHOW GRANTS ON DATABASE "<DATABASE>";

All queries should return without an access error. Empty result sets are fine when the underlying object has no matching rows. (Remember ACCOUNT_USAGE latency of up to ~90 minutes for brand-new users/roles.)

What is not required

The scan account does not need any of:
  • The ACCOUNTADMIN, SECURITYADMIN, SYSADMIN, or USERADMIN roles.

  • GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE (the two granular viewer database roles replace it).

  • OWNERSHIP on any object.

  • INSERT, UPDATE, DELETE, TRUNCATE, or any write/DDL privilege.

  • Any MODIFY/OPERATE on the warehouse beyond USAGE.