0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【アドベントカレンダーDay 5】TerraformのCI環境をGitHub Actionsで構築する(tflintはいいぞ)

Last updated at Posted at 2025-12-04

Terraformのコードを書いていると、「動くけど良くないコード」が混入しがちです。存在しないインスタンスタイプを指定していたり、非推奨の書き方をしていたり。terraform validateだけでは検出できない問題が結構あります。

本記事では、GitHub ActionsでTerraformのCIを構築する方法を紹介します。特にtflintの良さを伝えたいので、具体的な検出例も交えて解説します。

ワークフローの全体像

name: CI (Terraform)

on:
  pull_request:
    paths:
      - "**.tf"
  push:
    branches:
      - main
  workflow_dispatch:

env:
  TERRAFORM_VERSION: "1.12.0"

jobs:
  terraform-ci:
    name: Lint & Format - ${{ matrix.dir }} (Terraform)
    runs-on: ubuntu-latest
    strategy:
      matrix:
        dir:
          - account-settings
          - alb-ecs/infra
          - apigw-lambda/terraform
          - cloudfront-vpc-origin-internal-alb
          - cloudtrail
          - subdomain-phz/parent-phz
          - subdomain-phz/sub-phz
          - vpc
    
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 1

      - name: Install Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TERRAFORM_VERSION }}

      - name: Terraform init
        run: terraform init -backend=false
        working-directory: ${{ matrix.dir }}

      - name: Terraform validate
        run: terraform validate
        working-directory: ${{ matrix.dir }}

      - name: Terraform format
        run: terraform fmt -check -recursive
        working-directory: ${{ matrix.dir }}

      - name: Tflint install
        run: curl -s https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh | bash

      - name: Tflint init
        run: tflint --init
        working-directory: ${{ matrix.dir }}

      - name: Run tflint
        run: tflint --recursive
        working-directory: ${{ matrix.dir }}

このワークフローのポイント

ポイント 説明
matrix による並列実行 複数ディレクトリを並列でチェック
paths フィルター .tfファイルの変更時のみ実行
backend=false CI環境ではバックエンドに接続しない
4段階のチェック init → validate → fmt → tflint

それぞれ見ていきます。

トリガー設定

on:
  pull_request:
    paths:
      - "**.tf"
  push:
    branches:
      - main
  workflow_dispatch:

.tfファイルが変更されたPRと、mainブランチへのpushで実行されます。workflow_dispatchを入れておくと、手動実行もできるので便利です。

matrix による複数ディレクトリの並列チェック

strategy:
  matrix:
    dir:
      - account-settings
      - alb-ecs/infra
      - apigw-lambda/terraform
      # ...

私は、このGitHub Actionsを自分が作成したコード例のレポジトリに作っています。なので種類の異なるようなTerraformコードが一つのレポジトリに入っています。
そこで役に立ったのがmatrix指定です。これによって、簡単に全てのレポジトリに対して並列にチェックが回せます。
この部分のdir指定は静的なものでなければなりません。本当は必要な箇所を検出して欲しいんですが、ディレクトリの深さが統一されていない点や、その時の流行りによってinfra/terraformのどちらのフォルダ名を使うかがまちまち、という問題があり難しそうでした。
AWS re;Invent参加前はinfra派閥だったのですが、terraformを使っているサンプルコードをre;Inventで見つけてしまい迷い迷いです。

4段階のチェック

1. terraform init

- name: Terraform init
  run: terraform init -backend=false
  working-directory: ${{ matrix.dir }}

-backend=falseがポイントです。CI環境ではS3などのバックエンドに接続する必要はありません。プロバイダーのダウンロードだけ行います。

2. terraform validate

- name: Terraform validate
  run: terraform validate
  working-directory: ${{ matrix.dir }}

構文エラーや参照エラーをチェックします。ただし、validateで検出できる問題は限定的です。

3. terraform fmt

- name: Terraform format
  run: terraform fmt -check -recursive
  working-directory: ${{ matrix.dir }}

フォーマットが統一されているかをチェックします。-checkオプションで、フォーマットが崩れていたら失敗します。

4. tflint

- name: Tflint install
  run: curl -s https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh | bash

- name: Tflint init
  run: tflint --init
  working-directory: ${{ matrix.dir }}

- name: Run tflint
  run: tflint --recursive
  working-directory: ${{ matrix.dir }}

ここが本記事のメインです。tflintはterraform validateでは検出できない問題を見つけてくれます。

tflintはいいぞ

tflintは、Terraformのリンターです。構文的には正しいけど「良くないコード」を検出してくれます。

