[新機能]SnowflakeネイティブのIaC機能「DCM Projects」で同一アカウント内にDEV/PROD環境を構築してみた
さがらです。
Snowflakeの新機能として、DCM Projectsがリリースされています。SnowflakeオブジェクトをDEFINE文で宣言的に定義し、Plan→Deployのワークフローでデプロイできる、いわばSnowflake版のInfrastructure-as-Code(IaC)機能です。
これまでSnowflakeオブジェクトのIaC管理にはTerraformやSchemachangeなどの外部ツールが必要でしたが、DCM ProjectsによりSnowflakeネイティブで宣言的管理ができるようになります。
本記事では、**1つのDCM project folder(定義ファイル群)**に2つのtargetを定義し、DEV用とPROD用の別々のDCM project objectにテンプレート変数を切り替えてデプロイする手順を検証しました。実際に試してみたので、手順と確認結果をまとめます。
機能概要
DCM Projects(Database Change Management Projects)は、Snowflakeオブジェクトの望ましい状態をコードで定義し、宣言的に管理する機能です。
主な特徴は以下のとおりです。
- 宣言的な定義:
DEFINE文でオブジェクトの望ましい状態を定義します。依存関係は自動解決されるため、記述順序を気にする必要はありません - Plan→Deployワークフロー: デプロイ前に
PLANで差分を確認し、問題なければDEPLOYで反映します。Terraformのplan→applyと同じ考え方です - Jinja2テンプレート: 変数の置換、条件分岐、ループ、マクロに対応しています。環境ごとに異なるパラメータを1つの定義ファイルから展開できます
- サポート対象オブジェクト: Database、Schema、Table、Dynamic Table、(Secure) View、Internal Stage、Warehouse、Role / Database Role、Grant、Data Metric Function、Task、SQL Function、Tag、Authentication Policyなどを管理可能です。ただしSnowflakeオブジェクトの一部のみが対象であり、File Formatなどはサポート対象に含まれていません
- パイプライン管理:
REFRESH ALLでDynamic Tableの一括リフレッシュ、TEST ALLでデータ品質テストの一括実行が可能です - 管理手段: Snowsight、Snowflake CLI(v3.16以降)、SQL、Cortex Code CLIの4つのインターフェースから操作できます
制限事項
2026年3月20日時点で確認されている主な制限事項です。公式Docsの内容をベースにまとめています。
全般
- Previewのため、仕様変更の可能性があります
- サポート対象はSnowflakeオブジェクトの一部(subset)に限られます
- 1つのDCM project objectに対して異なるtemplating configurationを切り替えてdeployすると、レンダリング結果に含まれなくなったオブジェクトは次回deployでdrop対象になりえます。そのため、環境ごとに別のDCM project objectを使うことが推奨されます
- Deploy時の変更はCREATE OR ALTERベースの制約を受けます。少なくとも一部オブジェクト(例: Table)では、失敗時に部分的な変更が適用される可能性があるため注意が必要です
- 最大1,000 source files / 10,000 rendered object definitionsまでの制限があります。超過すると性能劣化や実行失敗の可能性があります
- Preview中は、changesetがすべての細かな変更を完全には捕捉しないことがあります
_snowは予約識別子です- テンプレート変数に機微情報を入れるべきではありません
オブジェクト固有の制限
- Database / Schema / Table / View / Dynamic Table: renameは未サポートです
- Table: column rename、互換性のない型変更、Search Optimization追加、列定義へのtag / masking policy / row access policyの追加は未サポートです。列順の変更も未サポートです
- Dynamic Table:
INITIALIZEはimmutableです。bodyの変更やrefresh modeの変更はre-initialization / full refreshが必要な場合があります。列順変更・renameも未サポートです - View: rename、列順変更は未サポートです
- Internal Stage: internal stageのみ対象で、encryption typeはimmutableです
- Warehouse:
INITIALLY_SUSPENDEDはimmutableです - Role / Database Role: Application Roleは未サポートです
- Grant: APPLICATION ROLE grants / CALLER grantsは未サポートです
- Tag:
PROPAGATEは未サポートです
Jinja2テンプレート
import、extends、include構文は未サポートです
前提条件
- Snowflake: AWS US West(Oregon)リージョン、Enterprise Edition(※AWS東京リージョンの環境では本機能が有効化されていなかったため、AWS US Westのトライアルアカウントを作成しています。)
- 本機能のステータス: 2026年3月20日時点ではPreview
- 必要な権限:
CREATE DCM PROJECT ON SCHEMA権限。加えて、project ownerにはプロジェクトで定義した全オブジェクトをdeployできる十分な権限が必要です - project ownerロール: 本記事では
DEFINE ROLEによるロール作成を含むため、CREATE ROLE権限を持つACCOUNTADMINを使用しています - DCM project objectの配置先: DCM project objectはschema-levelオブジェクトであるため、配置先となるdatabase / schemaが必要です(デプロイ先のdatabaseとは別概念です)
- 操作環境: Snowsight Workspace
事前準備
Workspaceの作成
Snowsight上でWorkspaceを作成します。
左メニューから「Projects」→「Workspaces」に移動し、左上のWorkspace名を押して「Private Workspace」を選択し、Workspaceの名前を入力して作成します。
下図のようにWorkspaceが作成できればOKです。

