俺のTerraform Pipeline #devio2022

Developers.IO 2022 〜技術で心を揺さぶる3日間〜 でTerrformのCI/CDについて発表させていただきました
2022.07.29

こんにちは!AWS事業本部コンサルティング部のたかくに(@takakuni_)です。

Developers.IO 2022 〜技術で心を揺さぶる3日間〜 にて、「俺のTerraform Pipeline」というテーマでお話しさせていただきました。

TerraformでCI/CDパイプラインを組むならどのような構成が取れるのか、構成例の1つとしてご紹介させていただきました。

動画

スライド

想定する視聴者

  • そろそろCI/CDパイプラインでTerraformを使いこなしてみようかなと言う人
  • Terraformのコードスキャン(動画だとtfsec)に興味がある人

発表ネタにしたブログ

今回は以下のブログについて、情報を掻い摘んで動画にしました。もしよければ重ねてご覧ください。

情報のアップデート

ここからは先ほど紹介したブログおよび動画時点で気がつかず、今になって紹介したいことがあるので、ここからは「私自身の成長記録」をつらつらと綴っていこうと思います。

みなさんも、以前のブログ、動画、このブログでムキムキのTerraformおじさんへの階段を駆け上がっていきましょう。

TerraformのDockerイメージ

まずはじめに、Amazon ECR Public GalleryでTerraformのDockerイメージが配布されていたことです。

動画時点では、terraform plan/applyコマンド用のCodeBuildプロジェクトで、毎度INSTALLフェーズにてTerraformのインストールを行なっていました。

buildspec_before.yaml

  version: 0.2
  env:
    exported-variables:
      - BuildID
      - BuildTag
  phases:
    install:
      runtime-versions:
        golang: 1.14
      commands:
        - "git clone https://github.com/tfutils/tfenv.git ~/.tfenv"
        - "ln -s ~/.tfenv/bin/* /usr/local/bin"
        - "tfenv install $TF_VERSION"
        - "tfenv use $TF_VERSION"
    pre_build:
      commands:
        - "terraform init -input=false -no-color"
    build:
      commands:
        - "terraform plan -input=false -no-color"
    post_build:
      commands:
        - "export BuildID=`echo $CODEBUILD_BUILD_ID | cut -d: -f1`"
        - "export BuildTag=`echo $CODEBUILD_BUILD_ID | cut -d: -f2`"

Amazon ECR Public Galleryで配布されたTerraformのDockerイメージを利用すると先ほどのINSTALLフェーズを省略することができます。

buildspec_after.yaml

  version: 0.2
  env:
    exported-variables:
      - BuildID
      - BuildTag
  phases:
    pre_build:
      commands:
        - "terraform init -input=false -no-color"
    build:
      commands:
        - "terraform plan -input=false -no-color"
    post_build:
      commands:
        - "export BuildID=`echo $CODEBUILD_BUILD_ID | cut -d: -f1`"
        - "export BuildTag=`echo $CODEBUILD_BUILD_ID | cut -d: -f2`"

もたらされる効果

ビルドログを確認するとネットワークの状況にもよりますが、INSTALLフェーズには「6秒」かかったという記録が取れました。

一方でINSTALLフェーズを短縮した場合、INSTALLフェーズは「1秒未満」まで短縮できました。

CodeBuildは実行時間あたりに対して課金が行われるため「コスト最適化」が微々たるものですが見込めます。

Build URL

ブログ時点では、CodeBuildの実行結果をレポート出力する方法として次の形式で出力を行なっていました。

https://${AWS::Region}.console.aws.amazon.com/codesuite/codebuild/${AWS::AccountId}/projects/#{TFSEC.BuildID}/build/#{TFSEC.BuildID}%3A#{TFSEC.BuildTag}/?region=${AWS::Region}

ご覧の通り、パワープレイな感じがするので調査したところ、CodeBuildでCODEBUILD_BUILD_URLという環境変数が同じ用途で使えるのでそちらにアップデートを行います。

buildspec.yaml

  version: 0.2
  env:
    exported-variables:
-     - BuildID
-     - BuildTag
+     - BUILD_URL
  phases:
    pre_build:
      commands:
        - "terraform init -input=false -no-color"
    build:
      commands:
        - "terraform plan -input=false -no-color"
    post_build:
      commands:
-       - "export BuildID=`echo $CODEBUILD_BUILD_ID | cut -d: -f1`"
-       - "export BuildTag=`echo $CODEBUILD_BUILD_ID | cut -d: -f2`"
+       - "export BUILD_URL=`echo $CODEBUILD_BUILD_URL`"

