Snowflake connections
DataMasque masks Snowflake tables in place. It reads rows from the table, masks them in memory, and loads the masked rows back into the table through a temporary internal stage that Snowflake manages. Masked data is not written to any storage outside Snowflake.
- Masking process
- Required privileges
- Connection parameters
- Encrypted connections
- Limitations
- Supported data types
- Unsupported key columns
Masking process
The minimum specs for masking Snowflake databases are 8 vCPUs and at least 16GiB RAM.
Snowflake databases are masked by querying the data from the table, applying the masking transformations in memory, and then loading the masked data back into the table through a temporary internal stage.
The following diagram shows the high level data masking workflow for Snowflake:

How masked data is written
Each worker that masks a table:
- Creates a temporary internal stage and a temporary table in the target schema,
with Snowflake server-side encryption
(
CREATE TEMPORARY STAGE ... ENCRYPTION = (TYPE = 'SNOWFLAKE_SSE')). - For each batch of masked rows, serialises the rows to gzipped NDJSON in memory
and uploads them to the stage with
PUT, over HTTPS. No file is written to disk. - When every batch it was assigned is staged, loads the staged rows into the temporary table
with one
COPY INTO, and applies the masked values to the target table with oneMERGEon the primary key, in one transaction.
If any step fails, that worker's transaction is rolled back and its rows are left unchanged. With more than one worker on a table, rows that other workers had already committed stay masked, so a failed run can leave the table partly masked. DataMasque drops the temporary stage and temporary table when the worker has merged its rows, and when a run fails or is cancelled. Snowflake removes them when the session ends.
Snowflake warehouses can be set to automatically suspend after a specified period of time. When the next queries are run the warehouse will resume. This usually only takes 1–2 seconds. In rare cases, this may take longer when Snowflake is provisioning the resources.
- DataMasque's timeout for testing a connection is 10 seconds.
- During a masking run the connection timeout is 120 seconds.
- By default
auto-suspend, andauto-resumeare enabled and the inactivity timeout is 10 minutes. - The values for
auto-suspendandauto-resumecan be set when creating warehouses or updated later using theALTER WAREHOUSEstatement. - If you're having trouble with timeouts, you could "pre-warm" the warehouse by testing the connection, or executing a simple query.
For more information, please refer to the official documentation:
Working with Warehouses
Warehouse considerations
Note: DataMasque supports masking Snowflake databases regardless of the selected Snowflake host cloud platform (AWS, Azure, or Google Cloud Platform). The internal stage is created in the same Snowflake account as the target table, so no cross-cloud data transfer takes place.
Changes to staging in 3.26.18
From DataMasque 3.26.18, Snowflake masking stages data only through the temporary internal stage. An S3 bucket, Azure Blob Storage container or storage integration is no longer required. Connections created in an earlier version may still hold these values, or an IAM Role ARN. DataMasque ignores them. The storage integration, bucket or container can be removed once nothing else depends on them.
The DataMasque Snowflake Marketplace App is unaffected: it has always used an internal stage.
Required privileges
DataMasque masks Snowflake tables with a role that you grant to the connection user. The role needs a small base set of privileges for every run. Some masks and table shapes make DataMasque run DDL on the target tables. Those need extra privileges, listed under Conditional privileges.
Base privileges
Every masking run needs:
USAGEon the warehouse, the target database and the target schema(s).SELECTandUPDATEon each target table. DataMasque reads rows withSELECTand applies masked rows with aMERGEthat only updates matched rows. It never inserts or deletes rows in the target table.
No CREATE privilege is needed for the write path.
The internal stage and the load table are TEMPORARY objects.
Snowflake does not require CREATE STAGE or CREATE TABLE on the schema to create temporary objects.
CREATE ROLE <masking-role>;
GRANT USAGE ON WAREHOUSE <warehouse> TO ROLE <masking-role>;
GRANT USAGE ON DATABASE <target-database> TO ROLE <masking-role>;
GRANT USAGE ON SCHEMA <target-database>.<target-schema> TO ROLE <masking-role>;
GRANT SELECT, UPDATE ON ALL TABLES IN SCHEMA <target-database>.<target-schema> TO ROLE <masking-role>;
-- Or, per table:
GRANT SELECT, UPDATE ON TABLE <target-database>.<target-schema>.<target-table> TO ROLE <masking-role>;
GRANT ROLE <masking-role> TO USER <connection-user>;
Conditional privileges
Grant these only when the ruleset or the table shape needs them.
| When | What DataMasque runs | Privilege |
|---|---|---|
A ruleset uses secure_shuffle or from_blob |
Creates and drops a permanent helper table in the target schema | CREATE TABLE on the target schema |
A ruleset has a build_temp_table task |
Creates a table in the target schema | CREATE TABLE on the target schema |
A ruleset has a truncate_table task |
TRUNCATE TABLE |
TRUNCATE on the table |
A unique-preserving mask (for example hash_columns or from_unique) covers a primary key or unique column, or a foreign key points at a masked column |
Drops and re-adds the PRIMARY KEY, UNIQUE and FOREIGN KEY constraints |
OWNERSHIP of the target table and of every table whose foreign key references it |
| A unique-preserving mask covers the table's key column | Adds a temporary surrogate key column and a sequence, then drops them | OWNERSHIP of the table and CREATE SEQUENCE on the target schema |
A masked column has a DEFAULT <sequence>.NEXTVAL |
Re-creates the sequence above the highest masked value and re-points every column default that uses it | OWNERSHIP of the sequence, CREATE SEQUENCE on its schema, and OWNERSHIP of every table that has a column default on that sequence |
| The target is a hybrid table with a non-unique index on a masked column | Drops and re-creates the index | OWNERSHIP of the table |
-- Only when needed; see the table above.
GRANT CREATE TABLE ON SCHEMA <target-database>.<target-schema> TO ROLE <masking-role>;
GRANT CREATE SEQUENCE ON SCHEMA <target-database>.<target-schema> TO ROLE <masking-role>;
GRANT TRUNCATE ON TABLE <target-database>.<target-schema>.<target-table> TO ROLE <masking-role>;
GRANT OWNERSHIP ON TABLE <target-database>.<target-schema>.<target-table> TO ROLE <masking-role> COPY CURRENT GRANTS;
GRANT OWNERSHIP ON SEQUENCE <target-database>.<target-schema>.<sequence> TO ROLE <masking-role> COPY CURRENT GRANTS;
Note: OWNERSHIP can be held by one role at a time.
Granting it to the masking role removes it from the previous owner.
Use COPY CURRENT GRANTS so other roles keep their existing privileges on the object.
When DataMasque re-creates a sequence, the new sequence is owned by the masking role.
Connection test
The connection test authenticates,
selects the database, schema (when set) and warehouse,
and runs a query.
It does not create any object.
A masking run creates a temporary stage and a temporary table in the schema of each target table;
the role needs USAGE on that schema and SELECT/UPDATE on the table.
Connection parameters
Snowflake specific parameters
For Snowflake databases, the following additional fields apply:
| Snowflake Account ID | Snowflake account identifier. Does not include the snowflakecomputing.com. This is in the format <organisation-name>-<account-name>, e.g. ABCDEFGH-XYZ12345. |
| Snowflake Warehouse | Default Snowflake warehouse to use. Optional when DataMasque is deployed as a Snowflake Native App: if left blank, DataMasque uses its own warehouse created when the application was installed. Specify a warehouse to override this, for example to use a larger warehouse for large masking runs. |
| Private Key | Name of an available Snowflake Private key. These can be uploaded via the Files page. |
| Private Key Passphrase | Passphrase for the private key (if needed). |
Snowflake Authentication
Snowflake has two types of authentication:
- Keypair (recommended)
- Password
To set up keypair authentication for Snowflake please refer to the documentation.
This is selected via radio buttons on the UI, shown in the screenshots below.
When the Password radio button is selected the Password field will be visible:

