🌟

TerraformにもCIを取り入れてみよう

に公開

初めに

初めまして!
皆様どうもこんばんわ、こんにちわ、おはようございます。
エンジニアの榎本です。

今回は、Terraformを使ったAWSインフラの構築・改修を少人数で担当した経験をもとに、Terraform向けCIの導入ポイントを分かりやすく解説します。

Terraform最大の強みは、インフラ構成をコードとして管理し、コミット履歴や差分をレビューできる点です。これにより、アプリケーション開発と同様にCI/CDを適用でき、運用効率と品質が大幅に向上します。

チームでTerraformを使い始める際は、できるだけ早い段階でCI/CDパイプラインを構築することを強くおすすめします。

権限管理について

CI/CDを整備することで権限管理にも影響が生じることを理解しておきましょう。
通常、AWSでTerraformのplanやapplyを実行するにはAWSのIAM権限が必要です。
大規模組織では、ユーザー数分のIAMユーザー発行や権限申請が煩雑になりがちですが、CI環境(例:GitHub Actions)にのみ権限を集中させることでIAMユーザーの数を抑え、管理コストを削減できます。

一方で、CIに権限を集中させると、CI環境外では手元で気軽にplanやapplyできなくなるという制約もあります。
これらのメリット・デメリットを踏まえたうえで、自社に最適な権限構成を検討してください。

ユースケース

  • ある程度のメンバーでインフラ開発・運用を行っており、Terraformを既に使っている
  • これからTerraformでチーム開発を始めようとしている

どんなCIがある?

以下にCIとして導入するのにおすすめの選択肢を列挙していきます。
最低限terraform planは導入しましょう

1. terraform plan の自動実行

  • PR作成/更新時に terraform plan を実行し、結果をコメントやチェック結果として表示
  • 変更点を明示的にレビューできるようにし、誤ったリソース操作を防止
  • リファレンス

※TerraformコードのPRでレビュワーが最も気にするのは、この変更をapplyしたときに実際のインフラに何が起こるかです。
そのため、必須なのが terraform plan の自動実行
planがエラーになる変更はそもそもマージすべきでないコードということになります。

2. terraform fmt

  • terraform fmt -check -recursive などでフォーマット違反を検出
  • コードベースのスタイルを統一し、無駄な差分やインデント崩れを排除
  • リファレンス

terraform fmtrecursiveオプションを入れるようにしましょう。
なぜかというとterraform fmtだけだとカレントディレクトリしかformatのチェックをしてくれず、カレントディレクトリ配下のtfファイルもチェックしたい場合、recursiveオプションが必要です。

3. terraform validate

terraformの基本的な構文チェック・設定チェックを行います。

  • プロバイダーやモジュールの設定ミス(型違い・必須パラメータ抜けなど)をCIで早期に検出
  • 実際のクラウドAPIを呼ばずに検証できるため、コストゼロで品質向上
  • リファレンス

4. tflint(+αでOK)

Terraform用の静的解析ツールです。

  • 変数未使用、リソース命名規則違反、ベストプラクティス逸脱を検出
  • カスタムルールを追加して、プロジェクト固有のコーディング規約を自動チェック
  • github

5. tfsec(+αでOK)

インフラのセキュリティ観点での静的解析を行います。

  • AWS IAMポリシーの危険な権限、公開S3バケットの有無などを検出
  • 検知ルールをカスタマイズし、組織のセキュリティポリシーに合わせたチェックが可能
  • github

6. Terraformのtest(任意)

Terraformのtestについては公式のtest以外にもいくつかのツールがあったりします。
小〜中規模プロジェクトではテスト自体のメンテナンスコストが高くなりがちです。
まずは上記のtest以外の静的チェックとplanを整備し、必要に応じてテストを追加するとよいでしょう。

terraformのtestについても触れられている以下の書籍やリファレンスを見て、導入するかどうか判断してみると良いでしょう。

sampleコード

name: Terraform Plans Comment

on:
  pull_request:
    paths:
      - "terraform/**"

jobs:
  terraform-plans:
    runs-on: ubuntu-latest

    permissions:
      contents: read
      id-token: write
      pull-requests: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ap-northeast-1
          role-to-assume: arn:aws:iam::xxx:role/github-actions-role

      - name: Set up Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Terraform Init
        run: terraform init

      - name: Terraform Plan
        id: plan
        run: terraform plan -no-color

      - name: Comment Terraform Plan
        uses: actions/github-script@v7
        if: github.event_name == 'pull_request'
        env:
          PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
        with:
          script: |
            const output = `#### Staging Terraform Plan 📖\`${{ steps.plan.outcome }}\`

            <details><summary>Show Plan</summary>

            \`\`\`\n
            ${process.env.PLAN}
            \`\`\`

            </details>`

            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: output
            })

name: Terraform CI

on:
  pull_request:
    paths:
      - "terraform/**"

jobs:
  terraform-ci:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Terraform Format Check
        run: terraform fmt -check -recursive

      - name: Terraform Validate
        run: terraform validate

CDは?

言わずもがなでterraform applyです。
これはworkflow_dispatchで動かせるようにすることがおすすめです。
staging環境で開発用のbranchで一度applyしてみたい!などの要望が出てくることもあるため、柔軟にデプロイできる仕組みにしてくと良いと思っています。


sampleコード

name: Terraform apply

on:
  workflow_dispatch:

jobs:
  terraform:
    name: Terraform apply
    runs-on: ubuntu-latest

    permissions:
      contents: read
      id-token: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ap-northeast-1
          role-to-assume: arn:aws:iam::xxxx:role/github-actions-role

      - name: Set up Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Terraform Init
        run: terraform init

      - name: Terraform Plan
        run: terraform plan -out=tfplan

      - name: Terraform Apply
        run: terraform apply -auto-approve tfplan

terraformのapplyに関しては、planで出力したファイルをそのままapply時に使うようにしています。
applyコマンドは通常、planも実行されるので、2重に実行されないような工夫として上記のような書き方をしています。
また、planで出たファイルを使うことで、途中でstateファイルが変更された際にも影響を受けないというメリットもあります。
(ざっくりapplyするぜ!って方は特に必要ないかもしれない)

終わりに

いかがでしたでしょうか?

Terraformの開発にCI/CDを導入すると、コード品質・セキュリティ・運用効率が飛躍的に向上します。
まずは plan => fmt => validate => review => applyの流れを一通り構築し、
チームのワークフローに合わせて徐々にtflinttfsectestなどを組み込んでいきましょう。

CIが定着すれば、どんな変更も安心してレビュー・適用できるインフラ運用基盤が手に入ります。

terraformの開発にもCI/CDは適用でき、開発が続く限り、CI/CDは動き続けます。
自分たちのチームで使いやすいようにメンテし続けて、快適なCI/CDライフを送りましょう!

DELTAテックブログ

Discussion