DCM project object用のdatabase / schemaの作成
DCM project objectはschema-levelオブジェクトであり、配置先となるdatabaseとschemaが必要です。本記事ではDCM_ADMIN.PROJECTSという専用のdatabase / schemaを作成し、ここにDEV用・PROD用の2つのDCM project objectを配置します。
USE ROLE ACCOUNTADMIN;
USE SECONDARY ROLES NONE;
-- DCM project object配置用
CREATE DATABASE IF NOT EXISTS DCM_ADMIN;
CREATE SCHEMA IF NOT EXISTS DCM_ADMIN.PROJECTS;
クエリ実行後、DCM_ADMINデータベースとPROJECTSスキーマが作成されていればOKです。

試してみた
ということで早速、DCM Projectsを使ってDEV/PROD環境を構築していきます!
1. DCM project folderの作成
Snowsight Workspace上にDCM project folderを1つ作成します。1つのproject folder内にmanifest.ymlでDEV / PRODの2つのtargetを定義し、同じ定義ファイル群をそれぞれの環境にデプロイする構成です。
作成したWorkspace内で「+」ボタンを押し、「DCM Project」を選択します。プロジェクト名を入力します(本記事ではmy_dcm_projectとします)。

今回は「Define default target environment」にチェックを入れて、下図のように設定しました。先程作成したデータベースとスキーマを選択し、Target nameは「DEV」としておきます。

すると、下図のように各フォルダ・ファイルが自動的に生成されました!manifest.ymlの中に、先程設定したロール・データベース・スキーマの情報も含まれています。

2. デフォルトで作られた各.sqlファイルを削除
今回は検証用の構成が決まっているため、デフォルトで作られた各.sqlファイルを削除します。
参考までに、どういったファイルなのか確認しながら、削除していきます。
sources/definitions/examples.sql
このファイルは、DCM Projectsを用いて各オブジェクトを定義する際の最も一般的な構文での定義方法がまとまっています。defineで各オブジェクトの設定を定義していくのがポイントです。

sources/definitions/jinja_demo.sql
DCM ProjectsはJinja2テンプレートに対応しているため、forやifを用いてプログラム的に各オブジェクトの定義を行なうことが出来ます。このファイルでは、manifest.ymlで定義していたteamsの各値に沿ってループを回して、複数のteam用のテーブルを作成する処理が書かれています。

sources/macros/grants_macro.sql
Jinja2テンプレートが利用できるため、dbtのようにmacroを作成することも出来ます。このmacroでは、あるチームに対して特定のDEVELOPERとUSAGEのロールを作成し、それぞれに対して権限を付与することを行っています。

デフォルトで作られた各.sqlファイル削除後のフォルダイメージ
下図のようになっていればOKです。今回はmacrosフォルダも不要のため削除しました。
※tmp.sqlは、私が最初にデータベース・スキーマを作成したクエリを記述したファイルです。

3. manifest.ymlの設定(DEV/PRODターゲット)
同一アカウント内でデータベースレベルでDEV/PROD環境を分離するため、manifest.ymlを以下のように設定します。
manifest_version: 2
type: DCM_PROJECT
default_target: DEV
targets:
DEV:
account_identifier: MY_ACCOUNT
project_name: DCM_ADMIN.PROJECTS.MY_DCM_PROJECT_DEV
project_owner: ACCOUNTADMIN
templating_config: DEV
PROD:
account_identifier: MY_ACCOUNT
project_name: DCM_ADMIN.PROJECTS.MY_DCM_PROJECT_PROD
project_owner: ACCOUNTADMIN
templating_config: PROD
templating:
defaults:
wh_auto_suspend: 60
configurations:
DEV:
db_name: DEV_DB
wh_name: DEV_WH
wh_size: X-SMALL
role_name: DEV_EXPLORER_ROLE
PROD:
db_name: PROD_DB
wh_name: PROD_WH
wh_size: SMALL
role_name: PROD_EXPLORER_ROLE

