Closed8

AWS CDK(TypeScript)を試してみる、GitHubActionsでCI/CDも。

kun432kun432

初期化〜デプロイ

Pythonではあるけど1回通してるのでさらっと。

boilerplateからプロジェクトが作成され、必要なライブラリ等がインストールされる。

$ mkdir cdk-app && cd cdk-app
$ cdk init --language typescript

テンプレートに変換

$ cdk synth

pythonでやっているのでcdk bootstrapは不要。

lib/cdk-app-stacks.tsはこんな感じで。

cdk-app/lib/cdk-app-stacks.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3'
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'

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

    new s3.Bucket(this, 'CreateBucket', {
      bucketName: "cdk-app",
      versioned: true,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
    })

    new dynamodb.Table(this, 'cdk-app', {
      tableName: "cdk-app",
      partitionKey: {
        name: 'id',
        type: dynamodb.AttributeType.STRING,
      },
      sortKey: {
        name: 'name',
        type: dynamodb.AttributeType.STRING,
      },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
      pointInTimeRecovery: true,
      timeToLiveAttribute: 'expired',
      removalPolicy: cdk.RemovalPolicy.DESTROY,
    })

  }
}

TypeScriptからJavaScriptにコンパイル

$ npm run build

デプロイ

$ cdk deploy

コードを変更したら以下の流れで。cdk synthcdk deployの中でやってるらしい。

$ npm run build
$ cdk deploy

これでOK。一旦削除する。

$ cdk destroy
kun432kun432

複数環境にデプロイ

複数環境でCDKを使う場合、いくつかの方法がある。

https://note.com/asahi_ictrad/n/nf10a4c89c1bf#d076d291-7a2c-42e5-9adb-6b5a30e8250d

https://techblog.raksul.com/entry/2022/12/15/164714

https://dev.classmethod.jp/articles/aws-cdk-deploy-dev-and-prod-stack/

https://dev.classmethod.jp/articles/aws-cdk-multi-environment-config/

https://abillyz.com/mamezou/studies/464

https://masawada.hatenablog.jp/entry/2022/12/23/220953

CDKの基本機能に近しいものから、コード的に解決するものまで、複数ある。が、TerraformやAnsibleとかDSLっぽい宣言で書くようなものを触ってた身からすると、なかなかにハードルが高い・・・(一応インフラエンジニアだし)

とりあえずやりたい、というかやったほうがいいよなぁーと思ってるのは以下。

  • リソース名に環境名を含めたい
  • リソースごとにパラメータも変えたい
  • スタックは環境ごとに分けて、スタック名に環境名を含めたい

ということでちょっと順番にためしてみる。

あと、

  • アカウントが単一(スタック名は絶対に変える必要がある)
  • アカウントが複数に分かれている(スタック名を変えなくても実現可能)

で変わってくるここも踏まえて。

kun432kun432

いろいろ調べてみた感じ、これが良さそうに思える。

https://note.com/asahi_ictrad/n/nf10a4c89c1bf#d076d291-7a2c-42e5-9adb-6b5a30e8250d

  • 単一アカウントで複数環境の場合はスタック名をわける必要がある。
    • cdk lsで分けて出力されるようにしたい
    • cdk deployの引数で指定したい。
    • スタック名をリソース名のプリフィクスとして使う。
    • なお、複数アカウントに分かれている場合でもスタック初期化時にenvで渡せばどちらでもいける。
  • 環境ごとの設定はcdk.jsonではなく、環境ごとにファイルを用意する。
    • cdk.jsonのcontextで渡す形だと、CLIでスタック名とコンテキストの両方を渡すことになるので冗長(だと思っている)
    • cdk.jsonだと静的にしか書けないが、TypeScriptで書くと以下のメリットがある
      • 設定に型を定義できる
      • 設定を書く場合にコード補完が効く

ということでやってみる。

$ mkdir cdk-app & cd cdk-app
$ cdk init --language typescript

ここから修正していく。今回はs3とdynamodbを追加してみる。まず、設定ファイルのディレクトリを作成。

$ mkdir config

設定ファイルの型定義を作成。環境間で差異がある設定をここで定義。設定項目は適当に選んでみた。

config/types.ts
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';

export type Config = {
    s3: S3Config, 
    dynamodb: DynamoDbConfig, 
}

type S3Config = {
    removablePolicy: cdk.RemovalPolicy,
    autoDeletionObjects: boolean,
    versioned: boolean,
}