Cloudformartionテンプレートもかなりスッキリしたのがわかります。

terraform_pipeline.yaml

  CodePipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: !Sub "${PrjPrefix}-tf-pipeline"
      ArtifactStore:
        EncryptionKey:
          Id: !GetAtt KeyS3Arthifact.Arn
          Type: "KMS"
        Location: !Ref BucketArtifacts
        Type: "S3"
      RoleArn: !GetAtt RoleTfPipelne.Arn
      Stages:
        - Name: "Source"
          Actions:
            - Name: "CodeCommit_Source"
              ActionTypeId:
                Category: "Source"
                Owner: "AWS"
                Provider: "CodeCommit"
                Version: "1"
              Configuration:
                RepositoryName: !GetAtt CodeCommit.Name
                BranchName: !Ref BranchName
                PollForSourceChanges: false
              OutputArtifacts:
                - Name: "Artifact_Source_CodeCommit_Source"
              RoleArn: !GetAtt RoleTfPipelne.Arn
              RunOrder: 1
        - Name: "tfsec_Stage"
          Actions:
            - Name: "Terraform_Security_Analysis"
              Namespace: TFSEC
              ActionTypeId:
                Category: "Build"
                Owner: "AWS"
                Provider: "CodeBuild"
                Version: "1"
              Configuration:
                ProjectName: !Ref ProjectTfsec
              InputArtifacts:
                - Name: "Artifact_Source_CodeCommit_Source"
              RoleArn: !GetAtt RoleTfPipelne.Arn
              RunOrder: 1
        - Name: "Terraform_Stages"
          Actions:
            - Name: "Terraform_Security_Analysis_Manual_Review"
              ActionTypeId:
                Category: "Approval"
                Owner: "AWS"
                Provider: "Manual"
                Version: "1"
              Configuration:
                CustomData: "tfsec review"
-               ExternalEntityLink: !Sub "https://${AWS::Region}.console.aws.amazon.com/codesuite/codebuild/${AWS::AccountId}/projects/#{TFSEC.BuildID}/build/#{TFSEC.BuildID}%3A#{TFSEC.BuildTag}/?region=${AWS::Region}"
+               ExternalEntityLink: "#{TFSEC.BUILD_URL}"
              RoleArn: !GetAtt RoleTfPipelne.Arn
              RunOrder: 1
            - Name: "Terraform_Plan"
              Namespace: "TERRAFORM"
              ActionTypeId:
                Category: "Build"
                Owner: "AWS"
                Provider: "CodeBuild"
                Version: "1"
              Configuration:
                ProjectName: !Ref ProjectTfplan
              InputArtifacts:
                - Name: "Artifact_Source_CodeCommit_Source"
              RoleArn: !GetAtt RoleTfPipelne.Arn
              RunOrder: 2
            - Name: "Terraform_Plan_Manual_Review"
              ActionTypeId:
                Category: "Approval"
                Owner: "AWS"
                Provider: "Manual"
                Version: "1"
              Configuration:
                CustomData: "Terraform plan review"
-               ExternalEntityLink: !Sub "https://${AWS::Region}.console.aws.amazon.com/codesuite/codebuild/${AWS::AccountId}/projects/#{TERRAFORM.BuildID}/build/#{TERRAFORM.BuildID}%3A#{TERRAFORM.BuildTag}/?region=${AWS::Region}"
+               ExternalEntityLink: "#{TERRAFORM.BUILD_URL}"
              RoleArn: !GetAtt RoleTfPipelne.Arn
              RunOrder: 3
            - Name: "Terraform_Apply"
              ActionTypeId:
                Category: "Build"
                Owner: "AWS"
                Provider: "CodeBuild"
                Version: "1"
              Configuration:
                ProjectName: !Ref ProjectTfapply
              InputArtifacts:
                - Name: "Artifact_Source_CodeCommit_Source"
              RoleArn: !GetAtt RoleTfPipelne.Arn
              RunOrder: 4

Snyk IaCもイイぞ

Snyk IaCは、tfsecの代替案として有効なソリューションです。

最近ブログもよく書いているので、重ねてご覧いただければ嬉しいです。

DevelopersIO 2022でSnyk Japanの相澤様に登壇いただきました。(セッションレポートになります。)

まとめ

「俺のTerraform Pipeline」の登壇レポートでした!「俺のTerraform Pipeline」を作ろうとしている方の参考になれば幸いです。

以上、AWS事業本部コンサルティング部のたかくに(@takakuni_)でした!