0
Help us understand the problem. What are the problem?

posted at

updated at

GitHub に AWS 認証情報を持たせずに、Actions で S3 Backend な Terraform の plan を実行する

この記事は、Classi developers Advent Calendar 2021 2日目の記事です。

はじめに

正式アナウンスに先んじて Twitter で話題 になっていた、GitHub Actions で AssumeRole が使える 件が、2021年10月末頃に GitHub 社より正式にアナウンスされました。
GitHub Actions: Secure cloud deployments with OpenID Connect
(正確には、OpenID Connect による認証をサポートした、という内容です。)

Classi では、ソースコード管理に GitHub を使っていて、インフラ構成管理には Terraform を利用しています。
Terraform は、Backend に Amazon S3 を指定することで、Terraform によるインフラ構成管理をチームでできるようにしています。

今回、この正式アナウンスを機に、GitHub 側に AWS の認証情報(アクセスキー)を持たせることなく、GitHub Actions で S3 Backend な Terraform の plan を実行できるようにしたので、その内容を紹介します。

前提

想定読者

  • AWS IAM User のアクセスキーを発行・管理せずに GitHub Actions から AWS リソースへアクセスしたい方
  • Terraform を GitHub Actions で実行させたい方

この記事に書かれていること

  • GitHub Actions で AssumeRole を利用するための AWS の設定手順
  • GitHub Actions で terraform plan を実行する Actions のサンプル

この記事に書かれていないこと

  • GitHub や GitHub Actions そのものの説明
  • Terraform そのものの説明
  • AWS や IAM そのものの説明
  • OIDC(OpenID Connect) の説明

手順

基本的には GitHub 公式の手順 に従って設定を進めるだけですが、ところどころ説明が端折られているので、やったことを順に記載していきます。

AWS 側の設定手順

ID Provider の作成

AWS 公式の手順

  • IAM ID プロバイダのコンソールに行き、「プロバイダを追加」をクリック
  • プロバイダのタイプ: OpenID Connect を選択
  • プロバイダの URL: https://token.actions.githubusercontent.com
    • URL 入力後、「サムプリントを取得」をクリックしてサムプリント取得
  • 対象者(Audience): sts.amazonaws.com
  • タグは任意に設定
  • 入力後はこんな感じ
    • スクリーンショット 2021-11-02 19.52.27.png

IAM ポリシーの作成