tflintが検出してくれる例

1. 存在しないインスタンスタイプ

# NG: tflintで検出される
resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t2.micro_typo"  # 存在しないタイプ
}
Error: "t2.micro_typo" is an invalid value as instance_type (aws_instance_invalid_type)

terraform validateはこれを通してしまいます。実際にapplyするまでエラーに気づけません。tflintなら即座に検出できます。

2. 非推奨の属性

# NG: tflintで検出される
resource "aws_instance" "example" {
  ami                  = "ami-0c55b159cbfafe1f0"
  instance_type        = "t3.micro"
  vpc_security_group_ids = ["sg-12345678"]
  
  # 非推奨: security_groups は EC2-Classic 用
  security_groups = ["default"]
}
Warning: "security_groups" is deprecated (aws_instance_deprecated_attribute)

EC2-Classicは廃止されたので、security_groupsではなくvpc_security_group_idsを使うべきです。

3. 無効なAMI ID形式

# NG: tflintで検出される
resource "aws_instance" "example" {
  ami           = "invalid-ami-id"  # AMI IDの形式が不正
  instance_type = "t3.micro"
}
Error: "invalid-ami-id" is an invalid value as ami (aws_instance_invalid_ami)

AMI IDはami-で始まる必要があります。タイポを事前に検出できます。

4. デフォルトのセキュリティグループ

# NG: tflintで検出される(ルールによる)
resource "aws_security_group_rule" "example" {
  type              = "ingress"
  from_port         = 22
  to_port           = 22
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]  # 全世界に公開
  security_group_id = aws_security_group.example.id
}
Warning: cidr_blocks contains "0.0.0.0/0" (aws_security_group_rule_invalid_cidr_block)

SSHを全世界に公開するのは危険です。tflintはセキュリティ上の問題も指摘してくれます。

terraform validateでは検出できない理由

terraform validate構文チェック参照の整合性チェックを行います。つまり、「HCLとして正しいか」「変数やリソースの参照が正しいか」を見ています。

一方、「t2.micro_typoというインスタンスタイプがAWSに存在するか」は、AWSのAPIを叩かないと分かりません。validateはAPIを叩かないので、この種のエラーは検出できません。

tflintはAWSリソースの有効な値をルールとして持っているので、APIを叩かなくても検出できます。

検出されない例(正しいコード)

# OK: 問題なし
resource "aws_instance" "example" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  vpc_security_group_ids = [aws_security_group.example.id]

  tags = {
    Name = "example"
  }
}

resource "aws_security_group" "example" {
  name        = "example"
  description = "Example security group"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port       = 22
    to_port         = 22
    protocol        = "tcp"
    security_groups = [aws_security_group.bastion.id]  # 特定のSGからのみ許可
  }
}

このコードは以下の点で問題ありません。

  • t3.microは有効なインスタンスタイプ
  • AMI IDの形式が正しい
  • vpc_security_group_idsを使っている(非推奨のsecurity_groupsではない)
  • SSHは特定のセキュリティグループからのみ許可

.tflint.hcl の設定

tflintの挙動は.tflint.hclファイルでカスタマイズできます。

plugin "aws" {
  enabled = true
  version = "0.36.0"
  source  = "github.com/terraform-linters/tflint-ruleset-aws"
}

rule "terraform_naming_convention" {
  enabled = true
}

rule "terraform_documented_variables" {
  enabled = true
}

AWSプラグインを有効にすると、AWS固有のルールが使えるようになります。tflint --initでプラグインがダウンロードされます。

CIで落ちたときの対応

tflintで引っかかった場合の対応は簡単です。

  1. エラーメッセージを確認
  2. 該当箇所を修正
  3. ローカルでtflintを実行して確認
  4. 再push

ローカルでも同じチェックができるので、pushする前に確認する習慣をつけると良いです。

# ローカルでの実行
cd your-terraform-dir
tflint --init
tflint

まとめ

本記事では、GitHub ActionsでTerraformのCIを構築する方法を紹介しました。

  • matrix で並列チェック: 複数ディレクトリを効率的にチェック
  • backend=false: CI環境ではバックエンド接続不要
  • 4段階のチェック: init → validate → fmt → tflint

特にtflintは、terraform validateでは検出できない問題を見つけてくれます。

  • 存在しないインスタンスタイプ
  • 非推奨の属性
  • 無効なリソース値
  • セキュリティ上の問題

applyする前に問題を検出できるので、本番環境での事故を防げます。tflintはいいぞ。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?