Help us understand the problem. What is going on with this article?

AWS CDKでLambdaデプロイしてみた【AWS CDK】

Advent Calendarはじめました

株式会社うるるでは今年から初のAdvent Calendarを実施しています。
この記事は違うのですが、みんな気合が入っているので興味あるかたは是非ご覧いただければ嬉しいです。
sc_1.png

TL; DR

早速ではございますが、タイトルについて書いていこうかと思います。
弊社内ではにわかにAWS CDKが流行りの兆しを見せてきております!たぶん!
社内でInfrastructure as Codeを推進しているのですが、元々はTerraformでやっていました。(実際既に運用ベースに乗っているものもあります。)
しかしある日、こんなワードを聞いてしまったのです。。
「インフラ強い会社は今だいたいCDKっすよ。」…ドキッとしてしまいました。
彼は勉強熱心で社外の色々な勉強会にも参加しており、雷に打たれた思いでした。
その日のうちにCDKに移行することを決め、翌日からちまちま書き始めた経緯です。

Advent Calendarスタート!!!しかし…

冒頭にも書いた通り、今年初めて弊社ではAdvent Calenderに取り組みます。
しかし気づいてしまったのです。
「あれ?今日からじゃね?大丈夫コレ?」
…リマインダーなどの通知を仕込むことを失念しておりました。。

通知してくれるLambdaをCDKでデプロイするまでやってみようと思います。
リポジトリはこちら
https://github.com/uluru-kinoshitat/cdk-lambda

アーキ図的なもの
cdk-lambda.png

CDKの基本

生成されたREADME.mdには下記記載があります。

  • npm run build compile typescript to js
  • npm run watch watch for changes and compile
  • npm run test perform the jest unit tests
  • cdk deploy deploy this stack to your default AWS account/region
  • cdk diff compare deployed stack with current state
  • cdk synth emits the synthesized CloudFormation template

まあ見ての通りですが、これ以外にも後述するcdk bootstrapというデプロイ時に生成されるCloudFormation定義を格納しておくS3バケットなどを作ってくれるコマンドは必須なので覚えておきましょう。

おまじない

最初のあれですね。
CDKは割とTypeScriptが公式感あるのでTypeScriptで書いていきます。
ちなみにエディタはVSCodeだと特にプラグインなくてもめちゃくちゃ恩恵受けられるのでオススメです。(公式も力入れてるっぽい)

$ cdk --version
1.17.1 (build fa4cb1f)
$ 
$ cdk init app --language=typescript
`cdk init` cannot be run in a non-empty directory!

いきなりエラーw
はい、空じゃないと怒られてしまうのできれいきれいなディレクトリでやりましょう。

$ cdk init app --language typescript
Applying project template app for typescript
Initializing a new git repository...
Executing npm install...
npm WARN deprecated fsevents@1.2.9: One of your dependencies needs to upgrade to fsevents v2: 1) Proper nodejs v10+ support 2) No more fetching binaries from AWS, smaller package size
npm WARN deprecated left-pad@1.3.0: use String.prototype.padStart()
node-pre-gyp WARN Using request for node-pre-gyp https download 
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN sub@0.1.0 No repository field.
npm WARN sub@0.1.0 No license field.

# Useful commands

 * `npm run build`   compile typescript to js
 * `npm run watch`   watch for changes and compile
 * `npm run test`    perform the jest unit tests
 * `cdk deploy`      deploy this stack to your default AWS account/region
 * `cdk diff`        compare deployed stack with current state
 * `cdk synth`       emits the synthesized CloudFormation template

今度は問題なく初期化が実施されました。
色々なファイルやらなんやらが生成されています。

files
$ pwd
/Users/user/Documents/GitHub/cdk-lambda
$
$ ls -l
total 504
drwxr-xr-x   15 user  staff     480 12  1 22:51 .
drwx------+  33 user  staff    1056 12  1 22:50 ..
drwxr-xr-x   12 user  staff     384 12  1 22:51 .git
-rw-r--r--    1 user  staff      93 12  1 22:51 .gitignore
-rw-r--r--    1 user  staff      65 12  1 22:51 .npmignore
-rw-r--r--    1 user  staff     369 12  1 22:51 README.md
drwxr-xr-x    3 user  staff      96 12  1 22:51 bin
-rw-r--r--    1 user  staff     112 12  1 22:51 cdk.json
-rw-r--r--    1 user  staff     159 12  1 22:51 jest.config.js
drwxr-xr-x    3 user  staff      96 12  1 22:51 lib
drwxr-xr-x  467 user  staff   14944 12  1 22:51 node_modules
-rw-r--r--    1 user  staff  227570 12  1 22:51 package-lock.json
-rw-r--r--    1 user  staff     525 12  1 22:51 package.json
drwxr-xr-x    3 user  staff      96 12  1 22:51 test
-rw-r--r--    1 user  staff     596 12  1 22:51 tsconfig.json
$
$ tree -I node_modules
.
├── README.md
├── bin
│   └── cdk-lambda.ts
├── cdk.json
├── jest.config.js
├── lib
│   └── cdk-lambda-stack.ts
├── package-lock.json
├── package.json
├── test
│   └── cdk-lambda.test.ts
└── tsconfig.json