GitHub 公式の手順 では IAM ポリシーの話は出てきませんが、Terraform が S3 にアクセスするのに権限設定が必要なため、以下の IAM ポリシーを作成します。(IAM ロールのインラインポリシーでも構いません)

  • IAM ポリシーのコンソールに行き、「ポリシーを作成」をクリック
  • JSON エディタに切り替えて↓の json を貼り付け
  • タグ、ポリシー名、説明は任意に設定
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::{YOUR_BUCKET_NAME}"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:PutObjectAcl",
                "s3:PutObject",
                "s3:GetObjectAcl",
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::{YOUR_BUCKET_NAME}/*"
        }
    ]
}

IAM ロールの作成

GitHub Actions から利用する IAM ロールを作成します。

  • IAM ロールのコンソールに行き、「ロールを作成」をクリック
  • ウェブ ID を選択
    • ID プロバイダー: 最初に作成した token.actions.githubusercontent.com:aud を選択
    • Audience: sts.amazonaws.com を選択
    • スクリーンショット 2021-11-02 20.02.47.png
  • アクセス権限
    • ↑で作成したポリシーを選択
  • タグ、ロール名、説明は任意に設定

信頼ポリシーの修正

GitHub 公式の手順 によると、信頼ポリシーにはデフォルトでは Condition に aud しか入ってないので sub も追加する必要があると書いてあります。

By default, the validation only includes the audience (aud) condition, so you must manually add a subject (sub) condition.
Edit the trust relationship to add the sub field to the validation conditions.

sub を指定することで、リポジトリやブランチ単位で制限をかけることができるようです。
今回、弊社ではリポジトリを絞りたかったため、Condition に以下を追加しました。

"StringLike": {
  "token.actions.githubusercontent.com:sub": "repo:{YOUR_ORGANIZATION_NAME}/{YOUR_REPOSITORY_NAME}:*"
}
  • {YOUR_ORGANIZATION_NAME} には、Organization 名を指定
  • {YOUR_REPOSITORY_NAME} には、この IAM ロールを引き受けることを許可するリポジトリを指定

{YOUR_ORGANIZATION_NAME} は Organization ではなく通常アカウントの指定でも使えると思うが未検証。

追加後の信頼ポリシー json 全体はこちらです。

{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::{YOUR_AWS_ACCOUNT_ID}:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:{YOUR_ORGANIZATION_NAME}/{YOUR_REPOSITORY_NAME}:*"
        }
      }
    }
  ]
}

信頼ポリシーを上記のように修正して IAM ロールの作成は完了。

GitHub Actions の用意

今回は、プルリクエストに terraform plan {TARGET_DIRECTORY} というコメントをした場合に、{TARGET_DIRECTORY} 配下で terraform plan を実行したかったため、それを実現する Action を用意しました。
実際に使っている yaml ファイルを一部加工したものがこちらです。

# プルリクのコメントに反応して plan を実行する action
name: 'terraform plan'

on:
  issue_comment:
    types: [created, edited]

jobs:
  plan:
    name: 'terraform plan'
    # プルリクエスト上で "terraform plan " から始まるコメントをした場合にのみ実行
    if: contains(github.event.comment.html_url, '/pull/') && startsWith(github.event.comment.body, 'terraform plan ')
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - name: get branch
        id: get_branch
        run: |
          echo "::set-output name=branch::$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" ${{ github.event.issue.pull_request.url }} | jq -r '.head.ref')"
      - uses: actions/checkout@v2
        with:
          ref: ${{ steps.get_branch.outputs.branch }}
      - name: get working directory
        id: work_dir
        run: |
          echo "::set-output name=dir::$(echo "${{ github.event.comment.body }}" | sed 's/terraform plan //g')"
      - name: configure aws credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: {作成した IAM ロールの ARN}
          role-session-name: {作成した IAM ロールの名前}
          aws-region: {デフォルトリージョン}
      - uses: hashicorp/setup-terraform@v1
        with:
          terraform_version: "1.0.0"
      - name: terraform init
        run: terraform init
        working-directory: ${{ steps.work_dir.outputs.dir }}
      - name: terraform plan
        continue-on-error: true
        id: plan
        run: terraform plan -no-color -lock=false
        working-directory: ${{ steps.work_dir.outputs.dir }}
      - name: Comment the plan result to the pull request
        uses: actions/github-script@v1
        env:
          STDOUT: "${{ steps.plan.outputs.stdout }}"
          TARGET_DIR: "${{ steps.work_dir.outputs.dir }}"
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          script: |
            github.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: process.env.TARGET_DIR + " terraform plan\n\n<details>\n<summary>result</summary>\n\n```\n" + process.env.STDOUT + "\n```\n\n</details>"
            })

ポイントは、以下の2点。

  • permissions での id-token: write 設定
  • 公式の Action aws-actions/configure-aws-credentialsv1 または v1.6.0 または master(ブランチ) を指定
    • OIDC に対応したのは 2021年11月23日にリリース された v1.6.0 からである
    • そのため、v1.6.0 または、最新の v1.x.x を追いかけている v1 または、常に最新である master ブランチを指定する必要がある
    • 本稿執筆時点での GitHub 公式の手順 では master を指定しているが、これは、GitHub 社による正式アナウンス時点で aws-actions/configure-aws-credentials の最新版(OIDC対応版)が未リリースだったためと思われる

もし、最後のステップの「Comment the plan result to the pull request」が Resource not accessible by integration というエラーで失敗する場合は、Personal Access Token を利用してみてください。
こちらの記事が参考になります。

おわりに

今回は terraform plan を実行するための Action や IAM ポリシーの紹介でしたが、やりたいことに合わせてポリシーの中身の修正と Action の修正で、他の AWS リソースへのアクセスを実現することができます。

ご参考まで。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
0
Help us understand the problem. What are the problem?