Atlas now manages multi-cluster and query acceleration settings on warehouses, file and refresh settings on external tables, storage and versioning settings on Iceberg tables, a warehouse on dynamic tables, credentials on stages, and volatility on functions.
A set of Snowflake object properties that inspection previously skipped is now part of the schema: they are read from the account, compared, and planned with the statements Snowflake accepts for each one.
Warehouses
A warehouse now takes its multi-cluster layout, its query acceleration settings, and the session parameters it runs statements under:
warehouse "WH" { size = XSMALL min_cluster_count = 1 max_cluster_count = 3 scaling_policy = ECONOMY enable_query_acceleration = true query_acceleration_max_scale_factor = 4 max_concurrency_level = 8 statement_timeout_in_seconds = 3600 statement_queued_timeout_in_seconds = 600}
Every one of them is optional, and a desired state that names none of them adopts the values the warehouse already has, so adding a warehouse to your schema never plans a change you did not ask for. scaling_policy is STANDARD or ECONOMY.
Iceberg, External, and Dynamic Tables
An Iceberg table takes storage_serialization_policy (COMPATIBLE or OPTIMIZED), path_layout (FLAT or HIERARCHICAL), iceberg_version, max_data_extension_time, and change_tracking. An external table takes the settings that decide which files it reads and when it picks up new ones, a dynamic table takes the warehouse that refreshes it, and both dynamic and hybrid tables take max_data_extension_time:
iceberg_table "FACT_CLAIM" { schema = schema.CLAIMS external_volume = "CLAIMS_VOL" catalog = "SNOWFLAKE" base_location = "claims/fact_claim" partition_by = ["MONTH(SERVICE_DATE)"] storage_serialization_policy = OPTIMIZED iceberg_version = 2 max_data_extension_time = 14 column "CLAIM_SK" { null = false type = NUMBER(38) }} external_table "EXT_CLAIM_FILES" { schema = schema.CLAIMS partition_by = ["INGEST_DATE"] pattern = ".*837.*[.]csv" auto_refresh = false refresh_on_create = false column "INGEST_DATE" { null = false type = DATE as = "TO_DATE(SPLIT_PART(METADATA$FILENAME, '/', 3), 'YYYY-MM-DD')" } location { stage = stage.CLAIM_STAGE path = "837/" }} dynamic_table "DT_CLAIM_MONTHLY" { schema = schema.CLAIMS target_lag = "60 minutes" refresh_mode = FULL warehouse = "CLAIMS_WH" max_data_extension_time = 9 as = "SELECT PLAN_ID FROM CLAIM_STG"}
Snowflake keeps change tracking on for an Iceberg table and rejects the statement that would turn it off, so change_tracking = true is accepted and recorded as nothing, which keeps it from replanning on every diff, and change_tracking = false is rejected with an error rather than planned into a statement that would fail.
Row access, aggregation, and join policies can now be attached to external and Iceberg tables as well, and masking and projection policies to the columns of an Iceberg table. See Snowflake policies for the attachment syntax.
Stages and Functions
An external stage takes a credentials map, and a function takes immutable, which declares the volatility Snowflake defaults to VOLATILE for:
stage "CLAIM_STAGE" { schema = schema.CLAIMS url = "s3://bucket/edi/" credentials = { AWS_KEY_ID = "AKIA" AWS_SECRET_KEY = "shhh" }} function "FN_ALLOWED_AMOUNT" { schema = schema.PUBLIC lang = SQL arg "BILLED" { type = FLOAT } return = FLOAT as = "BILLED" immutable = true}
- Credentials name a cloud location, so Snowflake takes them only together with a url and never together with a storage_integration. Both rules are checked when the file is evaluated.
- Snowflake never reports credentials back, so they are applied when the stage is created and never written into an inspected document.
- immutable = false is the default and records nothing, so it compares equal to an inspected function that reports VOLATILE.
Diffing Credentials on an Existing Stage
Because the inspected side never holds credentials, the two can only be compared by taking the declared ones at face value, which is what allowing sensitive values asks for. On an existing stage the change is therefore skipped unless the env opts in with sensitive = ALLOW:
env "snow" { schema { mode "snowflake" { sensitive = ALLOW } }}
This covers the declarative flow only. migrate diff clears the flag even when the active env sets it, so a credential is never written to a migration file. Rotate one with schema apply, or keep the value out of the schema and set it on the stage directly.
Getting Started
Snowflake support is part of Atlas Pro. Run atlas login to get started, and see the HCL reference for the full set of attributes on each block.