Github Actions で Terraform の構成ドリフト検出
今回のサンプルコードを上げているリポジトリです。
https://github.com/jDBTISK/tf-detect-drift-sample
想定読者
- Github で Terraform のコードを管理している、もしくはしようと思っている人
- Terraform 自体の経験はすでにあるよという人
ちなみに紹介しているコードが AWS 仕様になっているので、GCP や Azure のリソースを Terraform で管理している場合は若干置き換えて読んで頂く必要があります。
Github Actions や Terraform 自体の書き方については細かく触れていないのでご注意を。
構成ドリフト
コードで管理しているインフラの構成と、実リソースの構成に不一致がある場合「構成ドリフトが発生している」と呼ばれます。
terraform を使用してインフラを管理する場合、実際に作られたインフラの情報は terraform 自体のソースコードではなく、terraform コマンドの実行時に作成/更新される tfstate
ファイルに保存されます。
この tfstate
ファイルに記録されている内容と実リソースに差分が出ているかどうかで構成ドリフトを判断します。
本記事は Github Actions を用いて1日1回 Terraform で管理しているインフラリソースの構成ドリフトを検出しようという内容です。
なぜ構成ドリフト検出が必要なのか
最近は terraform や CDK 等の IaC ツールを用いてインフラもコードで管理する事が多くなっていますよね。
しかし、いざ terraform を使い始めると「terraform で作ったリソースを手動で変更してしまい terraform のコードと差分が出てしまう」現象に遭遇することがあります。
個人で開発している場合は自分だけ気をつければ回避出来ますが、人数が増えてくると「terraform で作ったリソースを絶対他の手段で変更しない」という統制も難しくなってきます。
「terraform で作ったリソースの設定がいつの間にか変更されてた!!」と気づくのは、terraform plan
や terraform apply
コマンドの実行時になります。
このときに出る差分を見て「いつの間にか設定変わっとるやん!!」となってから、誰が変更したのかを CloudTrail 等で追っかけて、元の設定(terraform 上の設定)に戻していいのか確認して... という作業が発生することになるのですが、これに気づくのが遅れるほど当初 terraform のコードを書いた人の想定とは異なる状態で運用されることになりますし、手動で変更してしまった人もどういう理由で操作したのか忘れてしまったりということがあります。
そのため、構成ドリフトが発生している場合はなるべく早い段階で検知できる仕組みがほしいということになります。
どうやって構成ドリフトを自動で検出するか
terraform plan
コマンドに対して -detailed-exitcode
オプションを付与すると終了コードで差分があるか判定出来るのでこれを利用します。
Github Actions では cron 式を用いたスケジュール実行も可能なので、これにより毎日1回構成ドリフトの有無を Slack に通知するということをやっていきたいと思います。
今回は Github Actions での例を紹介しますが、やることとしては「terraform plan -detailed-exitcode
の終了コードによって Slack への通知メッセージを変える」だけなので、GitLab CI/CD 等の他の CI ツールでもできるはずです。
Github Actions
まずはコードを貼ります。
name: drift
on:
schedule:
- cron: "0 20 * * *"
permissions:
id-token: write
contents: read
pull-requests: read
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: github
SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }}
jobs:
drift:
name: Drift
runs-on: ubuntu-latest
defaults:
run:
working-directory: src
steps:
- name: Checkout
uses: actions/checkout@v3
- uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: ${{ secrets.BACKEND_ASSUME_ROLE_ARN }}
role-session-name: github-actions-terraform-drift-session
aws-region: ${{ secrets.AWS_REGION }}
- name: Setup Terraform
uses: hashicorp/setup-terraform@v1
with:
terraform_version: ${{ secrets.TF_VERSION }}
- name: Terraform Init
run: terraform init
-backend-config "region=${{ secrets.AWS_REGION }}"
-backend-config "bucket=${{ secrets.BACKEND_S3_BUCKET }}"
- name: Terraform Plan
run: terraform plan
-var "assume_role_arn=${{ secrets.ASSUME_ROLE_ARN }}"
-detailed-exitcode
- name: Nothing Drift
if: ${{ success() }}
uses: rtCamp/action-slack-notify@v2
env:
SLACK_TITLE: Nothing Drift
SLACK_COLOR: good
- name: Detect Drift
if: ${{ failure() }}
uses: rtCamp/action-slack-notify@v2
env:
SLACK_TITLE: Detect Drift
SLACK_COLOR: danger
ポイントになるのは jobs の最後の 3 step です
- name: Terraform Plan
run: terraform plan
-var "assume_role_arn=${{ secrets.ASSUME_ROLE_ARN }}"
-detailed-exitcode
- name: Nothing Drift
if: ${{ success() }}
uses: rtCamp/action-slack-notify@v2
env:
SLACK_TITLE: Nothing Drift
SLACK_COLOR: good
- name: Detect Drift
if: ${{ failure() }}
uses: rtCamp/action-slack-notify@v2
env:
SLACK_TITLE: Detect Drift
SLACK_COLOR: danger
Terraform Plan
で変更がなかった場合(構成ドリフトが検出されなかった場合)は Nothing Drift
、変更があった場合は Detect Drift
が実行されます。
Nothing Drift
と Detect Drift
では Slack Notify という Github Actions Library を使用しています。
今回の例では毎日日本時間の5時(UTC の20時)に実行するようになっています。(実際には Github Actions のスケジュール実行は数分~20分くらい遅れるので5時10分台くらいに通知が来ることが多いです)
schedule:
- cron: "0 20 * * *"
実際の通知例
時間が5時からかけ離れてるのはスクショ取るために時間ずらして CI 動かしてたからなので気にしないでください
Discussion