initコマンドで生成されるいくつかのファイルにはデフォルトでディレクトリ名が振られます。

ファイルの中身はこんな感じ。
cdk-lambda-stack.ts
// CloudFormationのStackと同様にインフラを定義したファイル

import cdk = require('@aws-cdk/core');

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

    // The code that defines your stack goes here
  }
}
cdk-lambda.ts
// cdk build 実施時に呼び出されるファイル
#!/usr/bin/env node
import 'source-map-support/register';
import cdk = require('@aws-cdk/core');
import { CdkLambdaStack } from '../lib/cdk-lambda-stack';

const app = new cdk.App();
new CdkLambdaStack(app, 'CdkLambdaStack');
tsconfig.json
// TypeScriptの設定ファイル
{
  "compilerOptions": {
    "target":"ES2018",
    "module": "commonjs",
    "lib": ["es2018"],
    "declaration": true,
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": false,
    "inlineSourceMap": true,
    "inlineSources": true,
    "experimentalDecorators": true,
    "strictPropertyInitialization":false,
    "typeRoots": ["./node_modules/@types"]
  },
  "exclude": ["cdk.out"]
}
cdk.json
// CDKの設定ファイル(環境変数などで使えるが、今回は使わない)
{
  "app": "npx ts-node bin/sub.ts",
  "context": {
    "@aws-cdk/core:enableStackNameDuplicates": "true"
  }
}

Stackを書いていく

さきほどinitコマンドで生成されたlib/index.tsがリソース定義に当たるので編集していきます。
が、その前にinit打ったばっかりだと@aws-cdk/coreしかないのでエディタに怒られ続けるので先にインストールしておきましょう。

今回はLambda関数を作成するのとSSMを利用、IAMロールの作成、Cloudwatch eventでのトリガーを作成するため以下が必要になります。

npm install
$ npm install @aws-cdk/aws-lambda @aws-cdk/aws-ssm @aws-cdk/aws-iam @aws-cdk/aws-events @aws-cdk/aws-events-targets
npm WARN cdk-lambda@0.1.0 No repository field.
npm WARN cdk-lambda@0.1.0 No license field.

+ @aws-cdk/aws-events-targets@1.18.0
+ @aws-cdk/aws-lambda@1.18.0
+ @aws-cdk/aws-ssm@1.18.0
+ @aws-cdk/aws-events@1.18.0
+ @aws-cdk/aws-iam@1.18.0
added 40 packages from 4 contributors and audited 1764654 packages in 13.232s

8 packages are looking for funding.
Run "npm fund" to find out more.

Nodeのv8系はサポート切れてしまうのでv10系以上が推奨なので、RuntimeもNODEJS_10_Xに設定します。
またLambdaのソースを参照する部分ではAssetCodeクラスを使ってあげると、ローカルファイルをよきに計らって色々してくれるのでこっちにしましょう。ZIPにするのはめんどくさい。

