DCM Projects for Tasks
Overview
In the Get Started with Snowflake DCM Projects, Build Data Pipelines with Snowflake DCM Projects, and DCM Projects for Dynamic Tables guides, you learned how DCM Projects manage Snowflake infrastructure declaratively — from a single project up to dynamic-table pipelines that evolve without full recomputation.
In this guide, you'll focus on Tasks and Task graphs — the orchestration layer that drives scheduled and event-driven work across your pipeline.
Task graphs (DAGs, or directed acyclic graphs, of Tasks — meaning dependencies flow in one direction and no task can loop back to itself) are how most production Snowflake pipelines coordinate sequences of work: a root task kicks off every run, child tasks depend on their predecessors, streams and return values can gate downstream execution, and a finalizer closes out the run. Managing that whole graph as code — with retries, schedules, warehouse bindings, and config values promoted across environments — becomes much easier to maintain as a DCM Project.
You'll define a fifteen-task demo graph that shows off every interesting Task feature in one place:
- Root task with retries, overlap policy, and graph-level config pushed into children via
SYSTEM$GET_TASK_GRAPH_CONFIG - Finalizer task that emails a formatted HTML summary of every graph run — even when it failed mid-way
- Serverless task and multi-predecessor task patterns
- Stream-conditional and return-value-conditional child tasks
- Failing task with retries + dependent that gets skipped
- DCM-managed target state — DEFINE TASK ... STARTED lets you deploy tasks already running, no post-deploy ALTER TASK ... RESUME needed (or flip a task to
SUSPENDEDdeclaratively from Git) - DMF quality gate that routes rows to a target or quarantine table based on data metric function results
- Overlap policy on the root task — the new OVERLAP_POLICY parameter gives you granular control over concurrent graph execution
Along the way, you'll see two DCM features that make Tasks a first-class citizen of the DCM Project model:
- DEFINE TASK ... STARTED | SUSPENDED — declare the target state of every task so you never need an ALTER TASK ... RESUME post-script
DEFINE PROCEDURE— manage SQL stored procedures through DCM Plan & Deploy alongside the tasks that call them
Prerequisites
- Basic knowledge of Snowflake concepts (databases, schemas, tables, roles)
- Familiarity with SQL and Snowflake Tasks
- Completion of Get Started with Snowflake DCM Projects is recommended
What You'll Learn
- How to define a range of Task features declaratively with
DEFINE TASK - How to use the new STARTED | SUSPENDED target-state property to control task state through DCM deployments
- How to manage SQL procedures used by tasks with
DEFINE PROCEDURE - How to build a finalizer task that sends a formatted HTML email summary for every graph run
- How to add a DMF-based quality gate that routes rows to target or quarantine tables
- How to monitor ongoing health with a serverless failed-task alert
What You'll Need
- A Snowflake account with ACCOUNTADMIN access (or a role with sufficient privileges)
- A verified email address on your Snowflake user so the finalizer and alerts can reach you
- (Optional) Snowflake CLI v3.16.0+ if you prefer CLI over the Snowsight UI
What You'll Build
- A fully deployed task graph — root, finalizer, fifteen demo tasks, and a DMF quality-gate branch — all defined as code in a single DCM Project
- A serverless alert that emails you when any task in the database fails
- A reusable pattern for getting your whole orchestration layer into DCM
Create a Workspace from Git
In this step, you'll create a Snowsight Workspace linked to the sample DCM Project repository on GitHub.
- Navigate to Projects > Workspaces in Snowsight.
- Click Create (+) and select Git repository.
- Enter the repository URL: https://github.com/snowflake-labs/snowflake-dcm-projects
- Select an API Integration for GitHub (create one if needed).
- Select Public repository.
Once the workspace is created, you'll see the repository files in the file explorer. Navigate to Quickstarts/dcm-projects-for-tasks to find two directories:
DCM_Projects_Tasks/— The DCM Project itself (manifest, definitions). This is what DCM reads during Plan & Deploy.scripts/— Numbered SQL files that you'll run in Snowsight worksheets at different stages of this guide. These live outside the DCM project directory so they don't interfere with it.
| File | When to Run |
|---|---|
| scripts/01_pre_deploy.sql | Before the first DCM Plan & Deploy |
| scripts/02_post_deploy.sql | After the first successful deployment |
| scripts/03_cleanup.sql | When you're done and want to tear everything down |
Open scripts/01_pre_deploy.sql in a Snowsight worksheet — you'll use it in the next step.
Set Up Roles, Grants, and the Notification Integration
Before deploying the task graph, you need a dedicated DCM developer role, a DCM Project object, and an email notification integration that the finalizer and alerts will use.
Open scripts/01_pre_deploy.sql in a Snowsight worksheet and run each section in order. This script lives outside the DCM project directory, so it won't be picked up by Plan or Deploy.
1. Create a DCM Developer Role
USE ROLE ACCOUNTADMIN; CREATE ROLE IF NOT EXISTS dcm_developer; SET user_name = (SELECT CURRENT_USER()); GRANT ROLE dcm_developer TO USER IDENTIFIER($user_name);
2. Enable Inherited Grants
Inherited grants. An
INHERITEDgrant covers current and future objects of a type inside a container, as one grant rather than one per object. This project's access definitions use them. They require a one-time, account-level opt-in that is independent of DCM, and cannot be combined with WITH GRANT OPTION,CASCADEorRESTRICT.
ALTER ACCOUNT SET FEATURE_RBAC_INHERITED_GRANTS = 'ENABLED';
3. Grant Account-Level Privileges
Account-level privileges cannot be granted by a project on itself, so they all happen here, before the first plan. The script grants DCM_DEVELOPER a superset: every guide in this series shares this role, so one run of any guide's pre-deploy script prepares you for all of them, and each grant carries a comment saying what capability it buys. The ones this guide relies on are the task and alert privileges:
GRANT EXECUTE TASK ON ACCOUNT TO ROLE dcm_developer; GRANT EXECUTE MANAGED TASK ON ACCOUNT TO ROLE dcm_developer; GRANT EXECUTE ALERT ON ACCOUNT TO ROLE dcm_developer; GRANT EXECUTE MANAGED ALERT ON ACCOUNT TO ROLE dcm_developer;
A serverless task or alert needs both of its pair: EXECUTE TASK lets the owner run tasks it owns, and EXECUTE MANAGED TASK is additionally required when there is no warehouse. An alert that deploys but never fires is usually one of these missing. The quality-gate branch also needs EXECUTE DATA METRIC FUNCTION and the SNOWFLAKE.DATA_QUALITY_MONITORING_VIEWER application role, which the script grants too.
4. Create the DCM Project Object
USE ROLE dcm_developer; CREATE DATABASE IF NOT EXISTS dcm_demo; CREATE SCHEMA IF NOT EXISTS dcm_demo.projects; CREATE DCM PROJECT IF NOT EXISTS dcm_demo.projects.dcm_tasks_project_dev COMMENT = 'for testing DCM Projects with Tasks and task graphs';
5. Create the Email Notification Integration
Notification integrations are account-level objects — they live outside any DCM Project. The finalizer task and the failed-task alert both send through this integration:
USE ROLE ACCOUNTADMIN; CREATE NOTIFICATION INTEGRATION IF NOT EXISTS dcm_demo_email_notifications TYPE = EMAIL ENABLED = TRUE COMMENT = 'Used by DCM Tasks quickstart finalizer and alerts'; GRANT USAGE ON INTEGRATION dcm_demo_email_notifications TO ROLE dcm_developer;
6. Get Your Account Identifier and Username
SELECT CURRENT_ORGANIZATION_NAME() || '-' || CURRENT_ACCOUNT_NAME() AS account_identifier, CURRENT_USER() AS user_name;
Keep these values — you'll paste them into manifest.yml and the notification_recipient field in the next section.
7. Verify Your Email Address
The finalizer task, quality-issue notification, and failed-task alert all send email through SYSTEM$SEND_SNOWFLAKE_NOTIFICATION. This only works if the recipient email is verified on the Snowflake account.
- Click your username (bottom-left circle) in Snowsight, then select Settings → Profile.
- Under Email, confirm your address shows a ✅ verified badge.
- If not, click Verify and follow the link sent to your inbox.
If you skip this step, any task or alert that sends a notification will fail with: "Email recipients at indexes [1] are not allowed."
Important: Before running Plan & Deploy, update three values in
DCM_Projects_Tasks/manifest.yml: set account_identifier on the DCM_DEV target to the identifier you just got, set user under templating.configurations.DEV to your username, and set notification_recipient to your verified Snowflake user email. The values with a # <-- Replace with ... comment flag where to change things.
Note: After running this script, refresh your browser so Snowsight picks up the newly created DCM Project object.
Explore the Project Files
Navigate into the DCM_Projects_Tasks/ directory. Unlike earlier guides which were centered on tables and dynamic tables, this project's definitions are almost all about orchestration: helper functions, procedures, and the task graph itself.
Manifest
Open DCM_Projects_Tasks/manifest.yml:
manifest_version: 2 type: DCM_PROJECT default_target: DCM_DEV targets: DCM_DEV: account_identifier: MYORG-MY_DEV_ACCOUNT # <-- Replace with your account identifier project_name: DCM_DEMO.PROJECTS.DCM_TASKS_PROJECT_DEV project_owner: DCM_DEVELOPER templating_config: DEV DCM_PROD: account_identifier: MYORG-MY_PROD_ACCOUNT # <-- Replace with your account identifier project_name: DCM_DEMO.PROJECTS.DCM_TASKS_PROJECT_PROD project_owner: DCM_PROD_DEPLOYER templating_config: PROD templating: defaults: wh_size: "X-SMALL" runtime_multiplier: 5 configurations: DEV: env_suffix: "_DEV" user: "INSERT_YOUR_USER" # <-- Replace with your Snowflake username project_owner_role: "DCM_DEVELOPER" notification_recipient: "INSERT_YOUR_EMAIL" # <-- Replace with your verified email PROD: env_suffix: "" wh_size: "SMALL" project_owner_role: "DCM_PROD_DEPLOYER" user: "GITHUB_ACTIONS_SERVICE_USER" notification_recipient: "prod_alerts@example.com" runtime_multiplier: 10
Note how the root task sets OVERLAP_POLICY = 'NO_OVERLAP' (the default), which ensures each graph run completes before the next one starts. You can change this to ALLOW_CHILD_OVERLAP or ALLOW_ALL_OVERLAP if your pipeline benefits from concurrent runs.
Note how templating lets the same definitions produce a DEV graph with an X-SMALL warehouse and a short runtime_multiplier of 5, and a PROD graph with a SMALL warehouse and a multiplier of 10. Notification recipients differ per environment too.
Infrastructure — sources/definitions/infrastructure.sql
Warehouse, database, schema, and a read-only role — nothing task-specific yet, but this is the baseline that the rest of the project depends on:
DEFINE WAREHOUSE DCM_DEMO_4_WH{{env_suffix}} WITH WAREHOUSE_SIZE = '{{wh_size}}' AUTO_SUSPEND = 60 COMMENT = 'For Quickstart Demo of DCM Projects with Tasks'; DEFINE DATABASE DCM_DEMO_4{{env_suffix}} COMMENT = 'Quickstart Demo for DCM Projects with Tasks and task graphs'; DEFINE SCHEMA DCM_DEMO_4{{env_suffix}}.PIPELINE COMMENT = 'Task graph, helper functions, procedures, and DMFs';
Tables — sources/definitions/tables.sql
Five tables support the graph:
- WEATHER_DATA_SOURCE — the source that LOAD_RAW_DATA reads from
- RAW_WEATHER_DATA — the landing table where DMF checks run (DATA_METRIC_SCHEDULE = 'TRIGGER_ON_CHANGES')
- CLEAN_WEATHER_DATA — target for rows that passed
- QUARANTINED_WEATHER_DATA — target for rows that failed
- TASK_DEMO_TABLE — used by the stream-conditional task
The same file also declares the stream the graph depends on:
- DEMO_STREAM — a
DEFINE STREAMon TASK_DEMO_TABLE, read by the stream-conditional task DEMO_TASK_8
Procedures — sources/definitions/procedures.sql
This file uses DEFINE PROCEDURE, so procedure lifecycle is fully DCM-managed — you don't need separate CREATE OR ALTER PROCEDURE migrations for the demo procs.
DEFINE PROCEDURE DCM_DEMO_4{{env_suffix}}.PIPELINE.DEMO_PROCEDURE_1() RETURNS VARCHAR(16777216) LANGUAGE SQL EXECUTE AS OWNER AS $$ SELECT SYSTEM$WAIT(3); $$;
DEMO_PROCEDURE_2 is similar but fails ~50% of the time, which drives the "failing task" demo later.
Functions — sources/definitions/functions.sql
Three helper functions, all managed by DCM:
- RUNTIME_WITH_OUTLIERS — randomizes each task's duration so the graph feels realistic, with 1-in-10 outlier runs
- GET_TASK_GRAPH_RUN_SUMMARY — returns the current graph run as a JSON array (used by the finalizer)
- GET_ACTIVE_QUALITY_CHECKS — a UDTF listing every DMF currently attached to a given table
- HTML_FROM_JSON_TASK_RUNS — a Python function that renders the JSON run summary as an HTML table (used by the finalizer for the email body)
Expectations — sources/definitions/expectations.sql
DCM manages DMF attachments natively through ATTACH DATA METRIC FUNCTION, so the quality gate lives in the DCM Project using Snowflake's built-in system DMFs:
ATTACH DATA METRIC FUNCTION SNOWFLAKE.CORE.DUPLICATE_COUNT TO TABLE DCM_DEMO_4{{env_suffix}}.PIPELINE.RAW_WEATHER_DATA ON (ROW_ID) EXPECTATION NO_DUPLICATE_ROW_IDS (value = 0); ATTACH DATA METRIC FUNCTION SNOWFLAKE.CORE.NULL_COUNT TO TABLE DCM_DEMO_4{{env_suffix}}.PIPELINE.RAW_WEATHER_DATA ON (DS) EXPECTATION NO_NULL_DATES (value = 0); ATTACH DATA METRIC FUNCTION SNOWFLAKE.CORE.NULL_COUNT TO TABLE DCM_DEMO_4{{env_suffix}}.PIPELINE.RAW_WEATHER_DATA ON (ZIPCODE) EXPECTATION NO_NULL_ZIPCODES (value = 0);
Adding or removing an attachment here changes the CHECK_DATA_QUALITY task's behavior on the next deploy (it discovers attached DMFs at runtime via GET_ACTIVE_QUALITY_CHECKS) — no manual ALTER TABLE required.
Alerts — sources/definitions/alerts.sql
The failed-task alert is defined declaratively with DEFINE ALERT, so the alert lives alongside the tasks it monitors and evolves through the same Plan/Deploy cycle. The alert deploys with a STARTED target state, so it begins checking on its schedule as soon as you deploy. The condition filters on DATABASE_NAME because INFORMATION_SCHEMA.TASK_HISTORY returns task runs from every database in the account, not only the one named in its qualifier — without the filter, a failed task anywhere in the account would trigger this alert. The recipient email comes from the same {{notification_recipient}} manifest value the finalizer uses, so you only configure it in one place.
DEFINE ALERT DCM_DEMO_4{{env_suffix}}.PIPELINE.FAILED_TASK_ALERT SCHEDULE = '60 MINUTE' STARTED IF (EXISTS ( SELECT NAME, SCHEMA_NAME FROM TABLE(DCM_DEMO_4{{env_suffix}}.INFORMATION_SCHEMA.TASK_HISTORY( SCHEDULED_TIME_RANGE_START => (GREATEST( TIMEADD('DAY', -7, CURRENT_TIMESTAMP), SNOWFLAKE.ALERT.LAST_SUCCESSFUL_SCHEDULED_TIME())), SCHEDULED_TIME_RANGE_END => SNOWFLAKE.ALERT.SCHEDULED_TIME(), ERROR_ONLY => TRUE)) WHERE DATABASE_NAME = 'DCM_DEMO_4{{env_suffix}}')) THEN BEGIN LET task_names STRING := ( SELECT LISTAGG(DISTINCT(SCHEMA_NAME || '.' || NAME), ', ') FROM TABLE(RESULT_SCAN(SNOWFLAKE.ALERT.GET_CONDITION_QUERY_UUID()))); CALL SYSTEM$SEND_SNOWFLAKE_NOTIFICATION( SNOWFLAKE.NOTIFICATION.TEXT_HTML( 'Failed tasks detected: <b>' || :task_names || '</b>'), SNOWFLAKE.NOTIFICATION.EMAIL_INTEGRATION_CONFIG( 'dcm_demo_email_notifications', 'DCM Pipeline — Failed Task Alert', ARRAY_CONSTRUCT('{{notification_recipient}}'), NULL, NULL)); END;
The alert runs serverless (no warehouse specified) and monitors every task in the database for failures in the last interval — a broader scope than the finalizer. Think of them as complementary rather than overlapping: the finalizer gives you a detailed per-run summary (even when the graph failed mid-way), while the alert is a lighter-weight, database-wide safety net that only fires when something fails. SNOWFLAKE.ALERT.GET_CONDITION_QUERY_UUID() lets you re-use the result of the condition query so you don't have to re-run it to build the message.
Tasks — sources/definitions/tasks.sql
This is the heart of the project. Every task uses the STARTED | SUSPENDED target-state property — a DCM-only extension to DEFINE TASK that has no equivalent in CREATE TASK. With plain DDL, a new task is always created in a suspended state and you have to follow up with ALTER TASK ... RESUME (or SYSTEM$TASK_DEPENDENTS_ENABLE for a whole graph) to bring it online. By declaring the desired state inline, DCM deploys the graph fully wired up — no post-scripts needed — and resolves the root-vs-child state ordering for you. It also means that suspending a task is a declarative change: flip STARTED to SUSPENDED in Git, deploy, and DCM handles the transition.
Here's the root:
DEFINE TASK DCM_DEMO_4{{env_suffix}}.PIPELINE.DEMO_TASK_1 WAREHOUSE = 'DCM_DEMO_4_WH{{env_suffix}}' SCHEDULE = 'USING CRON 15 8-18 * * MON-FRI CET' SUSPEND_TASK_AFTER_NUM_FAILURES = 0 TASK_AUTO_RETRY_ATTEMPTS = 2 OVERLAP_POLICY = 'NO_OVERLAP' CONFIG = $${"RUNTIME_MULTIPLIER": {{runtime_multiplier}}}$$ COMMENT = 'Root task with retries, overlap policy, and a runtime-multiplier config' STARTED AS DECLARE RUNTIME_MULTIPLIER INTEGER := SYSTEM$GET_TASK_GRAPH_CONFIG('RUNTIME_MULTIPLIER'); RANDOM_RUNTIME VARCHAR := DCM_DEMO_4{{env_suffix}}.PIPELINE.RUNTIME_WITH_OUTLIERS(:RUNTIME_MULTIPLIER * 1000); BEGIN SELECT SYSTEM$WAIT(:RANDOM_RUNTIME, 'MILLISECONDS'); CALL SYSTEM$SET_RETURN_VALUE('✅ All stage files scanned'); END;
And the finalizer that closes out every run with a formatted HTML email summary:
DEFINE TASK DCM_DEMO_4{{env_suffix}}.PIPELINE.DEMO_FINALIZER WAREHOUSE = 'DCM_DEMO_4_WH{{env_suffix}}' FINALIZE = DCM_DEMO_4{{env_suffix}}.PIPELINE.DEMO_TASK_1 COMMENT = 'Sends a formatted HTML email summary after every graph run' STARTED AS DECLARE MY_ROOT_TASK_ID STRING; MY_START_TIME TIMESTAMP_LTZ; SUMMARY_JSON STRING; SUMMARY_HTML STRING; BEGIN MY_ROOT_TASK_ID := (CALL SYSTEM$TASK_RUNTIME_INFO('CURRENT_ROOT_TASK_UUID')); MY_START_TIME := (CALL SYSTEM$TASK_RUNTIME_INFO('CURRENT_TASK_GRAPH_ORIGINAL_SCHEDULED_TIMESTAMP')); SUMMARY_JSON := (SELECT DCM_DEMO_4{{env_suffix}}.PIPELINE.GET_TASK_GRAPH_RUN_SUMMARY(:MY_ROOT_TASK_ID, :MY_START_TIME)); SUMMARY_HTML := (SELECT DCM_DEMO_4{{env_suffix}}.PIPELINE.HTML_FROM_JSON_TASK_RUNS(:SUMMARY_JSON)); CALL SYSTEM$SEND_SNOWFLAKE_NOTIFICATION( SNOWFLAKE.NOTIFICATION.TEXT_HTML(:SUMMARY_HTML), SNOWFLAKE.NOTIFICATION.EMAIL_INTEGRATION_CONFIG( 'dcm_demo_email_notifications', 'DCM Task Graph Run Summary ({{env_suffix}})', ARRAY_CONSTRUCT('{{notification_recipient}}'), NULL, NULL)); CALL SYSTEM$SET_RETURN_VALUE('✅ Graph run summary email sent'); END;
Feature tour of the remaining tasks:
| Task | What it demonstrates |
|---|---|
| DEMO_TASK_2, DEMO_TASK_4 | Plain child tasks with a return value |
| DEMO_TASK_3 | Calling a stored procedure (DEMO_PROCEDURE_1) |
| DEMO_TASK_5 | Serverless task (no WAREHOUSE =) with two predecessors |
| DEMO_TASK_6 | Returns a value chosen at random — used downstream as a condition |
| DEMO_TASK_7 | Return value is a URL (clickable in Snowsight) |
| DEMO_TASK_8 | WHEN SYSTEM$STREAM_HAS_DATA(...) — skipped when the stream is empty |
| DEMO_TASK_9 | Calls a procedure that fails ~50% of the time; retries are inherited from the root task's TASK_AUTO_RETRY_ATTEMPTS. Watch this in Snowsight to see how retries and failures affect downstream tasks |
| DEMO_TASK_10 | Does not run when its predecessor (DEMO_TASK_9) fails after exhausting retries |
| DEMO_TASK_11 | WHEN SYSTEM$GET_PREDECESSOR_RETURN_VALUE(...) — runs conditionally on upstream return |
| DEMO_TASK_12 | Self-cancels 1 in 10 runs via SYSTEM$USER_TASK_CANCEL_ONGOING_EXECUTIONS |
| DEMO_TASK_13 | Two predecessors |
| DEMO_TASK_14 | Deployed as SUSPENDED — target-state property in action |
| DEMO_TASK_15 | Never runs because its predecessor is suspended |
Plus the quality-gate branch:
| Task | What it demonstrates |
|---|---|
| LOAD_RAW_DATA | Pulls rows from source into the landing table, occasionally injecting duplicate ROW_IDs |
| CHECK_DATA_QUALITY | Iterates through every DMF attached to the landing table and returns pass/fail as its return value |
| TRANSFORM_DATA | Conditional on CHECK_DATA_QUALITY return = "passed" — copies rows into the target table |
| ISOLATE_DATA_ISSUES | Conditional on the inverse — moves rows into quarantine |
| NOTIFY_ABOUT_QUALITY_ISSUE | Sends a quality-issue email through the same notification integration |
Because every task is declared with its dependencies (AFTER ...), DCM knows the full DAG. You never have to call SYSTEM$TASK_DEPENDENTS_ENABLE after deployment — DCM handles state resolution, including the SUSPENDED branch rooted at DEMO_TASK_14.
Plan and Deploy
With the manifest updated (account identifier, user, notification recipient) and the pre-deploy script run, you're ready to deploy the task graph.
Option A: Snowsight Workspaces
- Open
DCM_Projects_Tasks/manifest.ymlin the Workspaces file explorer. - At the bottom of the editor, confirm the project selector shows DCM_DEMO.PROJECTS.DCM_TASKS_PROJECT_DEV and the target shows DCM_DEV.

