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 1 year has 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?