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.


SQL - a DCM definition file (sources/definitions/platform.sql)
-- 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.


ScopeGenerally availableStill preview
Account objectsAUTHENTICATION POLICY, DATABASE, NETWORK POLICY, ROLE, SHARE, WAREHOUSE-
Database objectsAPPLICATION 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, VIEWEXTERNAL 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.


YAML - manifest.yml with dev and prod targets
# 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.



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.


SQL - create, plan, deploy, delta plan
-- 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.


Bash - the same loop from the Snowflake CLI
# 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.


DimensionDCM ProjectsTerraform providerschemachange
ModelDeclarative target state in SQLDeclarative target state in HCLImperative migration scripts
Where state livesThe account itself - no state fileA state file you host and lockA CHANGE_HISTORY table of scripts run
SupportSnowflake product, GA 7 Aug 2026Official support from v2.0.0, stable resources onlyCommunity tool: "no support or warranty"
ScopeSnowflake objects onlySnowflake plus IAM, storage, DNS, networkingAny SQL you can write
Renames and backfillsNot expressible - DEFINE/GRANT/ATTACH onlyAwkward; often needs manual importNative. Just write the SQL.
Drift detectionFull PLAN reads the live accountBounded by state accuracyNone - tracks scripts, not objects
Learning curve for a SQL teamLowMedium to highVery low
CostNo separate licence; operations bill as cloud services computeRunners and remote stateRunners

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.



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.



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 GAWhat it blocks
TEST and PREVIEW commandsDeclarative data-quality expectations. Your test gate stays external.
GitHub Actions for DCM ProjectsThe turnkey CI path. You can drive snow dcm from any runner, but you wire it yourself.
DEFINE PIPESnowpipe ingestion cannot be declared beside the tables it loads.
DEFINE STREAMStream-and-task CDC pipelines are only half-manageable.
DEFINE MASKING POLICYColumn masking stays in whatever manages it today.
DEFINE ROW ACCESS POLICYRow-level security stays outside the project.
ATTACH TAGTags can be defined; attaching them declaratively is preview.
Inherited grants, container-level MANAGE GRANTSCascading 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.


SQL - post-deploy checks worth automating
-- 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.



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.


Pranay Vatsal, Founder & CEO

Pranay Vatsal is the Founder & CEO of CelestInfo with deep expertise in Snowflake, data architecture, and building production-grade data systems for global enterprises.

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.