💨

serverless deploy On GitHub Actions

2023/04/20に公開

記事の内容

GitHub Actionsでserverless frameworkをAWSへデプロイする手順をまとめてみました。
AWSへデプロイする場合、ローカル端末からだとIAMユーザーのクレデンシャルをよく使いますが、
GitHub ActionsのようなCI/CDツールだと、IAMユーザーを使わなくても、OpenIDConnectを使えば
一時的なクレデンシャルでデプロイできます。管理面、セキュリティ面もこちらの方がグッドです。
その方法を整理してみました。

関係する技術・ツール

作業の流れ

  1. デプロイするAWSアカウントへOIDCProvider作成
  2. OIDCで許可するIAM RoleとIAM Policy作成
  3. GitHub Actionsで実施するworkflow作成
  4. GitHubで、workflowで使用するシークレットを設定する

1 ~ 2. デプロイするAWSアカウントへOIDCProvider作成・OIDCで許可するIAM RoleとIAM Policy作成

OIDCProvider

OIDCを使用するにはデプロイするAWSアカウントにOIDCProviderが必要になります。
GitHubのた めの場合は、https://token.actions.githubusercontent.comのurlを指定します。

IAM RoleとIAM Policy

OIDCProviderでどんな権限を使いたいか、あらかじめ用意しておく必要があります。
したがってOIDCProviderで許可するIAM Roleを作成します。
併せて、IAM Policyも作成してIAM Roleにアタッチします。

上記、コード化したいためaws-cdk(TypeScript)で作成してみました。

import { Construct } from "constructs";
import { StackProps, Stack, aws_iam } from "aws-cdk-lib";

export class cdkStack extends Stack {
  constructor(
    scope: Construct,
    id: string,
    props?: StackProps,
  ) {
    super(scope, id, props);

    const gitHubIdProvider = new aws_iam.CfnOIDCProvider(
      this,
      "GitHubIdProvider",
      {
        url: "https://token.actions.githubusercontent.com",
        clientIdList: ["sts.amazonaws.com"],
        thumbprintList: ["6938fd4d98bab03faadb97b34396831e3780aea1"],
      }
    );

    const oidcDeployRole = new aws_iam.Role(this, "GitHubOidcRole", {
      roleName: "github-oidc-role",
      assumedBy: new aws_iam.FederatedPrincipal(
        gitHubIdProvider.attrArn,
        {
          StringEquals: {
            "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
          },
          StringLike: {
            "token.actions.githubusercontent.com:sub":
              "repo:<GitHubアカウント名>/<リポジトリ名>:*",
          },
        },
        "sts:AssumeRoleWithWebIdentity"
      ),
    });

    const deployPolicy = new aws_iam.Policy(this, "deployPolicy", {
      policyName: "serverlessDeployPolicy",
      statements: [
        new aws_iam.PolicyStatement({
          effect: aws_iam.Effect.ALLOW,
          actions: [
            "cloudformation:*",
            "iam:*",
            "cognito-idp:*",
            "s3:*",
            "appsync:*",
            "logs:*",
            "lambda:*",
          ],
          resources: ["*"],
        }),
      ],
    });

    oidcDeployRole.node.addDependency(gitHubIdProvider);
    oidcDeployRole.attachInlinePolicy(deployPolicy);
  }
}

ちなみに、thumbprintListですが、6938fd4d98bab03faadb97b34396831e3780aea1となっているのは、環境固有ではないようです。
下記のAWSコンソールのOIDCプロバイダ作成画面に「プロバイダURL」を入力すると、簡単に確認できます。

こちらに取得方法など詳しいことが記載されています。
試しに、取得してみたらアルファベットは大文字ではありますが確かに同じ値でした。

$ openssl x509 -in certificate.crt -fingerprint -sha1 -noout                       
SHA1 Fingerprint=69:38:FD:4D:98:BA:B0:3F:AA:DB:97:B3:43:96:83:1E:37:80:AE:A1