ポイントは以下のとおりです。
targets配下にはaccount_identifier(同一アカウントなので同じ値)、project_name(DCM project objectの完全修飾名)、project_owner(デプロイ時のオーナーロール)、templating_config(使用するconfiguration名)を指定しますtemplating.defaultsでデフォルト値を定義し、templating.configurationsで環境固有の値を設定します- 変数の解決順序は「defaults → configuration → runtime override」の3段階です
4. データベース・スキーマ・ウェアハウスの定義
ここから、各オブジェクトの定義を行っていきます。
sources/definitions/ディレクトリ配下に、データベース・スキーマ・ウェアハウスの定義ファイルを作成します。
Jinja2のテンプレート変数{{ db_name }}を使用しているため、DEV configurationではDEV_DB、PROD configurationではPROD_DBに自動で展開されます。
sources/definitions/databases.sql
DEFINE DATABASE {{ db_name }};

sources/definitions/schemas.sql
DEFINE SCHEMA {{ db_name }}.RAW;
DEFINE SCHEMA {{ db_name }}.STG;
DEFINE SCHEMA {{ db_name }}.MART;

ウェアハウスのサイズもmanifest.ymlの{{ wh_size }}でパラメータ化しているため、DEVではX-SMALL、PRODではSMALLとターゲットごとに異なるサイズを指定できます。{{ wh_auto_suspend }}はtemplating.defaultsで定義した共通値が使われます。
sources/definitions/warehouses.sql
DEFINE WAREHOUSE {{ wh_name }}
WITH
WAREHOUSE_SIZE = '{{ wh_size }}'
AUTO_SUSPEND = {{ wh_auto_suspend }}
AUTO_RESUME = TRUE
INITIALLY_SUSPENDED = TRUE;

5. ロール・GRANTの定義
環境ごとにデータ参照用のロールを作成し、必要な権限をGRANTで付与します。テンプレート変数{{ role_name }}により、DEVではDEV_EXPLORER_ROLE、PRODではPROD_EXPLORER_ROLEが作成されます。
DCM ProjectsではDEFINE文とGRANT文を同一ファイルに記述できます。
sources/definitions/roles.sql
DEFINE ROLE {{ role_name }};
-- データベースへのUSAGE権限
GRANT USAGE ON DATABASE {{ db_name }} TO ROLE {{ role_name }};
-- 各スキーマへのUSAGE権限
GRANT USAGE ON SCHEMA {{ db_name }}.RAW TO ROLE {{ role_name }};
GRANT USAGE ON SCHEMA {{ db_name }}.STG TO ROLE {{ role_name }};
GRANT USAGE ON SCHEMA {{ db_name }}.MART TO ROLE {{ role_name }};
-- RAWスキーマ: テーブルへのSELECT権限(参照用)
GRANT SELECT ON ALL TABLES IN SCHEMA {{ db_name }}.RAW TO ROLE {{ role_name }};
-- STG/MARTスキーマ: Dynamic TableへのSELECT権限(参照用)
GRANT SELECT ON ALL DYNAMIC TABLES IN SCHEMA {{ db_name }}.STG TO ROLE {{ role_name }};
GRANT SELECT ON ALL DYNAMIC TABLES IN SCHEMA {{ db_name }}.MART TO ROLE {{ role_name }};
-- ウェアハウスのUSAGE権限
GRANT USAGE ON WAREHOUSE {{ wh_name }} TO ROLE {{ role_name }};
-- SYSADMINへのロール階層
GRANT ROLE {{ role_name }} TO ROLE SYSADMIN;

