4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Github ActionsでAWS CDKを自動デプロイしてみる

Last updated at Posted at 2021-12-03

はじめに

はじめまして!株式会社オークンのNHatanakaと申します!

現在会社の同僚と趣味でフットサルのLINEアプリケーションを作っていて、
せっかくなので初めてGithub Actionsを触ってみました!
なので今回はその時実装したコードを紹介したいと思います!

使ったもの

AWS CDK
Github Actions
direnv

構成

まず構成としては下図のようなパイプラインでデプロイ時にAPIGatewayとLambdaが作成されます。
構成図.png

ディレクトリ構成

iam:GithubActions上で使用するロールを作成するプロジェクトです。
futsal-backend:今回自動デプロイを組むプロジェクトでLambdaとAPIGatewayを作成します。

全体のディレクトリ構成
.
├── README.md
├──.github
│     └──workflows
│             └──cdk-deploy.yml
├── futsal-backend
│   └── ...
└── iam
│   └── ...
└──.envrc
futsal-backendプロジェクトのディレクトリ構成
.
├── README.md
├── bin
│   └── futsal-backend.ts
├── cdk.json
├── config
│   └── stack-const.ts
├── functions
│   └── message-api
│       └── index.py
├── jest.config.js
├── lib
│   └── futsal-backend-stack.ts
├── package-lock.json
├── package.json
├── test
│   └── futsal-backend.test.ts
└── tsconfig.json

iamプロジェクトのディレクトリ構成
.
├── README.md
├── bin
│   └── iam.ts
├── cdk.json
├── jest.config.js
├── lib
│   └── iam-stack.ts
├── package-lock.json
├── package.json
├── test
│   └── iam.test.ts
└── tsconfig.json

iamスタックの内容

Github Actions上で使用するロールを作成するスタックです。
環境変数として.envrcに登録が必要な情報としては下記になります。
GITHUB_ORG:GitHubリポジトリのOrganization名
REPOSITORY_NAME:GitHubリポジトリ名

iam-stack.ts

import * as cdk from '@aws-cdk/core';
import * as iam from '@aws-cdk/aws-iam';
import {Duration} from '@aws-cdk/core';

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

        const githubProvider = new iam.OpenIdConnectProvider(this, 'GithubProvider', {
            url: 'https://token.actions.githubusercontent.com',
            clientIds: ['sts.amazonaws.com'],
            thumbprints: ['a031c46782e6e6c662c2c87c76da9aa62ccabd8e']
        });

        const deploy_role = new iam.Role(this, 'FutsalDeployRole', {
            assumedBy: new iam.FederatedPrincipal(
                githubProvider.openIdConnectProviderArn,
                {
                    StringLike: {
                        'token.actions.githubusercontent.com:sub': `repo:${process.env.GITHUB_ORG}/${process.env.REPOSITORY_NAME}:*`
                    }
                },
                'sts:AssumeRoleWithWebIdentity',
            ),
            roleName: `FutsalDeployRole`,
            maxSessionDuration: Duration.seconds(3600)
        });
        deploy_role.addToPolicy(
            new iam.PolicyStatement({
                resources: ['*'],
                actions: [
                    'cloudformation:CreateStack',
                    'cloudformation:CreateChangeSet',
                    'cloudformation:DeleteChangeSet',
                    'cloudformation:DescribeChangeSet',
                    'cloudformation:DescribeStacks',
                    'cloudformation:ExecuteChangeSet',
                    'cloudformation:GetTemplate',
                    'cloudformation:DescribeStackEvents',
                    'cloudformation:DeleteStack',
                    's3:ListBucket',
                    's3:getBucketLocation',
                    's3:CreateBucket',
                    's3:*Object',
                    'iam:PassRole',
                ],
            })
        );

        new iam.Role(this, 'FutsalBackendStackDeployRoleForCloudFormation', {
            assumedBy: new iam.ServicePrincipal('cloudformation.amazonaws.com'),
            roleName: `FutsalBackendStackDeployRoleForCloudFormation`,
            maxSessionDuration: Duration.seconds(3600),
            managedPolicies: [
                iam.ManagedPolicy.fromAwsManagedPolicyName('AWSLambda_FullAccess'),
                iam.ManagedPolicy.fromAwsManagedPolicyName('AWSCloudFormationFullAccess'),
                iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonAPIGatewayAdministrator'),
                iam.ManagedPolicy.fromAwsManagedPolicyName('IAMFullAccess'),
                iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonS3FullAccess'),
            ],
        });
    }
}