type DynamoDbConfig = {
    removablePolicy: cdk.RemovalPolicy,
    billingMode: dynamodb.BillingMode,
    pointInTimeRecovery: boolean,
}

ちなみにS3のバケット名やDynamoDBのテーブル名などもここで定義しても良いが、自分の場合はスタック名から生成させたかったので、ここでは定義しないことにした。

cdkのライブラリを読み込んでるので、補完も効くしミスが起こりにくいのは良い。

では環境ごとにファイルを用意する。今回は開発と本番の2種類とする。微妙に設定を変えてみた。

開発

config/dev.ts
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';

import { Config } from "./types";

export const s3Config: Config["s3"] = {
    removablePolicy: cdk.RemovalPolicy.DESTROY,
    autoDeletionObjects: true,
    versioned: false,
}

export const dynamodbConfig: Config["dynamodb"] = {
    removablePolicy: cdk.RemovalPolicy.DESTROY,
    billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
    pointInTimeRecovery: true,
}

export const config: Config = {
    s3: s3Config,
    dynamodb: dynamodbConfig,
}

本番

config/prod.ts
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';

import { Config } from "./types";

export const s3Config: Config["s3"] = {
    removablePolicy: cdk.RemovalPolicy.RETAIN,
    autoDeletionObjects: false,
    versioned: true,
}

export const dynamodbConfig: Config["dynamodb"] = {
    removablePolicy: cdk.RemovalPolicy.RETAIN,
    billingMode: dynamodb.BillingMode.PROVISIONED,
    pointInTimeRecovery: true,
}

export const config: Config = {
    s3: s3Config,
    dynamodb: dynamodbConfig,
}

ファイルを分けたことでdiffで比較しやすいのも良い。

ではスタック定義を修正。ここで設定ファイルの型を取り込みつつ、設定とスタック名をStackProps経由で取り込む。

lib/cdk-app-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3'
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'

import { Config } from "../config/types";

interface CdkAppStackProps extends cdk.StackProps {
  stackName: string;
  config: Config;
}

export class CdkAppStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: CdkAppStackProps) {
    super(scope, id, props);
    const {deployEnv, stackName, config} = props;
    
    new s3.Bucket(this, 'createBucket', {
      bucketName: `${stackName}-s3-bucket`,
      versioned: config.s3.versioned,
      removalPolicy: config.s3.removablePolicy,
      autoDeleteObjects: config.s3.autoDeletionObjects,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
    })

    new dynamodb.Table(this, 'createTable', {
      tableName: `${stackName}-dynamodb-table`,
      partitionKey: {
        name: 'id',
        type: dynamodb.AttributeType.STRING,
      },
      sortKey: {
        name: 'name',
        type: dynamodb.AttributeType.STRING,
      },
      billingMode: config.dynamodb.billingMode,
      pointInTimeRecovery: config.dynamodb.pointInTimeRecovery,
      timeToLiveAttribute: 'expired',
      removalPolicy: config.dynamodb.removablePolicy,
    })

  }
}

設定は環境ごとに共通のものはそのまま定義してしまえばいいし、環境ごとに分ける必要があるものはconfig.*で取り込めば良い。

リソース名には受け取ったスタック名を付与している。

最後にcdkのcliで実行されるファイル。ここで、設定ファイルを取り込んでそれぞれのスタックを生成する。

bin/cdk-app.ts
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CdkAppStack } from '../lib/cdk-app-stack';
import { config as devConfig } from '../config/dev'
import { config as prodConfig } from '../config/prod'

const app = new cdk.App();

const appName = "cdk-app"
const stackNameDev = `${appName}-dev`
const stackNameProd = `${appName}-prod`

new CdkAppStack(app, stackNameDev, {
  env: {
    region: process.env.CDK_DEV_REGION,
    account: process.env.CDK_DEV_ACCOUNT,
  },
  stackName: stackNameDev,
  config: devConfig,
});

new CdkAppStack(app, `${appName}-prod`, {
  env: {
    region: process.env.CDK_PROD_REGION,
    account: process.env.CDK_PROD_ACCOUNT,
  },
  stackName: stackNameProd,
  config: prodConfig,
});

appNameは、新規プロジェクトごとに毎回cdk initしなくても、この状態をテンプレートとしておけば、ここを変更するだけで量産できるかなということで。そしてそれをベースに環境ごとのスタック名を生成している。