6. テーブルの定義
RAWスキーマにECサイトの注文データを格納するテーブルを定義します。
sources/definitions/raw_tables.sql
DEFINE TABLE {{ db_name }}.RAW.CUSTOMERS (
CUSTOMER_ID INT,
CUSTOMER_NAME VARCHAR(100),
EMAIL VARCHAR(200),
REGION VARCHAR(50),
CREATED_AT TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP()
);
DEFINE TABLE {{ db_name }}.RAW.ORDERS (
ORDER_ID INT,
CUSTOMER_ID INT,
ORDER_DATE DATE,
ORDER_STATUS VARCHAR(20),
TOTAL_AMOUNT DECIMAL(10,2),
CREATED_AT TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP()
);
DEFINE TABLE {{ db_name }}.RAW.ORDER_ITEMS (
ORDER_ITEM_ID INT,
ORDER_ID INT,
PRODUCT_NAME VARCHAR(200),
QUANTITY INT,
UNIT_PRICE DECIMAL(10,2),
CREATED_AT TIMESTAMP_NTZ DEFAULT CURRENT_TIMESTAMP()
);

7. Internal Stageの定義
データ取り込み用のInternal Stageを定義します。ディレクトリテーブルの設定も可能です。
sources/definitions/stages.sql
DEFINE STAGE {{ db_name }}.RAW.DATA_STAGE
DIRECTORY = (ENABLE = TRUE);

8. Dynamic Tableの定義
STGスキーマとMARTスキーマにDynamic Tableを定義し、RAW→STG→MARTのパイプラインを構成します。
RAW→STG→MARTの3層構成で、STG層ではデータのクレンジング(TRIM、LOWER、UPPER等)を行い、MART層では集計・結合を行うパイプラインです。STG層のDynamic TableはTARGET_LAG = DOWNSTREAM、MART層はTARGET_LAG = '1 hour'に設定しています。
sources/definitions/stg_dynamic_tables.sql
DEFINE DYNAMIC TABLE {{ db_name }}.STG.STG_CUSTOMERS
WAREHOUSE = {{ wh_name }}
TARGET_LAG = DOWNSTREAM
AS
SELECT
CUSTOMER_ID,
TRIM(CUSTOMER_NAME) AS CUSTOMER_NAME,
LOWER(EMAIL) AS EMAIL,
UPPER(REGION) AS REGION,
CREATED_AT
FROM {{ db_name }}.RAW.CUSTOMERS;
DEFINE DYNAMIC TABLE {{ db_name }}.STG.STG_ORDERS
WAREHOUSE = {{ wh_name }}
TARGET_LAG = DOWNSTREAM
AS
SELECT
O.ORDER_ID,
O.CUSTOMER_ID,
O.ORDER_DATE,
UPPER(O.ORDER_STATUS) AS ORDER_STATUS,
O.TOTAL_AMOUNT,
O.CREATED_AT
FROM {{ db_name }}.RAW.ORDERS O;
DEFINE DYNAMIC TABLE {{ db_name }}.STG.STG_ORDER_ITEMS
WAREHOUSE = {{ wh_name }}
TARGET_LAG = DOWNSTREAM
AS
SELECT
OI.ORDER_ITEM_ID,
OI.ORDER_ID,
TRIM(OI.PRODUCT_NAME) AS PRODUCT_NAME,
OI.QUANTITY,
OI.UNIT_PRICE,
OI.QUANTITY * OI.UNIT_PRICE AS LINE_TOTAL,
OI.CREATED_AT
FROM {{ db_name }}.RAW.ORDER_ITEMS OI;

sources/definitions/mart_dynamic_tables.sql
DEFINE DYNAMIC TABLE {{ db_name }}.MART.MART_CUSTOMER_ORDERS
WAREHOUSE = {{ wh_name }}
TARGET_LAG = '1 hour'
AS
SELECT
C.CUSTOMER_ID,
C.CUSTOMER_NAME,
C.EMAIL,
C.REGION,
COUNT(O.ORDER_ID) AS TOTAL_ORDERS,
SUM(O.TOTAL_AMOUNT) AS TOTAL_SPENT,
MIN(O.ORDER_DATE) AS FIRST_ORDER_DATE,
MAX(O.ORDER_DATE) AS LAST_ORDER_DATE
FROM {{ db_name }}.STG.STG_CUSTOMERS C
LEFT JOIN {{ db_name }}.STG.STG_ORDERS O
ON C.CUSTOMER_ID = O.CUSTOMER_ID
GROUP BY C.CUSTOMER_ID, C.CUSTOMER_NAME, C.EMAIL, C.REGION;
DEFINE DYNAMIC TABLE {{ db_name }}.MART.MART_PRODUCT_SALES
WAREHOUSE = {{ wh_name }}
TARGET_LAG = '1 hour'
AS
SELECT
OI.PRODUCT_NAME,
COUNT(DISTINCT OI.ORDER_ID) AS ORDER_COUNT,
SUM(OI.QUANTITY) AS TOTAL_QUANTITY,
SUM(OI.LINE_TOTAL) AS TOTAL_REVENUE,
AVG(OI.UNIT_PRICE) AS AVG_UNIT_PRICE
FROM {{ db_name }}.STG.STG_ORDER_ITEMS OI
GROUP BY OI.PRODUCT_NAME;

