AWS LambdaのFunctionをapexで管理する



### 今回の要件
1. 動作に必要な設定値が定義化できる
2. dev, prod環境の使い分けが出来る

### 動作に必要なパラメーターが定義化できる
apexではprojectで共通の設定値、各function毎の設定値をそれぞれjsonファイルに書き出してdeploy時に読み出してくれます。

http://apex.run/#structuring-projects

### dev, prod環境の使い分けが出来る
AWS Lambda自体にはdev, prodを切り分ける方法がないのですが、apexでは1つのfunctionに対して複数の環境用の定義ファイルを持てるようになっていて、deploy時に対象を切り分けられるようになっていて、指定した環境ごとに名前を分けて別のfunctionとしてデプロイしてくれるようになっています。

http://apex.run/#multiple-environments

### 注意
#### deploy前に必要な事前処理を自動化する
実際に運用してみると、何度かcloneしたままdeployをかけてしまいnpm modulesが空のままデプロイされてしまう事故が起きました。
apexにはbuild, deploy, cleanが実行されるタイミングをhook出来るので、コードがzipされるbuild時にnpm installがなされるようにして問題を解消しました。

http://apex.run/#supported-hooks

#### 環境を間違えてdeployしないために
deploy時に環境を指定出来るのはいいのですが、それだとコマンド履歴からデプロイしようとした時などにデプロイ先環境を間違って事故が起きかねません。ベストプラクティスはわかりませんがIAMでdeploy環境を制限するのが簡単かる安全なように思います。何個かやり方はありそうですが、今回は作業時のインスタンスに付与しているIAMからはdev*で始まるfunctionしか操作できないようにする事で、普段の作業環境からはprod環境にdeployが出来ないように対応しました。

apexに必要なRoleはこちら
http://apex.run/#minimum-iam-policy


### 環境別Functionをdeployするまでの全コマンド

apex initコマンドでプロジェクトを作成します。対話形式でプロジェクト名、説明文を入力するとapexプロジェクトに必要なファイルが生成され、同時AWS側にLambdaの管理に必要なRole、Policyが生成されます。

```
% AWS_PROFILE=personal apex init

  _ ____ _______ __
  / \ | _ \| ____\ \/ /
  / _ \ | |_) | _| \ /
  / ___ \| __/| |___ / \
  /_/ \_\_| |_____/_/\_\

  Enter the name of your project. It should be machine-friendly, as this
  is used to prefix your functions in Lambda.

  Project name: lambda_sample

  Enter an optional description of your project.

  Project description: sample for lambda

  [+] creating IAM lambda_sample_lambda_function role
  [+] creating IAM lambda_sample_lambda_logs policy
  [+] attaching policy to lambda_function role.
  [+] creating ./project.json
  [+] creating ./functions

  Setup complete, deploy those functions!

  $ apex deploy
```

生成されたRole, Policyは以下の通り入力したfunction名に紐づく名前で生成されます。
* Roles ... lambda_sample_lambda_function
* Policy ... lambda_sample_lambda_logs


さっそくdeployしてみましょう。

```
AWS_REGTION=ap-northeast-1 AWS_PROFILE=personal apex deploy
   • creating function         env= function=hello
   • created alias current     env= function=hello version=1
   • function created          env= function=hello name=lambda_sample_hello version=1
```

ちゃんとfunctionがdeployできました
![image.png](https://qiita-image-store.s3.amazonaws.com/0/18486/2f327134-920c-828c-2925-4a716af8f1e2.png)

次に環境別にdeployできるようにしてみましょう、以下のようにproject.jsonとfunction.jsonを環境別に分離します。

```
% tree [git][* master]:~/dev/lambda
.
├── functions
│   └── hello
│   ├── function.dev.json
│   ├── function.json
│   └── index.js
├── project.dev.json
└── project.json
```
```
% cat project.json
{
  "name": "lambda_sample",
  "description": "sample for lambda",
  "memory": 128,
  "timeout": 5,
  "role": "arn:aws:iam::hogehuga:role:role/lambda_sample_lambda_function",
  "environment": {}
}% % cat project.dev.json
```
```
% cat project.dev.json
{
  "name": "dev_lambda_sample",
  "description": "sample for lambda",
  "memory": 128,
  "timeout": 5,
  "role": "arn:aws:iam::hogehuga:role/lambda_sample_lambda_function",
  "environment": {}
}
```
* 環境別にnameを指定することで、deployするfunctionを別のものとすることができます。

```
% cat functions/hello/function.json
{
  "environment": { "target": "prod" }
}
```
```
% cat functions/hello/function.dev.json

{
  "environment": { "target": "dev" }
}
```

こちらもdeployしてみましょう。

```
% AWS_REGTION=ap-northeast-1 AWS_PROFILE=personal apex deploy                                                                                                                   [git][* master]:~/dev/lambda
   • config unchanged          env= function=hello
   • code unchanged            env= function=hello
   • updated alias current     env= function=hello version=6

% AWS_REGTION=ap-northeast-1 AWS_PROFILE=personal apex deploy --env dev                                                                                                         [git][* master]:~/dev/lambda
   • creating function         env=dev function=hello
   • created alias current     env=dev function=hello version=1
   • function created          env=dev function=hello name=dev_lambda_sample_hello version=1
```

ちゃんと環境別に別functionとしてdeployする事ができました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/18486/fe744627-4e3e-c947-e7eb-1a8e98dc5f91.png)