When Keypair is selected the Private Key dropdown box and Private Key Passphrase field will be visible:

The dropdown box will display all Private Key files available to be used. These can be uploaded via the Files page. If the selected key also requires a passphrase, enter it into the Private Key Passphrase field. The passphrase can be reset by clicking the trash icon or selecting a different Private Key. If the selected key does not require a passphrase, leave the field blank.
Snowflake Advanced Settings
For Snowflake databases, the following fields are optional:
| Schema | The name of the schema that DataMasque will operate on. If not specified, the database user's default schema will be used. |
| Role | Name of the default role to use. |
Encrypted connections
All DataMasque connections to Snowflake take place over HTTPS.
This includes the PUT that uploads masked data to the internal stage.
Limitations
Only standard and hybrid tables are supported, not dynamic or Iceberg.
run_sqltasks can only execute one SQL statement per task. Tasks containing more than one SQL statement may cause an error.mask_unique_keytasks are not supported.DataMasque does not allow masking of tables with Time Travel enabled. See Snowflake Time Travel Security below for details and mitigation steps.
Snowflake Time Travel Security
Snowflake's Time Travel feature allows querying historical data for up to 90 days (Enterprise) or 1 day (Standard). When Time Travel is enabled, original unmasked data remains accessible via historical queries even after masking, creating a significant security risk.
Users can recover sensitive data using queries like:
SELECT * FROM table_name AT (TIMESTAMP => '2024-01-01'::timestamp)
DataMasque enforces Time Travel checking by default
and will block masking of tables with Time Travel enabled.
This behaviour can be downgraded to a warning
with the allow_masking_tables_with_time_travel option.
How to Disable Time Travel
Set the DATA_RETENTION_TIME_IN_DAYS parameter to 0 for objects containing sensitive data:
For individual tables:
ALTER TABLE schema_name.table_name SET DATA_RETENTION_TIME_IN_DAYS = 0;
For entire schemas:
ALTER SCHEMA schema_name SET DATA_RETENTION_TIME_IN_DAYS = 0;
For entire databases:
ALTER DATABASE database_name SET DATA_RETENTION_TIME_IN_DAYS = 0;
Warning! The MIN_DATA_RETENTION_TIME_IN_DAYS account parameter can override attempts to disable Time Travel.
If set at the account level by an ACCOUNTADMIN, it enforces a minimum retention period even when DATA_RETENTION_TIME_IN_DAYS = 0.
Check account-level settings:
SHOW PARAMETERS LIKE 'MIN_DATA_RETENTION_TIME_IN_DAYS' IN ACCOUNT;
If this returns a value > 0, contact your ACCOUNTADMIN to set MIN_DATA_RETENTION_TIME_IN_DAYS = 0 at account level.
Verification
Before running DataMasque, verify Time Travel is disabled (expected result is 0):
SHOW TABLES LIKE 'your_table_name' ->> SELECT "data_retention_time_in_days" FROM $1;
For more information, refer to the Snowflake Time Travel documentation.
Supported data types
The following Snowflake datatypes are supported.
Note that only base datatypes are listed, not their aliases
(e.g., number is listed but not decimal, which is an alias for number).
integer is included despite being an alias because it has specific constraints on scale:
| Data Type | Description |
|---|---|
| number | Numbers up to 38 digits, with an optional precision and scale. |
| integer | Synonymous with NUMBER, except that precision and scale can’t be specified (that is, it always defaults to NUMBER(38, 0)). |
| float | Double-precision (64 bit) IEEE 754 floating-point numbers. |
| varchar(n) | Unicode UTF8 strings up to 16,777,216 bytes in length. Some characters may require multiple bytes to store. |
| binary | Binary data up to 8MB (8,388,608 bytes) in length. |
| boolean | TRUE or FALSE. |
| date | Date value with no time. |
| time | Time value with no date. |
| timestamp_ltz | TIMESTAMP with local time zone. |
| timestamp_ntz | TIMESTAMP with no time zone. |
| timestamp_tz | TIMESTAMP with time zone. |
Refer to the Snowflake data types documentation for information about the aliases for these data types.
Unsupported key columns
| Data Type | Unsupported Format |
|---|---|
| float | All |
| binary | All |
| time | Precision >= 7 |
| timestamp_ltz | Precision >= 7 |
| timestamp_ntz, datetime | Precision >= 7 |
| timestamp_tz | Precision >= 7 |
| variant | All |
| object | All |
| map | All |
| geography | All |
| geometry | All |
| vector | All |
Notes:
datetimeis an alias fortimestamp_ntz.- Likewise,
timestampis an alias for one of thetimestamp_tz,timestamp_ntz, ortimestamp_ltzformats, depending on session parameters.- The default precision (decimal places of seconds) for
timeand the varioustimestamp_*formats is 9 digits, but in order to support use as a key column, the precision must be at most 6 digits. Thus, to use atimeortimestamp_*column as a key column, be sure to explicitly specify a precision of at most 6 when declaring the column, for exampletimestamp_ntz(6).