CodePipeline で承認プロセスを設けた Terraform workspace の CI/CD パイプライン実装

CodePipeline で Terraform workspace 環境の開発/本番それぞれの CI/CD パイプラインを考えてみました。
2020.01.24

先日、CodeCommit への push をトリガーに terraform apply をサクッと実行するシンプルなパイプラインの記事を投稿しました。

今回はもうちょっと発展させて以下のような要件を取り込んでみたいと思います。

  • terraform workspace 環境でも使えるようにしたい
  • 本番環境は terraform apply の前に承認者による承認プロセスを入れたい

イメージは以下のとおりです。

開発環境向け

1-1. develop ブランチへ push
1-2. dev workspace に terraform apply を実行

本番環境向け

2-1. develop ブランチから master ブランチへ merge
2-2. prod workspace で terraform plan を実行
2-3. 承認者に後続処理の承認要求をメール通知
2-4. 本番用パイプラインで後続処理を「承認」
2-5. prod workspace で terraform apply を実行

概要がわかっていただけたかと思いますので、それではパイプラインを構築していきます。

Terraform Backend の作成

S3 および DynamoDB を使った Terraform Backend ですが、この部分は変更がありませんので前回の記事を参照ください。

Terraform workspace の作成

workspace の作成はパイプラインに入れる必要はありません。以下のコマンドをローカル実行し、事前に作成しておきます。

$ terraform workspace new  dev
Created and switched to workspace "dev"!

You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.

$ terraform workspace new prod
Created and switched to workspace "prod"!

You're now on a new, empty workspace. Workspaces isolate their state,
so if you run "terraform plan" Terraform will not see any existing state
for this configuration.

$ terraform workspace list
  default
  dev
* prod

以上で OK です。ちなみに Terraform workspace 環境の場合、S3 や DynamoDB の backend は以下のように env: 階層が追加され、tfstate も LockID も分離して管理されます。

CodeCommit の作成

こちらも手順としては変更ありませんので前回の記事を参照ください。
構成としては、develop および master ブランチの 2 つが存在する状態です。

dev 用 CI/CD パイプライン

CodePipeline の作成

基本的には前回の記事と同じなので、前回手順を参考にすすめてください。一部、buildspec.yml の書き方を変えたので、[Build -optional] での環境変数のみ、下記のように変更しています。(前回の手順だと、[CodePipeline 作成]の手順12.の箇所です)

Name Value Type 説明
TF_VERSION 0.12.19 Plaintext Terraform バージョン
TF_CMD apply Plaintext terraform コマンド
TF_WS dev Plaintext workspace 名
TF_OPT -input=false -auto-approve -no-color Plaintext terraform コマンドのオプション

前回までは Build プロジェクトのなかで terraform planterraform apply を実行してたのですが、開発環境においては事前に自分で terraform plan 流してるだろうし、ダメならコケて終わりなので必要ないかと思って外しました。

また、terraform planterraform apply ではオプションが異なるのですが buildspec.yml を共通化したかったので、コマンドオプションも TF_OPT 環境変数に入れて渡すことにしました。

workspace 対応のために、workspace 名の指定が必要となりましたので、こちらは TF_WS に入れることにしました。buildspec.yml は以下のとおり。

buildspec.yml

version: 0.2

phases:
  install:
    runtime-versions:
      docker: 18
    commands:
      - yum install unzip -y
      - wget https://releases.hashicorp.com/terraform/"$TF_VERSION"/terraform_"$TF_VERSION"_linux_amd64.zip
      - unzip terraform_"$TF_VERSION"_linux_amd64.zip
      - mv terraform /usr/local/bin/
  pre_build:
    commands:
      - terraform init -input=false -no-color
      - terraform workspace select $TF_WS -no-color
  build:
    commands:
      - terraform $TF_CMD $TF_OPT

  post_build:
    commands:
      - echo terraform $TF_CMD completed on `date`

CodeBuild 用の IAM ロール設定

前回同様に、今回も CodeBuild 用 IAM ロールに AdministratorAccess ポリシーをアタッチして検証しました。

