Tag: dcm

  • Snowflake DCM Projects: Infrastructure as Code, Native

    Snowflake DCM Projects: Infrastructure as Code, Native

    Until August 2026, managing Snowflake infrastructure as code meant one of two paths. You either adopted Terraform with the Snowflake provider — an external tool with its own state file, version lag behind new Snowflake features, and a separate CI/CD pipeline to learn and maintain. Or you ran Schemachange or a migration-script pattern — imperative SQL files executed in order, no dry-run, no idempotency, no rollback if something failed halfway through.

    Both paths work. Both also mean that your Snowflake infrastructure is being managed by something that lives outside Snowflake and has an imperfect model of what’s actually in your account.

    Snowflake DCM Projects — Database Change Management, GA on August 7 2026 — is the native alternative. You write DEFINE statements in SQL files describing your desired state. You run PLAN to see exactly what will change. You run DEPLOY and Snowflake reconciles the diff. No external tool. No state file to lose. No provider lag. And from July 2026, you can have Cortex Code author and debug your DEFINE files with a natural-language prompt.

    This is the complete practitioner guide: how DCM Projects works, when to reach for it over Terraform, the full CI/CD pipeline with GitHub Actions, and every production gotcha documented in the official docs.

    TL;DR

    • DCM Projects is a native Snowflake object (schema-level) that manages other Snowflake objects declaratively. You write DEFINE statements in SQL files, run PLAN to preview changes, and DEPLOY to apply them. All state is tracked inside Snowflake — no external state file.
    • The workflow is Terraform-like but Snowflake-native: one DCM project per target environment (DEV, STAGING, PROD), all pointing at the same parameterized definition files, deployed independently via Jinja template variables.
    • Jinja2 templating is first-class: dictionaries, loops, conditionals, and macros work in definition files. Use loops to provision the same database + role + warehouse stack for multiple teams in one DEFINE file.
    • PLAN DELTA (preview, July 2026) evaluates only changed definitions and their dependents — dramatically faster feedback during active development on large projects.
    • Inherited grants (preview, July 2026) let you write one GRANT ... INHERITED statement that automatically applies to every current and future object of a specified type within a container. No more grant drift on new tables.
    • Snowflake provides reusable GitHub Actions (dcm/plan, dcm/deploy) and sample workflows for full PR-based CI/CD. OIDC authentication is recommended — no stored secrets.
    • DCM Projects supports up to 10,000 entities per project and a maximum of 10 MB total definition file size. Projects exceeding these limits may timeout during PLAN or DEPLOY.
    • DCM Projects are available on all Snowflake editions at no additional cost beyond the cloud services compute consumed by PLAN and DEPLOY operations.

    The Plan-Then-Deploy Lifecycle

    The mental model maps directly to Terraform if you’ve used it. You write desired state. You preview the diff. You apply. The difference is that the state Snowflake compares against is the live account — not a separate state file that can diverge from reality.

    📷 three zones, one workflow — source files parameterised with jinja, commands run against the live account, environments deployed independently

    The key difference from Terraform’s state model: when you run PLAN, Snowflake queries your live account to compare current state against your definitions. If someone manually altered a table outside of DCM, PLAN will show that as a change. The source of truth is always the account, not a file on disk. This eliminates the class of bugs that come from state file drift — but it also means that manual changes outside DCM are always detected and potentially overwritten on the next DEPLOY.

    Writing Your First DEFINE File

    DEFINE statements are the core primitive. They look like SQL DDL but describe desired state rather than imperative commands. Snowflake figures out whether to CREATE, ALTER, or do nothing based on the diff between your DEFINE and the live object.

    -- definitions.sql
    -- Describes the desired state of a complete analytics stack for one team
    
    create DATABASE {{ team_name }}_DB
      COMMENT = 'Analytics database for {{ team_name }} team';
    
    create SCHEMA {{ team_name }}_DB.RAW
      COMMENT = 'Raw ingestion layer';
    
    create SCHEMA {{ team_name }}_DB.ANALYTICS
      COMMENT = 'Curated analytics layer — analyst-facing';
    
    create WAREHOUSE {{ team_name }}_WH
      WITH
        WAREHOUSE_SIZE = '{{ wh_size | default("SMALL") }}'
        AUTO_SUSPEND   = 300
        AUTO_RESUME    = TRUE
      COMMENT = 'Compute for {{ team_name }} team';
    
    use ROLE {{ team_name }}_ADMIN;
    use ROLE {{ team_name }}_ANALYST;
    
    -- Grants declared alongside objects — reconciled on every DEPLOY
    GRANT OWNERSHIP ON DATABASE {{ team_name }}_DB    TO ROLE {{ team_name }}_ADMIN;
    GRANT OWNERSHIP ON WAREHOUSE {{ team_name }}_WH   TO ROLE {{ team_name }}_ADMIN;
    GRANT USAGE     ON WAREHOUSE {{ team_name }}_WH   TO ROLE {{ team_name }}_ANALYST;
    GRANT USAGE     ON DATABASE  {{ team_name }}_DB    TO ROLE {{ team_name }}_ANALYST;
    GRANT USAGE     ON SCHEMA    {{ team_name }}_DB.ANALYTICS TO ROLE {{ team_name }}_ANALYST;
    GRANT SELECT    ON ALL TABLES IN SCHEMA {{ team_name }}_DB.ANALYTICS
                                              TO ROLE {{ team_name }}_ANALYST;
    GRANT ROLE {{ team_name }}_ADMIN    TO ROLE SYSADMIN;
    

    This file is parameterised with {{ team_name }} and {{ wh_size }}. To provision the Finance team with a LARGE warehouse, you pass those variables at plan or deploy time. To provision five teams in one shot, wrap the whole file in a Jinja {% for team_name in teams %} loop and pass a list.

    The manifest.yml — environment targets and templating config

    # manifest.yml
    # Declares target environments and their templating configurations
    
    version: 1
    
    targets:
      DEV:
        account_identifier: myorg-myaccount-dev
        project_name: ANALYTICS.PROJECTS.ANALYTICS_DCM_DEV
        project_owner: DCM_DEVELOPER_ROLE
        templating_config: dev_config
    
      STAGING:
        account_identifier: myorg-myaccount-staging
        project_name: ANALYTICS.PROJECTS.ANALYTICS_DCM_STAGING
        project_owner: DCM_STAGING_ROLE
        templating_config: staging_config
    
      PROD:
        account_identifier: myorg-myaccount-prod
        project_name: ANALYTICS.PROJECTS.ANALYTICS_DCM_PROD
        project_owner: DCM_PROD_ROLE   # service account only — not individual devs
        templating_config: prod_config
    
    default_target: DEV
    
    configurations:
      dev_config:
        variables:
          teams: ['Engineering']
          wh_size: 'XSMALL'
          suffix: 'DEV'
    
      staging_config:
        variables:
          teams: ['Engineering', 'Analytics']
          wh_size: 'SMALL'
          suffix: 'STAGING'
    
      prod_config:
        variables:
          teams: ['Engineering', 'Analytics', 'Finance', 'HR']
          wh_size: 'MEDIUM'
          suffix: ''
    

    The manifest is the single source of truth for your environment topology. Each CI/CD workflow reads account_identifier, project_name, and project_owner directly from the manifest — environment-specific config lives here, not scattered across GitHub secrets.

    Running PLAN and DEPLOY

    The full command set is available via SQL, Snowflake CLI, Snowsight UI, or Cortex Code. The CLI is the best choice for local development and CI/CD pipelines:

    # Navigate to your project directory
    cd ./analytics-dcm-project/
    
    # Run PLAN against the default target (DEV from manifest)
    snow dcm plan
    
    # Run PLAN against PROD to see what a production deployment would change
    snow dcm plan --target PROD --save-output
    
    # Run PLAN DELTA — only evaluate definitions changed since last deploy
    # Use during active development for fast feedback; always run full PLAN before deploying
    snow dcm plan --delta
    
    # Deploy to DEV
    snow dcm deploy
    
    # Deploy to PROD with a deployment alias (like a commit message for the deployment)
    snow dcm deploy --target PROD --alias "Add HR team infrastructure - ticket PLAT-421"
    
    # Preview what PURGE would do (dry run)
    snow dcm plan --target DEV  # should show all objects as DROPs if you purge after
    
    # Purge a sandbox DCM project — drops all managed objects
    EXECUTE DCM PROJECT MY_SCHEMA.PROJECTS.ANALYTICS_DCM_DEV PURGE AS "sandbox_cleanup";
    DROP DCM PROJECT MY_SCHEMA.PROJECTS.ANALYTICS_DCM_DEV;
    

    Always run full PLAN before DEPLOY. PLAN DELTA is fast but only evaluates changed definitions — it won’t catch external changes (a table manually dropped or altered outside DCM since the last deployment). PLAN DELTA is for development feedback; full PLAN is the pre-deploy gate.

    DCM Projects vs Terraform vs Schemachange

    📷 dcm projects is not a replacement for every terraform use case — but for snowflake-only infrastructure it removes an entire layer of external tooling

    DimensionTerraform + Snowflake ProviderSchemachangeSnowflake DCM Projects
    State managementExternal state file (S3/GCS/local)Migration history tableInside Snowflake — always live
    Dry-run / previewterraform planNone — scripts run directlyPLAN / PLAN DELTA
    IdempotencyYesDepends on migration designYes — DEPLOY skips matching objects
    Multi-env supportWorkspaces / varsManual per-env scriptsmanifest.yml targets + Jinja vars
    New Snowflake feature supportProvider update lag (weeks)Immediate (plain SQL)Day-0 (DEFINE added same GA)
    Non-Snowflake infraYes — multi-cloud providersNoSnowflake objects only
    AI-assisted authoringNone nativeNoneCortex Code DCM skill
    CostTool + provider maintenanceMinimalCloud Services compute only

    The honest comparison: if your team already runs Terraform for multi-cloud infrastructure — S3 buckets, IAM roles, Kafka clusters, and Snowflake objects in one plan — stick with Terraform. DCM Projects doesn’t replace it for cross-cloud IaC. It replaces it for Snowflake-only infrastructure, where the external tool overhead was never really worth it.

    CI/CD with GitHub Actions

    Snowflake publishes reusable GitHub Actions for DCM Projects in the snowflakedb/snowflake-actions repository. Four actions cover the full lifecycle: dcm/parse-manifest, dcm/connection-test, dcm/plan, and dcm/deploy.

    📷 every pr triggers a full production plan — reviewers see create/alter/drop before approving, not after

    # .github/workflows/dcm_pr_to_main.yml
    # Runs PLAN against PROD on every PR — gives reviewers a full diff before merge
    
    name: DCM Plan on PR
    
    on:
      pull_request:
        branches: [main]
        paths:
          - 'dcm/**'
    
    jobs:
      plan:
        runs-on: ubuntu-latest
        environment: PROD
        permissions:
          contents: read
          id-token: write       # Required for OIDC — no stored secrets
          pull-requests: write  # Post plan summary as PR comment
        steps:
          - uses: actions/checkout@v4
    
          - uses: snowflakedb/snowflake-actions/dcm/plan@v3
            id: plan
            with:
              target: PROD
              project-path: dcm/analytics/
              snowflake-user: ${{ env.SNOWFLAKE_USER }}
              # OIDC: no SNOWFLAKE_PASSWORD secret needed
    
          - name: Post plan summary to PR
            uses: actions/github-script@v7
            with:
              script: |
                const summary = `${{ steps.plan.outputs.summary }}`;
                github.rest.issues.createComment({
                  ...context.repo,
                  issue_number: context.payload.pull_request.number,
                  body: summary
                });
    # .github/workflows/dcm_deploy_prod.yml
    # Deploys to STAGING then PROD on merge to main
    
    name: DCM Deploy
    
    on:
      push:
        branches: [main]
        paths: ['dcm/**']
    
    jobs:
      deploy-staging:
        runs-on: ubuntu-latest
        environment: STAGING
        permissions:
          contents: read
          id-token: write
        steps:
          - uses: actions/checkout@v4
          - uses: snowflakedb/snowflake-actions/dcm/deploy@v3
            with:
              target: STAGING
              project-path: dcm/analytics/
              snowflake-user: ${{ env.SNOWFLAKE_USER }}
              alias: "Deploy from ${{ github.sha }}"
    
      deploy-prod:
        needs: deploy-staging     # Production blocked until staging succeeds
        runs-on: ubuntu-latest
        environment: PROD
        permissions:
          contents: read
          id-token: write
        steps:
          - uses: actions/checkout@v4
          - uses: snowflakedb/snowflake-actions/dcm/deploy@v3
            with:
              target: PROD
              project-path: dcm/analytics/
              snowflake-user: ${{ env.SNOWFLAKE_SERVICE_USER }}
              alias: "Promote from staging - ${{ github.sha }}"
    

    The sample workflows in the Snowflake Labs DCM repository include a DROP detection step that parses plan_result.json and blocks deployment if the changeset contains top-level DROPs on databases, schemas, tables, or stages. This is a safety guardrail, not a guarantee — nested destructive changes or ALTER operations that effectively drop data won’t be caught by it.

    The Three New Capabilities to Know (July 2026 Preview)

    PLAN DELTA — fast feedback during development

    On a project with 500+ entities, a full PLAN can take several minutes. PLAN DELTA evaluates only the definition files you changed since the last deployment, plus any downstream definitions that depend on them. For tightening a masking policy or tweaking a dynamic table schedule, PLAN DELTA gives you sub-30-second feedback. Always switch to a full PLAN before deploying.

    Inherited grants — close the grant drift problem permanently

    The most painful recurring problem in Snowflake governance is grant drift: a new table is created, nobody remembers to grant SELECT to the analyst role, and the dashboard breaks. Inherited grants solve this at the DCM level:

    -- grants.sql
    -- One INHERITED grant covers all current AND future tables in this schema
    
    GRANT SELECT
      ON ALL TABLES IN SCHEMA ANALYTICS_DB.ANALYTICS
      TO ROLE ANALYTICS_ANALYST
      INHERITED;   -- applies automatically to tables created after this DEPLOY
    
    GRANT SELECT
      ON ALL VIEWS IN SCHEMA ANALYTICS_DB.ANALYTICS
      TO ROLE ANALYTICS_ANALYST
      INHERITED;
    

    Inherited grants require a behavior-change parameter enabled at account level, independent of DCM Projects. This parameter affects all future object grants in your account — not just DCM-managed ones. Read the Managing access with inherited grants section before enabling it in production.

    ATTACH TAG — declarative tagging reconciled on every deploy

    Tag assignment used to be a post-deployment manual step or a separate Terraform resource. With ATTACH TAG in DCM Projects, tag assignments are part of your definition files and reconciled on every DEPLOY — including column-level tags for PII classification that your masking policies depend on:

    -- tags.sql
    -- Declaratively assigns PII tags — reconciled on every DEPLOY
    
    ATTACH TAG GOVERNANCE_DB.TAGS.PII_EMAIL
      TO COLUMN ANALYTICS_DB.ANALYTICS.CUSTOMERS.EMAIL;
    
    ATTACH TAG GOVERNANCE_DB.TAGS.PII_PHONE
      TO COLUMN ANALYTICS_DB.ANALYTICS.CUSTOMERS.PHONE_NUMBER;
    
    ATTACH TAG GOVERNANCE_DB.TAGS.GDPR_SUBJECT
      TO TABLE ANALYTICS_DB.ANALYTICS.CUSTOMERS;
    

    The Gotchas

    Removing a DEFINE statement expresses intent to DROP — not intent to unmanage.

    If you delete a DEFINE TABLE ... line from your definitions file and run DEPLOY, DCM drops the table. This is the correct declarative behaviour — the desired state no longer includes that table. If you want to stop managing an object without dropping it, use ALTER TABLE ... UNSET DCM PROJECT to detach it first, then remove the DEFINE statement. Missing this step is how teams accidentally drop production tables during a refactor.

    DEPLOY failure mid-execution can leave objects in a partially-applied state.

    Unlike Terraform which can plan a rollback, a DCM DEPLOY that fails partway through has applied some DDL statements and not others. The fix in most cases is to fix the root cause and run DEPLOY again — DCM reconciles from wherever it left off. For a critical failure, the Recover an earlier defined state workflow lets you replay an earlier deployment artifact. This is why PLAN + manual review before DEPLOY is non-negotiable in production.

    DCM project owner must hold every role that receives GRANT OWNERSHIP — or PLAN fails.

    If your definitions include GRANT OWNERSHIP ON TABLE ... TO ROLE TEAM_ADMIN, the DCM project owner role must hold TEAM_ADMIN (directly or through role hierarchy). DCM checks for potential owner lockout at PLAN time and fails before making any changes if the hierarchy isn’t right. This is a safety feature — but it means your DCM project owner role needs a carefully designed privilege stack before you can express ownership transfers in definitions.

    Projects over 1,000 entities can timeout on PLAN or DEPLOY.

    The official docs state that PLAN or DEPLOY for large projects can take 10 minutes or more, and exceeding the 10,000-entity or 10 MB limits can cause timeouts. Mitigation: split large projects along natural boundaries (separate business units, separate data domains) rather than running one mega-project. Consolidating definitions into fewer files also speeds up PLAN and DEPLOY. And use PLAN DELTA during active development to avoid full-project evaluations on every edit.

    Jinja template variables render in cleartext — never put credentials in them.

    The official docs include this as an explicit warning: rendered SQL definitions don’t redact any values inserted by Jinja template variables, and those rendered files are stored in the DCM project’s deployment artifacts. Use opaque identifiers, environment names, and configuration values in Jinja — never API keys, passwords, or connection strings. Use Snowflake Secrets for credentials, referenced by name, not by value.

    PLAN DELTA won’t catch external changes to your account since the last deployment.

    PLAN DELTA only evaluates changed definition files — it doesn’t query the live account for the unchanged portions. If someone manually altered a view outside of DCM since your last deployment, PLAN DELTA won’t detect that drift. Full PLAN always compares against the live account. The rule: PLAN DELTA for development speed, full PLAN as the pre-deploy gate in every CI/CD pipeline.

    The One Principle

    “Write what you want. Let Snowflake figure out how to get there. The PLAN is the diff — read it before every DEPLOY, especially the DROP lines.”

    FAQ

    What is Snowflake DCM Projects and when did it go GA?

    DCM Projects (Database Change Management) is Snowflake’s native infrastructure-as-code system. You write DEFINE statements in SQL files describing your desired Snowflake object state, run PLAN to preview changes, and DEPLOY to apply them. Snowflake tracks state internally — no external state file. It went generally available on August 7 2026 and is available on all Snowflake editions at no additional cost beyond cloud services compute.

    Should I replace Terraform with Snowflake DCM Projects?

    It depends on your scope. If you manage only Snowflake objects and have been using Terraform purely for that, DCM Projects is a cleaner native alternative with day-zero feature support and no state file drift. If you manage multi-cloud infrastructure — S3 buckets, IAM, Kafka, and Snowflake together — keep Terraform for the cross-cloud layer. DCM Projects only manages Snowflake objects.

    What is the difference between PLAN and PLAN DELTA in DCM Projects?

    PLAN compares all your definition files against the current live account state and produces a full changeset. PLAN DELTA evaluates only the definition files that changed since the last deployment, plus any downstream definitions that depend on them. PLAN DELTA is dramatically faster on large projects — use it during active development for quick feedback. Always run a full PLAN before deploying to catch any external changes to your account that PLAN DELTA would miss.

    How do I prevent DCM Projects from accidentally dropping objects?

    Two safeguards: first, always review the PLAN output before DEPLOY — look specifically at the DROP entries. Second, if using GitHub Actions, add the DROP detection step from the Snowflake Labs sample workflows, which parses plan_result.json and blocks the deploy workflow if it finds top-level DROPs on databases, schemas, tables, or stages. If you want to stop managing an object without dropping it, use ALTER TABLE … UNSET DCM PROJECT to detach it before removing its DEFINE statement.

    What are inherited grants in DCM Projects and why do they matter?

    Inherited grants let you write a single GRANT statement with the INHERITED keyword that automatically applies to every current and future object of a specified type within a container — for example, all current and future tables in a schema. This permanently closes the grant drift problem: a new table is created, the grant applies automatically without any manual step or separate Terraform resource. Inherited grants require enabling a behavior-change parameter at the account level, independent of DCM Projects itself.

    Can Cortex Code write my DCM Project definition files for me?

    Yes. The Cortex Code DCM skill can scaffold a new project from scratch, author and edit DEFINE statements and Jinja templates, run PLAN and DEPLOY, interpret plan output, and diagnose failures. Available in Cortex Code CLI, Snowsight, the VS Code extension, and Cortex Code Desktop. Use natural-language prompts like “Add a new dynamic table for customer spending” or “Why did my last plan fail?” and Cortex Code handles the authoring and debugging loop.

    Related reading: Snowflake Dynamic Data Masking & Row Access Policies · Using MCP Servers with Snowflake · Cortex AI token usage monitoring · Identifying hidden Cortex AI token costs · dbt State — stop rebuilding what hasn’t changed · DCM Projects overview (official) · Deploy and manage DCM Projects (official) · Snowflake Labs DCM repository (GitHub)