【Terraform】GitHub Actionsを使ってAWS上でインフラを構築する

こんにちは。

みなさんはIaCを使ってますか?

Infrastructure as Codeという名前の通り、AWS、GCP、Azureといったクラウドサービスを使ったインフラをコードに起こすことができます。

操作・設定ミスを防げたり、Git管理することで変更履歴が分かるのが最大のメリットだと思います。

クラウドの管理画面で、指さし確認とかダブルチェックとかしなくてよくなります😊

IaCはいろいろありますが、どのクラウドでも使えるTerraformが一番使われている気がします。自分は経験したことはないですが、マルチクラウドで構築するならIaCはTerraform一択なのかもですね。

また、最近は初めてGitHub ActionsでCI/CDパイプラインを構築してるところなので、Git-flowとうまく組み合わせればハッピーになれるのでは??と思い、記事を書くことにしました!

1. Terraformのインストール

※WSL(Ubuntu)で進めます。

実はインストールだけはかなり前にやったのでうろ覚えですが、公式サイトのLinuxのUbuntu/Debianの手順で良かったはずです。

2025/1/26現在で最新バージョンは1.10.5のようです。最新バージョンが入ってればOK!

2. 構成を考える🤔

本番、ステージング、開発環境を作りたいので、main, staging, developブランチにプッシュやマージがあれば反映って感じですね。

もちろん手元でちゃんとインフラ作られることを確認してコミットするので、クラウドに構築する環境はtestingも作ることになります。

まとめるとこんな感じ。

環境ブランチ構築方法
本番mainGitHub Actionsでterraform apply
ステージングstagingGitHub Actionsでterraform apply
開発developGitHub Actionsでterraform apply
検証用手元でterraform apply

3. デプロイ用ユーザーのアクセスキーを各所にセット

AWS上に構築するにあたって、デプロイ用のIAMユーザーアカウントを使います。

趣味での開発なら自分のIAMユーザーアカウントでもいいですが、仕事の場だったりすると、その人がいなくなったらいろいろと面倒なので、マネコンを使わないAPIだけのユーザーを作ることをオススメします。

※APIのみのユーザー作成やアクセスキーの発行手順は割愛します。

3-1. GitHub Actionsに設定

GitHub Actionsで環境変数は、機密情報とそうでない情報の2種類あるようです。アクセスキーはしっかり機密情報なので、Secretsの方に登録します。

Settings > Secrets and variables > Secretsで設定しましょう。

3-2. 手元の環境に設定

~/.aws/credentialsファイルにこんな感じでプロファイルを作成します。

手元で動かすときは、今回でいう「isub-deploy」を使ってterraform applyしていきます。

4. Terraformを使うための下準備

4-1. ファイル構成

用意したのは以下のファイルたちです。

実際は.gitとか.vscodeとかもありますが、必要なモノだけ抜粋してます。

$ tree -a
.
├── .github
│   └── workflows
│       └── terraform-apply.yml
├── deploy_testing.sh
└── main.tf

4-2. 最初のおまじない

最初にTerraformを使うための準備をします。

$ terraform init

まだあまりよく分かってないですが、プロバイダーの初期設定をするようです。何度実行しても大丈夫ですが、クラウドサービスに変更がない限りは1度だけで良いようです。

自分はPHPとNode.jsの人ということもあり、「composer install」「npm install」のようなモノだと理解しました。(違ってたらゴメンナサイ)

「.terraform」ディレクトリと「.terraform.lock.hcl」ファイルが生成されればOK!

4-3. 検証環境用のworkspaceを作成

Terraformは本番とかステージングとか、環境を分けるときにworkspaceを切り替えてapplyします。

今回は検証用環境ということで「testing」というworkspace名で作成しますが、後で設定するパイプラインでも、そのworkspaceを使うよう組みます。

$ terraform workspace new testing

「terraform.tfstate.d/testing」ディレクトリが作られればOK!

複数人でIaCを構築することも考慮して、「terraform.tfstate.d/testing/.gitkeep」を作っておくと親切かもです。

5. コードを書く

いよいよ書いていきます。

今回はECRリポジトリ1つ作るだけです。main.tfに以下を記載します。

provider "aws" {
  region = "ap-northeast-1"
}

resource "aws_ecr_repository" "ecr" {
  name = "iac_sample/${terraform.workspace}"
}
  • aws_ecr_repository:ECRリポジトリを作成する
  • ecr:Terraform内で管理されるリソースID。ARNとかに影響はない
  • iac_sample/${terraform.workspace}:testingというworkspaceの場合は「iac_sample/testing」というリポジトリ名で作成する

もしかしたらですが、ここでもterraform initをやっておいた方が良いかもしれません。

providerの初期設定なら、このタイミングで初めてAWSが出てきたわけですし。

6. デプロイ用スクリプトを書く

このままterraform applyをすると、3-2で設定したプロファイルを使われないので、参照してもらうようにします。

deploy_testing.shに以下を記載します。

#!/bin/bash

export AWS_PROFILE="isub-deploy"

# -auto-approveは変更前の確認をすっ飛ばすオプション
terraform apply -auto-approve

いざ、検証用環境としてデプロイ!!

$ bash deploy_testing.sh

うぇ~い😎 ECRリポジトリが無事作られました!!

7. GitHub Actionsによるデプロイ管理用のS3バケットを作成