上記のコードを手動でデプロイして作成されたロールのARNをGitHubにSecretに登録します。
余談ですがGithub Actionsは最近AWSクレデンシャルを直接渡さずにIAMロールが使えるように
なるという最高すぎることがあり、なんとなくCloudFormationを眺めながらCDKにしてみました。(あまり意味がないことを理解しつつ)

futsal-backendの内容

ここでは実際にGithub Actionsで自動デプロイするリソースの定義を行なっています。

futsal-backend-stack.ts
import * as cdk from '@aws-cdk/core';
import {Duration} from '@aws-cdk/core';
import {resolve} from 'path';
import lambda = require('@aws-cdk/aws-lambda');
import apigw = require('@aws-cdk/aws-apigateway');

export class FutsalBackendStack extends cdk.Stack {
    constructor(scope: cdk.Construct, id: string, systemEnv: string, props?: cdk.StackProps) {
        super(scope, id, props);

        const messageApiFunction = new lambda.Function(this, 'MessageApiFunction', {
            functionName: `MessageApiFunction${systemEnv}`,
            code: lambda.Code.fromAsset(resolve(__dirname, '../functions/message-api')),
            timeout: Duration.minutes(5),
            handler: 'index.handler',
            runtime: lambda.Runtime.PYTHON_3_8,
            environment: {
                CHANNEL_ACCESS_TOKEN: String(process.env.CHANNEL_ACCESS_TOKEN),
                SECRET_KEY: String(process.env.SECRET_KEY)
            }
        })

        new apigw.LambdaRestApi(this, 'Endpoint', {
            handler: messageApiFunction,
            deployOptions: {
                stageName: systemEnv
            }
        });
    }
}

またbin/futsal-backend.tsで環境名(systemEnv)を環境変数から取得するようにし
その値をCloudFormationやリソースに含めることで環境別にリソースが生成されるようにしています。

futsal-backend.ts
# !/usr/bin/env node
import 'source-map-support/register';
import * as cdk from '@aws-cdk/core';
import { FutsalBackendStack } from '../lib/futsal-backend-stack';

const systemEnv: string = process.env.SYSTEM_ENV || '';

const app = new cdk.App();
new FutsalBackendStack(app, `FutsalBackendStack${systemEnv}`, systemEnv);

Github Actionsのワークフロー

さてやっとGithub Actionsのワークフローです。
今回はGitLabFlowを想定しておりmain・staging・productionの各ブランチに
マージされた際に起動しマージされたブランチ名を取得してSYSTEM_ENVに対応した環境名を
設定するように設計しました。

cdk-deploy.yml
name: AWS CDK
on:
  push:
    branches:
      - main
      - staging
      - production
    paths:
      - futsal-backend/**
env:
  FUTSAL_DEPLOY_ROLE: ${{ secrets.FUTSAL_DEPLOY_ROLE }}
  FUTSAL_BACKEND_STACK_DEPLOY_ROLE: ${{ secrets.FUTSAL_BACKEND_STACK_DEPLOY_ROLE }}

permissions:
  id-token: write
  contents: read
jobs:
  aws_cdk:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: "${{ env.FUTSAL_DEPLOY_ROLE }}"
          aws-region: ap-northeast-1
          role-duration-seconds: 900
          role-session-name: GitHubActionsTestSession

      - name: Setup Node
        uses: actions/setup-node@v2
        with:
          node-version: '14'

      - name: npm ci
        run: npm ci
        working-directory: ./futsal-backend

      - name: Set System Development
        if: contains(toJSON(github.ref), 'main')
        run: echo SYSTEM_ENV=Dev >> $GITHUB_ENV

      - name: Set System Env Staging
        if: contains(toJSON(github.ref), 'staging')
        run: echo SYSTEM_ENV=Stg >> $GITHUB_ENV

      - name: Set System Env Production
        if: contains(toJSON(github.ref), 'production')
        run: echo SYSTEM_ENV=Prod >> $GITHUB_ENV

      - name: CDK Deploy
        if: contains(github.event_name, 'push')
        run: npx cdk deploy --require-approval never --role-arn "${{ env.FUTSAL_BACKEND_STACK_DEPLOY_ROLE }}"
        working-directory: ./futsal-backend

検証

いい感じですね。
スクリーンショット 2021-12-04 1.19.01.png

感想

今回初めてGithub Actionsを触って見ましたが非常に勉強になりました!
普段の業務では主にCircleCIを使っているのですが今後の新しいプロジェクトでは
Github Actionsも選択肢に入る気がします。(IAMユーザー作らなくていいのが大きすぎる)
また今回私自身ブログの投稿が初めてでエンジニア経験がそこまで長いわけではないので、
おかしなところもあると思いますがご容赦頂けたら幸いです。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?