[AWS]LambdaからLambdaを呼ぶIAMロール設定とnode.js実装メモ

# やりたいこと

* AWSにてLambda関数を2つ作成する。
* 1つ目のLambda関数が呼ばれたら、その関数内から2つ目のLambda関数を呼び出す。
* 1つ目のLambda関数は、2つ目のLambda関数から戻り値が返ってくるまでは終了しない。

# Lambda関数用IAMロールとポリシー

下記のIAMロールを作成する。

AWSマネジメントコンソール、IAM設定画面にて、「ロールの作成」を押す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/3ff817c2-fc00-523f-078b-95bc8a4ca72f.png)

「AWSサービス」ユースケース「Lambda」を選択し「次のステップ：アクセス権限」を押す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/f5f40158-a636-a9f3-96e6-9cd2650218e6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/1483d0f0-ab56-2f00-0db2-5700bd36e38c.png)

「ポリシーの作成」を押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/85016823-6100-f57a-073c-980148ff9754.png)

「JSON」タブを選択し、下記を実装する。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:*",
                "logs:*",
                "lambda:*"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "iam:PassedToService": "lambda.amazonaws.com"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:DescribeLogStreams",
                "logs:GetLogEvents",
                "logs:FilterLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:log-group:/aws/lambda/*"
        }
    ]
}
```

「次のステップ：タグ」を押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/90385e0d-5be3-a780-564d-e7350d91b068.png)

タグの設定は任意なので、不要ならそのまま「次のステップ：確認」を押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/58da4588-891a-05c3-105c-0a8f3018aff0.png)

任意のポリシー名を記入する。
ここでは下記を入力する。

ポリシー名
`LambdaPolicyYamatoSample210612a`

説明は空欄のままで問題ない。
「ポリシーの作成」ボタンを押す。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/3b7c3b4b-0938-0ff3-836c-40b6c337aae7.png)

下記の表示にて、ポリシーが作成されたことを確認する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/159ef58d-e81f-0ac4-2a86-424fcf4e8951.png)

このポリシー「LambdaPolicyYamatoSample210612a」を、
ロールの作成画面にて割り当てる。
ポリシーのフィルタで「LambdaPolicyYamatoSample210612a」を検索して選択する。
チェックを入れて「次のステップ：タグ」を押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/f175f733-e492-61fb-8d89-fdca0043dad8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/f72980d3-8cce-753b-730f-1a9353c8f5bc.png)

タグの追加は任意なので、不要なら「次のステップ：確認」を押す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/f857ccc2-9473-ad53-bd73-07af3f0b3c30.png)

任意のロール名で作成する。今回は下記で作成する。ロール名を入力し「ロールの作成」ボタンを押す。
`LambdaRoleYamatoSample210612a`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/015b8d41-1836-0bd7-fb31-34eac633c150.png)

下記の表示にて、ロールが作成されたことを確認する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/868f43dd-0925-5b47-fda6-8a641cb8f73d.png)

以上で、ポリシー「LambdaPolicyYamatoSample210612a」が割り当てられたロール「LambdaRoleYamatoSample210612a」が作成された。
このロールをLambda関数に適用する。

# Lambda関数の作成

## 1つ目の関数を作成

AWSマネジメントコンソールのLambda画面にて「関数の作成」を押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/6942907f-ad06-17c0-83c8-faefd93b19ce.png)

下記の通り作成する。

* 「一から作成」を選ぶ。
* 関数名は任意。今回は `yamatoSampleLambdaFunction210612a` とする。
* ランタイムは Node.js 14.x とする。
* アクセス権限は「既存のロールを使用する」を選び、先ほど作成したロール「LambdaRoleYamatoSample210612a」を適用する。

「関数の作成」ボタンを押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/66073468-ebd2-fed4-4bcc-b2c594090792.png)

下記の表示で、Lambda関数が作成されたことを確認する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/c9ca252a-69e4-171d-5054-6ba807f6005c.png)

## 2つ目の関数を作成

同様の手順で、2つ目の関数を作成する。
関数名は任意だが、今回は `yamatoSampleLambdaFunction210612b` とする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/8992d2c1-0580-1376-e49d-ed8f91da5b8d.png)


以上で、2つのLambda関数

* yamatoSampleLambdaFunction210612a
* yamatoSampleLambdaFunction210612b

が作成された。

# 各関数にログ出力処理を仕込む

それぞれの関数が起動した際、動いたことが分かるように、ログ出力をコードに埋め込む。
各関数の index.js に下記を実装する。
コードを修正したら「Deploy」ボタンを押す。

### 関数A `yamatoSampleLambdaFunction210612a`

```js
console.log("関数A「yamatoSampleLambdaFunction210612a」が呼ばれました。");
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/cbe336b3-1930-0236-5667-5dc96e350a7b.png)