さて、ここからはパイプラインで動くよう設定していきます。

Terraformはapplyで作成した各リソースのARNを保持します。差分を見て足りないモノを新規作成したり、不要なものを削除したりするためです。

testingの場合、「terraform.tfstate.d/testing」ディレクトリにtfstateファイルとバックアップファイルが出来上がってるはずです。

これらのファイルがあるからこそ、何度terraform applyしても作成済みだから何もせずに終わってくれます。

ですが、パイプライン上だと毎回新規作成扱いになってしまい、2回目以降はエラーになります。

(これに結構悩みました・・)

解決策として、パイプラインでS3にtfstateファイルとバックアップファイルをアップすることにします。

ざっくりいうと、こんな流れです。

1. S3からファイルをダウンロード
2. terraform apply
3. 出来上がった(更新された)ファイルをS3にアップロード

ということで、S3バケットを1つ作ってください。

これをmain.tfに入れたら本末転倒なので、IaC管理外で作成する必要があります。

8. パイプラインを作成

最後にGitHub Actionsで自動デプロイをしてもらう設定をします。

「.github/workflows/terraform-apply.yml」を作成し、下記を記載します。

name: Terraform Apply

on:
  push:
    branches:
      - main
      - staging
      - develop

jobs:
  terraform:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Terraform
        uses: hashicorp/setup-terraform@v1
        with:
          terraform_version: "1.10.5"

      # AWS認証情報の取得
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_DEPLOY_USER_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_DEPLOY_USER_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      # Terraformの初期化
      - name: Initialize Terraform
        run: terraform init

      # ブランチ名のワークスペースを作成
      - name: Create workspace
        run: terraform workspace new ${{ github.ref_name }}

      # S3から状態ファイルをダウンロード
      - name: Download Terraform state from S3
        run: |
          aws s3 cp s3://isub-deploy/${{ github.ref_name }}/terraform.tfstate terraform.tfstate.d/${{ github.ref_name }}/terraform.tfstate || echo "State file does not exist, skipping download"
          aws s3 cp s3://isub-deploy/${{ github.ref_name }}/terraform.tfstate.backup terraform.tfstate.d/${{ github.ref_name }}/terraform.tfstate.backup || echo "Backup state file does not exist, skipping download"

      # Terraformの適用
      - name: Terraform Apply
        run: terraform apply -auto-approve

      # S3へ状態ファイルをアップロード
      - name: Upload Terraform state to S3
        run: |
          aws s3 cp terraform.tfstate.d/${{ github.ref_name }}/terraform.tfstate s3://isub-deploy/${{ github.ref_name }}/terraform.tfstate
          aws s3 cp terraform.tfstate.d/${{ github.ref_name }}/terraform.tfstate.backup s3://isub-deploy/${{ github.ref_name }}/terraform.tfstate.backup
  • ${{ secrets.~~ }}:GitHub ActionsのSecretsで設定したやつ
  • ${{ github.ref_name }}:ブランチ名

いざ、developブランチにコミット!!

ドヤァ😉

ちなみに、初回のパイプラインは最後のバックアップファイルのアップロードでミスります。

まだリソースが一切ない状態では、バックアップが生成されないようです。

そういう分岐を入れてもいいかもですが、初回だけだしそんなに考えなくてもいいかな・・と思ってます。

9. 余談

手元で検証用環境でさわっていたころ、main.tfは以下のようにしていました。

provider "aws" {
  region = "ap-northeast-1"
  profile = "isub-deploy"
}

これならshファイルでapplyさせる必要はなくなるのですが、GitHub Actionsでクレデンシャルを読み込めないエラーになるので、パイプラインに任せたいのであればproviderでprofileを指定するのはオススメしません!

profileはWSLのUbuntu内で設定しただけだから、パイプラインで読み込めないのは当たり前なんですけどねw

パイプラインの前段階でアクセスキーを設定してるので、あわよくば無視してくれないかなぁと思ってましたがダメでした😅

ちなみに、なぜ今回はEC2とかS3とかメジャーなモノでなくECRリポジトリにしたかというと、最近弊社のブログでNext.jsをLambda Web Adapterで構築する記事がちょこちょこあり、それにECRリポジトリが使われているからです。

Lambda Web Adapterはこのあたりの記事もご参照ください!

Web側のパイプラインでNext.jsで開発されたモノをビルドしてECRリポジトリへプッシュして、IaC側ではそれをトリガーにLambda Web Adapterに反映させるつもりです。

EventBridgeでそういうトリガーを設定できるらしいので、前段階としてプッシュ先のECRリポジトリを作りました!

10. まとめ・感想

GitHub ActionsからTerraformでAWSインフラを構築してみました。

GitHub Actions(というかCI/CDパイプラインの構築自体)もTerraformも初めてで、すごく勉強になりました!

IaCはSAMならさわったことはありますが、Cloudformationを経由しないTerraformの方が個人的に使いやすい感じがしています。

みなさんもステキなIaCライフを!!

それではサウナへ行ってきます♨️🧖

11. 参考

投稿者プロフィール

KatoShingo
学んだことをアウトプットしていきます!
好きなこと:音楽鑑賞🎵 / ドライブ🚗 / サウナ🧖

最近の記事

  1. AWS
  2. AWS
  3. AWS

制作実績一覧

  1. Checkeys