ちなみにenvのところは、手元でCLIで実行するならば

  • AWSアカウントが単一の場合はCDK_DEFAULT_REGION/CDK_DEFAULT_ACCOUNTで揃えてもOK
  • AWSアカウントが環境ごとに別れている場合もプロファイルを引数で与えることになると思うからCDK_DEFAULT_REGION/CDK_DEFAULT_ACCOUNTでもOK

なんだけど、CI/CD回したりすることを考えて明示的に分けた。上記の環境変数が定義されていない場合はデフォルトのプロファイルで実行される様子。

TSをJSに変換

$ npm run build

では確認してみる。

$ cdk ls
cdk-app-dev
cdk-app-prod

ちゃんと別々のスタックとして認識している。

開発用スタックを作成してみる。

$ cdk deploy cdk-app-dev

ではリソースを見てみる。

$ aws s3 ls | grep "cdk-app"
2023-05-15 06:50:25 cdk-app-dev-s3-bucket

$ aws dynamodb list-tables --output text | grep "cdk-app"
TABLENAMES      cdk-app-dev-dynamodb-table

次に本番リソースを作成してみる。

$ cdk deploy cdk-app-prod

再度リソースを確認してみる。

$ aws s3 ls | grep "cdk-app"
2023-05-15 06:50:25 cdk-app-dev-s3-bucket
2023-05-15 06:54:53 cdk-app-prod-s3-bucket

$ aws dynamodb list-tables --output text | grep "cdk-app"
TABLENAMES      cdk-app-dev-dynamodb-table
TABLENAMES      cdk-app-prod-dynamodb-table

リソースがそれぞれで作成されている。で設定がちゃんと違っているかを確認してみる。例えばS3のバージョニングとかDynamoDBの課金モードとか。

$ aws s3api get-bucket-versioning --bucket cdk-app-dev-s3-bucket --output text
(何も表示されない)
$ aws s3api get-bucket-versioning --bucket cdk-app-prod-s3-bucket --output text
Enabled

$ aws dynamodb describe-table --table-name cdk-app-dev-dynamodb-table --query 'Table.Billi
ngModeSummary.BillingMode' --output text
PAY_PER_REQUEST
$ aws dynamodb describe-table --table-name cdk-app-prod-dynamodb-table --query 'Table.Bill
ingModeSum
None

おっと、DynamoDBで"PROVISIONED"の場合はキャパシティユニットを設定しないとダメなんだった。そこは後で直すとしてとりあえずちゃんと効いている様子。

では削除。開発だけ削除する。

$ cdk destroy cdk-app-dev

$ aws s3 ls | grep "cdk-app"
2023-05-15 06:54:53 cdk-app-prod-s3-bucket

$ aws dynamodb list-tables --output text | grep "cdk-app"
TABLENAMES      cdk-app-prod-dynamodb-table

本番だけが残っている。想定通り。本番も削除してみる。

$ cdk destroy cdk-app-prod

$ aws s3 ls | grep "cdk-app"
2023-05-15 06:54:53 cdk-app-prod-s3-bucket

$ aws dynamodb list-tables --output text | grep "cdk-app"
TABLENAMES      cdk-app-prod-dynamodb-table

こちらは本番だけ削除ポリシーで残す設定にしているため残っている。スタックの管理からはもう外れることになるので、別途削除が必要。

kun432kun432

環境ごとに設定が変わることにより、必要だったり不要だったりみたいな値が出てきた場合。例えばDynamoDBでPROVISIONEDモードの場合はread/writeCapacityが必須みたいな。

ChatGPTに聞きながらやってみた感じだとこう。

まず型定義を修正。共通のものとそうでないものをわける。こういう書き方ができるのね。

config/types.ts
type CommonDynamoDbConfig = {
    removablePolicy: cdk.RemovalPolicy,
    pointInTimeRecovery: boolean,
};

type ProvisionedDynamoDbConfig = CommonDynamoDbConfig & {
    billingMode: dynamodb.BillingMode.PROVISIONED,
    readCapacity: number,
    writeCapacity: number,
};

type PayPerRequestDynamoDbConfig = CommonDynamoDbConfig & {
    billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
};

type DynamoDbConfig = ProvisionedDynamoDbConfig | PayPerRequestDynamoDbConfig;