9. タスクの定義(COPY INTOによるデータ取り込み)
Internal StageにアップロードされたCSVファイルを各テーブルに取り込むためのタスクをDEFINE TASKで定義します。
各タスクはCRONスケジュールで毎日午前2時(JST)に実行される設定です。PURGE = TRUEを指定しているため、COPY INTO成功後にステージ上のファイルは自動削除されます。
なお、タスクはDeployされた時点ではサスペンド状態で作成されます。
sources/definitions/tasks.sql
-- CUSTOMERSテーブルへのCOPY INTOタスク
DEFINE TASK {{ db_name }}.RAW.LOAD_CUSTOMERS
WAREHOUSE = {{ wh_name }}
SCHEDULE = 'USING CRON 0 2 * * * Asia/Tokyo'
AS
COPY INTO {{ db_name }}.RAW.CUSTOMERS
FROM @{{ db_name }}.RAW.DATA_STAGE/customers
FILE_FORMAT = (TYPE = 'CSV' FIELD_OPTIONALLY_ENCLOSED_BY = '"' SKIP_HEADER = 1 NULL_IF = ('NULL', 'null', ''))
ON_ERROR = 'CONTINUE'
PURGE = TRUE;
-- ORDERSテーブルへのCOPY INTOタスク
DEFINE TASK {{ db_name }}.RAW.LOAD_ORDERS
WAREHOUSE = {{ wh_name }}
SCHEDULE = 'USING CRON 0 2 * * * Asia/Tokyo'
AS
COPY INTO {{ db_name }}.RAW.ORDERS
FROM @{{ db_name }}.RAW.DATA_STAGE/orders
FILE_FORMAT = (TYPE = 'CSV' FIELD_OPTIONALLY_ENCLOSED_BY = '"' SKIP_HEADER = 1 NULL_IF = ('NULL', 'null', ''))
ON_ERROR = 'CONTINUE'
PURGE = TRUE;
-- ORDER_ITEMSテーブルへのCOPY INTOタスク
DEFINE TASK {{ db_name }}.RAW.LOAD_ORDER_ITEMS
WAREHOUSE = {{ wh_name }}
SCHEDULE = 'USING CRON 0 2 * * * Asia/Tokyo'
AS
COPY INTO {{ db_name }}.RAW.ORDER_ITEMS
FROM @{{ db_name }}.RAW.DATA_STAGE/order_items
FILE_FORMAT = (TYPE = 'CSV' FIELD_OPTIONALLY_ENCLOSED_BY = '"' SKIP_HEADER = 1 NULL_IF = ('NULL', 'null', ''))
ON_ERROR = 'CONTINUE'
PURGE = TRUE;

10. DEVターゲットへのPlan & Deploy
定義ファイルが揃ったので、まずはDEVターゲットに対してPlanを実行します。
Snowsight WorkspaceのDCM Project画面で、ターゲットを「DEV」に切り替え、「Plan」にした状態で、再生マークのボタンを押します。

すると、作成されるオブジェクトの一覧が表示されます。各オブジェクトをクリックすると、設定値がどのようになっているか詳細も確認可能です。

ちなみに、右上のSummarizeを押すと、英語にはなりますがどういった変更が行われるかの要約もしてくれます。


問題なければ「Deploy」ボタンを押してデプロイを実行します。公式Docを見ると「Alias」は、Think of the deployment alias like a commit message for your code change.と書いてあったので、「first deploy」と入れておきます。


デプロイが正常に完了すると、下図のように表示されます。

デプロイ後、実際にオブジェクトが作成されているか確認します。(以下はクエリを用いた確認方法の例です。)
USE DATABASE DEV_DB;
SHOW SCHEMAS;
SHOW TABLES IN SCHEMA RAW;
SHOW TASKS IN SCHEMA RAW;
SHOW DYNAMIC TABLES IN SCHEMA STG;
SHOW DYNAMIC TABLES IN SCHEMA MART;