lambda.Function
    const adventarNotifyFunction = new Function(this, 'SlackAdventar', {
      code: AssetCode.fromAsset('src'),  //ここで指定するとZIP化してUploadまでしてくれる

書きあがったコードはこちら

cdk-lambda-stack.ts
import cdk = require('@aws-cdk/core');
import { Function, AssetCode, Runtime } from '@aws-cdk/aws-lambda';
import { Role, ServicePrincipal, ManagedPolicy } from '@aws-cdk/aws-iam';
import { Rule, Schedule } from '@aws-cdk/aws-events';
import { LambdaFunction } from '@aws-cdk/aws-events-targets';
import { StringParameter } from '@aws-cdk/aws-ssm';

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

    // IAM Role for Lambda with SSM policy.
    const executionLambdaRole = new Role(this, 'secureLambdaRole', {
      roleName: 'lambdaSecureExecutionRole',
      assumedBy: new ServicePrincipal('lambda.amazonaws.com'),
      managedPolicies: [
        ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
        ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMReadOnlyAccess'),
      ]
    });

    // Call Slack channel by plain text.
    const slackChannel = StringParameter.fromStringParameterAttributes(this, 'slackChannel', {
      parameterName: '/Lambda/production/slack-channel',
    });

    // Configure for Lambda function.
    const adventarNotifyFunction = new Function(this, 'SlackAdventar', {
      functionName: 'slack-adventar-notify',
      runtime: Runtime.NODEJS_10_X,
      code: AssetCode.fromAsset('src'),
      handler: 'index.handler',
      timeout: cdk.Duration.seconds(300),
      role: executionLambdaRole,
      environment: {
        TZ: "Asia/Tokyo",
        SLACK_CHANNEL: slackChannel.stringValue,
      }
    });

    // Cloudwatch event for run notification at 1PM JST in Advent Calendar term.
    const rule = new Rule(this, 'Rule', {
      schedule: Schedule.expression('cron(0 4 1-25 12 ? 2019)')
    });

    rule.addTarget(new LambdaFunction(adventarNotifyFunction));
  }
}

const app = new cdk.App();
new CdkLambdaStack(app, 'adventarNotification');
app.synth();

ざっとこんな感じです。
今回はリソース量が大した量ではないので、1ファイルにまとめてしまいました。

SSMを登録する

SlackチャンネルとWebhookはSSMのパラメータストアから呼び出すようにしたいので、下記コマンドで個別に登録していきます。

環境変数 属性
SLACK_CHANNEL String
SLACK_MEMBERS StringList
SLACK_WEBHOOK SecureString
$ aws ssm put-parameter \
>   --type 'String' \
>   --name '/Lambda/production/slack-channel' \
>   --value '$CHANNEL_NAME' #Slackのチャンネル名
{
    "Version": 1,
    "Tier": "Standard"
}
$ 
$ 
$ # https://github.com/aws/aws-cli/issues/3076
$ aws ssm put-parameter \
>   --cli-input-json '{"Name": "/Lambda/production/slack-webhook", "Type": "SecureString",  "Value": "$WEBHOOK_URL"}'  #SlackのWebhook URL
{
    "Version": 1,
    "Tier": "Standard"
}
$ 
$ 
$ aws ssm put-parameter \
>   --type 'StringList' \
>   --name '/Lambda/production/slack-members' \
>   --value "dummy,hogehoge,fugafuga,hogefuga,..."  #SlackのユーザーID
{
    "Version": 1,
    "Tier": "Standard"
}

上記のように設定し、Webhookなどのクレデンシャルはお漏らししないように気をつけます。

Lambdaを書いていく

単純なSlack通知なので、特にこれといった面白みはないのですが…一応書いていきます。
ちょいハマりポイントとしては以下でした。

  • Slackのメンションの指定方法が<@username> -> <@user_id>に変わってた。

         -> A lingering farewell to the username

  • SSMで設定したWebhookとチャンネル名の呼び出し方

src/index.jsを作成して、中に処理を記述していきます。

src/lambda/index.js
const request = require('request');
const AWS = require('aws-sdk');
const ssm = new AWS.SSM();

exports.handler = async function(event, context) {

  // Get WebHook URL from SSM parameter store.
  const slackWebhook = await ssm.getParameter({
    Name: '/Lambda/production/slack-webhook',
    WithDecryption: true,
  }).promise();
  let SLACK_WEBHOOK = slackWebhook.Parameter.Value;

  // Get user_id from SSM parameter store.
  const slackMember = await ssm.getParameter({
      Name: '/Lambda/production/slack-members',
      WithDecryption: true,
  }).promise();
  let member = slackMember.Parameter.Value.split(',');

  // Inject SLACK_CHANNEL value.
  const SLACK_CHANNEL = process.env.SLACK_CHANNEL;

  // create message
  const now = new Date();
  const Today =  now.getDate();
  const mention = "<@" + member[Today] + ">";
  let message = mention + "\n";
  message += '*' + "Advent Calendar " + Today + "日目 よろしくお願いします!" + '*' + "\n";
  message += 'https://adventar.org/calendars/4548';

  // Request configure
  const options = {
    url: SLACK_WEBHOOK,
    headers: {
      'Content-type': 'application/json'
    },
    body: {
      "text": message,
      "channel": SLACK_CHANNEL,
      "username": "uluru",
      "icon_emoji": "uluru",
    },
    json: true
  };

  // Sending message
  request.post(options, function(error, response, body) {
    if (!error && response.statusCode == 200) {
      context.done(null, body);
    } else {
      console.log('error: ' + response.statusCode);
      context.done(null, 'error');
    }
  });
}

