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 fmt
にrecursive
オプションを入れるようにしましょう。
なぜかというと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
の流れを一通り構築し、
チームのワークフローに合わせて徐々にtflint
やtfsec
、test
などを組み込んでいきましょう。
CIが定着すれば、どんな変更も安心してレビュー・適用できるインフラ運用基盤が手に入ります。
terraformの開発にもCI/CDは適用でき、開発が続く限り、CI/CDは動き続けます。
自分たちのチームで使いやすいようにメンテし続けて、快適なCI/CDライフを送りましょう!
Discussion