RareJob Tech Blog

レアジョブテクノロジーズのエンジニア・デザイナーによる技術ブログです

GCPリソースを CDK for Terraform で作成する

はじめまして、DMP (データマネジメントプラットフォーム) グループの すぎみつ です。

最近はスキルアップ手当*1で昇降デスクの購入を検討しています。電動式昇降デスクのすゝめによると自分に最適なデスクの高さは74cmでした。

さて、DMP グループでは現在 GCP のリソース管理に CDK for Terraform を利用していまして、CDK for Terraform の利用例についてご紹介します。

はじめに

DMP グループはレアジョブグループのデータ基盤の整備をメイン業務の一つとしています。 現在は既にあるデータ基盤を新たに BigQuery を中心とした Google Cloud Platform に移行しています。
レアジョブ英会話を始めとした各サービスは AWS で構成されており、収集対象となるデータは AWS から GCP に転送する流れとなります。
今回詳細には触れませんが、データ転送を行うパイプラインは AWS 側にあり、リソースの管理は AWS CDK で行っています。弊社の AWS リソースの管理は殆ど AWS CDK で統一されておりそれに倣っています。
AWSGCP を扱う環境なので、IaC は Terraform や Pulumi といったマルチクラウド対応のサービスを使いたいところですが、同じように書ける CDK for Terraform を利用することにしました。

CDK for Terraformとは

CDK for Terraform は簡単に言うと HCL を書かなくても、AWS CDK で書ける Terraformです。マルチクラウド対応する CDK になります。
www.terraform.io

AWS CDK とコンポーネントを共有していて、AWS CDK のようにコードで書いた定義を Terraform の HCL プロジェクトで利用する インフラ構成ファイル( Json ファイル)に変換し、Terraform でリソースの作成を行えます。

CDK for Terraform でのプロジェクト作成からデプロイまでの一連の操作は CDKTF CLI で行います。 https://mktg-content-api-hashicorp.vercel.app/api/assets?product=terraform-cdk&version=v0.11.2&asset=website%2Fdocs%2Fcdktf%2Fconcepts%2Fimages%2Fcdktf-terraform-workflow.png&width=4096&height=3070 https://www.terraform.io/cdktf/concepts/cdktf-architecture

サンプルの紹介

簡単にサンプルを紹介します。

サービスアカウントと GCS バケットの作成し、作成した GCS バケットに対してサービスアカウントからのアクセス権を許可する例になります。

CDK For Terraform のインストールとそれに必要な準備は公式の以下リンクを参照してください。
Install CDK for Terraform and Run a Quick Start Demo | Terraform - HashiCorp Learn

プロジェクトの作成

CDKTF CLI を実行していきます。 今回のプロジェクトは TypeScript で作成します。

$ cdktf init --template=typescript

プロジェクト初期化後のディレクトリ構成は以下のようになります。
cdk_for_tf というディレクトリ名以下にプロジェクトを作成しました。

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

実装

修正後の main.ts は以下になります。

import { Construct } from "constructs";
import { App, TerraformStack, GcsBackend } from "cdktf";
import { 
  GoogleProvider, 
  ServiceAccount, 
  StorageBucket, 
  StorageBucketIamBinding 
} from "./.gen/providers/google";


class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name);

    // リモートバックエンドの指定
    new GcsBackend(this, {
      bucket: 'cdktf-example-remote-backend',
      prefix: 'state',
    })

   const provider = new GoogleProvider(this, 'Set GCP provider',{
     project: `${project_id}`
   })

   // サービスアカウントの作成
   const serviceAccount = new ServiceAccount(this, 'Create Service Account',{
      provider: provider,
      accountId: 'cdktf-example-service-account',
      displayName: 'cdktf-example-service-account',
   })

    // GCSバケット作成
    const bucket = new StorageBucket(this, 'Create GCS Bucket', {
      provider: provider,
      location: 'ASIA-NORTHEAST1',
      name: 'cdktf-example-gcs-bucket',
      uniformBucketLevelAccess: true,
      dependsOn: [serviceAccount] 
    })

    // GCSバケットへの権限追加
    new StorageBucketIamBinding(this, 'Add role roles/storage.legacyBucketReader',
    {
      provider: provider,
      bucket: bucket.name,
      role: 'roles/storage.legacyBucketReader',
      members: [`serviceAccount:${serviceAccount.email}`],
      dependsOn: [serviceAccount, bucket]
    })

    // オブジェクトへの権限追加
    new StorageBucketIamBinding(this, 'Add role roles/storage.objectViewer',
    {
      provider: provider,
      bucket: bucket.name,
      role: 'roles/storage.objectViewer',
      members: [`serviceAccount:${serviceAccount.email}`],
      dependsOn: [serviceAccount, bucket]
    })
  } 
}

const app = new App();
new MyStack(app, "cdk_for_tf");
app.synth();

デプロイ