ざっとこんな感じです。
特に面白みもなく。笑

注意すべき点はsrc配下に必要なパッケージをインストールあげないといけないところですね。
今回はrequestが必要なのでrootディレクトリで下記を実施しています。

npm install --prefix
$ npm install --prefix ./src request
npm WARN saveError ENOENT: no such file or directory, open '/Users/user/Documents/GitHub/cdk-lambda/src/package.json'
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN enoent ENOENT: no such file or directory, open '/Users/user/Documents/GitHub/cdk-lambda/src/package.json'
npm WARN src No description
npm WARN src No repository field.
npm WARN src No README data
npm WARN src No license field.

+ request@2.88.0
added 48 packages from 59 contributors and audited 63 packages in 2.997s

デプロイ

コードが書き終わったのでデプロイしていきたいと思います。
まずはcdk bootstrapでS3バケットを作成していきます。
その後npm run buildでコンパイルを実施して、cdk deployでリソースを作成していきます。

cdk bootstrap

対象ディレクトリでcdk bootstrapと打つだけです。

cdk bootstrap
$ pwd
/Users/user/Documents/GitHub/cdk-lambda

# failed
$ cdk bootstrap
⨯ Unable to compile TypeScript:
lib/cdk-lambda-stack.ts:40:16 - error TS2304: Cannot find name 'lambda'.

40       runtime: lambda.Runtime.NODEJS_10_X,
                  ~~~~~~

Subprocess exited with error 1

$ cdk bootstrap
ENOENT: no such file or directory, open '../src/index.js'
Subprocess exited with error 1

# Success
$ cdk bootstrap
 ⏳  Bootstrapping environment aws://608092561803/us-east-1...
CDKToolkit: creating CloudFormation changeset...
 0/2 | 22:41:38 | CREATE_IN_PROGRESS   | AWS::S3::Bucket | StagingBucket 
 0/2 | 22:41:39 | CREATE_IN_PROGRESS   | AWS::S3::Bucket | StagingBucket Resource creation Initiated
 1/2 | 22:42:01 | CREATE_COMPLETE      | AWS::S3::Bucket | StagingBucket 
 2/2 | 22:42:03 | CREATE_COMPLETE      | AWS::CloudFormation::Stack | CDKToolkit 
 ✅  Environment aws://608092561803/us-east-1 bootstrapped.

この時点でエラーがあると教えてくれます。
問題なければS3バケットとCloudFormationのスタックが作成されます。
sc_2.png

npm run build

では続いてコンパイルするためにnpm run buildを実施します。
コンパイラはTypeScriptなのでtscが走ります。

cdk.json
{
  "app": "npx ts-node bin/cdk-lambda.ts"
}
$ npm run build

> cdk-lambda@0.1.0 build /Users/user/Documents/GitHub/cdk-lambda
> tsc

特に何か出力される訳ではないのですが、jsファイルに変換されたmonogalib / bin配下にできます。
またcdk.outというディレクトリが作成され、CloudFormationの定義のようなものが作成されます。