### 関数B `yamatoSampleLambdaFunction210612b`
```js
console.log("関数B「yamatoSampleLambdaFunction210612b」が呼ばれました。");
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/a2a2a968-b02b-200f-3cec-1ce8aa03edff.png)


# 各関数をテスト実行してログが出力されることを確認する

関数を試しに動かしてみる。「テスト」を押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/0494c662-c53c-f4bc-aa48-60f961ca3af7.png)


「新しいテストイベントの作成」で、イベントテンプレートは「hello-world」を選択する。
任意のイベント名を入力する。
ここでは `yamatoTestEventA` とする。
「作成」ボタンを押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/b2a3c227-9952-4d90-74e0-fb50e61ad7ab.png)

この状態で「テスト」ボタンを押すと、Lambda関数が実行される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/d8c754e7-499c-4081-1371-e50c6e3a243a.png)



ログが出力されているかどうか確認する。
「モニタリング」タブを選び、「CloudWatchのログを表示」を押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/14e82337-0c58-c6aa-77ad-5861e2a12595.png)


ログストリームのリンクをクリックする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/b4856c70-1f42-6771-6381-323c1ed547fc.png)

詳細を確認する。ログが出力されている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/0bb19a7e-7a27-02be-d401-e4619f87fe8e.png)

つまり、関数Aが起動すると、ログが出力される、ということが確認できた。
同様の手順で関数Bも動かして、関数Bのログも出力されることを確認する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/5158c905-5d59-8766-ba48-064fc4c0b982.png)

以上で、関数AとB、それぞれを動かして、ログが出力されることが確認できた。


# 関数AからBを呼び出す

関数AからBを呼び出すには、
関数Aの index.js に下記を実装する。

```js
const AWS = require('aws-sdk');
const lambda = new AWS.Lambda();

exports.handler = async (event) => {
    console.log("関数A「yamatoSampleLambdaFunction210612a」が呼ばれました。");
    let payload = {
        "message":"関数Bへ渡したい文字列があればここに記入する。",
    }
    // ペイロードをStringにする。
    payload = JSON.stringify(payload);
    let params = {
        FunctionName:"yamatoSampleLambdaFunction210612b",
        InvocationType:"RequestResponse",
        Payload:payload
    }
    let callLambda = '';
    try {
        // Lambda関数Bを呼び出す。
        callLambda = await lambda.invoke(params).promise();
    } catch (e) {
        console.log("[ERROR]関数呼び出し失敗!",e)
    }
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

```

### 解説
`let params` の `FunctionName` に、呼び出し先のLambda関数名を指定する。
`payload` で呼び出し先の関数へパラメータを渡すこともできる。


### 動作確認

「テスト」を押して関数Aを動かし、ログが出力されることを確認する。
関数Aが起動すれば、関数Aが関数Bを呼び出すので、結果として関数Bのログも出力される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/55771fc3-9b44-6ae7-fbab-de47925424a6.png)

関数Bのログを確認する。たしかに出力されている。つまり関数Aが関数Bを呼び出すことに成功している。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/6eadb807-c34e-e275-632e-3fbfe63b1135.png)

# 関数Aから関数Bへパラメータを渡して戻り値を返してもらう

関数Aから関数Bを呼び出す際、
引数を渡して、関数Bで受け取り、
何らかの処理をした結果を関数Aに戻す。

その場合、下記の実装にする。

### 関数A index.js

```js
const AWS = require('aws-sdk');
const lambda = new AWS.Lambda();

exports.handler = async (event) => {
    console.log("関数A「yamatoSampleLambdaFunction210612a」が呼ばれました。");
    let payload = {
        "id": 1234,
        "name": "Kenichiro-Yamato",
        "message": "こんにちは。私の名前は大和賢一郎です。関数Aから渡されました。",
    }
    // ペイロードをStringにする。
    payload = JSON.stringify(payload);
    let params = {
        FunctionName:"yamatoSampleLambdaFunction210612b",
        InvocationType:"RequestResponse",
        Payload:payload
    }
    let callLambda = '';
    try {
        // Lambda関数Bを呼び出す。
        callLambda = await lambda.invoke(params).promise();
        console.log("関数Bからの戻り値: " + JSON.stringify(callLambda));
    } catch (e) {
        console.log("[ERROR]関数呼び出し失敗!",e)
    }
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

```


### 関数B index.js


```js
exports.handler = async (event, body, context) => {
    console.log("関数B「yamatoSampleLambdaFunction210612b」が呼ばれました。関数Aから次のパラメータを渡されました。");
    console.log(JSON.stringify({event: event, body: body, context: context}, null, 2));
    const response = {
        statusCode: 200,
        message: '関数Aさんへ。私は関数Bです。パラメータ受け取りました。ありがとう。',
        event: event,
    };
    return response;
};
```


ログは下記の通り。パラメータの受け渡しと戻り値の返しが正常に実行されている。

### 関数Aのログ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/16924f22-c000-1174-d340-da76e83244d5.png)


### 関数Bのログ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75451/e53d224d-57df-78fb-e6f6-60467524d58f.png)