RevComm Tech Blog

コミュニケーションを再発明し 人が人を想う社会を創る

CDK for Terraform × Typescript × GCP をやってみた

この記事は、RevComm Advent Calender 19 日目の記事です。

はじめに

こんにちは、株式会社 RevComm でフロントエンドチームでエンジニアをしている高橋(@katakana_33)と申します。

今回は、私が RevComm に入って初めて見たソースコードが Terraform だった事と関連して、CDK for Terraform と GCP の連携について、実践した使用感を書きました。

RevComm の良さの1つとして、GitHub Organization 内の他プロダクトのリポジトリを見れる事でプロダクト全体の理解を深め、自身の専門職種外の技術スキルにも目を向けられる点があります。 RevCommのオンボーディング期間のその日のオンボードが終了した後、私が初めて見たソースコードが担当するプロダクトの Terraform でした。未経験の技術だったこともあり、そこで今回は、CDK for TerraformとGCPの連携について調査と実践をしました。

TL;DR

  • 0 から IaC を導入する場合は、メリットの方が大きい。
  • 移行コストと導入必要性を考慮した際、移行しない事も選択肢の 1 つ。

CDK for Terraform とは

CDK for Terraform by Hashicorp

AWS CDKチームとHashicorp 社が共同開発をしたツールで、 AWS CDK と Terraform の良い所を合わせたものです。

AWS CDK の開発者 kit を使用して、インフラリソースの定義とデプロイを行えます。 公式より以下概念図 image

特徴

  • HCL の学習を必要とせず、普段使用しているメジャーな言語で IaC を記述できる。以下がサポートされているプログラミング言語。
    • TypeScript、Python、Java、C#、Go。(2022 年 12 月時点)
  • Terraform エコシステム全体にアクセスできて、各機能のテストを行える。
  • マルチプロバイダが可能な為、ベンダーロックインを回避できる。

技術比較

Terraform CDK CDK for Terraform
HCLの学習 必要 不要 不要
マルチプロバイダ 可能 不可能(AWSのみ) 可能
参考文献(有志の記事を含む) 多い 多い 少ない(有志の記事が少ない、イレギュラーケースがまだ明るみになっていない可能性が高い)

実践

今回 は、web サービスでよく使いそうな GCP の以下の resources(本記事ではcloud infrastructure resources を指します。) を作成し deploy します。

  • Compute Network
  • Compute SubNetwork
  • Compute Disk
  • IP address

前提条件

あらかじめ以下の環境を用意してください

環境構築

上記の環境を構築したら、CDK for Terraform 独自の project を構築します

npm install --global cdktf-cli@latest     // cdktf package install
cdktf help                                // check install
mkdir learn-cdktf && cd learn-cdktf       // create workingDirectory
cdktf init --template=typescript --local  // create project(入力すると以下質問が出る)
? Project Name         // 任意の名前
? Project Description  // 任意の説明
? Do you want to start from an existing Terraform project? (デフォルトNo)
? Do you want to send crash reports to the CDKTF team? (デフォルトYes)
? What providers do you want to use? google

質問に答えると、以下の構成ファイル群が生成されます。

.
├── __tests__
├── cdktf.json
├── help
├── jest.config.js
├── main.ts
├── package-lock.json
├── package.json
├── setup.js
└── tsconfig.json

GCP provider を使えるようにするには、このファイル群があるディレクトリにて以下のcommand を実行します。(GCP 以外の各種 provider が欲しい際は公式を参照ください。)

npm install @cdktf/provider-google

これで CDK for Terraform × Typescript × GCP の環境が構築できました。

resources 作成

構築する provider resources は、ルートディレクトリ直下の main.ts の Stack(Hashicorp:Stacks)に記述します。 以下は初期状態です。 TerraformStack を継承した MyStack が自動生成されています。

// Copyright (c) HashiCorp, Inc
// SPDX-License-Identifier: MPL-2.0
import { Construct } from "constructs";
import { App, TerraformStack } from "cdktf";
 
class MyStack extends TerraformStack {
 constructor(scope: Construct, id: string) {
   super(scope, id);
 
   // define resources here
 }
}
 
const app = new App();
new MyStack(app, "project-name"); // 環境構築時に質問で回答したproject名がここに入る
app.synth();

MyStack の constructor の中で provider の instance を生成します。 config 情報の project の value には、実存する GCP project 名を記入ください。

class MyStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
    super(scope, id);
    new GoogleProvider(this, "google", {
      project: "project-name",
    })
    // define resources here
  }
}

project 名を正確に書かずに deploy をした場合は以下の error がでます。 (project 名を hoge として deploy した結果)

│ Error: Error creating Disk: googleapi: Error 400: Consumer 'projects/hoge' is invalid: Resource projects/hoge could not be found..

provider instance を作成したら、任意の resources を書きます。 今回は以下ソースコードの resources を作成し deploy します。 resources は constructor 内に定義します。 使用するGCP resourcesの使用方法は、公式リファレンスには殆ど記載がないので、 以下の GitHub のソースコードから使用する resources を import して、各 resources に必要な key と value を渡します。

