🦭

TFLintとdiggerでTerraformのCI/CDを構築する

2025/03/30に公開

IaC (Infrastructure as Code) を実現するためのツールとしてTerraformがあります。この記事ではTFLint (リンター) とdigger (CI/CD オーケストレータ) をGitHub Actions上で動かすことを目標に、TerraformのCI/CDを構築する手順を紹介します。diggerについては国内の採用事例が少なく情報が見つけにくかったので、何かの参考になれば幸いです。

前提

AWSリソースを扱う複数の環境 (本番とステージング) を1つのリポジトリで管理することを想定して設定しています。ディレクトリ構成は以下の通りです。

.
├── .github
│   └── workflows
│       ├── ci.yml
│       └── digger_workflow.yml
├── .gitignore
├── .terraform-version
├── .tflint.hcl
├── digger.yml
└── environments
    ├── production
    │   ├── .terraform.lock.hcl
    │   ├── backend.tf
    │   ├── main.tf
    │   ├── outputs.tf
    │   ├── providers.tf
    │   ├── terraform.tf
    │   └── variables.tf
    └── staging
        ├── .terraform.lock.hcl
        ├── backend.tf
        ├── main.tf
        ├── outputs.tf
        ├── providers.tf
        ├── terraform.tf
        └── variables.tf

Terraformのインストール

バージョン管理ツールであるtfenvを使用して、Terraformをインストールします。導入の目的としては開発者のローカルPCにおける環境差異をなくし、diggerでもローカルと同じバージョンのTerraformを使用するためです。

brew install tfenv
touch .terraform-version
tfenv install
.terraform-version
1.11.3

TFLintの設定

TFLintとは

TFLintはTerraformのリントツールです。プラグインを読み込ませることで、AWSといったクラウドプロバイダーのレベルで構文をチェックしてくれるので、Terraformの標準コマンドである teffaform validate よりも厳密なチェックが行えます。

また、公式のスタイルガイドでも紹介されており、非推奨となった構文やベストプラクティスに沿っていない書き方を注意してくれるため、コード品質の向上にも大きく寄与します。

インストール・設定ファイルの作成

TFLintをインストールして、プロジェクトルートに設定ファイル .tflint.hcl を作成します。

brew install tflint
touch .tflint.hcl
.tflint.hcl
plugin "terraform" {
  # すべてのルールを有効にする
  preset = "all"
  enabled = true
}

# AWSプラグインを設定する
plugin "aws" {
    enabled = true
    version = "0.38.0"
    source  = "github.com/terraform-linters/tflint-ruleset-aws"
}

試しに不正なコードを書いてみて、TFLintが検知できることを確認しましょう。