$ echo "69:38:FD:4D:98:BA:B0:3F:AA:DB:97:B3:43:96:83:1E:37:80:AE:A1" | sed s/://g
6938FD4D98BAB03FAADB97B34396831E3780AEA1

3. GitHub Actionsで実施するworkflow作成

.github/workflow/sls-deploy.ymlとして作成します。

大きく、

  1. セットアップ(checkout → runtimeの設定 → package類のインストール)
  2. 単体テスト
  3. デプロイ

のフローで作成しようと思います。

今回はモノレポ構成でdevがデフォルトブランチ。slsというディレクトリのソースの変更をdevにマージされたらデプロイする条件をイベントにします。
また3のデプロイについては、2までのstepが成功した場合に実施するようにします。

name: serverless Deploy

on:
  push:
    branches:
      - dev
    paths:
      - sls/**

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    name: deploy
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [16.x]
    steps:
      # setup phase
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - name: setup ubuntu
        run: sudo apt-get update && sudo apt-get install -y ghostscript
      - name: install package
        run: yarn --cwd sls install
      # test phase
      - name: execute test
        run: yarn --cwd sls test
        env:
          ENV1_FOR_TEST: ${{ secrets.ENV1_FOR_TEST }}
          ENV2_FOR_TEST: ${{ secrets.ENV2_FOR_TEST }}
      # deploy phase
      - if: ${{ success() }}
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-region: ap-northeast-1
          role-to-assume: ${{ secrets.OIDC_ROLE }}
      - name: serverless deploy
        uses: serverless/github-action@v3.1
        with:
          args: -c "cd ./sls && serverless deploy --verbose"
          entrypoint: /bin/sh
        env:
          ENV1_FOR_DEPLOY: ${{ secrets.ENV1_FOR_DEPLOY }}
          ENV2FOR_DEPLOY: ${{ secrets.ENV2_FOR_DEPLOY }}

こちらのGitHub公式のリファレンスを元に作成しました。上記1,2のIAM RoleとPolicyの作成についても記載されています。aws-actions/configure-aws-credentialsというアクションを使います。

  • aws-region: デプロイするリージョン
  • role-to-assume: 1~2で作成したIAM RoleのArn

注意点としては、

permissions:
  id-token: write
  contents: read

リファレンスにもありますが、こちらはほぼ必須になるかと思います。
contentsはcheckoutするために必要です。

余談ですが、単体テストにあたりghostscriptが必要なためインストールしています。
こちらOSに依存するソフトウェアなのですが、このようにセットアップしておくことで、GitHub上のUbuntuでもテストを実行できます。

4. GitHubで、workflowで使用するシークレットを設定する

GitHubでIAM RoleのArnを設定します。
暗号化してくれる分には構わないので、シークレットで設定しました。
「OIDC_ROLE」という名前で設定します。

ちなみに、ENV1_FOR_TEST、ENV1_FOR_DEPLOYなどのようにテストや、デプロイに使用する環境変数も設定するとworkflowで使用できます。

実行してみる

実行されるには、作成したsls-deploy.ymlが、デフォルトブランチ(dev)にあれば実行されます。したがって、ymlをfeatureブランチで作成していれば、devにマージしたタイミングで実行されます。
下記はマージ前なので、workflowは表示されていませんが、マージされると、サイドメニューにserverless Deployが表示されると思います。

無事実行されてデプロイまで完了すれば、下記のようにworkflow成功になります。

OIDCProviderを作成しないと、エラーとなり失敗するため、下記のようにデプロイが中断されます。

Error: No OpenIDConnect provider found in your account for https://token.actions.githubusercontent.com

エラーメッセージは詳細に出力されました。もし失敗してしまう場合はメッセージ頼りに原因探ることになるでしょう。

以上になります。
これで無事、GitHub ActionsでAWSへのデプロイをCI/CDできるようになりました!

NCDCエンジニアブログ

Discussion