AWS Lambda を作成・更新するための gulpfile 雛形


Lambda をいろいろな用途で使い出すと function の登録や更新が面倒なので、自分が使い回している gulpfile の雛形を晒しておきます。

## 機能

`lambda.yml` という設定ファイルに環境毎の lambda の設定情報を記載しておきます。

`gulp build --env={production|development}` で、コードの build、zip化、function 登録・更新までを自動で行います。

## 必要なもの

package.json の devDependencies には、

- del
- gulp
- gulp-awslambda
- gulp-install
- gulp-load-plugins
- gulp-rename
- gulp-zip
- js-yaml
- run-sequence
- yargs

が必要です。

> dependencies にある場合は、省略可。

## 雛形
```javascript:gulpfile.js
const gulp = require('gulp')
const $ = require('gulp-load-plugins')()
const argv = require('yargs').argv
const env = argv.env || 'development'

gulp.task('build', () =>
  require('run-sequence')('clean', 'lambda')
)

gulp.task('clean', (done) =>
  require('del')(['build/*', '!build/node_modules', 'dist/*'], done)
)

gulp.task('prepareCode', () =>
  gulp.src('src/**/*.js')
    .pipe(gulp.dest('build'))
)

gulp.task('prepareConfig', () =>
  gulp.src(`config/${env}.yml`)
    .pipe($.rename('default.yml'))
    .pipe(gulp.dest('build/config'))
)

gulp.task('preparePackages', () =>
  gulp.src('./package.json')
    .pipe(gulp.dest('build'))
    .pipe($.install({production: true}))
)

gulp.task('lambda', ['prepareCode', 'prepareConfig', 'preparePackages'], () => {
  const lambdaConfig = require('js-yaml').safeLoad(require('fs').readFileSync('lambda.yml', 'utf8'))[env]

  return gulp.src(['build/**', '!build/package.json'])
    .pipe($.zip('lambda.zip'))
    .pipe($.awslambda(lambdaConfig.lambda, lambdaConfig.config))
    .pipe(gulp.dest('dist'))
})
```

```lambda.yml
default:
  config: &config
    region: ap-northeast-1
  lambda: &default
    Handler: lambda.handler
    Runtime: nodejs4.3
    MemorySize: 128
    Timeout: 60

production:
  config:
    <<: *config
    profile: production
  lambda:
    <<: *default
    FunctionName: {FUNCTION_NAME}
    Description: {DESCRIPTION}
    Role: {ROLE_ARN}

development:
  config: *config
  lambda:
    <<: *default
    FunctionName: {FUNCTION_NAME}-dev
    Description: {DESCRIPTION}
    Role: {ROLE_ARN}
```

## やってること

- clean

  build 用フォルダの削除。

- prepareCode

  ソースファイル（src/**/*.js）を build 以下にコピーする。

- prepareConfig

  環境用設定ファイル（config/*.yml）を選択し、build/config/default.yml としてコピーする。

- preparePackages

  lambda 実行に必要な node_modules を build フォルダにインストールする。
  
  > native build が必要なパッケージには非対応。

- gulp task: lambda

  build フォルダ以下を lambda.zip として固めて lambda に登録。
  function がない場合は新規に作成、ある場合はコードを反映。登録情報に変更がある場合は更新後にコード反映。

- gulp task: build

  clean 後に lambda 実行。

## 補足

### lambda function 用の設定ファイルが必要

  [node-config](https://www.npmjs.com/package/config)（+ js-yaml）で環境毎の設定ファイルをロードし利用する手法を採用しています。
  違うやり方が好きな人は、gulpfile の `prepareConfig` の部分を適宜修正してください。

### lambda.yml の AWS Credential について

  `config` 項に `config.region` 以外何も指定していない場合、認証情報として awscli の default profile の利用を試みます。
  違う profile を指定する場合は、`config.profile` に profile 名 を指定してください。

> Node の AWS SDK は現時点で role_arn に対応していないため、assume-role は自前で行っておく必要があります。
> [AssumeRole が面倒な人向け簡易 shell script](http://qiita.com/u-minor/items/4eca27cce64d3f4248ba)

### lambda.yml の Role について

  lambda 実行用の IAM Role は事前に awscli もしくは管理コンソールで作成しておき、その ARN を指定します。

IAM Role を作るには、

```json:AssumeRolePolicy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

上記 AssumeRolePolicy.json を用意して

```command-line
$ aws iam create-role \
  --role-name {LAMBDA_ROLE_NAME} \
  --assume-role-policy-document file://AssumeRolePolicy.json
```

で role 作成。

```command-line
$ aws iam attach-role-policy \
  --role-name {LAMBDA_ROLE_NAME} \
  --policy-arn arn:aws:iam::aws:policy/AWSLambdaExecute
```

で作成した role に管理ポリシーの AWSLambdaExecute を付与。
追加でインラインポリシーを指定したい場合は、

```json:InlinePolicy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect":"Allow",
      "Action":"sdb:*",
      "Resource":"arn:aws:sdb:*"
    }
  ]
}
```

のような InlinePolicy.json を用意（追加で付与したい権限を設定）して、

```command-line
$ aws iam put-role-policy \
  --role-name {LAMBDA_ROLE_NAME} \
  --policy-name {POLICY_NAME} \
  --policy-document file://InlinePolicy.json
```

で追加付与。

Role ARN は、

```command-line
$ aws iam get-role \
  --role-name {LAMBDA_ROLE_NAME} \
  --query Role.Arn \
  --output text
```

で確認できます。