environments/staging/main.tf
+resource "aws_instance" "web_app" {
+  // invalid comment
+  ami           = "ami-no-such-image"
+  instance_type = "t1.no-such-instance"
$ cd environments/staging
$ tflint --config=$(realpath ../../.tflint.hcl)
4 issue(s) found:

Warning: [Fixable] Single line comments should begin with # (terraform_comment_syntax)

  on main.tf line 2:
   2:   // invalid comment
   3:   ami           = "ami-no-such-image"

Reference: https://github.com/terraform-linters/tflint-ruleset-terraform/blob/v0.11.0/docs/rules/terraform_comment_syntax.md

Error: "ami-no-such-image" is invalid AMI ID. (aws_instance_invalid_ami)

  on main.tf line 3:
   3:   ami           = "ami-no-such-image"

Error: "t1.no-such-instance" is an invalid value as instance_type (aws_instance_invalid_type)

  on main.tf line 4:
   4:   instance_type = "t1.no-such-instance"

Warning: "t1.no-such-instance" is previous generation instance type. (aws_instance_previous_type)

  on main.tf line 4:
   4:   instance_type = "t1.no-such-instance"

Reference: https://github.com/terraform-linters/tflint-ruleset-aws/blob/v0.38.0/docs/rules/aws_instance_previous_type.md

CIの設定

続いて、GitHub Actionsでフォーマットとリントをの設定をしていきます。フォーマットには標準の terraform fmt コマンド、リントにはTFLintを利用するのですが、それぞれマーケットプレイスでアクションが提供されています。

https://github.com/hashicorp/setup-terraform
https://github.com/marketplace/actions/setup-tflint

基本的にこれらのアクションを利用するだけになります。ワークフローファイル (.github/workflows/ci.yml) を作成して、2つのジョブを記載します。

.github/workflows/ci.yml
name: CI
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  format:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
        with:
          # `.terraform-version` で指定されたバージョンを使用する
          terraform_version: $(head -n 1 .terraform-version)
      - name: Run Terraform Format
        run: terraform fmt --diff --recursive
  lint:
    runs-on: ubuntu-latest
    needs: format
    steps:
      - uses: actions/checkout@v4
      - uses: terraform-linters/setup-tflint@v4
        with:
          tflint_version: v0.56.0

      - name: Init TFLint
        run: tflint --init
        env:
          GITHUB_TOKEN: ${{ github.token }}

      - name: Run TFLint
        run: tflint --config "$(pwd)/.tflint.hcl" --recursive --format compact

今回のように環境ごとにディレクトリを分割する構成では、--chdir オプションを使って各ディレクトリに移動してから tflint を実行する形になります。また、--format compact オプションを付与すると、GitHub上でコードの差分とエラーを紐づけて確認できるので便利です。

diggerの設定

diggerとは

diggerはTerraform向けのCI/CD オーケストレーションツールです。類似ツールとして公式が提供するHCP Terraformがありますが、GitHub Actionsへの統合が容易であり、フリープランでリソース数の制限がないことからdiggerを採用しました。

使用イメージとしては、以下のようにPR上で terraform plan の実行結果が確認できたり、コメントから terraform apply を実行できたりします。

インストール・設定ファイルの作成

公式チュートリアルに従って進めていきます。GitHub上の設定として以下の2つが必要です。

  • diggerを使用するリポジトリに対して、Digger GitHub Appを有効にする
  • [1]リポジトリのシークレットにAWSの認証情報を追加する


Digger GitHub Appを有効にする


GitHub Secretsに認証情報を設定する

続いて、プロジェクトルートに 設定ファイル (digger.yml) を作成します。

digger.yml
projects:
  - name: production
    dir: ./environments/production
  - name: staging
    dir: ./environments/staging

# digger applyが成功したらPRを自動マージする
auto_merge: true
auto_merge_strategy: "merge"

最後にワークフローファイル (digger_workflow.yml) を作成します。

.github/workflows/digger_workflow.yml
name: Digger

on:
  workflow_dispatch:
    inputs:
      spec:
        required: true
      run_name:
        required: false

run-name: "${{inputs.run_name}}"

jobs:
  digger-job:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      actions: write
      id-token: write
      pull-requests: write
      issues: read
      statuses: write

    steps:
      - uses: actions/checkout@v4
      - name: ${{ fromJSON(github.event.inputs.spec).job_id }}
        run: echo "job id ${{ fromJSON(github.event.inputs.spec).job_id }}"
      - uses: diggerhq/digger@vLatest
        with:
          digger-spec: ${{ inputs.spec }}
          setup-aws: true
          setup-terraform: true
          # `.terraform-version` で指定されたバージョンを使用する
          terraform-version: $(head -n 1 .terraform-version)
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        env:
          GITHUB_CONTEXT: ${{ toJson(github) }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Terraformを使っていると、terraform plan はパスしても terraform apply で失敗することがよくあります。デフォルトの設定では、安全のためPRのコメントから digger apply を適用するようになっており、PRのマージ後に発火させるよりも安全に適用できます。

ブランチ保護ルールが効かない?

GitHubのブランチ保護ルールを有効にしたのですが、リントエラーが出ている状態でも digger apply を実行できてしまいました。diggerの機能としては提供されておらず、こちらのコメントしか情報ソースがないのですが、制限をかけられないのは地味に残念です。

おわりに

TerraformのCI/CD構築について紹介しました。こういった自動化ワークフローは、一度構築することでその後の開発効率が向上します。インフラの変更を容易にするため、今後も力を入れていこうと思います。

https://github.com/yuki-yamamura/learn-terraform-cicd

脚注
  1. AWSの認証に環境変数を使用する場合の設定 ↩︎

株式会社FLAT テックブログ

Discussion