各環境ごとの設定ファイルはこんな感じ。あえて間違って書いてみたらちゃんと型チェックは効いてた。

config/dev.ts
export const dynamodbConfig: Config["dynamodb"] = {
    removablePolicy: cdk.RemovalPolicy.DESTROY,
    billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
    pointInTimeRecovery: true,
}
config/prod.ts
export const dynamodbConfig: Config["dynamodb"] = {
    removablePolicy: cdk.RemovalPolicy.RETAIN,
    billingMode: dynamodb.BillingMode.PROVISIONED,
    pointInTimeRecovery: true,
    writeCapacity: 1,
    readCapacity: 1,
}

でスタック定義はこうなる。

lib/cdk-app-stack.ts
    let dynamodbTableProps: dynamodb.TableProps = {
      tableName: `${stackName}-dynamodb-table`,
      partitionKey: {
        name: 'id',
        type: dynamodb.AttributeType.STRING,
      },
      sortKey: {
        name: 'name',
        type: dynamodb.AttributeType.STRING,
      },
      pointInTimeRecovery: config.dynamodb.pointInTimeRecovery,
      timeToLiveAttribute: 'expired',
      billingMode: config.dynamodb.billingMode,
    };

    if (config.dynamodb.billingMode === dynamodb.BillingMode.PROVISIONED) {
      dynamodbTableProps = {
        ...dynamodbTableProps,
        readCapacity: config.dynamodb.readCapacity,
        writeCapacity: config.dynamodb.writeCapacity,
      };
    }

    new dynamodb.Table(this, `createTable`, dynamodbTableProps);

共通となる設定のプロパティを作っておいて、一部の設定で分岐させて設定を上書き&追加、最後にnew Tableでテーブル作成という感じ。

もうちょっとシンプルに書けないかなーと思ったりするけれど、こういう書き方ができるのもCDKならではかもしれない(DSL的なやつでチェックまで効かせてこれやろうと思うと結構ツラそうで、もうちょっとゆるく書きそう)

kun432kun432

で、せっかくなのでCI/CDを設定してみる。

まずはIAM OIDCプロバイダの設定が必要になるけど、これはアカウントで1つ作成すれば良いので、上記のCDKプロジェクトとは別のCDKプロジェクトを作って設定することとする。

まんまこれだな。

https://dev.classmethod.jp/articles/create-resources-used-for-oidc-integration-with-github-with-aws-cdk/

ただし、上記にも書いたとおり、 OIDCプロバイダの設定はアカウントで1つ作成になるので、単一アカウントで複数環境を構築するような場合は、ちょっと考えないといけない。今回は、CDKのこのプロジェクトでは複数環境を意識しなくてもいいこととした。詳細は後述。

プロジェクト作成。

$ mkdir cdk-openid-github && cd cdk-openid-github 
$ cdk init --language typescript 

基本的には上記の記事通りにすれば良いのだけど、上記の記事ではprincipalFederatedSubを特定のレポジトリの全ブランチで使用可能なように*で指定していたけど、

  • CDKプロジェクトをどんどん増やしてGitHub AtionsでCI/CD回す、というような場合に、OIDCプロバイダの設定はアカウント単位で1つだけで良い上、このレポジトリの複製をたくさん作るのはめんどくさそう。
  • プルリクエストでもCI/CD走らせたい。

ということで、複数列挙できるような形にするのがよいと考えた。

そこで、OIDCプロバイダー設定は1つだけ作って、各CDKプロジェクトのレポジトリごとに特定の(複数の)ブランチからと、そのレポジトリへのプルリクエストから使用可能なように設定を渡す、という感じにしてみた。

コードを見たほうがわかりやすい。

bin/cdk-openid-github.ts
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CdkOpenidGithubStack } from '../lib/cdk-openid-github-stack';

const app = new cdk.App();
new CdkOpenidGithubStack(app, 'CdkOpenidGithubStack', {
  env: {
    region: process.env.CDK_DEFAULT_REGION,
    account: process.env.CDK_DEFAULT_ACCOUNT,
  },
  principalFederatedSubs: {
    'cdk-app': [
      'repo:kun432/aws-cdk-github-actions-sample:ref:refs/heads/main',
      'repo:kun432/aws-cdk-github-actions-sample:ref:refs/heads/dev',
      'repo:kun432/aws-cdk-github-actions-sample:pull_request',
    ],
  },
});