- Click Plan. Review the changeset — you should see the warehouse, database, schema, tables, procedures, functions, and every task listed as
CREATE.

- Click Deploy.
Option B: Snowflake CLI
cd Quickstarts/dcm-projects-for-tasks/DCM_Projects_Tasks snow dcm plan --target DCM_DEV snow dcm deploy --target DCM_DEV
Deployment takes about 30–60 seconds. When it succeeds, every STARTED task is already running state — the schedule on DEMO_TASK_1 will fire the root task at the next CRON slot, and DEMO_TASK_14 and DEMO_TASK_15 are correctly in SUSPENDED state.
Post-Deploy: Seed Data and a Manual Run
scripts/02_post_deploy.sql seeds the source table and triggers the first graph run. Everything the graph depends on is already DCM-managed — the stream, the DMF attachments, and the failed-task alert, which deploys STARTED (see tables.sql, expectations.sql and alerts.sql above).
DEMO_STREAM is declared with DEFINE STREAM next to its source table and deploys empty. DEMO_TASK_8 has WHEN SYSTEM$STREAM_HAS_DATA('...DEMO_STREAM'), so without data it is skipped on every graph run — exactly the "conditional execution on a stream" scenario.
Open that script in a Snowsight worksheet and walk through it section by section.
1. Seed the Source Table and Run the Graph
INSERT INTO dcm_demo_4_dev.pipeline.weather_data_source (...) VALUES (...); EXECUTE TASK dcm_demo_4_dev.pipeline.demo_task_1;
The graph kicks off immediately — you don't have to wait for the CRON schedule.
Tip: You can dynamically override the graph config for a single run with the
USING CONFIGclause. For example, to speed up the demo tasks:EXECUTE TASK dcm_demo_4_dev.pipeline.demo_task_1 USING CONFIG = '{"RUNTIME_MULTIPLIER": 1}';This overrides RUNTIME_MULTIPLIER for that one execution without changing the task definition.
2. Force-Run the Failed-Task Alert (optional)
Because the alert deploys with a STARTED target state, it is already evaluating on its 60-minute schedule — no RESUME is needed. To see it fire on demand rather than waiting for the schedule, force-run it after the graph run has finished — a run takes several minutes:
EXECUTE ALERT dcm_demo_4_dev.pipeline.failed_task_alert;
This evaluates the alert condition immediately against recent task history. Run it straight after EXECUTE TASK and no task has failed yet, so it evaluates to false and sends nothing; run it once DEMO_TASK_9 has failed and it fires.
View the Task Graph
Navigate to Monitoring → Task History in Snowsight. Filter by database DCM_DEMO_4_DEV and you'll see the run you just triggered.
Click into the run to see the full graph:

You should see:
- DEMO_TASK_1 (root) and DEMO_FINALIZER both succeeded
- Most child tasks green; DEMO_TASK_8 skipped (empty stream) and DEMO_TASK_11 either ran or skipped depending on DEMO_TASK_6's random return
- Occasionally DEMO_TASK_9 fails (its procedure errors ~50% of the time); because the root task has TASK_AUTO_RETRY_ATTEMPTS = 2, Snowflake retries before marking it failed — if all attempts fail, DEMO_TASK_10 is skipped
- DEMO_TASK_14 and DEMO_TASK_15 stay suspended — DCM deployed them that way on purpose
- The quality-gate branch takes one of two paths:
- CHECK_DATA_QUALITY → TRANSFORM_DATA (clean rows make it to the target)
- CHECK_DATA_QUALITY → ISOLATE_DATA_ISSUES → NOTIFY_ABOUT_QUALITY_ISSUE (bad rows quarantined + email sent)
Check your inbox — you should see two kinds of notification email from this quickstart:
| Email subject | Sent by | When it fires |
|---|---|---|
| DCM Task Graph Run Summary (_DEV) | DEMO_FINALIZER — a DCM-managed finalizer task | After every graph run, with a formatted HTML summary of task statuses, return values, and durations |
| DCM Pipeline — Failed Task Alert | FAILED_TASK_ALERT — the DCM-managed serverless alert (defined in alerts.sql, deployed STARTED) | Every 60 minutes, only when at least one task failed since the last check |
The finalizer gives you per-run detail; the alert is a background safety net that catches failures even if the finalizer itself errors out.
Run the graph a few more times (EXECUTE TASK dcm_demo_4_dev.pipeline.demo_task_1;) to see the random branches exercise themselves. Each run produces a fresh summary email thanks to the finalizer. To test the alert on demand, run EXECUTE ALERT dcm_demo_4_dev.pipeline.failed_task_alert;.
Iterate on the Graph Through DCM
The real power of putting Tasks in DCM shows up when you want to change something. Suppose you want DEMO_TASK_14 to actually run in DEV. Open tasks.sql, change its target state:
DEFINE TASK DCM_DEMO_4{{env_suffix}}.PIPELINE.DEMO_TASK_14 ... STARTED AS SELECT SYSTEM$WAIT(3);
Click Plan. The changeset shows a single ALTER TASK to flip the state. Click Deploy. Next graph run, DEMO_TASK_14 and DEMO_TASK_15 both execute.
This is the pattern: schema change, Plan, Deploy. No drift between environments — and because target state is part of the definition, if someone manually suspends a task outside DCM, the next deploy will re-enable it.
Cleanup
When you're done, open scripts/03_cleanup.sql in a Snowsight worksheet and run it. It purges everything the project manages, drops the now-empty project object, and drops the notification integration this guide created:
EXECUTE DCM PROJECT dcm_demo.projects.dcm_tasks_project_dev PURGE; DROP DCM PROJECT IF EXISTS dcm_demo.projects.dcm_tasks_project_dev; USE ROLE ACCOUNTADMIN; DROP INTEGRATION IF EXISTS dcm_demo_email_notifications;
PURGE drops every object the project created — database, tables, stream, task graph, alert, functions, procedures, warehouse, roles, and all their data. It is irreversible, and it leaves the project object behind, which is why the DROP follows.
The dcm_demo database, its projects schema and the dcm_developer role are shared across every guide in this series, so the script leaves them in place, commented out. The dcm_demo.projects schema holds the project objects for every DCM guide in this series, so only drop it once you have finished with all of them. Check what it contains first:
SHOW DCM PROJECTS IN SCHEMA dcm_demo.projects;
The inherited-grants account parameter enabled during setup also stays on. It is account-wide and the other guides in this series need it; the script carries the commented-out line to turn it off:
-- ALTER ACCOUNT SET FEATURE_RBAC_INHERITED_GRANTS = 'DISABLED';
If PURGE ever fails, the script tells you to suspend the root task and the alert first, so nothing keeps consuming credits while you investigate. The known cause is a data metric function attachment, which this guide has; dropping the table the DMF is attached to removes the attachment, after which PURGE succeeds.
Conclusion and Resources
In this guide, you learned how to:
- Define a complete task graph as code using DCM Projects — root, finalizer, conditional branches, retries, and quality gates, all in SQL definition files
- Use the STARTED | SUSPENDED target-state property on
DEFINE TASKso every deployment lands the graph in exactly the state you wanted, no post-scripts required - Manage stored procedures through DCM with
DEFINE PROCEDURE, so the procs your tasks call version alongside the tasks themselves - Send formatted HTML email summaries from a finalizer task using reusable JSON-summary and HTML-rendering helper functions
- Build a DMF-backed quality gate that uses return-value routing to push clean rows to a target table and bad rows to quarantine — and make the set of checks completely data-driven
- Monitor graph health with a DCM-managed serverless alert (
DEFINE ALERT) that emails you whenever any task in the database fails
The combination of DCM Projects and Tasks gives you the same production-grade workflow for orchestration that you already have for tables: version-controlled definitions, environment-aware deployments, and Plan/Deploy reviewability for every change.
What's Next
You've completed the 4-part DCM series. Ready to go deeper?
- DCM Projects Documentation — full reference for advanced DCM features: release channels, target-state management for more object types, and CI/CD integration patterns.
Related Resources
- DCM Projects Documentation
- Snowflake Tasks and Task Graphs
- Finalizer Tasks
- Data Metric Functions
- OVERLAP_POLICY for Task Graphs
- EXECUTE TASK with Dynamic Config
- Alerts and Notifications
- Get Started with Snowflake DCM Projects
- Build Data Pipelines with Snowflake DCM Projects
- DCM Projects for Dynamic Tables
- Sample DCM Projects Repository
This content is provided as is, and is not maintained on an ongoing basis. It may be out of date with current Snowflake instances