📌

[GitHub Actions] Terraform実行基盤の共通化

2024/09/02に公開

今更ながらPS4版ドラゴンクエスト11sクリアしました。
想像以上に面白かったですね。
ファミコン版ドラゴンクエスト3からプレイしている私としては
技術の進歩を感じます。

本題

弊社ではTerraformの実行をGitHubActionsで行っています。
PullRequestを作成したらDryRunが行われ、変更内容をPullRequestのコメントに追記、
マージされたら反映するようにしています。

Terraformでの各環境の管理はディレクトリで分けています。

  • 構成イメージ
terraform/
├── README.md
├── environments
│   ├── common
│   ├── development
│   ├── production
│   └── staging
├── modules
    └── vpc

課題

上記構成でそれぞれ専用のymlを準備して運用していましたが、
変数やPullRequestの作成時のブランチの違いのみで
Version情報などを更新するときに複数ファイルに対し同じ変更を
行うといった冗長な作業が発生していました。

そのため共通部分は別のymlに切り出して運用するようにしました。

ymlの作成

まずは共通部分のymlを作成します。

実行するための前提条件として
OIDC用のIAM Roleの作成とSlack通知を行うためIncoming Webhook URLが必要ですが、
本記事では割愛します。

runs-onの箇所でubuntu-latestを指定していますが、
ARM版で実行する場合はwgetで取得しているURLを以下に変更する必要があります。

https://github.com/suzuki-shunsuke/tfcmt/releases/download/${TFCMT_VERSION}/tfcmt_linux_arm64.tar.gz

前提条件

  • AWSのGitHubActions用のIAM Roleの作成済であること

  • 作成したIAM RoleをGitHubActionsのGitHubのSecretsにAWS_ROLE_ARNとして登録していること

  • SlackのIncoming Webhook URLをGitHubのSecretsにSLACK_WEBHOOK_URLとして登録していること

  • terraform_shared.yml


name: "Terraform(Shared)"

on:
  workflow_call:
    inputs:
      MY_ENV_DIRCTORY:
        description: "環境ディレクトリ"
        required: true
        default: "default"
        type: string
    secrets:
      AWS_ROLE_ARN:
        description: 'AWS IAM ROLE ARN'
        required: true
      SLACK_WEBHOOK_URL:
        description: 'Slack Webhook URL'
        required: true



env:
  SLACK_USERNAME: GitHubActions
  SLACK_CHANNEL: slack-channel-name
  SLACK_ICON: https://octodex.github.com/images/Robotocat.png
  ENV_DIRCTORY: ${{ inputs.MY_ENV_DIRCTORY }}

permissions:
  id-token: write
  contents: read
  pull-requests: write

jobs:
  terraform_share:
    name: "TerraformCI(Shared)"
    runs-on: ubuntu-latest

    # Use the Bash shell regardless whether the GitHub Actions runner is ubuntu-latest, macos-latest, or windows-latest
    defaults:
      run:
        shell: bash

    steps:
      # Checkout the repository to the GitHub Actions runner
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1
          
      - name: setup tfcmt
        env:
          TFCMT_VERSION: v4.10.0
        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: Setup Terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.9.1
      # Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc.
      - name: Terraform Init
        run: terraform -chdir=terraform/environments/$ENV_DIRCTORY init -upgrade

      # Checks that all Terraform configuration files adhere to a canonical format
      - name: Terraform Format
        run: terraform -chdir=terraform/environments/$ENV_DIRCTORY fmt -check

      # Generates an execution plan for Terraform
      - name: Terraform Plan
        run: |
          tfcmt plan -patch=false -- terraform -chdir=terraform/environments/$ENV_DIRCTORY plan -no-color -input=false
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
          PR_NUMBER: ${{ github.event.number }}

      - name: Terraform Apply
        if: (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main')  && github.event.pull_request.merged == true
        run: |
          tfcmt plan -patch=false -- terraform -chdir=terraform/environments/$ENV_DIRCTORY apply -auto-approve -no-color -input=false
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
          PR_NUMBER: ${{ github.event.number }}
      #-- Slack通知 --#
      # 成功

      - name: Slack Notification on Success
        if: ${{ success() }}
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_TITLE: Deploy Success
          SLACK_COLOR: good
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
          # 失敗
      - name: Slack Notification on Failure
        if: ${{ failure() }}
        uses: rtCamp/action-slack-notify@v2
        env:
          SLACK_TITLE: Deploy Failure
          SLACK_COLOR: danger
          SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}

続いて共有ymlを呼び出すymlを作成します。

  • terraform_development.yml
name: "Terraform(Deployment)"

on:
  pull_request:
    branches:
      - develop

    types: [opened, synchronize, reopened, closed]
    paths:
      - "terraform/environments/development/*.tf"
	  - ".github/workflows/terraform_shared.yml"
	  - ".github/workflows/terraform_development.yml"

  workflow_dispatch:

jobs:
  deploy-tf:
    uses: ./.github/workflows/terraform_shared.yml
    with:
      MY_ENV_DIRCTORY: development
    secrets:
      AWS_ROLE_ARN: ${{ secrets.AWS_ROLE_ARN }}
      SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

仕様解説

  • develop or main branchにマージされたときのみApply(反映)を行う
  • 本ケースはdevelopブランチにマージされたときのみにトリガーしてほしいのでdevelopブランチのみ。
  • developmentディレクトリのtfファイルが変更されたときにもトリガーする。
  • 合わせてterraform_shared.ymlとterraform_development.ymlが変更されたときにもトリガーする。
  • 成功/失敗時にSlack通知を行う。

まとめ

最初のほうはymlの数も少なく運用コストは低かったのですが、
GitHubActions化を進めていくにあたり、
ymlの数が増えてきて運用負荷が増加傾向にあったため、
少しでも運用コストが下げれればという思いから本施策を実施しています。

terraformのバージョンを上げるとなった場合でも一つの箇所の修正だけで
良くなったため、運用効率は上がったと感じています。

現状は全環境のインフラに対しCI/CDへの取り組みは道半ばでもあり、
引き続き運用効率化に取り組んでいきたいと思います。

Goals Tech Blog

Discussion