principalFederatedSubsに値が配列のハッシュを指定する。キーはプロジェクト名でこれがはIAMロールの名前に使う。そして値に必要なブランチとプルリクを登録する。

で、スタック定義でこれをプロパティとして受け取って、ループでIAMロールを作っていく形にした。もちろんOIDCプロバイダの作成は1回だけ。

lib/cdk-openid-github-stack.ts
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as iam from 'aws-cdk-lib/aws-iam';

export interface cdkOpenIdGithubStackProps extends cdk.StackProps {
  principalFederatedSubs: { [key: string]: string[] };
}

export class CdkOpenidGithubStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: cdkOpenIdGithubStackProps) {
    super(scope, id, props);

    const accountId = cdk.Stack.of(this).account;
    const region = cdk.Stack.of(this).region;

    const gitHubIdProvider = new iam.OpenIdConnectProvider(
      this,
      'GitHubIdProvider',
      {
        url: 'https://token.actions.githubusercontent.com',
        clientIds: ['sts.amazonaws.com'],
      }
    );    
    
    const deployPolicy = new iam.Policy(this, 'cdkDeployPolicy', {
      policyName: `cdk-deploy-policy`,
      statements: [
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          actions: [
            's3:getBucketLocation',
            's3:List*',
            'cloudformation:CreateStack',
            'cloudformation:CreateChangeSet',
            'cloudformation:DeleteChangeSet',
            'cloudformation:DescribeChangeSet',
            'cloudformation:DescribeStacks',
            'cloudformation:DescribeStackEvents',
            'cloudformation:ExecuteChangeSet',
            'cloudformation:GetTemplate',
          ],
          resources: [
            'arn:aws:s3:::*',
            `arn:aws:cloudformation:${region}:${accountId}:stack/CDKToolkit/*`,
            `arn:aws:cloudformation:${region}:${accountId}:stack/*/*`,
          ],
        }),
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          actions: ['s3:PutObject', 's3:GetObject'],
          resources: [`arn:aws:s3:::cdk-*-assets-${accountId}-${region}/*`],
        }),
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          actions: ['ssm:GetParameter'],
          resources: [
            `arn:aws:ssm:${region}:${accountId}:parameter/cdk-bootstrap/*/version`,
          ],
        }),
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          actions: ['iam:PassRole'],
          resources: [
            `arn:aws:iam::${accountId}:role/cdk-*-cfn-exec-role-${accountId}-${region}`,
          ],
        }),
      ],
    });

    Object.keys(props.principalFederatedSubs).forEach((key) => {
      const oidcDeployRole = new iam.Role(this, `${key}-githubOidcRole`, {
        roleName: `${key}-github-oidc-role`,
        assumedBy: new iam.FederatedPrincipal(
          gitHubIdProvider.openIdConnectProviderArn,
          {
            'StringEquals': {
              'token.actions.githubusercontent.com:sub':
              props.principalFederatedSubs[key]
            },
          },
          'sts:AssumeRoleWithWebIdentity'
        ),
      });
      oidcDeployRole.attachInlinePolicy(deployPolicy);
    })
  }
}

TypeScriptの型定義とか食わず嫌い(というかTypeScript自体があまり好みではない)なこともあって、実は今回真面目にいろいろ調べながらやってみたのだけども、やはり型チェックでミスを弾けるのは良いと感じた。多少なりともTypeScriptと仲良く慣れた気がする、ほんのちょっとだけだけど。

プロジェクトを追加する場合は、

  principalFederatedSubs: {
    'cdk-app': [
      'repo:kun432/aws-cdk-github-actions-sample:ref:refs/heads/main',
      'repo:kun432/aws-cdk-github-actions-sample:ref:refs/heads/dev',
      'repo:kun432/aws-cdk-github-actions-sample:pull_request',
    ],
    'sample-app': [
      'repo:kun432/sample-app:ref:refs/heads/main',
      'repo:kun432/sample-app:ref:refs/heads/dev',
      'repo:kun432/sample-app:pull_request',
    ],
  },

みたいな感じで増やしてデプロイすれば、GitHub ActionsでデプロイするためのIAMロールがプロジェクトごとに作成されるようになる。

ちなみにプルリクエストからも許可するというのはこれを参考にした。

https://dev.classmethod.jp/articles/githubactions-pull-request-event-aws/

kun432kun432

上記で作成したIAMロールを使って、GitHub Actionsを使ったCDKプロジェクトのデプロイを設定していく。

