![見出し画像](https://assets.st-note.com/production/uploads/images/129253523/rectangle_large_type_2_bfc89e3a68ca2c349547faf1ac3c83ca.png?width=1200)
[AWS]Step FunctionsをIaC管理ByTerraform
概要
以前の記事でStep Functionsの管理をIaC化(terraform)したことについてになります。AWS上で動いていたStep Functionsは当時50以上存在していました。ただし各Step Functionsは本番環境と検証環境で分かれておらず、ACLに変更を加える場合は全環境に影響がある状態でした。変更方法や新規作成方法もStep FunctionsのWorkflowStudioで直接変更や新規作成していたので変更履歴を追うことも難しく、デグレしたこともあったそうです。そのため要件としてはASLのリポジトリ管理とIaC化に伴っての安全なデプロイが求めらていました。
既存のStep Functionsのリストを抽出
まずはTerraform管理下におくため、既存で稼働しているStep Functionsをリスト化する必要がありました。AWS CLIでリストファイルに出力します。
aws stepfunctions list-state-machines --query 'stateMachines[].[name,stateMachineArn]' --output text > work/stepfunctions.list
全てのStep Functionsをjsonで抽出
リストファイルをインプットとしてjsonファイルに出力します。実行前に../../src/awsディレクトリを作成しておきます。
while read Target
do
_STATENAME=$(echo $Target | awk '{print $1}')
_DEFINITION=$(aws stepfunctions describe-state-machine --state-machine-arn arn:aws:states:ap-northeast-1:XXXXXXXXX:stateMachine:${_STATENAME} --query definition --output text)
echo -n ${_DEFINITION} > ../../src/aws/${_STATENAME}.json
done < work/stepfunctions.list
ここまででTerraformにインプットする準備としては完了です。
Terraform管理化にする
tfファイルを準備
サンプルコード stepfunctionsの公式モジュールを利用します。
data "aws_caller_identity" "current" {}
locals {
src_dir = "../../src/aws"
}
module "step_function" {
source = "terraform-aws-modules/step-functions/aws"
for_each = {
"HelloWorld" = {
role_arn = "arn:aws:iam::XXXXXXXXXXXX:role/service-role/StepFunctions-role"
}
}
"TestStateMachine" = {
role_arn = "arn:aws:iam::XXXXXXXXXXXX:role/service-role/StepFunctions-role"
}
}
name = each.key
definition = templatefile("${local.src_dir}/${each.key}.json", {
})
type = "STANDARD"
# IAMロールは作成しない。
create_role = false
use_existing_role = true # すでにあるIAMロールを利用する。
role_arn = each.value.role_arn
tags = {
Module = "Stepfunctionsv1"
}
}
output "step_function-info" {
value = ({ for k, v in module.step_function : k => {
"01_ARN" = try(v.state_machine_arn, null)
} })
}
このまま実行するとHelloWorldとTestStateMachineというStep Functionsが新規作成されるだけなのでインポートします。
Terraform Import
Terraform 1.5からリリースされたimport blockを使ってもよかったのですが、今回は一つ一つ確認しながら進めたかったので手動で実行しました。(とはいえスクリプト化してましたが。)
import コマンドサンプル 対象分繰り返す
terraform import module.step_function["HelloWorld"].aws_sfn_state_machine.this[0] arn:aws:states:ap-northeast-1:XXXXXXXX:stateMachine:HelloWorld
差分がないか確認
DryRunを実行して差分がないかを確認します。差分があれば定義ファイルを修正して合わせるか差分内容が影響ないものであれば差分なしとします。
terraform plan
CI/CD設定
ローカルからのTerraform実行だと開発メンバーの環境 Verison差異などの理由によってよからぬことが発生する可能性があります。そのためGitHubActionsでDryRunとApplyを実行できるようにします。
OIDC設定
GitHubActionsからAWS権限で実行できるようにIAMロールを作成します。これもTerraformで作成します。github_org、github_repositories、iam_role_nameは環境に合わせて変更します。thumbprintの箇所は値入れなくても良くなったかもしれません。
module "oidc-with-github-actions" {
source = "thetestlabs/oidc-with-github-actions/aws"
github_org = "GitHubのオーガニゼーション名"
github_repositories = [
"対象リポジトリ名1",
"対象リポジトリ名2"
]
iam_role_name = "Sample_OIDC_Role"
iam_role_description = "Enable GitHub OIDC access"
max_session_duration = 7200 // 2 hours
iam_role_policy = "AWSStepFunctionsFullAccess"
iam_role_path = "/security/"
thumbprint_list = [
"6938fd4d98bab03faadb97b34396831e3780aea1",
"1c58a3a8518e8759bf075b76b750d4f2df264fcd"
]
}
発行したIAMロールをGitHubで設定
対象のリポジトリのURLにアクセス
Setting → Actions → [New repository secret]
Name: AWS_ROLE_ARN
Secret: OIDCで発行したIAMのARN
GitHubActionsのymlを準備
mainブランチへのPullRequestが作成されたらDryRunが実行されて内容をコメント追記します。
mainブランチへマージされたらApplyが実行され実行内容をコメント追記されます。
Slack通知するために事前にImcommingWebhookを発行しIAM Role同様シークレットに登録しておきます。
Step Functionsのmain.tfファイルは terraform/environments/stepfunctions に配置してある前提になっています。
他ファイルが変更になっても無駄にGitHubActionsが実行されないように明示的にstepfunctions用のファイルが変更された場合というトリガーを組み込んでいます。
name: "StepfunctionsByTerraform"
on:
pull_request:
branches:
- main
types: [opened, synchronize, reopened, closed]
paths:
- "terraform/environments/stepfunctions/main.tf"
- "terraform/src/aws/*.json"
workflow_dispatch:
#-------------------------#
# 環境変数
#-------------------------#
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
SLACK_USERNAME: GitHubActions
SLACK_CHANNEL: terraform_notifications
SLACK_ICON: https://octodex.github.com/images/Robotocat.png
ENV_DIRCTORY: stepfunctions
permissions:
id-token: write
contents: write
pull-requests: write
jobs:
terraform:
name: "TerraformCI(stepfunctions)"
runs-on: ubuntu-latest
environment: stepfunctions
# 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@v3
- name: Configure AWS credentials from stepfunctions account
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ap-northeast-1
- name: setup tfnotify
run: |
sudo curl -fL -o tfnotify.tar.gz https://github.com/mercari/tfnotify/releases/download/v0.7.0/tfnotify_linux_amd64.tar.gz
sudo tar -C /usr/bin -xzf ./tfnotify.tar.gz
- name: setup tfcmt
env:
TFCMT_VERSION: v3.4.1
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.5.5
# 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
# 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 -- terraform -chdir=terraform/environments/$ENV_DIRCTORY plan -no-color -input=false
# On push to main, build or change infrastructure according to Terraform configuration files
# Note: It is recommended to set up a required "strict" status check in your repository for "Terraform Cloud". See the documentation on "strict" required status checks for more information: https://help.github.com/en/github/administering-a-repository/types-of-required-status-checks
- name: Terraform Apply
if: github.event.pull_request.merged == true
run: |
tfcmt plan -patch -- terraform -chdir=terraform/environments/$ENV_DIRCTORY apply -auto-approve -no-color -input=false
#-- Slack通知 --#
# 成功
- name: Slack Notification on Success
if: ${{ success() }}
uses: rtCamp/action-slack-notify@v2
env:
SLACK_TITLE: Deploy Success
SLACK_COLOR: good
# 失敗
- name: Slack Notification on Failure
if: ${{ failure() }}
uses: rtCamp/action-slack-notify@v2
env:
SLACK_TITLE: Deploy Failure
SLACK_COLOR: danger
実際の運用
Step Functionsで利用する定義ファイルはASLというjsonライクなファイルですが、ASLファイルはわかりにくいため、検証環境のStep Functionsをworkflow Studio で編集、稼働確認後にその定義ファイルをコピペしPullRequestを作成するという運用フローにしています。二度手間感は否めないのですが、環境を分離することができいきなり本番に手をいれるということがなく事故率低減にもつながっているのではと考えています。