prod 用 CI/CD パイプライン

CodePipeline の作成

dev 用のパイプラインは 2 つのステージ(ソースアーティファクトの作成、terraform apply)のみでしたが、prod 用は以下のような 4 つのステージを作成していきます。

  1. ソースアーティファクトの作成
  2. terraform plan を実行
  3. 承認プロセス
  4. terraform apply を実行

それでは Create pipeline から開始します。

1. CodePipeline 管理コンソールから Create pipeline をクリック
2. パイプライン名を入力し、New service role を選択して Next を選択
3. ソースプロバイダーに AWS CodeCommit を選択。該当のリポジトリと、master ブランチを指定
4. 検出オプションは Amazon CloudWatch Events (recommended) を選択して Next を選択
5. ビルドプロバイダーに AWS CodeBuild を選択
6. Create Project をクリックし、ビルドプロジェクトを作成(別のウィンドウが起動します)
7. プロジェクト名を入力
8. [Environment Image]で Managed image を選択し、以下のように選択

  • Operating system: Amazon Linux2
  • Runtime: Standard
  • Image: aws/codebuild/amazonlinux2-x86_64-standard:2.0
  • Image version: Always use the latest image for this runtime version
  • Environment type: Linux

9. [Privileged] の下にあるチェックボックスにチェックを入れ、New service role を選択
10. Buildspec のセクションは、Use a buildsped file を選択
11. Log セクションで、CloudWatch logs にチェックを入れ Continue to CodepiPeline をクリック
12. 元のウィンドウに戻り、[Environment variables] で以下の環境変数を設定し Next をクリック

Name Value Type 説明
TF_VERSION 0.12.19 Plaintext Terraform バージョン
TF_CMD plan Plaintext terraform コマンド
TF_WS prod Plaintext workspace 名
TF_OPT -input=false -lock=false -no-color terraform コマンドのオプション

13. CodeDeploy は不要ですので、Skip deploy stage をクリック
14. Create pipeline でパイプラインを作成

CodeBuild 用の IAM ロール設定

  1. IAM 管理コンソールを開き、[Roles]から先ほど CodeBuild 設定のなかで作成した IAM ロールを検索し、IAM ロール名をクリック
  2. [Permissions]タブを開き、[Attach policies]をクリック
  3. ビルド対象のリソース管理に必要なポリシーをアタッチ。(今回も AdministratorAccess をアタッチしましたが、必要に応じて必要な権原に絞り込んでください)

SNS 設定

次に、承認要求のメールを通知するための SNS 設定を行います。

1. SNS 管理コンソールを開き、[Topics] から Create topic をクリック
2. トピック名 を入力し、Create topic をクリック
3. トピック一覧に戻り、作成したばかりのトピックをクリック
4. Subscriptions タブから Create subscription をクリック
5. 3.で作成したトピック、プロトコル(Email)、Endpiont(Email アドレス) を指定し、Create subscription をクリック
6. Confirm メールが届くので、リンクをクリックし Confirm する

 

承認ステージの追加

CodePipeline 管理コンソールに戻り、prod 用パイプラインに承認ステージを追加します。

1. CodePipeline 管理コンソールから prod 用パイプラインを開き、Edit をクリック
2. [Build] ステージの下にある Add Stage をクリック
3. ステージ名を入力し、Add stage をクリック(私は terraform-apply-prod-approval としました)
4. 作成したステージ内の Add action group をクリック
5. アクション名を入力、アクションプロバイダで Manual approval を選択。SNS topic ARN は先ほど作成したものを指定し、必要に応じてコメントを入力し、Done

terraform apply ステージの追加

承認プロセスが完了した後の terraform apply を実行するステージを設定します。

1. CodePipeline 管理コンソールから prod 用パイプラインを開き、Edit をクリック
2. 承認ステージの下にある Add Stage をクリック
3. ステージ名を入力し、Add stage をクリック(私は terraform-apply-prod としました)
4. 作成したステージ内の Add action group をクリック
5. アクション名を入力、アクションプロバイダで AWS CodeBuild を選択。その後は、基本的には dev 環境と同じように Build project の設定を進めます。Add environment variable については、以下のように設定し、Done

