Snowflake DCM Projects Just Went GA: Do You Still Need Terraform?
Quick answer: Snowflake DCM Projects went generally available on 7 August 2026. You declare target state in SQL DEFINE statements, Snowflake computes the diff, and you review a PLAN before you DEPLOY - no state file to lose. It takes over most of what schemachange did for plain DDL, but it does not replace Terraform: DCM only manages objects inside Snowflake, it cannot run imperative SQL such as backfills or renames, and the GA release note still lists eight sub-features as preview.
Last updated: August 2026
Snowflake DCM Projects reached general availability on 7 August 2026. Four days ago. Three things have been fighting over your Snowflake DDL for years: the Terraform provider, schemachange, and whatever bash-plus-SnowSQL contraption someone wrote in 2021 before leaving the company. DCM Projects is Snowflake's own answer - declarative, native, no extra licence, no state file to lose.
That framing is accurate and incomplete in equal measure. The GA release note itself lists eight sub-features that are still in preview, including the TEST and PREVIEW commands and the GitHub Actions integration. And definition files only permit DEFINE, GRANT and ATTACH statements, which rules out an entire class of migration work that schemachange handles without blinking.
Here is the three-way comparison a platform lead needs before committing a quarter of engineering time to a migration.
What DCM Projects actually do
A DCM project is a first-class Snowflake object living in a schema, like a table or a task. You point it at a directory of SQL definition files describing the state you want, and Snowflake works out the difference between that and the account.
The mental model that matters: the account is the state. Terraform keeps a .tfstate file that must stay in sync with reality, and every platform team has a war story about the day it did not. schemachange keeps a CHANGE_HISTORY table recording which scripts ran, not what the objects look like now. DCM reads the live account on every PLAN. There is no second copy of the truth to reconcile.
-- sources/definitions/platform.sql
DEFINE DATABASE analytics{{ env_suffix }}
COMMENT = 'Target state owned by the analytics_platform DCM project';
DEFINE SCHEMA analytics{{ env_suffix }}.raw;
DEFINE SCHEMA analytics{{ env_suffix }}.marts;
DEFINE WAREHOUSE transform_wh{{ env_suffix }}
WAREHOUSE_SIZE = '{{ wh_size }}'
AUTO_SUSPEND = 60
AUTO_RESUME = TRUE;
DEFINE ROLE analyst{{ env_suffix }};
DEFINE TABLE analytics{{ env_suffix }}.raw.orders (
order_id NUMBER,
customer_id NUMBER,
order_ts TIMESTAMP_NTZ,
total_amount NUMBER(12,2),
status VARCHAR
) CHANGE_TRACKING = TRUE;
DEFINE DYNAMIC TABLE analytics{{ env_suffix }}.marts.order_summary
WAREHOUSE = transform_wh{{ env_suffix }}
TARGET_LAG = '15 minutes'
AS
SELECT
order_id,
customer_id,
order_ts::DATE AS order_date,
total_amount
FROM analytics{{ env_suffix }}.raw.orders
WHERE status <> 'cancelled';
GRANT USAGE ON DATABASE analytics{{ env_suffix }}
TO ROLE analyst{{ env_suffix }};
GRANT SELECT ON DYNAMIC TABLE analytics{{ env_suffix }}.marts.order_summary
TO ROLE analyst{{ env_suffix }};
Note the constraint that shapes everything downstream: definition files accept DEFINE, GRANT and ATTACH and nothing else. Names must be fully qualified. Deleting a DEFINE tells Snowflake to drop the object.
Snowflake's own summary of the interfaces: you can work with DCM Projects using Snowsight Workspaces, the Snowflake CLI (snow dcm) in your local IDE, natural-language prompts with Cortex Code, SQL commands, or an automated CI/CD pipeline. Definition files live locally or in a Snowflake Workspace, and the SQL form reads them from a stage path.
The CREATE OR ALTER foundation underneath it
DCM Projects did not appear from nowhere. It rests on the same idea as CREATE OR ALTER, which reached GA on 14 July 2026, three and a half weeks earlier. DEFINE applies the same create-it-or-reshape-it semantics and repeats most of the same limitations. What it does not do is mirror the object list one for one, and that catches people out: DCM can define alerts and sequences that CREATE OR ALTER has no form for, while CREATE OR ALTER handles network policies, semantic views and application roles that DCM's supported-entity list does not (application roles are called out there as unsupported). Check both lists, not one. Either way, idempotent DDL used to be something you scripted yourself; now it is first-class SQL.
Snowflake's release note headlines the milestone as 24 object types. The SQL reference splits them like this.
| Scope | Generally available | Still preview |
|---|---|---|
| Account objects | AUTHENTICATION POLICY, DATABASE, NETWORK POLICY, ROLE, SHARE, WAREHOUSE | - |
| Database objects | APPLICATION ROLE, DATABASE ROLE, DATA METRIC FUNCTION, DYNAMIC TABLE, FILE FORMAT, FUNCTION, MASKING POLICY, NETWORK RULE, PROCEDURE, ROW ACCESS POLICY, SCHEMA, SEMANTIC VIEW, STAGE, TABLE, TAG, TASK, VIEW | EXTERNAL FUNCTION, FUNCTION (Snowpark Container Services), PIPE, STREAM, VERSIONED SCHEMA |
The same limitations reappear on the DCM side, almost word for word. CREATE OR ALTER cannot rename an object or a column and cannot change column order. CREATE OR ALTER TABLE ... AS SELECT is unsupported, as is adding search optimization. Incompatible data type changes are rejected. Tasks must be suspended before alteration. Read that with a migration in mind: a column rename is not expressible declaratively, so the moment your model changes shape you are outside the project doing manual DDL - precisely the job schemachange was built for.
What a DCM project looks like on disk
The layout is fixed: manifest.yml at the root, definition files under sources/definitions/, optional macros under sources/macros/, rendered artefacts in out/. Naming inside those directories is yours, and the CLI only uploads what is under sources.
# manifest.yml - sits at the project root, next to sources/
manifest_version: 2
type: DCM_PROJECT
default_target: DEV
targets:
DEV:
account_identifier: MYORG-MY_DEV_ACCOUNT
project_name: OPS.PROJECTS.ANALYTICS_PLATFORM_DEV
project_owner: DCM_DEVELOPER
templating_config: DEV
PROD:
account_identifier: MYORG-MY_PROD_ACCOUNT
project_name: OPS.PROJECTS.ANALYTICS_PLATFORM
project_owner: DCM_DEPLOYER
templating_config: PROD
templating:
defaults:
env_suffix: "_DEV"
wh_size: "X-SMALL"
configurations:
DEV:
env_suffix: "_DEV"
wh_size: "X-SMALL"
PROD:
env_suffix: ""
wh_size: "MEDIUM"
The templating surface is Jinja2, deliberately narrowed. Supported: string replacements, lists, dictionaries and nested dictionaries, conditionals, loops, and both global macros in sources/macros/ and in-file macros. Not supported: the import, extends and include tags. Global macros are already visible across the project, so import is largely beside the point.
- Variable precedence is three-tier. Global defaults lose to configuration variables, which lose to the runtime variables you pass at execution time.
- Dictionaries are the fan-out tool. Loop over a nested dictionary to generate per-team databases, schemas and warehouses from one definition file instead of copy-pasting blocks.
- Targets carry the owner role.
project_ownernames the role that owns the project object in that account, andproject_namemust be fully qualified. Mismatched roles between environments are a common first-deploy failure.
The plan-then-deploy loop
If you have used Terraform this feels familiar within ninety seconds. If you have used schemachange it feels like a different job, because you no longer think in migrations.
-- 1. The project object itself lives in a schema, like any other object CREATE DCM PROJECT ops.projects.analytics_platform COMMENT = 'Analytics platform target state'; -- 2. Dry run. Returns the CREATE / ALTER / DROP set Snowflake intends to apply. EXECUTE DCM PROJECT ops.projects.analytics_platform PLAN USING CONFIGURATION PROD FROM '@ops.projects.dcm_stage/analytics_platform'; -- 3. Apply it, and name the deployment so the history is readable EXECUTE DCM PROJECT ops.projects.analytics_platform DEPLOY AS "release-2026-08-11" USING CONFIGURATION PROD FROM '@ops.projects.dcm_stage/analytics_platform'; -- 4. Inner dev loop only. Evaluates just the definitions you changed plus -- anything downstream of them, so it is fast and partially blind. EXECUTE DCM PROJECT ops.projects.analytics_platform PLAN DELTA USING CONFIGURATION DEV FROM '@ops.projects.dcm_stage/analytics_platform';
Every deployment attempt gets a number - DEPLOYMENT$1 onward - and you can attach an alias, which behaves like a commit message for infrastructure. Use it. Numbered deployments with no labels are useless during an incident. That history is queryable: the DCM_DEPLOYMENT_HISTORY Information Schema table function returns successful and failed deployments for a project, going back twelve months.
# Create the project object in the target account (one time) snow dcm create --target PROD # Full plan - use this as the PR gate. Reads manifest.yml from the cwd. snow dcm plan --target PROD # Point at a project directory elsewhere on disk snow dcm plan --target PROD --from /path/to/analytics_platform # Override a template variable at runtime snow dcm plan --target DEV --variable "wh_size='SMALL'" # Deploy, with an alias that shows up in the deployment history snow dcm deploy --target PROD --alias "release-2026-08-11" # Audit trail snow dcm list-deployments --target PROD
PLAN DELTA, shown above, evaluates only the definitions you changed plus the definitions downstream of them. Much faster, and a trap in CI for reasons we will come to. The other command worth knowing about early is PURGE, which removes every entity the project manages along with its grants and attachments. Snowflake's wording is blunt: purge is destructive by design, for non-production projects such as development sandboxes or demos. That is not decoration.
Snowflake DCM Projects vs Terraform vs schemachange
These three are not competing for the same job as cleanly as the marketing suggests.
| Dimension | DCM Projects | Terraform provider | schemachange |
|---|---|---|---|
| Model | Declarative target state in SQL | Declarative target state in HCL | Imperative migration scripts |
| Where state lives | The account itself - no state file | A state file you host and lock | A CHANGE_HISTORY table of scripts run |
| Support | Snowflake product, GA 7 Aug 2026 | Official support from v2.0.0, stable resources only | Community tool: "no support or warranty" |
| Scope | Snowflake objects only | Snowflake plus IAM, storage, DNS, networking | Any SQL you can write |
| Renames and backfills | Not expressible - DEFINE/GRANT/ATTACH only | Awkward; often needs manual import | Native. Just write the SQL. |
| Drift detection | Full PLAN reads the live account | Bounded by state accuracy | None - tracks scripts, not objects |
| Learning curve for a SQL team | Low | Medium to high | Very low |
| Cost | No separate licence; operations bill as cloud services compute | Runners and remote state | Runners |
The row that settles most arguments is scope. Everything else is preference; that one is physics.
Where Terraform still wins
DCM Projects manages objects inside Snowflake. That is the whole boundary.
- The cloud side of every integration. An external stage needs a storage integration in Snowflake plus a trust policy and bucket policy on AWS or Azure. DCM handles neither half - storage integrations are not in its supported object list. Terraform does both, in one plan, in the right order.
- Account-perimeter objects. Users, security and notification integrations, API integrations and resource monitors are absent from the DCM supported-entity list. If compliance requires those in version control, Terraform stays.
- Private connectivity and networking. PrivateLink endpoints, VPCs, route tables, DNS. None of it exists inside Snowflake's object model.
- CI maturity. Policy-as-code with OPA or Sentinel, and plan output every platform engineer can already read. DCM's GitHub Actions integration was still preview on GA day.
- Multi-tool estates. If Snowflake is one of six platforms you run, a second IaC toolchain is a real operational cost.
One caveat before a big-bang cutover: the provider's support policy is narrower than most teams assume. Snowflake's own wording is that official support starts with v2.0.0 for stable resources only, and that all previous versions and preview resources are not officially supported. Preview resources are disabled by default and can break without a major version bump. If your estate is pinned to a 0.x provider you are already outside that boundary, and that is a migration you owe yourself either way.
Where schemachange is still the right answer
The instinct is to write schemachange off, because declarative beats imperative and native beats community. Resist it for one reason: schemachange runs arbitrary SQL, and DCM definition files accept only DEFINE, GRANT and ATTACH. So the whole category that is neither pure DDL nor application logic - backfills, data corrections, column renames done as copy-and-swap, reprocessing after a bad load - has no home in a DCM project.
- Small, script-first estates. A few dozen objects, one or two environments, a team that thinks in SQL files. The V/R/A prefix convention plus a
CHANGE_HISTORYtable is enough. - Ordered, irreversible migrations. When step three must run after step two because of what step two did to the data, you want migrations, not a diff engine.
- Objects nothing declarative covers yet. Anything outside the DCM supported list needs a script.
- The honest caveat. schemachange is explicitly "a community-developed tool, not an official Snowflake offering" that "comes with no support or warranty." If that fails your procurement review, it failed last month too.
The pattern we now recommend to clients: Terraform for the account perimeter, DCM Projects for in-database target state, a small migration runner for the imperative residue. Three tools sounds like a lot until you notice you already had three, and one was a bash script.
What was still in preview on GA day
This section decides whether a full replacement is on the table, and it will not appear in any launch blog. The 7 August GA release note names these as remaining in preview.
| Still preview at GA | What it blocks |
|---|---|
| TEST and PREVIEW commands | Declarative data-quality expectations. Your test gate stays external. |
| GitHub Actions for DCM Projects | The turnkey CI path. You can drive snow dcm from any runner, but you wire it yourself. |
| DEFINE PIPE | Snowpipe ingestion cannot be declared beside the tables it loads. |
| DEFINE STREAM | Stream-and-task CDC pipelines are only half-manageable. |
| DEFINE MASKING POLICY | Column masking stays in whatever manages it today. |
| DEFINE ROW ACCESS POLICY | Row-level security stays outside the project. |
| ATTACH TAG | Tags can be defined; attaching them declaratively is preview. |
| Inherited grants, container-level MANAGE GRANTS | Cascading grants and delegated grant admin without account-level SECURITYADMIN. |
Add the explicit gaps from the supported-entities docs: application roles and CALLER grants unsupported, virtual columns not yet supported on DEFINE TABLE, masking and row access policy attachment not yet supported, search optimization not addable, tag propagation unsupported.
Put plainly: if governance leans on masking policies, row access policies and tag-driven classification - financial services, healthcare, anything regulated - that half of your estate cannot move to DCM on GA terms yet. Stream and pipe pipelines are in the same position. Scope phase one to platform infrastructure and modelled tables.
The gotchas that will actually bite you
1. Deleting a definition drops the object. Comment out a DEFINE TABLE during a refactor, forget to uncomment it, and the next DEPLOY drops the table. Objects that exist but are no longer defined get dropped, by design. Time Travel saves the data; it does not save the afternoon. Make reviewing the drop list a named step in your PR template, not something a reviewer is trusted to spot in a wall of output.
2. PLAN DELTA is the wrong CI gate. It skips unchanged definitions, so it does not detect changes made outside DCM since the last deployment. Someone hand-alters a warehouse in Snowsight during an incident, delta plan reports no changes, and your deploy silently reverts their fix. Delta belongs in the inner dev loop. Run a full PLAN before anything touches production.
3. Ownership follows the deploying role. By default the role that deploys a project holds OWNERSHIP of every object it creates, so a personal role in dev and a service role in prod leaves you with two different ownership graphs - which surfaces months later as a grant that works in dev and fails in prod. Pin project_owner per target from day one. Note also what DCM does and does not reconcile on grants: remove a GRANT from the definitions and the next deploy revokes it, exactly as removing a DEFINE drops the object, but privileges granted outside the project are not in scope. DCM is a target-state tool for the grants you declare, not a privilege cleanup tool for the ones you did not.
4. The cost trap is cloud services, not warehouses. Snowflake's documentation puts it plainly: PLAN and DEPLOY are primarily metadata operations and incur cloud services compute cost, similar to running DDL scripts with ALTER or CREATE statements. That sounds free, and usually is, because cloud services are only charged once daily consumption exceeds 10% of that day's warehouse usage. Now picture a dev or automation account with almost no warehouse activity, and CI running a full PLAN on every pull request against a project where Snowflake warns that 1,000+ entities can push PLAN or DEPLOY past ten minutes. High cloud services, near-zero warehouse credits to net it against. Worth measuring on your own account before you assume it rounds to nothing - we have not benchmarked it, and neither has anyone else four days after GA.
-- Projects the account knows about
SHOW DCM PROJECTS IN SCHEMA ops.projects;
-- What a role can actually reach after a deploy. Run this in CI, because
-- privileges granted outside the project are not in the project's scope -
-- DCM will not clean up grants it never managed.
SHOW GRANTS TO ROLE analyst;
-- Cloud services is the line to watch once CI runs PLAN on every PR
SELECT usage_date,
credits_used_compute,
credits_used_cloud_services,
credits_adjustment_cloud_services
FROM SNOWFLAKE.ACCOUNT_USAGE.METERING_DAILY_HISTORY
WHERE service_type = 'WAREHOUSE_METERING'
AND usage_date >= DATEADD('day', -30, CURRENT_DATE())
ORDER BY usage_date DESC;
How to actually decide
Three questions about your own estate settle it.
- Cloud resources in code today? Then Terraform stays. The only question is whether it keeps in-database objects too, and the answer is usually no.
- Governance built on masking or row access policies? Then DCM cannot own governance on GA terms yet.
- How much change is genuinely imperative? More than a handful of backfills or renames in your last fifty merges means you still need a migration runner.
For a greenfield Snowflake platform in August 2026, DCM Projects is the right default for in-database objects. For an established estate with working Terraform, the migration that pays for itself is narrow - move tables, views, dynamic tables, tasks, warehouses and roles into DCM, leave integrations, users and cloud resources in Terraform, and stop pretending one tool covers both sides of that boundary.
And retire the bash script. Whatever else you decide, retire the bash script.
Related Articles
Frequently Asked Questions
Q: Should I replace Terraform with Snowflake DCM Projects?
Not entirely. DCM Projects only manages objects inside Snowflake, so anything cloud-side - IAM roles, S3 buckets, PrivateLink, DNS - still needs Terraform, and account-perimeter objects like users, resource monitors and integrations are absent from the DCM supported list. The practical split: Terraform for the perimeter, DCM Projects for in-database target state.
Q: Is Snowflake DCM Projects free?
There is no separate licence or add-on. Snowflake's documentation states that DCM Projects is available for all Snowflake editions and that there are no DCM Projects-specific costs beyond the compute the operations incur. PLAN and DEPLOY are described as primarily metadata operations that incur cloud services compute cost, similar to running DDL scripts. Watch that line on automation accounts with little warehouse activity, since cloud services are only free up to 10% of the day's warehouse usage.
Q: What happens if I delete a DEFINE statement from a DCM project?
Snowflake drops the object on the next deploy. Objects that exist in the account but are no longer defined are treated as intentional deletions. This is why plan-then-deploy discipline matters: run a full PLAN, read the drop list explicitly, and never let a delete reach production without a named human approving it.
Q: Which object types does CREATE OR ALTER support in Snowflake?
Snowflake's 14 July 2026 release note headlines the GA as 24 object types. The SQL reference splits them as follows. Account level: authentication policy, database, network policy, role, share and warehouse. Database level: application role, database role, data metric function, dynamic table, file format, function, masking policy, network rule, procedure, row access policy, schema, semantic view, stage, table, tag, task and view. External function, Snowpark Container Services functions, pipe, stream and versioned schema are listed as preview. Check the reference page before relying on any one entry, since items move from preview to GA between releases.
Q: Can DCM Projects run data migrations or backfills?
No. Definition files accept only DEFINE, GRANT and ATTACH, so there is no place for INSERT, MERGE or UPDATE. Column renames are also out of reach, since CREATE OR ALTER cannot rename objects or columns. Keep a small migration runner such as schemachange for imperative work.
Q: Does DCM Projects work with GitHub Actions?
The dedicated GitHub Actions integration was still listed as preview in the 7 August 2026 GA release note. You can run DCM from any CI system today with the Snowflake CLI - snow dcm plan as the pull request gate, snow dcm deploy --alias on merge - but you wire the pipeline yourself rather than dropping in a supported action.