環境変数(構成変数)はいろいろあるみたいだけど、とりあえずレポジトリ単位で変数を設定することにした。

以下を登録しておく。AWSアカウントなんかはsecretでいいかもしれない。

  • DEPLOY_ROLE_NAME
    • 上で作成したOIDCプロバイダを使うIAMロールを指定
  • CDK_DEV_ACCOUNT / CDK_DEV_REGION
    • 開発環境のAWSアカウントIDとリージョン
  • CDK_PROD_ACCOUNT / CDK_PROD_REGION
    • 本番環境のAWSアカウントIDとリージョン

で、GitHub Actionsのワークフローはこんな感じ。開発環境向け。

.github/workflows/deploy-dev.yaml
on:
  push:
    branches:
      - dev
  pull_request:
    branches:
      - dev
    types:
      - opened
      - synchronize
      - reopened
  workflow_dispatch:

env:
  DEPLOY_ROLE_NAME: ${{ vars.DEPLOY_ROLE_NAME }}
  CDK_DEV_ACCOUNT: ${{ vars.CDK_DEV_ACCOUNT }}
  CDK_DEV_REGION: ${{ vars.CDK_DEV_REGION }} 

jobs:
  deploy-dev:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - run: |
          echo "CDK_DEV_ACCOUNT=${{ env.CDK_DEV_ACCOUNT }}"
          echo "CDK_DEV_REGION=${{ env.CDK_DEV_REGION }}"
      - name: Checkout
        uses: actions/checkout@v3
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: arn:aws:iam::${{ env.CDK_DEV_ACCOUNT }}:role/${{ env.DEPLOY_ROLE_NAME }}
          aws-region: ${{ env.CDK_DEV_REGION }}
      - name: Setup Node
        uses: actions/setup-node@v1
        with:
          node-version: "18"
      - name: CDK package install
        run: npm ci
      - name: CDK Diff Check
        if: (github.event_name == 'pull_request') || (github.event_name == 'push') || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/dev')
        run: |
          npm run cdk diff cdk-app-dev
      - name: CDK Deploy
        if: (github.event_name == 'push') || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/dev')
        run: |
          npm run cdk deploy cdk-app-dev -- --require-approval never
  • プッシュ(プルリクエストのマージ)時はcdk diffしたあとにcdk deploy
  • プルリクエスト時はcdk diffまで。cdk deployはしない。

としている。このへんの設定、実はChatGPTに聞きながらドキュメントと照らし合わせながら書いた。もっとシンプルに書けないかなーともお願いしてみたけど、ちゃんと設定しないと想定しない動きになるよ、と怒られた。はい、もっと勉強します。

本番向けも基本的に設定内容は同じで、開発向けとなっているところを本番用に書き換えるだけ。

.github/workflows/deploy-prod.yaml
on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
    types:
      - opened
      - synchronize
      - reopened
  workflow_dispatch:

env:
  DEPLOY_ROLE_NAME: ${{ vars.DEPLOY_ROLE_NAME }}
  CDK_PROD_ACCOUNT: ${{ vars.CDK_PROD_ACCOUNT }}
  CDK_PROD_REGION: ${{ vars.CDK_PROD_REGION }} 

jobs:
  deploy-prod:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - run: |
          echo "CDK_PROD_ACCOUNT=${{ env.CDK_PROD_ACCOUNT }}"
          echo "CDK_PROD_REGION=${{ env.CDK_PROD_REGION }}"
      - name: Checkout
        uses: actions/checkout@v3
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: arn:aws:iam::${{ env.CDK_PROD_ACCOUNT }}:role/${{ env.DEPLOY_ROLE_NAME }}
          aws-region: ${{ env.CDK_PROD_REGION }}
      - name: Setup Node
        uses: actions/setup-node@v1
        with:
          node-version: "18"
      - name: CDK package install
        run: npm ci
      - name: CDK Diff Check
        if: (github.event_name == 'pull_request') || (github.event_name == 'push') || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')
        run: |
          npm run cdk diff cdk-app-prod
      - name: CDK Deploy
        if: (github.event_name == 'push') || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main')
        run: |
          npm run cdk deploy cdk-app-prod -- --require-approval never

あとは実際に試してみるとよい。手元で試してみた限りは想定通りになっていた。

このスクラップは2023/05/16にクローズされました