ファイルの中身はこんな感じ。
cdk-lambda.js
#!/usr/bin/env node
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
require("source-map-support/register");
const cdk = require("@aws-cdk/core");
const cdk_lambda_stack_1 = require("../lib/cdk-lambda-stack");
const app = new cdk.App();
new cdk_lambda_stack_1.CdkLambdaStack(app, 'CdkLambdaStack');
//# sourceMappingURL=data:application/json;base64,hogehogehogefugafugafugafugafuga
cdk-lambda-stack.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const cdk = require("@aws-cdk/core");
const aws_lambda_1 = require("@aws-cdk/aws-lambda");
const aws_iam_1 = require("@aws-cdk/aws-iam");
const aws_events_1 = require("@aws-cdk/aws-events");
const aws_events_targets_1 = require("@aws-cdk/aws-events-targets");
const aws_ssm_1 = require("@aws-cdk/aws-ssm");
// import lambda = require('@aws-cdk/aws-lambda');
// import events = require('@aws-cdk/aws-events');
// import targets = require('@aws-cdk/aws-events-targets');
// import * as ssm from '@aws-cdk/aws-ssm';
// import ssm = require('@aws-cdk/aws-ssm');
// import iam = require('@aws-cdk/aws-iam');
const fs = require("fs");
class CdkLambdaStack extends cdk.Stack {
    constructor(scope, id, props) {
        super(scope, id, props);
        // The code that defines your stack goes here
        // IAM Role for Lambda with SSM policy.
        const executionLambdaRole = new aws_iam_1.Role(this, 'secureLambdaRole', {
            roleName: 'lambdaSecureExecutionRole',
            assumedBy: new aws_iam_1.ServicePrincipal('lambda.amazonaws.com'),
            managedPolicies: [
                aws_iam_1.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
                aws_iam_1.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMReadOnlyAccess'),
            ]
        });
        // Call Slack channel by plain text.
        const slackChannel = aws_ssm_1.StringParameter.fromStringParameterAttributes(this, 'slackChannel', {
            parameterName: '/Lambda/production/slack-channel',
        });
        // Configure for Lambda function.
        const adventarNotifyFunction = new aws_lambda_1.Function(this, 'SlackAdventar', {
            functionName: 'slack-adventar-notify',
            runtime: aws_lambda_1.Runtime.NODEJS_10_X,
            code: new aws_lambda_1.InlineCode(fs.readFileSync('./src/index.js', { encoding: 'utf-8' })),
            handler: 'index.handler',
            timeout: cdk.Duration.seconds(300),
            role: executionLambdaRole,
            environment: {
                TZ: "Asia/Tokyo",
                SLACK_CHANNEL: slackChannel.stringValue,
            }
        });
        // Cloudwatch event for run notification at 1PM JST in Advent Calendar term.
        const rule = new aws_events_1.Rule(this, 'Rule', {
            schedule: aws_events_1.Schedule.expression('cron(0 4 1-25 12 * 2019)')
        });
        rule.addTarget(new aws_events_targets_1.LambdaFunction(adventarNotifyFunction));
    }
}
exports.CdkLambdaStack = CdkLambdaStack;
const app = new cdk.App();
new CdkLambdaStack(app, 'adventarNotification');
app.synth();
//# sourceMappingURL=data:application/json;base64,hogehogehogefugafugafuga
cdk-lambda-stack.d.ts
import cdk = require('@aws-cdk/core');
export declare class CdkLambdaStack extends cdk.Stack {
    constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps);
}
cdk.out
 ls -l cdk.out