Name Value Type 説明
TF_VERSION 0.12.19 Plaintext Terraform バージョン
TF_CMD apply Plaintext terraform コマンド
TF_WS prod Plaintext workspace 名
TF_OPT -input=false -auto-approve -no-color Plaintext terraform コマンドのオプション

これでパイプラインとしては完成です。

CI/CD パイプラインの検証

dev 環境に push

前回の記事と同じ config.tf を含めて、以下のファイルを準備します。

  • config.tf
  • buildspec.yml (上記参照)
  • s3.tf (今回も S3 バケットの作成で動作確認します)

バケット名が重複しないように terraform.workspace を付与しています。実行する workspace によって devprod の値が入ります。

s3.tf

resource "aws_s3_bucket" "cm-terraform-000" {
  bucket = "cm-terraform-${terraform.workspace}-000"
  acl    = "private"
}

config.tf

terraform {
  required_version = ">= 0.12.0"

  backend "s3" {
    encrypt        = true
    bucket         = "terraform-tfstate"
    dynamodb_table = "terraform-state-lock" 
    key            = "terraform.tfstate"
    region         = "ap-northeast-1"
  }
}

develop 環境に push します。

marumo.atsushi@:~/Project/terraform-cicd (develop)
$ ls -l
total 40
-rw-r--r--  1 marumo.atsushi  staff  575  1 22 18:39 buildspec.yml
-rw-r--r--  1 marumo.atsushi  staff  336  1 21 09:22 config.tf
-rw-r--r--  1 marumo.atsushi  staff  121  1 24 05:49 s3.tf

$ git push
Warning: Permanently added 'git-codecommit.ap-northeast-1.amazonaws.com,52.119.218.16' (RSA) to the list of known hosts.
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 307 bytes | 307.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
To ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/terraform-cicd
   54edaa2..cf564d9  develop -> develop

dev 用パイプラインの確認

dev 用の CodePipeline が正常に実行されていることを確認します。

S3 バケット cm-terraform-dev-000 が作成されていることを確認します。

dev 用のパイプラインが正常に動作していることが確認できましたね。

develop → master への merge

それでは prod 用のパイプラインを動作させるために、develop ブランチを master ブランチに merge しましょう。

CodeCommit 管理コンソールからリポジトリの develop ブランチを開き、Create pull request をクリック。

Destination に master、Source に develop を指定し、Title および必要に応じて Description を入力し、Create pull request をクリック。

作成したプルリクエストを開き、Merge をクリック。

Merge strategy は任意のものを指定します。merge 後にブランチを削除したくない場合は Delete source branch develop after merging? のチェックを外し、Merge pull request をクリック。

これで prod 用のパイプラインがトリガーされることになります。

prod 用パイプラインの確認

prode 用の CodePipeline を開くと、master ブランチへの merge によってパイプラインが動作しているはずです。Build(terraform plan) ステージが実行された後、承認ステージの terraform-apply-prod-approval で Pending していることが判りますね。

SNS トピックのサブスクリプションで指定したアドレスに以下のようなメールが届きます。

リンクをクリックすると CodePipeline 管理コンソールが開きますので、Build ステージの terraform_planDetails をクリックし、変更内容を確認します。

変更内容に問題がなければ、承認ステージ内の Review をクリック。必要に応じてコメントを入力し、Approve をクリック。

承認後、terraform_apply ステージが実行されていることを確認します。

S3 バケット cm-terraform-prod-000 が作成されていることを確認します。

正常に作成されていることが確認できましたね!検証は以上です!

さいごに

Terraform workspace を利用した環境での CI/CD パイプラインを試してみました。本番環境へのデプロイは、必要最低限の安全措置を承認プロセスを設けることで確保しています。

これが正解というワケではなく、あくまで 1 つの案として参考にしていただければと思います。

以上!大阪オフィスの丸毛(@marumo1981)でした!