-- ロールとグラントの確認
SHOW ROLES LIKE 'DEV_EXPLORER_ROLE';
SHOW GRANTS TO ROLE DEV_EXPLORER_ROLE;

11. サンプルデータの投入と動作確認
サンプルCSVの準備
DEV環境にサンプルデータを投入して、タスクによるCOPY INTOとDynamic Tableのパイプラインが正しく動作するか確認します。
まず、以下の3つのCSVファイルをローカルに用意します。
customers.csv
CUSTOMER_ID,CUSTOMER_NAME,EMAIL,REGION,CREATED_AT
1,田中太郎,tanaka@example.com,tokyo,2025-12-01 10:00:00
2,佐藤花子,sato@example.com,osaka,2025-12-05 11:30:00
3,鈴木一郎,suzuki@example.com,tokyo,2025-12-10 09:15:00
4,高橋美咲,takahashi@example.com,fukuoka,2025-12-15 14:00:00
5,伊藤健太,ito@example.com,osaka,2025-12-20 16:45:00
6,渡辺由美,watanabe@example.com,nagoya,2026-01-03 08:30:00
7,山本拓也,yamamoto@example.com,tokyo,2026-01-08 13:00:00
8,中村さくら,nakamura@example.com,sapporo,2026-01-12 10:20:00
9,小林大輔,kobayashi@example.com,fukuoka,2026-01-18 15:10:00
10,加藤愛,kato@example.com,tokyo,2026-01-25 11:00:00
orders.csv
ORDER_ID,CUSTOMER_ID,ORDER_DATE,ORDER_STATUS,TOTAL_AMOUNT,CREATED_AT
1001,1,2026-01-15,completed,15000.00,2026-01-15 10:30:00
1002,2,2026-01-18,completed,8500.00,2026-01-18 14:00:00
1003,1,2026-01-22,completed,23000.00,2026-01-22 09:45:00
1004,3,2026-02-01,completed,4200.00,2026-02-01 11:20:00
1005,4,2026-02-05,shipped,31500.00,2026-02-05 16:00:00
1006,5,2026-02-10,completed,9800.00,2026-02-10 13:30:00
1007,2,2026-02-14,completed,12000.00,2026-02-14 10:15:00
1008,6,2026-02-20,shipped,7600.00,2026-02-20 15:45:00
1009,7,2026-03-01,completed,18500.00,2026-03-01 08:00:00
1010,8,2026-03-05,pending,5400.00,2026-03-05 12:30:00
1011,3,2026-03-08,completed,11200.00,2026-03-08 14:20:00
1012,9,2026-03-10,shipped,26800.00,2026-03-10 09:00:00
1013,10,2026-03-12,completed,3900.00,2026-03-12 11:45:00
1014,1,2026-03-15,pending,14700.00,2026-03-15 16:30:00
1015,5,2026-03-18,completed,8900.00,2026-03-18 10:00:00
order_items.csv
ORDER_ITEM_ID,ORDER_ID,PRODUCT_NAME,QUANTITY,UNIT_PRICE,CREATED_AT
1,1001,ワイヤレスイヤホン,1,8000.00,2026-01-15 10:30:00
2,1001,スマホケース,2,3500.00,2026-01-15 10:30:00
3,1002,USBケーブル,5,1700.00,2026-01-18 14:00:00
4,1003,モニターアーム,1,15000.00,2026-01-22 09:45:00
5,1003,マウスパッド,2,4000.00,2026-01-22 09:45:00
6,1004,キーボードカバー,3,1400.00,2026-02-01 11:20:00
7,1005,4Kモニター,1,29000.00,2026-02-05 16:00:00
8,1005,HDMIケーブル,1,2500.00,2026-02-05 16:00:00
9,1006,ワイヤレスマウス,2,4900.00,2026-02-10 13:30:00
10,1007,Webカメラ,1,12000.00,2026-02-14 10:15:00
11,1008,USBハブ,2,3800.00,2026-02-20 15:45:00
12,1009,メカニカルキーボード,1,18500.00,2026-03-01 08:00:00
13,1010,マウスパッド,1,4000.00,2026-03-05 12:30:00
14,1010,キーボードカバー,1,1400.00,2026-03-05 12:30:00
15,1011,ワイヤレスイヤホン,1,8000.00,2026-03-08 14:20:00
16,1011,USBケーブル,2,1600.00,2026-03-08 14:20:00
17,1012,4Kモニター,1,29000.00,2026-03-10 09:00:00
18,1013,スマホケース,1,3900.00,2026-03-12 11:45:00
19,1014,Webカメラ,1,12000.00,2026-03-15 16:30:00
20,1014,USBケーブル,3,900.00,2026-03-15 16:30:00
21,1015,ワイヤレスマウス,1,4900.00,2026-03-18 10:00:00
22,1015,マウスパッド,1,4000.00,2026-03-18 10:00:00
CSVファイルのアップロード
用意したCSVファイルをSnowsightから内部ステージにアップロードします。
タスクの定義でFROM @{{ db_name }}.RAW.DATA_STAGE/customersのようにサブパスを指定しているため、アップロード先のパスをファイルごとに分けます。
customers.csv→ パス:/customers/orders.csv→ パス:/orders/order_items.csv→ パス:/order_items/