GitHub: cdktf-provider-google

 class MyStack extends TerraformStack {
 constructor(scope: Construct, id: string) {
   super(scope, id);
   // define resources here
 
   const ZONE = "asia-northeast1-b";
   const PROJECT_ID = "katakana-id";   // 任意のID 各resourcesに付与する
   const PROJECT_NAME = "katakana-tr"; // GCP projectの名前
   const PROJECT_PREFIX = "example";
   const PROJECT_PREFIX_NAME = `${PROJECT_PREFIX}-${PROJECT_NAME}`;
   const PROJECT_PREFIX_ID = `${PROJECT_PREFIX}-${PROJECT_ID}`;
 
   new GoogleProvider(this, "google", {
     project: PROJECT_NAME,
   })
 
   const network = new ComputeNetwork(this, `${PROJECT_PREFIX_ID}-network`, {
     name: `${PROJECT_PREFIX_NAME}-network`,
     routingMode:"REGIONAL",
     autoCreateSubnetworks:false,
   })
 
   const subnetwork = new ComputeSubnetwork(this, `${PROJECT_PREFIX_ID}-subnetwork`, {
     name: `${PROJECT_PREFIX_NAME}-subnetwork`,
     network: network.selfLink,
     ipCidrRange: "10.10.0.0/16",
     region: "asia-northeast1",
 });
 
   const address = new ComputeAddress(this, `${PROJECT_PREFIX_ID}-address`, {
     name: `${PROJECT_PREFIX_NAME}-address`,
     subnetwork: subnetwork.selfLink,
     region: subnetwork.region,
     addressType: "INTERNAL",
 });
 
   const disk = new ComputeDisk(this, `${PROJECT_PREFIX_ID}-disk`, {
     name: `${PROJECT_PREFIX_NAME}-disk`,
       zone: ZONE,
       type: "pd-standard",
       size: 10,
       image: "ubuntu-os-cloud/ubuntu-2204-lts",
   });
 
   new ComputeInstance(this, `${PROJECT_PREFIX_ID}-instance`, {
     name: `${PROJECT_PREFIX_NAME}-instance`,
       zone: ZONE,
       machineType: "e2-medium",
       bootDisk: {
         autoDelete: false,
         source: disk.selfLink
       },
       networkInterface: [
         {
           networkIp: address.address,
           subnetwork: subnetwork.selfLink,
         }
       ],
       canIpForward: false,
       tags: ["iap"]
   });
 }
}
 
const app = new App();
new MyStack(app, "dev");
new MyStack(app, "stg");
new MyStack(app, "prod");
app.synth();

Stack を 環境毎に初期化することで、同じ宣言内容で個別の deploy が可能となります。

Deploy

最後に deploy です。 手順は以下のとおりです。

  1. GCP 認証・deploy 内容の確認
  2. 対象を指定して deploy

GCP認証

以下のコマンドで GCP への認証をします。

gcloud auth application-default login

GoogleProviderClass の config という引数の credentials という入力用変数でも認証を行うことができますが、今回は楽に認証が行えるコマンド認証を使っています。

deploy内容の確認

cdktf planコマンドで確認します。出力内容を全てを載せると量が多くなる為、 以下は、google_compute_instance の deploy 内容です。

cdktf plan
create-disk    # google_compute_instance.example-katakana-id-instance (example-katakana-id-instance) will be created
              + resource "google_compute_instance" "example-katakana-id-instance" {
                  + can_ip_forward       = false
                  + cpu_platform         = (known after apply)
                  + current_status       = (known after apply)
                  + deletion_protection  = false
                  + guest_accelerator    = (known after apply)
                  + id                   = (known after apply)
                  + instance_id          = (known after apply)
                  + label_fingerprint    = (known after apply)
                  + machine_type         = "e2-medium"
                  + metadata_fingerprint = (known after apply)
                  + min_cpu_platform     = (known after apply)
                  + name                 = "example-katakana-tr-instance"
                  + project              = (known after apply)
                  + self_link            = (known after apply)
                  + tags                 = [
                      + "iap",
                    ]
                  + tags_fingerprint     = (known after apply)
                  + zone   
省略

対象を指定し deploy 複数の stack がある場合は、stack 名を指定しないと以下の error になります。

  • cdktf plan (stack 名)
Usage Error: Found more than one stack, please specify a target stack. Run cdktf deploy <stack> with one of these stacks: (stack名), (stack名2)

deploy 対象に間違いなければ deploy します。 この場合も、複数 stack がある場合は、指定します。 deploy 時、以下の3つの選択肢があります、Approve をすると deploy されます。

  • cdktf deploy (stack名)
    • Approve
    • Dismiss
    • Stop

error が無ければ、cdktf plan で出力された内容が出力され、 最後に以下の出力が表示されて、deploy 完了です。

Apply complete! Resources: 5 added, 0 changed, 0 destroyed.

画像は、私の個人環境にて、今回 deploy された resources になります。 これで、GCP の Network、SubNetwork、IP address、 Disk が作成できました。

GCP deploy

終わりに

今回の実践にあたり、 「CDK for Terraform × Typescript × GCP」で IaC と deploy を行った使用感は以下となります。

  • Terraform の利点を享受できたこと。(環境構築、diff、deploy のしやすさ。)
  • Typescript フレンドリーに package の import、provider resources の実装ができる。
  • 各 provider で使用する resources class を把握すれば、ESLint 等の Lint を入れてる人は恩恵がわかりやすく、言語補完が効くのでスラスラ書ける。
  • 変数や関数の再利用が書きやすく、読みやすい。

私が個人開発をする際は、CDK for Terraform を使うと思います。

RevComm では一緒に働く仲間を募集しています。 このブログを読んで興味を持ってくれた方、そうでない方も、 ぜひ採用サイトをチェックしてみてください!

www.revcomm.co.jp