CodePipeline で簡単 Terraform CI/CD パイプラインの実装

今回は、CodeCommit への push をトリガーに CodeBuild で terraform apply する CodePipeline を作成してみたいと思います。ざっくり環境は以下のとおりです。

環境

  • Terraform Backend
    • S3
    • DynamoDB
  • CodeCommit
  • CodeBuild
  • CodePipeline

Terraform Backend の作成

今回は CI/CD パイプラインを使って Terraform を管理しますので、tfstate ファイルは共有可能な場所に保存する必要があります。また、このパイプラインは複数人が利用することが想定されるため、git push のタイミングによっては、同時に terraform apply が動作し tfstate に競合が発生してしまう可能性があります。

これらの課題は Terraform の Backend 機能を利用することで簡単に以下のような設定を行うことが出来ます。

  • tfstate の保存先に S3 を利用
  • DynamoDB を利用した State Locking の実装

それでは Backend で利用する S3 バケット、および DynamoDB テーブルを作成します。

S3 バケットの作成

  1. S3 管理コンソールを開き Create Bucket をクリック
  2. S3 バケット名を入力、リージョンを選択して[次へ]をクリック
  3. バージョニングとデフォルト暗号化を有効にして[次へ]をクリック
  4. Block all public access を選択して[次へ]をクリックし、バケットを作成

DynamoDB テーブルの作成

  1. DynamoDB 管理コンソールを開き、 Create table をクリック
  2. 任意のテーブル名を指定。例えば terraform-state-lock のように指定します
  3. 図のように、主キーとして LockID を指定し、Use default settings にチェックして作成

Terraform 設定ファイル

Terraform ブロックの Backend 設定で、先ほど作成したバケット名およびテーブル名を指定します。

terraform {
  required_version = ">= 0.12.0"

  backend "s3" {
    encrypt        = true
    bucket         = "terraform-tfstate"     <-- S3 バケット名
    dynamodb_table = "terraform-state-lock"  <-- DynamoDB テーブル名
    key            = "terraform.tfstate"
    region         = "ap-northeast-1"
  }
}

provider "aws" {
  region  = "ap-northeast-1"
  version = "~> 2.45"
}

CodeCommit 作成

  1. CodeCommit 管理コンソールから、Create repository をクリック
  2. リポジトリ名を入力し、Create で作成します。作成後にリポジトリに移動します。
  3. Create file をクリック

4. readme.md のようなサンプルファイルを作成し、Commit changes をクリック

5. 今回は develop ブランチを使いたいので、左のメニューから Branches を開き Create branch をクリック
6. ブランチ名に develop を入力、from に master を指定し、Create branch をクリックしてブランチを作成

CI/CD パイプライン

CodePipeline 作成

  1. CodePipeline 管理コンソールから Create pipeline をクリック
  2. パイプライン名を入力し、New service role を選択して Next を選択

3. ソースプロバイダーに AWS CodeCommit を選択。先ほど作成したリポジトリと、develop ブランチを指定
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

  • キー名: TF_VERSION
  • 値: 0.12.19
  • タイプ: Plaintext

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

CodeBuild 用の IAM ロール設定

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

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

CodeCommit への Git 接続設定

CodeCommit への接続は、IAM ユーザを作成して SSH や HTTPS で接続できます。設定方法等については、以下の記事を参照ください。

構成ファイルの準備

先ほど作成した config.tf を含めて、以下のファイルを準備します。

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

buildspec.yml では、Terraform のダウンロード、インストールを行います。バージョンの指定は CodePipeline 設定のなかで CodeBuild 向けの環境変数として設定した TF_VERSION で指定します。pre_build 内の terraform initterraform plan でエラーがあった場合は、build は実行されずエラー終了します。今回は特に承認処理等を入れていないので、terraform plan が正常処理されると、そのまま terraform apply まで実行されます。(ローカル環境で事前に terraform plan を行い、変更、削除されるリソースを確認してください

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 plan -input=false -no-color

  build:
    commands:
      - terraform apply -input=false -auto-approve -no-color

  post_build:
    commands:
      - echo terraform apply completed on `date

S3 バケットを 1 つ作成するだけの簡単な tf ファイルとしました。

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

CodeCommit リポジトリへの push

CodeCommit リポジトリの develop ブランチに push します。

marumo.atsushi@:~/Project/terraform-cicd (develop)
$ ls -l
total 32
-rw-r--r--  1 marumo.atsushi  staff  607  1 21 08:51 buildspec.yml
-rw-r--r--  1 marumo.atsushi  staff  314  1 21 08:51 config.tf
-rw-r--r--  1 marumo.atsushi  staff    6  1 21 08:46 readme.md
-rw-r--r--  1 marumo.atsushi  staff  192  1 21 08:51 s3.tf

$ git add .

$ git commit -m "Initial commit"
[develop 8b78804] Initial commit
 3 files changed, 48 insertions(+)
 create mode 100644 buildspec.yml
 create mode 100644 config.tf
 create mode 100644 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: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 883 bytes | 883.00 KiB/s, done.
Total 5 (delta 0), reused 0 (delta 0)
To ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/terraform-cicd
   ea86283..8b78804  develop -> develop

しばらくすると CodePipeline が動作し、CodeBuild が実行されます。

もしエラーになっている場合は、ビルドプロジェクトの [Details] をクリックし、ビルド実行時の Tail ログを確認しながらトラブルシューティングしてください。

S3 バケットも正常に作成されていますね。

検証は以上です。

さいごに

Code シリーズを使った簡単な Terraform CI/CD パイプラインを作成してみました。今回は単に terraform apply を実行する単純なパイプラインでしたので、次は Terraform workspace と絡めて、環境によっては terraform apply 前に承認プロセスを必要とするようなパイプラインを検討してみたいと思います。

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

参考