アップロード後、ステージ上にファイルが配置されていることを確認します。3つのCSVファイルがそれぞれのサブパスに表示されていればOKです。
LIST @DEV_DB.RAW.DATA_STAGE;

タスクのRESUMEと手動実行
タスクはDeployされた時点ではサスペンド状態(stateがsuspended)です。まずSHOW TASKSで状態を確認します。
SHOW TASKS IN SCHEMA DEV_DB.RAW;

state列がsuspendedであることが確認できます。
タスクを有効化するには、ALTER TASK ... RESUMEを実行します。今回は3つの独立したタスク(親子関係なし)なので、それぞれ個別にRESUMEします。
ALTER TASK DEV_DB.RAW.LOAD_CUSTOMERS RESUME;
ALTER TASK DEV_DB.RAW.LOAD_ORDERS RESUME;
ALTER TASK DEV_DB.RAW.LOAD_ORDER_ITEMS RESUME;
再度SHOW TASKSを実行し、state列がstartedになっていればOKです。
SHOW TASKS IN SCHEMA DEV_DB.RAW;

タスクのスケジュールは毎日午前2時(JST)ですが、今回は動作確認のためにEXECUTE TASKで手動実行します。
EXECUTE TASK DEV_DB.RAW.LOAD_CUSTOMERS;
EXECUTE TASK DEV_DB.RAW.LOAD_ORDERS;
EXECUTE TASK DEV_DB.RAW.LOAD_ORDER_ITEMS;
各テーブルにデータが取り込まれていることを確認します。
SELECT COUNT(*) FROM DEV_DB.RAW.CUSTOMERS; -- 10件
SELECT COUNT(*) FROM DEV_DB.RAW.ORDERS; -- 15件
SELECT COUNT(*) FROM DEV_DB.RAW.ORDER_ITEMS; -- 22件

REFRESH ALLによるDynamic Tableの一括リフレッシュ
COPY INTOの実行後、Dynamic TableがTARGET_LAGに基づいて自動リフレッシュされるのを待つこともできますが、DCM ProjectsのREFRESH ALLコマンドを使うとプロジェクト配下のDynamic Tableを即座に一括リフレッシュできます。
SQLで実行する場合は以下のとおりです。REFRESH ALLはプロジェクトが管理する全Dynamic Tableとその上流のDynamic Tableを対象にリフレッシュを実行します。結果はJSON形式で返され、各Dynamic Tableのinserted_rows(挿入行数)、deleted_rows(削除行数)、data_timestamp(データの鮮度を示すタイムスタンプ)が確認できます。
EXECUTE DCM PROJECT DCM_ADMIN.PROJECTS.MY_DCM_PROJECT_DEV
REFRESH ALL;

MART層のDynamic Tableにデータが反映されていることを確認します。顧客ごとの注文集計や商品ごとの売上集計が正しく表示されていればOKです。
SELECT * FROM DEV_DB.MART.MART_CUSTOMER_ORDERS ORDER BY TOTAL_SPENT DESC;
SELECT * FROM DEV_DB.MART.MART_PRODUCT_SALES ORDER BY TOTAL_REVENUE DESC;

