Terraform × GitHub Actionsでドリフト検出
この記事はNHN テコラス Advent Calendar 2024の19日目の記事です。
はじめに
インフラ管理において、コードと実環境の同期は非常に重要です。
たとえば、手動でリソースを変更した場合や、コードの適用漏れがあった場合、実環境が意図しない状態になるリスクがあります。
本記事では、GitHub Actionsを用いてTerraformコードと実環境の差分を定期的に検出し、Slackで通知する仕組みを構築する方法をご紹介します。
今回の仕組みでは以下を実現することを目的としています。
- コードと実環境の差分を定期的に検出
- 差分がある場合はSlackに通知
- 運用負担を軽減するため自動化を実現
コード内容
Github Actionsのワークフローファイルは以下になります。
name: Terraform Plan定期実行 on: workflow_dispatch: schedule: - cron: '0 1 * * *' permissions: id-token: write contents: read env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} SLACK_CHANNEL: ${{ secrets.SLACK_CHANNEL }} IAM_ROLE: ${{ secrets.IAM_ROLE }} jobs: initialization: runs-on: ubuntu-latest outputs: dir: ${{ steps.find-files.outputs.file_dirs }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Find main.tf files id: find-files run: | echo "file_dirs=`find . -type f -name 'main.tf' | grep -v '/_.*' | xargs -I {} dirname {} | jq -R -s -c 'split("\n")[:-1]' | jq -c .`" >> $GITHUB_OUTPUT tf_plan: needs: initialization runs-on: ubuntu-latest outputs: folder: ${{ steps.update.outputs.folder }} strategy: fail-fast: false matrix: dir: ${{ fromJson(needs.initialization.outputs.dir) }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: ${{ secrets.IAM_ROLE }} role-session-name: github-actions-terraform-drift-session aws-region: ap-northeast-1 - name: Setup Terraform uses: hashicorp/setup-terraform@v3 with: terraform_version: 1.3.9 terraform_wrapper: false - name: Terraform Init working-directory: ${{ matrix.dir }} run: terraform init - name: Terraform Plan id: plan working-directory: ${{ matrix.dir }} run: terraform plan -detailed-exitcode -var-file terraform.tfvars.actions - name: Slack Notification uses: slackapi/slack-github-action@v1.25.0 if: ${{ failure() }} with: channel-id: '${{ secrets.SLACK_CHANNEL }}' payload: | { "text": ":rotating_light: *terraformフォルダに差分があります (${{ matrix.dir }})*\n\n", "blocks": [ { "type": "section", "text": { "type": "mrkdwn", "text": ":rotating_light: *terraformフォルダに差分があります (${{ matrix.dir }})*\n\n" } }, { "type": "context", "elements": [ { "type": "mrkdwn", "text": "GitHub Actions URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" } ] }, { "type": "divider" } ] } env: SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
差分が検出されると、以下のようにSlackで通知されます。
通知内容には、どのフォルダで差分が発生したか、詳細ログへのリンクが含まれています。
実装概要
ワークフロー上の処理内容は以下になります。
- Terraform Planの実行
- 各フォルダ内でprovider情報が記載されたmain.tfファイルを検索し、フォルダパスを検出
- 検出したフォルダに対し、matrix関数を用いて並列でTerraform Planを実行
- 差分検知には
-detailed-exitcode
オプションを使用 - Terraform Planの実行時に必要な値は
terraform.tfvars.actions
ファイルで仮の値を指定
- Slack通知
- 差分が検出されたフォルダのみSlackに通知
- 通知にはSlack AppのBotを使用
実装ポイント
フォルダ構成
GitHub上では以下のようにAWSアカウントごとにディレクトリを分割してコードを管理しています。
provider情報があるファイル(今回の場合は、main.tf)を検索し、見つかったファイルのフォルダパスを変数に格納する形にしています。
terraform ├── dev_aws_account │ ├── main.tf │ ├── aws_vpc.tf │ ├── aws_ec2.tf │ ├── ... │ ├── terraform.tfvars.actions ├── stag_aws_account │ ├── main.tf │ ├── aws_vpc.tf │ ├── aws_ec2.tf │ ├── ... │ ├── terraform.tfvars.actions ├── prod_aws_account │ ├── main.tf │ ├── aws_vpc.tf │ ├── aws_ec2.tf │ ├── ... │ ├── terraform.tfvars.actions └── README.md
Terraform Plan実行時の入力値について
以下のようにvariable変数にデフォルト値を格納せず、Terraform実行時にコマンドプロンプトから一度きり入力するという運用にしている場合、ひと手間必要になります。
variable "database_name" { sensitive = true } variable "master_username" { sensitive = true }
以下のように空の値を格納したtfvarsファイルを用意してTerraform Plan実行時に指定することで実行エラーを回避しています。
database_name = "" master_username = ""
差分検知方法について
Terraform Planの-detailed-exitcode
オプションを利用して差分有無を終了コードで判定しています。
終了コードが異常終了の場合、Actionsのjobsも失敗判定になるため、失敗判定になったものだけSlackに通知するように処理しています。
通知では差分があることだけ通知して、実際の差分はjobsの詳細画面から確認することが可能です。
まとめ
GitHub ActionsとTerraformの組み合わせで、より効率的なインフラ運用を目指してみてください!
テックブログ新着情報のほか、AWSやGoogle Cloudに関するお役立ち情報を配信中!
Follow @twitterインフラエンジニアです。刹那的に生きてます。
Recommends
こちらもおすすめ
Special Topics
注目記事はこちら
データ分析入門
これから始めるBigQuery基礎知識
2024.02.28
AWSの料金が 10 %割引になる!
『AWSの請求代行リセールサービス』
2024.07.16