total 80
-rw-r--r--  1 user  staff   4902 12  1 22:41 CdkLambdaStack.template.json
-rw-r--r--  1 user  staff   4938 12  1 22:41 adventarNotification.template.json
-rw-r--r--  1 user  staff     20 12  1 22:41 cdk.out
-rw-r--r--  1 user  staff  11674 12  1 22:41 manifest.json
-rw-r--r--  1 user  staff   7352 12  1 22:41 tree.json
CdkLambdaStack.template.json
{
  "Resources": {
    "secureLambdaRole067C996A": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [
            {
              "Action": "sts:AssumeRole",
              "Effect": "Allow",
              "Principal": {
                "Service": "lambda.amazonaws.com"
              }
            }
          ],
          "Version": "2012-10-17"
        },
        "ManagedPolicyArns": [
          {
            "Fn::Join": [
              "",
              [
                "arn:",
                {
                  "Ref": "AWS::Partition"
                },
                ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
              ]
            ]
          },
          {
            "Fn::Join": [
              "",
              [
                "arn:",
                {
                  "Ref": "AWS::Partition"
                },
                ":iam::aws:policy/AmazonSSMReadOnlyAccess"
              ]
            ]
          }
        ],
        "RoleName": "lambdaSecureExecutionRole"
      },
      "Metadata": {
        "aws:cdk:path": "CdkLambdaStack/secureLambdaRole/Resource"
      }
    },
    "SlackAdventar6AF5ACBE": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Code": {
          "ZipFile": "const request = require('request');\nconst AWS = require('aws-sdk');\nconst ssm = new AWS.SSM();\n\nexports.handler = async function(event, context) {\n\n  // Get WebHook URL from SSM parameter store.\n  const slackWebhook = await ssm.getParameter({\n    Name: '/Lambda/production/slack-webhook',\n    WithDecryption: true,\n  }).promise();\n  let SLACK_WEBHOOK = slackWebhook.Parameter.Value;\n\n  // Get user_id from SSM parameter store.\n  const slackMember = await ssm.getParameter({\n      Name: '/Lambda/production/slack-members',\n      WithDecryption: true,\n  }).promise();\n  let member = slackMember.Parameter.Value.split(',');\n\n  // Inject SLACK_CHANNEL value.\n  const SLACK_CHANNEL = process.env.SLACK_CHANNEL;\n\n  // create message\n  const now = new Date();\n  const Today =  now.getDate();\n  const mention = \"<@\" + member[Today] + \">\";\n  let message = mention + \"\\n\";\n  message += '*' + \"Advent Calendar \" + Today + \"日目 よろしくお願いします!\" + '*' + \"\\n\";\n  message += 'https://adventar.org/calendars/4548';\n\n  // Request configure\n  const options = {\n    url: SLACK_WEBHOOK,\n    headers: {\n      'Content-type': 'application/json'\n    },\n    body: {\n      \"text\": message,\n      \"channel\": SLACK_CHANNEL,\n      \"username\": \"uluru\",\n      \"icon_emoji\": \"uluru\",\n    },\n    json: true\n  };\n\n  // Sending message\n  request.post(options, function(error, response, body) {\n    if (!error && response.statusCode == 200) {\n      context.done(null, body);\n    } else {\n      console.log('error: ' + response.statusCode);\n      context.done(null, 'error');\n    }\n  });\n}\n"
        },
        "Handler": "index.handler",
        "Role": {
          "Fn::GetAtt": [
            "secureLambdaRole067C996A",
            "Arn"
          ]
        },
        "Runtime": "nodejs10.x",
        "Environment": {
          "Variables": {
            "TZ": "Asia/Tokyo",
            "SLACK_CHANNEL": {
              "Ref": "slackChannelParameter"
            }
          }
        },
        "FunctionName": "slack-adventar-notify",
        "Timeout": 300
      },
      "DependsOn": [
        "secureLambdaRole067C996A"
      ],
      "Metadata": {
        "aws:cdk:path": "CdkLambdaStack/SlackAdventar/Resource"
      }
    },
    "SlackAdventarAllowEventRuleCdkLambdaStackRule69AC60575315E536": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "Action": "lambda:InvokeFunction",
        "FunctionName": {
          "Fn::GetAtt": [
            "SlackAdventar6AF5ACBE",
            "Arn"
          ]
        },
        "Principal": "events.amazonaws.com",
        "SourceArn": {
          "Fn::GetAtt": [
            "Rule4C995B7F",
            "Arn"
          ]
        }
      },
      "Metadata": {
        "aws:cdk:path": "CdkLambdaStack/SlackAdventar/AllowEventRuleCdkLambdaStackRule69AC6057"
      }
    },
    "Rule4C995B7F": {
      "Type": "AWS::Events::Rule",
      "Properties": {
        "ScheduleExpression": "cron(0 4 1-25 12 * 2019)",
        "State": "ENABLED",
        "Targets": [
          {
            "Arn": {
              "Fn::GetAtt": [
                "SlackAdventar6AF5ACBE",
                "Arn"
              ]
            },
            "Id": "Target0"
          }
        ]
      },
      "Metadata": {
        "aws:cdk:path": "CdkLambdaStack/Rule/Resource"
      }
    }
  },
  "Parameters": {
    "slackChannelParameter": {
      "Type": "AWS::SSM::Parameter::Value<String>",
      "Default": "/Lambda/production/slack-channel"
    }
  }
}

cdk deploy

コンパイルまで完了したのでいよいよcdk deployでリソースを作成していきます。
初回の場合いきなり作成されることはないのですが、2回目以降は普通に無視して作りにいくので注意してしてください。
sc_3.png

sc_4.png

CloudFormationも確認できます。
sc_5.png

ちなみに変更がかかるとこんな感じでUpdateがかかります。
sc_6.png

無事メンション付きで通知がきました!
sc_7.png

まとめ

Lambdaも楽にデプロイできて幸せになれますね!
ZIP化して…S3にあげて…パスコピーして…みたいなのがないので楽でいいですね!
弊社だと割とServerless Frameworkが主流ですが、SAMとうまく掛け合わせてローカルテストまでできるようになれば完全に駆逐できそう…!!!
嫌いな訳じゃないんですが、ちょっと多機能すぎるかな…というのと、AWS環境しか利用していないという背景があり、私個人としてはAWS CDKに統合していきたいのが本音です。
今後はユーザー作成やPersonalizeなどもCDKでガンガン書いていくつもりなので、気が向けばまた記事書くかもしれません。

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
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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