12. PRODターゲットへのPlan & Deploy
続いてPRODターゲットにも同様にデプロイします。PRODターゲットでは、manifest.ymlでMY_DCM_PROJECT_PRODという別のDCM project objectを指定しているため、DEV環境には影響しません。
まず、以下のクエリを実行して、PROD用のDCM project objectを作成します。
USE ROLE ACCOUNTADMIN;
CREATE DCM PROJECT IF NOT EXISTS DCM_ADMIN.PROJECTS.MY_DCM_PROJECT_PROD
COMMENT = 'PROD用のDCMプロジェクト';
次に、WorkspaceのDCM Project画面で、ターゲットを「PROD」に切り替え「Plan」の状態で、再生ボタンを押します。(PROD用のDCM project objectが認識されておらずエラーとなる場合は、ブラウザの画面の更新を実施すれば直るはずです。)

DEVと同様のオブジェクトが、PROD_DBに対して新規作成される差分として表示されます。ウェアハウスサイズがSMALLになっている点も確認してください。

問題なければ「Deploy」ボタンを押してデプロイを実行します。


デプロイが正常に完了したら、PROD環境のオブジェクトを確認します。PROD_DBにもDEV_DBと同じスキーマ構成・オブジェクトが作成されており、ウェアハウスPROD_WHのサイズがSMALL、PROD_EXPLORER_ROLEに対して各オブジェクトへの権限が付与されていればOKです。
USE DATABASE PROD_DB;
SHOW SCHEMAS;
SHOW TABLES IN SCHEMA RAW;
SHOW TASKS IN SCHEMA RAW;
SHOW DYNAMIC TABLES IN SCHEMA STG;
SHOW DYNAMIC TABLES IN SCHEMA MART;
SHOW WAREHOUSES LIKE 'PROD_WH';

-- ロールとグラントの確認
SHOW ROLES LIKE 'PROD_EXPLORER_ROLE';
SHOW GRANTS TO ROLE PROD_EXPLORER_ROLE;

これで、**1つのDCM project folder(定義ファイル群)**を使いながら、DEV用とPROD用の別々のDCM project objectに対して、テンプレート変数を切り替えて同一構成をデプロイできることが確認できました!
※PROD環境に対して、タスクの有効化とDynamic Tableのリフレッシュは別途必要となるためご注意ください。
おまけ: 定義ファイルからオブジェクトを変更・削除するとどうなるか
DCM Projectsでは、定義ファイルからDEFINE文を削除すると次回のDeployでそのオブジェクトがdrop対象になるとされています。実際にそうなるのか、確かめてみます。
変更点その1:ウェアハウス定義のコメントアウト
sources/definitions/warehouses.sqlの内容をコメントアウトします。

変更点その2:タスクの定義変更
1つのタスクだけ、毎日3時に実行されるように変更します。

Planの実行
DEVターゲットに対してPlanを実行します。

すると、Planの結果は下図のようになりました。DEV_WHがDROP対象として表示され、タスクはALTER対象でどの設定値が変わるのかもわかるようになっています。

Deployの実行
そのままDeployを実行します。(これもおまけですが、Aliasは日本語でも問題なかったです。)

Deploy後、実際にウェアハウスが削除されているか確認すると、下図の通り何も表示されなかったので削除されていることがわかります。
SHOW WAREHOUSES LIKE 'DEV_WH';

タスクについても、設定値が変更され、stateもstartedのままであることがわかります。これは素晴らしい!

最後に
Snowflake DCM Projectsを使って、同一アカウント内にDEV/PROD環境を構築する手順を試してみました。
実際に触ってみて感じたのは、テンプレート変数によるDEV/PROD環境の分離が非常にシンプルに実現できるという点です。manifest.ymlのtemplating.configurationsでdb_nameやwh_sizeを定義するだけで、同じ定義ファイル群から異なるパラメータの環境をデプロイできるのは便利でした。(dbtに慣れている方はスムーズに導入できると思います。)
また、Plan→Deployの安全なワークフローがネイティブに組み込まれているのも良い点です。Terraformのplan→applyに慣れている方であれば、同じ感覚で使えると思います。Dynamic TableのREFRESH ALLやデータ品質テストのTEST ALLがDCM Projectの機能として統合されている点も、データパイプラインの管理において便利だと感じました。
一方で、File FormatなどDCM管理対象外のオブジェクトが存在する点や、プレビュー機能のためrenameの制限をはじめとする各種制限事項がある点も押さえておくべきポイントです(2026年3月20日時点)。
いずれにせよ、今後のアップデートがとても楽しみな機能です!!






