使用 GitHub Actions 进行 Terraform 执行的自动化
想要将其自动化的感觉是什么?
-
- Pull Request作成時にterraform planを実行し、その結果をPRにコメントしたい。
-
- Pull Requestをマージしたときにterraform applyを実行したい。
-
- tfstateをいい感じに分けたい。
- 変更した部分だけterraformを実行したい。
下面是实际代码。
-
- https://github.com/rinchsan/terraform-github-actions
-
- ECRのリポジトリを1つ作るだけのシンプルな例です。
- 公式が出している、GitHub ActionsでTerraformを自動化するチュートリアルは こちら にあります。
讲解
计划.yml
- planはPR作成時や変更時にGitHub Actionsを実行しましょう。
name: Plan
on:
pull_request:
branches:
- master
strategy.matrix に何か配列を指定すると、それぞれを使って並列にジョブを実行することができます。
1つの tfstate ファイルに対応するディレクトリごとに strategy.matrix.dir に追記していくイメージです。
Dev環境、Stg環境などの環境ごとに resources/dev/ 、 resources/stg/ などに分割して strategy.matrix.dir に追加していくのがよくありそうです。
resources/dev/ 配下でさらに resources/dev/api/ とか resources/dev/worker/ みたいな感じでドリルダウンしていくのも良さそうです。
resources/dev/ecs/ とか resources/dev/rds/ みたいにリソースの種類ごとに分けるのもアリかもです。
jobs:
plan:
name: Plan
runs-on: ubuntu-latest
strategy:
matrix:
dir: [
resources/shd/ecs,
]
-
- とりあえずCheckoutしてきて、 technote-space/get-diff-action を使ってmasterとの差分を取得しましょう。
自分でgit diffするように実装してもいいと思うんですが、面倒ですよね。
ここで取得した差分が存在するかどうかによって、 matrix.dir ごとに terraform plan を実行するかどうかを後続のstepで判断します。
id に何か文字列を指定しておくと、後続のstepで実行結果などを参照することができます。
先ほど指定していた strategy.matrix.dir は ${{ matrix.dir }} みたいな感じで参照することができます。
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Check diff
id: diff
uses: technote-space/get-diff-action@v4.0.2
with:
PATTERNS: |
modules/**/*.tf
${{ matrix.dir }}/**/*.tf
-
- 今回はAWSを想定しているのでAWSの認証を行いましょう。
- ここでは普通にIAM Userのキーペアを使っていますが、会社のセキュリティ事情によってAssumeRoleとかでSessionTokenを取得しないといけなかったりする場合は、頑張ってください。
- name: Configure aws credentials
if: steps.diff.outputs.diff
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1
-
- Hashicorp公式が提供している hashicorp/setup-terraform を使って terraform コマンドをインストールしましょう。
if で steps.diff.outputs.diff を指定することで、 Check diff stepで差分が得られたときのみ実行するようにします。
- name: Setup terraform
if: steps.diff.outputs.diff
uses: hashicorp/setup-terraform@v1.2.1
with:
terraform_version: 0.13.2
terraform fmt でフォーマットをチェックしておきます。
フォーマットはPRをマージするまでに直ればいいので、ここでは continue-on-error: true を指定して後続のstepは実行します。
- name: Check format
id: fmt
if: steps.diff.outputs.diff
run: terraform fmt -check -recursive
working-directory: ${{ matrix.dir }}
continue-on-error: true
${{ matrix.dir }} で terraform init を実行しましょう。
今回のサンプルではTerraformのBackend設定やProvider設定などを書いた init.tf をディレクトリごとに用意する想定ですが、このファイルを共通化したい場合はリポジトリルートに init/init.tf などを用意して、 ${{ matrix.dir }} にコピーしてから terraform init を実行したりするとよいでしょう。
${{ matrix.dir }} を元に tfstate の key を指定することもお忘れなく。
- name: Initialize
id: init
if: steps.diff.outputs.diff
run: terraform init
working-directory: ${{ matrix.dir }}
terraform get で依存モジュールのダウンロードなどを行い、 terraform validate で設定を検証しましょう。
- name: Download modules
if: steps.diff.outputs.diff
run: terraform get
working-directory: ${{ matrix.dir }}
- name: Validate
if: steps.diff.outputs.diff
run: terraform validate
working-directory: ${{ matrix.dir }}
terraform plan を実行しましょう。
terraform plan で -no-color オプションを指定しているのは、後続のstepでPRにコメントを残す際に表示がおかしくなるのを防ぐためです。
- name: Plan
if: steps.diff.outputs.diff
id: plan
run: terraform plan -no-color
working-directory: ${{ matrix.dir }}
continue-on-error: true
actions/github-script を使って terraform fmt や terraform plan の結果をPRにコメントしてもらいましょう。
いちいちGitHub Actionsの実行結果ページへ行く必要がないので楽ですね。
このスクリプトは上述の公式チュートリアルにあるものを拝借しています。
- name: Comment
if: steps.diff.outputs.diff
uses: actions/github-script@v3.0.0
env:
PLAN: "terraform\n${{ steps.plan.outputs.stdout }}"
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const output = `## \`${{ matrix.dir }}\`
#### Terraform Format and Style ?\`${{ steps.fmt.outcome }}\`
#### Terraform Plan ?\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`${process.env.PLAN}\`\`\`
</details>`;
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
申请.yml
terraform apply はmasterブランチへのPush時に実行しましょう。
strategy まわりはplanと同様です。
name: Apply
on:
push:
branches:
- master
jobs:
apply:
name: Apply
runs-on: ubuntu-latest
strategy:
matrix:
dir: [
resources/shd/ecs,
]
-
- applyでは最新のPRの差分を見て実行するかどうかを判断したいので、 ref と fetch-depth を指定します。
actions/checkout@v2 はデフォルトでは最新のコミット、つまり今回の場合はPRのマージコミットしかfetchしてくれないため、 HEAD^ と HEAD の差分を取得するために fetch-depth: 2 を指定します。
steps:
- name: Checkout
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 2
terraform コマンドのインストールとAWSの認証はplanと同様に行います。
- name: Setup terraform
uses: hashicorp/setup-terraform@v1.2.1
with:
terraform_version: 0.13.2
- name: Configure aws credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1
-
- 最後に HEAD^ との git diff を取って、 resources/ もしくは modules/ に変更があった場合のみ、 terraform apply を実行します。
-
- 小さいですがあまりスクリプトは書きたくないなと思うので、いい感じに差分を取得できるアクションを書いてみたいですね。
-
- この結果はGitHub Actionsの実行結果ページに行って確認しましょう。
もし失敗していたら新たに修正ブランチをmasterから切ってそのPRをマージする形で対応しましょう。
- name: Apply
run: |
DIFF=$(git diff --name-only HEAD^ modules ${{ matrix.dir }})
if [[ ${DIFF} = *resources* || ${DIFF} = *modules* ]]; then
cd ${{ matrix.dir }}
terraform init
terraform get
terraform apply -auto-approve
fi
shell: bash
印象
在一些不支持Orb的CircleCI上努力编写脚本来实现,写得相当漂亮和令人满意。