deploy コマンドで構成ファイルの生成及びリソースの作成まで纏めてできるためcdktf deployを実行します。尚、GCP のクレデンシャルは gcloud の認証で済ませているものとします。

$ cdktf deploy
cdk_for_tf  Initializing the backend...
cdk_for_tf  
            Successfully configured the backend "gcs"! Terraform will automatically
            use this backend unless the backend configuration changes.
cdk_for_tf  Initializing provider plugins...
            - Finding hashicorp/google versions matching "4.25.0"...
cdk_for_tf  - Using hashicorp/google v4.25.0 from the shared cache directory
cdk_for_tf  Terraform has created a lock file .terraform.lock.hcl to record the provider
            selections it made above. Include this file in your version control repository
            so that Terraform can guarantee to make the same selections by default when
            you run "terraform init" in the future.
cdk_for_tf
cdk_for_tf  Terraform has been successfully initialized!
cdk_for_tf  Terraform used the selected providers to generate the following execution
            plan. Resource actions are indicated with the following symbols:
            + create
            Terraform will perform the following actions:
cdk_for_tf    # google_service_account.CreateServiceAccount (Create Service Account) will be created
              # google_storage_bucket.CreateGCSBucket (Create GCS Bucket) will be created
              # google_storage_bucket_iam_binding.Addroleroles--storagelegacyBucketReader (Add role roles--storage.legacyBucketReader) will be created
              # google_storage_bucket_iam_binding.Addroleroles--storageobjectViewer (Add role roles--storage.objectViewer) will be created

            Plan: 4 to add, 0 to change, 0 to destroy.
          ───────────────────────────────────────────────────────────────────
            Saved the plan to: plan

            To perform exactly these actions, run the following command to apply:
            terraform apply "plan"
cdk_for_tf  google_service_account.CreateServiceAccount (Create Service Account): Creating...
cdk_for_tf  google_service_account.CreateServiceAccount (Create Service Account): Creation complete after 2s 
cdk_for_tf  google_storage_bucket.CreateGCSBucket (Create GCS Bucket): Creating...
cdk_for_tf  google_storage_bucket.CreateGCSBucket (Create GCS Bucket): Creation complete after 2s 
cdk_for_tf  google_storage_bucket_iam_binding.Addroleroles--storagelegacyBucketReader (Add role roles--storage.legacyBucketReader): Creating...
            google_storage_bucket_iam_binding.Addroleroles--storageobjectViewer (Add role roles--storage.objectViewer): Creating...
cdk_for_tf  google_storage_bucket_iam_binding.Addroleroles--storageobjectViewer (Add role roles--storage.objectViewer): Creation complete after 9s 
cdk_for_tf  google_storage_bucket_iam_binding.Addroleroles--storagelegacyBucketReader (Add role roles--storage.legacyBucketReader): Creation complete after 9s 
cdk_for_tf  
            Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

deploy が成功しました。
出力を見ると内部では構成ファイルを作成してから、terraform planの実行後、terraform applyが実行されているのが確認できますね。

結果確認

デプロイされた結果を確認します。

# バケット一覧確認
$ gsutil ls
gs://cdktf-example-gcs-bucket/
gs://cdktf-example-remote-backend/  # リモートバックエンド用に予め作成したバケット

# バケットのメタ情報確認
$ gsutil ls -L -b gs://cdktf-example-remote-backend/
gs://cdktf-example-remote-backend/ :
        Storage class:                  STANDARD
        Location type:                  region
        Location constraint:            ASIA-NORTHEAST1
        ~省略~
        Public access prevention:       enforced

バケットに対する権限を確認します。(コマンド結果は一部省略しています)

$ gsutil iam get gs://cdktf-example-gcs-bucket/
{
  "bindings": [
    {
      "members": [
        "serviceAccount:cdktf-example-service-account@${project_id}.iam.gserviceaccount.com"
      ],
      "role": "roles/storage.legacyBucketReader"
    },
    {
      "members": [
        "serviceAccount:cdktf-example-service-account@${project_id}.iam.gserviceaccount.com"
      ],
      "role": "roles/storage.objectViewer"
    }
}

想定通りにリソースが作成できたことを確認できました。

懸念するところ

  • 商用サポートはない
  • 利用者が少ない気がする
    • Web 上の記事が少ないです。この記事もそうですがあってもエントリ的なものになっています。

今利用している GCP サービスの種類も構成も大きく無く、致命的な問題に遭遇していないですが、今後利用を拡大していく上で障壁になる可能性はあります。バグ判断や代替策案を講じるために費やす時間の大きさを懸念するところではあります。
お前の技術力次第だろと言われればおしまいですが。。。

おわりに

今回は CDK for Terraform の簡単な例を紹介をしました。
今後は GCP の基盤も拡張されていくので、運用を踏まえた具体的な良し悪しを紹介できたらと思います。

*1:弊社の福利厚生の1つでスキルアップ用途だけなく、在宅ワーク環境の整備のためにも利用できます。