tfcmtとtfsecを導入して、terraformのCIツールを充実させよう

## はじめに
[tfcmt](https://github.com/suzuki-shunsuke/tfcmt)を導入すると、プルリク内にterraform planの結果を表示することができます。
また[tfsec](https://github.com/aquasecurity/tfsec)を導入することで、作成するリソースがセキュリティやベストプラクティスの観点からセキュリティ上で問題がないか検知してくれます。
2つのツールをCIとして導入することで、快適なTerraformライフを送りましょう。

#### 想定読者
- Planの結果をActionsの中から確認するのが手間な方
- Terraformのコードの品質とセキュリティを向上させたい方

#### 前回の記事
- https://qiita.com/bkend_2/items/b651614576a239004c3f

## ツールの説明

### tfcmt
- terraform planやapplyの結果をプルリクにコメントするツール
- Actionsのページへ移動する手間を解消する

### tfsec
- Terraformコード向けの静的解析ツール
- セキュリティ上の問題を検出する

## Terraformのコード
認証はOIDCを使用します。
[OIDCをTerraformで作成](https://qiita.com/bkend_2/items/0d2b2f68c008c7cbe030)

### tfcmtを導入
tfcmtを使用するために、2つのstepを追加します。
1. tfcmtをインストール
2. tfcmtを使用して、terraform planを実行

※ TOKENには、発行したアクセストークンをsecretsに保管しています。発行方法は[こちら](https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)。

#### .github/workflows/dev_plan.yml
```yml:.github/workflows/dev_plan.yml
name: tfcmtを導入

on:
  push:
    branches:
      - feature/*
      - hotfix/*
  pull_request:
    branches:
      - feature/*
      - hotfix/*

permissions:
  id-token: write
  contents: read

jobs:
  terraform_dev_plan:
    name: terraform - dev - plan
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.5.4

      - name: AWS OIDC credential
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT }}:role/${{ secrets.OIDC_ROLE }}
          aws-region: ap-northeast-1

      - name: setup tfcmt # 1. tfcmtをインストール
        env:
          TFCMT_VERSION: v3.4.1
        run: |
          wget "https://github.com/suzuki-shunsuke/tfcmt/releases/download/${TFCMT_VERSION}/tfcmt_linux_amd64.tar.gz" -O /tmp/tfcmt.tar.gz
          tar xzf /tmp/tfcmt.tar.gz -C /tmp
          mv /tmp/tfcmt /usr/local/bin
          tfcmt --version

      - name: Terraform Init
        run: terraform init -upgrade
        working-directory: ./envs/dev

      - name: Terraform fmt
        run: terraform fmt -check

      - name: Terraform Validate
        run: terraform validate -no-color

      - name: terraform refresh
        run: terraform refresh -no-color -lock=false -var="env=dev"
        working-directory: ./envs/dev

      - name: Terraform Plan
        run: |
          terraform plan \
          -no-color \
          -lock=false \
          -var="env=dev"
        working-directory: ./envs/dev

      - name: Terraform Plan cmt # 2. tfcmtを使用して、terraform planを実行
        run: tfcmt plan -patch -- terraform plan -no-color -input=false -lock=false -var="env=dev"
        working-directory: ./envs/dev
        env:
          GITHUB_TOKEN: ${{ secrets.TOKEN }}

```

### tfsecを導入
- tfsecでチェックし、危険性が「critial」と「high」があった場合はCIが失敗させます。
- 危険性が「medium」と「low」があってもCIが失敗しないようにしています。


#### .github/workflows/dev_tfsec.yml
```yml:.github/workflows/dev_tfsec.yml
name: dev環境にtfsec

on:
  push:
    branches:
      - feature/*
      - hotfix/*
  pull_request:
    branches:
      - feature/*
      - hotfix/*

jobs:
  tfsec:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Setup Go # Golangをインストール
      uses: actions/setup-go@v4
      with:
        go-version: 1.20.8

    - name: Install tfsec # tfsecをインストール
      run: go install github.com/aquasecurity/tfsec/cmd/tfsec@v1.28.1

    - name: Install jq # jqをインストール
      run: sudo apt-get install jq

    - name: Show Result tfsec # ./envs/devに対してtfsecを実行
      run: tfsec ./envs/dev || true

    - name: Run tfsec # ./envs/devに対してtfsecを実行し、結果をjson形式で出力
      run: tfsec ./envs/dev --format json > tfsec_output.json || true

    - name: Check tfsec results # tfsecの結果を確認し、critical/highの結果があればCIが失敗
      run: |
        critical_count=$(jq '[.results[] | select(.severity == "CRITICAL")] | length' tfsec_output.json)
        high_count=$(jq '[.results[] | select(.severity == "HIGH")] | length' tfsec_output.json)
        if [ $critical_count -eq 0 ] && [ $high_count -eq 0 ]; then
          echo "No critical or high findings, CI passes."
          exit 0
        else
          echo "Critical or high findings detected, CI fails."
          exit 1
        fi
```

## 動作確認
- tfcmtの動作確認です。planの結果が表示されています。
![tfcmt.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2148800/fac9f9f2-e8db-f076-3d60-9371a844594e.png)

- tfsecの動作確認です。セキュリティスキャンされています。
![スクリーンショット 2023-09-23 15.03.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2148800/a04ba218-346c-ed2d-61b2-8f337a155217.png)

## おわりに
tfcmtやtfsecを導入することでコードレビューがしやすくなり、セキュリティもチェックしてくれるのでおすすめです。
どちらか一方でもCIに導入してみてください。
最後まで読んでいただきましてありがとうございました。

今回作成したgithubのリポジトリ : https://github.com/hikobend/terraform-qiita


## 参考文献
- [suzuki-shunsuke/tfcmt](https://github.com/suzuki-shunsuke/tfcmt)
- [tfcmt で Terraform の CI/CD を改善する](https://zenn.dev/shunsuke_suzuki/articles/improve-terraform-cicd-with-tfcmt)
- [tfcmt](https://suzuki-shunsuke.github.io/tfcmt/)
- [aquasecurity/tfsec](https://github.com/aquasecurity/tfsec)
- [【tfsec】Terraformの静的セキュリティスキャンを行ってみよう！](https://dev.classmethod.jp/articles/tfsec-overview-scanning/)
- [tfsecの基本的な使い方を説明します](https://zenn.dev/cloud_ace/articles/how-to-use-tfsec)