ソーシャル経済メディア「NewsPicks」のエンジニアの中村です。最近はデータ基盤の開発・運用や、プロダクト開発におけるAI利用のためのルール整備・ツール開発などに取り組んでいます。
NewsPicksではデータ基盤としてSnowflakeを運用しており1、Snowflakeのインフラ管理を行うためのツールとしてCDK for Terraform(CDKTF)を利用しています2。 本記事では、CDKTFを使ったSnowflakeインフラ管理の概要と、実際に運用して感じたメリット・デメリットについて紹介します。想定読者はSnowflakeの運用に携わる方のうち、TerraformもCDKTFも全く使ったことがない方、あるいはTerraformは触ったことがあるがCDKTFは初めての方です。何かしらの参考になれば幸いです。
【注意事項】 本記事を執筆しようと思っていた矢先にCDKTFの開発停止が開発元のHashiCorpから発表されました3。 現時点ではOpen Construct Foundation(OCF)がCDKTFの開発を引き継ぐ意向を表明していますが、具体的な詳細はまだ明らかになっていません4。 状況が明確になるまでCDKTFの新規採用は慎重に検討することをおすすめします。いますぐ始めたい方はCDKTFとコンセプトが類似しているPulumi等のツールを検討した方がいいかもしれません。
この記事はNewsPicks Advent Calendar 2025の24日目の記事です。
CDKとは
CDKTFの話をする前に、そのコンセプト的な祖先と言うべきAWS Cloud Development Kit(CDK)について簡単に説明します。
CDKはTypeScript等、通常のプログラミング言語を用いてAWSのインフラを管理できるツールで、AWSが公式に提供しているものです。
AWSでInfrastructure as Code(IaC)を実現するためのツールといえば他にCloudFormationやTerraformが有名ですが、これらがYAMLやHCLといった設定ファイル、設定専用言語を用いるのに対して、CDKはアプリケーション開発に使うような通常のプログラミング言語によってインフラ定義が書けるのが最大の特長です。TypeScriptをはじめとする複数の言語がサポートされています。CDKではプログラミング言語で書いたインフラ定義からCloudFormationテンプレートが生成され、そのテンプレートに基づいてAWSリソースがデプロイされるという仕組みになっています。
NewsPicksではAWSのインフラ管理にCDK(TypeScript)を全面的に採用しており、アプリケーションエンジニアであっても日々快適にインフラの変更を行える環境が実現できています5。
CDKTF とは
CDK for Terraform(CDKTF)は、HashiCorp社が提供するしていた、CDKと同様の仕組みによって様々なクラウドインフラ管理ができるツールです。
CDKと同様の仕組みというのは、
- プログラミング言語でインフラ設定が書けて、書き方がCDKと同じ
- 多言語対応のライブラリを提供するための内部的な仕組み(jsii)がCDKと同じ
という意味です。
では逆にCDKと何が違うのかというと、以下の点が重要です。
- CloudFormationテンプレートを生成するCDKとは異なり、CDKTFはTerraformの設定(HCLやJSON)を生成してTerraformでデプロイを実行する
- そのため、Terraformが対応しているAWS以外のクラウドサービスのリソースも管理できる
要するに、CDKTFを使えばTypeScriptでSnowflakeのインフラ設定が書けるということです。
なぜCDKTFを使うのか
Snowflakeのインフラ管理を何らかのIaCツールで行うとして、CDKTFを使う理由は何でしょうか?CDKTFは結局のところTerraformのラッパーなので、素のTerraformを使うのがいちばん素直な選択肢にも思えます。その中であえてCDKTFを選んだ理由は以下の通りです。
すでにAWSのインフラ管理にCDKを利用していたから
NewsPicksのアプリケーションの大半はAWS上で稼働しており、前述の通りそのインフラ管理にはCDKを活用しています。 CDKによって誰もがAWSのインフラを安全かつ快適に変更できる環境がすでに整っていたため、SnowflakeでもCDKTFによって同様の開発者体験を実現したいと考えました。
他サービスでのCDKTF採用実績があったから
AWS以外のクラウドサービスの管理ですでにCDKTFを採用した実績がありました6。 組織内で実績のある技術を使うのはやはり安心感がありますし、すでにCDK/CDKTFが定着している以上、Snowflakeでも同じツールを採用するのが自然でした。
TypeScriptによる開発者体験がよいから
個人的にはこれが最も重要だと感じています。一般的なプログラミング言語に備わっているクラスや制御構文を用いてインフラ定義を抽象化できるというメリットに加えて、TypeScriptの型によるサポートがとにかく強力で、開発者体験が最高です。
どんなIaCツールを使うにせよ、インフラの変更のためには正しい設定値を探してドキュメントを漁ったり、開発環境にデプロイして動作確認をしたりといった作業に多くの時間を使うことになりますが、型のサポートがあれば、文法ミスや存在しない設定値を書いてしまうといったつまらないエラーからはほぼ解放されます。もちろんデプロイ時にしかわからない問題は残りますが、開発に伴うストレスが大きく軽減されます。
CDKTF超入門
ここからはCDKTFを使ってSnowflakeにインフラをデプロイするまでの手順を一通り見ていきます。詳細は適宜省略するのでそのままでは動きませんが、流れはつかめるはずです。
プロジェクト初期化
CDKTFプロジェクトを新規作成するにはcdktf initコマンドを使います。また、Snowflakeを扱うために必要なパッケージもインストールします。事前にNodeとTerraformをインストールしておく必要があります。
npx cdktf init --template="typescript" npm install @cdktf/provider-snowflake
プロジェクト名や各種設定を尋ねられるのでよしなに設定してください。
インフラ定義
main.tsというファイルが作成されているはずなので、ここにインフラ定義を書いていきます。
ここでは試しにデータベースを定義してみます。
import { Database } from "@cdktf/provider-snowflake/lib/database"; import { SnowflakeProvider } from "@cdktf/provider-snowflake/lib/provider"; import { App } from "cdktf"; import { TerraformStack } from "cdktf"; import { Construct } from "constructs"; class MySnowflakeStack extends TerraformStack { constructor(scope: Construct, id: string) { super(scope, id); new SnowflakeProvider(this, "MySnowflakeProvider", { organizationName: "my_organization", accountName: "my_account", user: "my_user", role: "my_role", warehouse: "my_warehouse", // ...その他、必要な認証情報を記入 }); new Database(this, "MyDatabase", { name: "MY_DATABASE", }); } } const app = new App(); new MySnowflakeStack(app, "MySnowflakeStack"); app.synth();
何をやっているかはコードから簡単に推測できると思いますが、CDKTFの主要登場人物について説明します。
プロバイダー(SnowflakeProvider)
CDKTFがSnowflakeオブジェクトの操作に使用する認証情報を定義しています。Terraformのプロバイダー設定に相当します。
コンストラクト(Database)
MY_DATABASEという名前のデータベースを定義しています。
Snowflakeの各種のオブジェクトそれぞれに対応するコンストラクトが用意されています。
Terraformでいうリソースに相当しますが、CDKTFにおけるコンストラクトはTypeScriptの普通のクラスでもあり、独自コンストラクトを定義することも可能です(後述)。
スタック(MySnowflakeStack)
CDKTFのデプロイ単位がスタックです。1つのスタックで複数のコンストラクトをまとめてデプロイすることができますし、別のスタックに入れたコンストラクトは別々にデプロイできます。
スタックのconstructor()の中に必要なコンストラクトのインスタンスを作成していくことでインフラを定義するというのが、CDKTFの基本的なお作法です。
アプリケーション(App)
最後の3行はひとまずおまじないだと思っておいて大丈夫です。
デプロイ
インフラの差分をcdktf diffコマンドで確認し、デプロイはcdktf deployコマンドで実行します。
npx cdktf diff MySnowflakeStack npx cdktf deploy MySnowflakeStack
削除
インフラを削除する際は、不要になったコンストラクトをコードから削除した上でcdktf deployを実行すればOKです。
スタック全体を削除したい場合はcdktf destroyコマンドを使います。
npx cdktf destroy MySnowflakeStack
以上であなたもCDKTFマスターです!
ここがうれしいCDKTF
とはいえ、この単純すぎる例だけではCDKTFのメリットが伝わらないと思うので、TypeScriptで設定が書けることのうれしさが感じられる例をいくつか紹介します。
制御構文
データベースの例を拡張して、
- 複数のデータベースを作成したい
- 特定のデータベースを開発用のSnowflakeアカウントにだけ作成したい
といった要件を考えてみます。
CDKTFなら慣れ親しんだfor文やif文を使ってこれらを簡単に表現できます。
// 複数データベース const databaseNames = ["MY_DATABASE_A", "MY_DATABASE_B", "MY_DATABASE_C"]; for (const databaseName of databaseNames) { new Database(this, databaseName, { name: databaseName, }); }
// 環境ごとの設定 const needsExtraDatabase = true; // 実際には環境変数等から設定値を渡す想定 const databaseNames = ["MY_DATABASE_A", "MY_DATABASE_B", "MY_DATABASE_C"]; if (needsExtraDatabase) { databaseNames.push("MY_EXTRA_DATABASE"); } for (const databaseName of databaseNames) { new Database(this, databaseName, { name: databaseName, }); }
YAML等の設定ファイルや設定専用言語の場合、このような要件が複雑になってくると、独自構文を駆使したり、マクロやテンプレート的な仕組みを導入したり、結局自作プログラムで設定ファイルを生成したりといったことになりがちですが、CDKTFでははじめから設定自体がプログラムになっているので簡単に実装できます。
モジュール
上の例では例示を簡単にするために環境ごとの設定と言いつつハードコードしてしまいましたが、このような設定はTypeScriptのモジュールとして別ファイルで管理するのが便利です。
// config.ts interface Config { needsExtraDatabase: boolean; } const configs: { [key: string]: Config } = { prod: { needsExtraDatabase: false, }, dev: { needsExtraDatabase: true, }, }; export const config = configs[process.env.SNOWFLAKE_ENVIRONMENT!];
// main.ts import { config } from "./config"; const needsExtraDatabase = config.needsExtraDatabase; // 以下同様
単に設定がモジュール化できてキレイになったというだけでなく、Config型を定義したことによってミスなく設定が書けるというのも大きなポイントです。
独自コンストラクト
CDKTFで用意されているコンストラクトはそれぞれが1種類のSnowflakeオブジェクトに対応しているため、実際に利用する際には粒度が細かすぎると感じることがあります。例えば、データベースとその中のスキーマ、ロールとそれに対する権限付与(grant)などはセットで管理したいことが多いでしょう。あるいは、特定のプロジェクトやアプリケーションで使用するSnowflakeオブジェクト(ユーザー、ロール、ウェアハウス等々)をまとめて管理したいこともあるかもしれません。
このような場合、独自のコンストラクトを定義して関連するSnowflakeオブジェクトをまとめて扱うことができます。
class DatabaseAndSchemas extends Construct { public readonly database: Database; public readonly schemas: Schema[] = []; constructor(scope: Construct, id: string, props: { databaseName: string; schemaNames: string[] }) { super(scope, id); this.database = new Database(this, props.databaseName, { name: props.databaseName, }); for (const schemaName of props.schemaNames) { const schema = new Schema(this, schemaName, { name: schemaName, database: props.databaseName, dependsOn: [this.database], }); this.schemas.push(schema); } } } class MySnowflakeStack extends TerraformStack { constructor(scope: Construct, id: string) { // ... new DatabaseAndSchemas(this, "MyDatabaseAndSchemas", { databaseName: "MY_DATABASE", schemaNames: ["MY_SCHEMA_A", "MY_SCHEMA_B"], }); } }
独自コンストラクトの実体は単なるTypeScriptのクラスであり、Constructを継承してさえいれば何を書いてもいいので、設定を抽象化する上で非常に自由度が高いです。
適切に使えばコードの見通しが良くなり、保守も容易になります。ただしあまり凝りすぎるとリファクタリングが難しくなったりもするので諸刃の剣という感はあります。
型
記事では伝わりにくいのですが、以上のような例すべてを書く上で、TypeScriptの型によるサポートが非常に強力です。
たとえ複雑なインターフェースやクラスを定義しても、型に誤りがあればエディタ上ですぐにわかりますし、cdktf diffやcdktf deployした際にもコンパイルに即失敗するので、自信を持って設定を書くことができます。
また、当然ながら型定義へのジャンプ等もできるので、エディタ上で多くの調べ物が完結するのもうれしいところです。
ここがつらいよCDKTF
インフラ設定を書くという点においては非常に快適なCDKTFですが、実際にこれを使ってSnowflakeを運用する上ではいくつかの課題もありました。
結局Terraformについても知っている必要がある
CDKTFはTerraformのラッパーであり、最終的にはterraformによってインフラのデプロイが行われます。 問題なく動いている時はよいのですが、トラブル発生時にはTerraformの知識が必要になる場面があります。
そもそも、cdktf diffやcdktf deployの実行時に表示されるログがterraform planやterraform applyの出力そのままです。そのため、エラー対応のために表示される推奨コマンドなどもcdktfではなくterraformが前提となっており、本当にまったくTerraformの知識がない状態でCDKTFを使い始めてしまうとうろたえます。
実際に遭遇したケースでは、Terraformプロバイダーのバージョンアップ後にterraform init -reconfigureを要求されたり、デプロイに失敗してステートファイルがロックされたままになってしまった際にterraform force-unlockの実行が必要になったりということがありました。
デプロイに時間がかかる
cdktf deployを実行すると必ずterraform planが走ります。これはCDKTFで管理しているTerraformのリソースが多くなるほど時間がかかります。Snowflakeの場合、権限(grant)をCDKTFで管理しているとリソース数が多くなりがちなため、権限を含むスタックのデプロイに時間がかかるようになってしまいました。
リファクタリングが大変
独自コンストラクトを用いた設定の抽象化は自由度が高く便利ですが、後からリファクタリングしたくなることもあります。
しかしCDKもCDKTFもリファクタリングは案外大変です。
あるコンストラクトを別のコンストラクトに移動させたり、スタックをまたいで移動させたりするのが容易ではないのです。
一応、CDKTF公式のリファクタリングガイドはあるのですが、スタックまたぎの場合は結局terraform importとterraform state rmを使う必要があるなど、やはりTerraformの知識が必要になってしまいます7。
突然の開発停止宣言
これはおまけではありますが、バージョン1到達前のオープンソースプロダクトとはいえ、まさか何の猶予もなく突然開発停止になるとは思いませんでした。 Githubのコミットログを見ると、ちょうど我々がSnowflakeでCDKTFを使い始めた昨年初め頃から開発が停滞しているのですが、当時気付くのは難しかったです。 OCFがうまく開発を引き継いでくれることを願いますが、状況の推移次第では他のツールへの移行も検討する必要があるかもしれません。
おわりに
CDKTFを用いたSnowflakeインフラ管理の概要を紹介しました。
Terraformのラッパーであるというアーキテクチャの性質により運用上つらいところもありますが、TypeScriptで設定が書けるという体験の良さは他に代えがたいものがあります。
CDKTF自体の開発は現在不安定な状況にあるものの、CDKやPulumi等の類似ツールも含めて「型が強力なプログラミング言語で設定を書く」というコンセプトはもっと普及してほしいと思っています(YAMLとJSONからは永遠に逃れられない気もしつつ……)。
- NewsPicksのデータ基盤の概要については次をご参照ください: Amazon Redshiftからの移行で積年の課題を解決↩
- CDKTFであらゆるSnowflakeオブジェクトを管理しているわけではなく、特にテーブルについてはschemachangeやdbt等、他のツールも利用しています↩
- The Future of Terraform CDK↩
- Open Construct Foundation Announces Initiative to Continue CDKTF Development↩
- なぜCDKを使う「べき」なのだろう? - Uzabase for Engineers↩
- New Relic と AWS CDK for Terraform を用いた NewsPicks の SLO 監視システムの構築↩
- Refactoring - CDK for Terraform↩