

# Lambda を使用するカスタムリソース
<a name="template-custom-resources-lambda"></a>

Lambda 関数とカスタム リソースを関連付けた場合、この関数はカスタムリソースが作成、更新、または削除されるたびに呼び出されます。CloudFormation は Lambda API を呼び出して関数を呼び出し、すべてのリクエストデータ (リクエストタイプやリソースプロパティなど) を関数に渡します。Lambda 関数と CloudFormation との組み合わせによる機能性およびカスタマイズ性を活用して、スタック作成中の AMI ID の動的な検索やユーティリティ関数 (文字列反転関数など) の使用などの広範なシナリオを実現できます。

カスタムリソースとその仕組みの概要については、「[カスタムリソースのしくみ](template-custom-resources.md#how-custom-resources-work)」を参照してください。

**Topics**
+ [チュートリアル: Lambda-backed カスタムリソースを使用した遅延メカニズムの作成](walkthrough-lambda-backed-custom-resources.md)
+ [`cfn-response` モジュール](cfn-lambda-function-code-cfnresponsemodule.md)

# チュートリアル: Lambda-backed カスタムリソースを使用した遅延メカニズムの作成
<a name="walkthrough-lambda-backed-custom-resources"></a>

このチュートリアルでは、サンプルの CloudFormation テンプレートを使用して Lambda-backed カスタムリソースを設定および起動する方法を示します。このテンプレートは、指定した時間だけスタックのデプロイを一時停止する遅延メカニズムを作成します。これは、依存リソースが作成される前にリソースが安定するのを待つ場合など、リソースのプロビジョニング中に意図的な遅延を発生させる必要がある場合に便利です。

**注記**  
以前は Lambda-backed カスタムリソースによる AMI ID の取得が推奨されていましたが、現在は AWS Systems Manager パラメータの使用を推奨しています。この方法により、テンプレートの再利用性が高まり簡単にメンテナンスできます。詳細については、「[Systems Manager Parameter Store からプレーンテキスト値を取得する](dynamic-references-ssm.md)」を参照してください。

**Topics**
+ [概要:](#walkthrough-lambda-backed-custom-resources-overview)
+ [サンプルテンプレート](#walkthrough-lambda-backed-custom-resources-sample-template)
+ [サンプルテンプレートのチュートリアル](#walkthrough-lambda-backed-custom-resources-sample-template-walkthrough)
+ [前提条件](#walkthrough-lambda-backed-custom-resources-prerequisites)
+ [スタックを起動する](#walkthrough-lambda-backed-custom-resources-createfunction-createstack)
+ [リソースのクリーンアップ](#walkthrough-lambda-backed-custom-resources-createfunction-cleanup)
+ [関連情報](#w2aac11c45b9c24b9c23)

## 概要:
<a name="walkthrough-lambda-backed-custom-resources-overview"></a>

このチュートリアルで使用するスタックテンプレートのサンプルは、Lambda-backed カスタムリソースを作成します。このカスタムリソースは、スタックの作成中に設定可能な遅延 (デフォルトでは 60 秒) を発生させます。遅延は、カスタムリソースのプロパティが変更された場合にのみ、スタックの更新中に発生します。

このテンプレートでは以下のリソースが提供されます。
+ カスタムリソース
+ Lambda 関数
+ Lambda が CloudWatch にログを書き込めるようにする IAM ロール。

また、次の 2 つの出力も定義します。
+ 関数が実際に待機した時間。
+ Lambda 関数の実行ごとに生成される一意の識別子。



**注記**  
CloudFormation は無料のサービスですが、Lambda は関数のリクエスト数とコードの実行時間に基づいて課金します。Lambda の料金の詳細については、「[AWS Lambda の料金](https://aws.amazon.com/lambda/pricing/)」を参照してください。

## サンプルテンプレート
<a name="walkthrough-lambda-backed-custom-resources-sample-template"></a>

遅延メカニズムを含む Lambda-backed カスタムリソースのサンプルテンプレートは、以下のとおりです。

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-json"></a>

```
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "LambdaExecutionRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [{
            "Effect": "Allow",
            "Principal": { "Service": ["lambda.amazonaws.com"] },
            "Action": ["sts:AssumeRole"]
          }]
        },
        "Path": "/",
        "Policies": [{
          "PolicyName": "AllowLogs",
          "PolicyDocument": {
            "Statement": [{
              "Effect": "Allow",
              "Action": ["logs:*"],
              "Resource": "*"
            }]
          }
        }]
      }
    },
    "CFNWaiter": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Handler": "index.handler",
        "Runtime": "python3.9",
        "Timeout": 900,
        "Role": { "Fn::GetAtt": ["LambdaExecutionRole", "Arn"] },
        "Code": {
          "ZipFile": { "Fn::Join": ["\n", [
            "from time import sleep",
            "import json",
            "import cfnresponse",
            "import uuid",
            "",
            "def handler(event, context):",
            "  wait_seconds = 0",
            "  id = str(uuid.uuid1())",
            "  if event[\"RequestType\"] in [\"Create\", \"Update\"]:",
            "    wait_seconds = int(event[\"ResourceProperties\"].get(\"ServiceTimeout\", 0))",
            "    sleep(wait_seconds)",
            "  response = {",
            "    \"TimeWaited\": wait_seconds,",
            "    \"Id\": id ",
            "  }",
            "  cfnresponse.send(event, context, cfnresponse.SUCCESS, response, \"Waiter-\"+id)"
          ]]}
        }
      }
    },
    "CFNWaiterCustomResource": {
      "Type": "AWS::CloudFormation::CustomResource",
      "Properties": {
        "ServiceToken": { "Fn::GetAtt": ["CFNWaiter", "Arn"] },
        "ServiceTimeout": 60
      }
    }
  },
  "Outputs": {
    "TimeWaited": {
      "Value": { "Fn::GetAtt": ["CFNWaiterCustomResource", "TimeWaited"] },
      "Export": { "Name": "TimeWaited" }
    },
    "WaiterId": {
      "Value": { "Fn::GetAtt": ["CFNWaiterCustomResource", "Id"] },
      "Export": { "Name": "WaiterId" }
    }
  }
}
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-yaml"></a>

```
AWSTemplateFormatVersion: "2010-09-09"
Resources:
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: "AllowLogs"
          PolicyDocument:
            Statement:
              - Effect: "Allow"
                Action:
                  - "logs:*"
                Resource: "*"
  CFNWaiter:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Runtime: python3.9 
      Timeout: 900
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile:
          !Sub |
          from time import sleep
          import json
          import cfnresponse
          import uuid
​
          def handler(event, context):
            wait_seconds = 0
            id = str(uuid.uuid1())
            if event["RequestType"] in ["Create", "Update"]:
              wait_seconds = int(event["ResourceProperties"].get("ServiceTimeout", 0))
              sleep(wait_seconds)
            response = {
              "TimeWaited": wait_seconds,
              "Id": id 
            }
            cfnresponse.send(event, context, cfnresponse.SUCCESS, response, "Waiter-"+id)
  CFNWaiterCustomResource:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt CFNWaiter.Arn
      ServiceTimeout: 60
Outputs:
  TimeWaited:
    Value: !GetAtt CFNWaiterCustomResource.TimeWaited
    Export:
      Name: TimeWaited
  WaiterId:
    Value: !GetAtt CFNWaiterCustomResource.Id
    Export:
      Name: WaiterId
```

## サンプルテンプレートのチュートリアル
<a name="walkthrough-lambda-backed-custom-resources-sample-template-walkthrough"></a>

次のスニペットでは、Lambda 関数がどのようにカスタムリソースに関連付けられているのか、およびその出力を理解できるように、サンプルテンプレートの要素を抜粋して解説します。

[AWS::Lambda::Function](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-lambda-function.html) リソース `CFNWaiter`  
`AWS::Lambda::Function` リソースは、関数のソースコード、ハンドラー名、ランタイム環境、実行ロールの Amazon リソースネーム (ARN) を指定します。  
`Handler` プロパティは Python ソースコードを使用するため、`index.handler` に設定されます。インライン関数のソースコードを使用する場合の受け入れ済みのハンドラー識別子の詳細については、「[AWS::Lambda::Function Code](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-zipfile)」を参照してください。  
ソースファイルは Python コードであるため、`Runtime` は `python3.9` として指定されます。  
`Timeout` は 900 秒に設定されます。  
`Role` プロパティは `Fn::GetAtt` 関数を使用して、テンプレートの `AWS::IAM::Role` リソースで宣言された `LambdaExecutionRole` 実行ロールの ARN を取得します。  
`Code` プロパティは、関数コードを、Python 関数を使用してインラインで定義します。サンプルテンプレートの Python 関数は、以下を実行します。  
+ UUID を使用して一意の ID を作成する
+ リクエストが作成または更新されたかどうかをチェックする
+ `Create` または `Update` のリクエスト中に、`ServiceTimeout` に指定された時間だけスリープ状態にする
+ 待機時間と一意の ID を返す

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-lambda-resource-json"></a>

```
...
    "CFNWaiter": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Handler": "index.handler",
        "Runtime": "python3.9",
        "Timeout": 900,
        "Role": { "Fn::GetAtt": ["LambdaExecutionRole", "Arn"] },
        "Code": {
          "ZipFile": { "Fn::Join": ["\n", [
            "from time import sleep",
            "import json",
            "import cfnresponse",
            "import uuid",
            "",
            "def handler(event, context):",
            "  wait_seconds = 0",
            "  id = str(uuid.uuid1())",
            "  if event[\"RequestType\"] in [\"Create\", \"Update\"]:",
            "    wait_seconds = int(event[\"ResourceProperties\"].get(\"ServiceTimeout\", 0))",
            "    sleep(wait_seconds)",
            "  response = {",
            "    \"TimeWaited\": wait_seconds,",
            "    \"Id\": id ",
            "  }",
            "  cfnresponse.send(event, context, cfnresponse.SUCCESS, response, \"Waiter-\"+id)"
          ]]}
        }
      }
    },
...
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-lambda-resource-yaml"></a>

```
...
  CFNWaiter:
    Type: AWS::Lambda::Function
    Properties:
      Handler: index.handler
      Runtime: python3.9 
      Timeout: 900
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile:
          !Sub |
          from time import sleep
          import json
          import cfnresponse
          import uuid
​
          def handler(event, context):
            wait_seconds = 0
            id = str(uuid.uuid1())
            if event["RequestType"] in ["Create", "Update"]:
              wait_seconds = int(event["ResourceProperties"].get("ServiceTimeout", 0))
              sleep(wait_seconds)
            response = {
              "TimeWaited": wait_seconds,
              "Id": id 
            }
            cfnresponse.send(event, context, cfnresponse.SUCCESS, response, "Waiter-"+id)
...
```

[AWS::IAM::Role](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-iam-role.html) リソース `LambdaExecutionRole`  
`AWS::IAM:Role` リソースは Lambda 関数の実行ロールを作成します。これには、Lambda にその使用を許可するロール割り当てポリシーが含まれます。また、CloudWatch Logs へのアクセスを許可するポリシーも含まれます。

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-iam-role-json"></a>

```
...
    "LambdaExecutionRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [{
            "Effect": "Allow",
            "Principal": { "Service": ["lambda.amazonaws.com"] },
            "Action": ["sts:AssumeRole"]
          }]
        },
        "Path": "/",
        "Policies": [{
          "PolicyName": "AllowLogs",
          "PolicyDocument": {
            "Statement": [{
              "Effect": "Allow",
              "Action": ["logs:*"],
              "Resource": "*"
            }]
          }
        }]
      }
    },
...
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-iam-role-yaml"></a>

```
...
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: "AllowLogs"
          PolicyDocument:
            Statement:
              - Effect: "Allow"
                Action:
                  - "logs:*"
                Resource: "*"
...
```

[AWS::CloudFormation::CustomResource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-customresource.html) リソース `CFNWaiterCustomResource`  
カスタムリソースは、`!GetAtt CFNWaiter.Arn` を使用して、ARN を持つ Lambda 関数にリンクします。`ServiceTimeout` で設定されているように、作成および更新オペレーションに 60 秒の待機時間を実装します。リソースは、プロパティが変更された場合にのみ、更新オペレーションのために呼び出されます。

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-custom-resource-json"></a>

```
...
    "CFNWaiterCustomResource": {
      "Type": "AWS::CloudFormation::CustomResource",
      "Properties": {
        "ServiceToken": { "Fn::GetAtt": ["CFNWaiter", "Arn"] },
        "ServiceTimeout": 60
      }
    }
  },
...
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-custom-resource-yaml"></a>

```
...
  CFNWaiterCustomResource:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken: !GetAtt CFNWaiter.Arn
      ServiceTimeout: 60
...
```

`Outputs`  
このテンプレートの `Outputs` は `TimeWaited`と `WaiterId` です。`TimeWaited` 値は、`Fn::GetAtt` 関数を使用して、ウェーターリソースが実際に待機した時間を指定します。`WaiterId` は `Fn::GetAtt` 関数を使用して、生成され実行に関連付けられた一意の ID を提示します。

### JSON
<a name="walkthrough-lambda-backed-custom-resources-sample-template-output-json"></a>

```
...
  "Outputs": {
    "TimeWaited": {
      "Value": { "Fn::GetAtt": ["CFNWaiterCustomResource", "TimeWaited"] },
      "Export": { "Name": "TimeWaited" }
    },
    "WaiterId": {
      "Value": { "Fn::GetAtt": ["CFNWaiterCustomResource", "Id"] },
      "Export": { "Name": "WaiterId" }
    }
  }
}
...
```

### YAML
<a name="walkthrough-lambda-backed-custom-resources-sample-template-output-yaml"></a>

```
...
Outputs:
  TimeWaited:
    Value: !GetAtt CFNWaiterCustomResource.TimeWaited
    Export:
      Name: TimeWaited
  WaiterId:
    Value: !GetAtt CFNWaiterCustomResource.Id
    Export:
      Name: WaiterId
...
```

## 前提条件
<a name="walkthrough-lambda-backed-custom-resources-prerequisites"></a>

Lambda や CloudFormation など、対応するサービスをすべて使用するには、IAM アクセス許可が必要です。

## スタックを起動する
<a name="walkthrough-lambda-backed-custom-resources-createfunction-createstack"></a>

**スタックを作成するには**

1. [サンプルテンプレート](#walkthrough-lambda-backed-custom-resources-sample-template) セクションから希望するテンプレート (YAML または JSON) を見つけ、`samplelambdabackedcustomresource.template` という名前を付けてお使いのマシンに保存します。

1. クラウドフォーメーション コンソール の[https://console.aws.amazon.com/cloudformation/](https://console.aws.amazon.com/cloudformation/) 開きます：

1. **[スタック]** ページでは、右上の **[スタックの作成]** を選択してから、**[新しいリソースを使用 (標準)]** を選択します。

1. **[前提条件 - テンプレートの準備]** で、**[既存のテンプレートを選択]** を選択します。

1. **[テンプレートの指定]** で **[テンプレートファイルのアップロード]** を選択し、**[ファイルの選択]** を選択します。

1. 前に保存した `samplelambdabackedcustomresource.template` テンプレートファイルを選択します。

1. [**次へ**] を選択します。

1. **[スタック名]** に **SampleCustomResourceStack** と入力し、**[次へ]** を選択します。

1. このチュートリアルでは、タグの追加も詳細設定の指定も不要です。[**次へ**] を選択します。

1. スタックの名前が正しいことを確認し、**[作成]** を選択します。

CloudFormation によってスタックが作成されるまでに数分かかることもあります。進捗状況を監視するには、スタックイベントを確認します。詳細については、「[CloudFormation コンソールからスタック情報を表示する](cfn-console-view-stack-data-resources.md)」を参照してください。

スタックを正常に作成できた場合は、スタック内のすべてのリソース (Lambda 関数、カスタムリソースなど) も作成されています。Lambda 関数とカスタムリソースを正常に使用できました。

Lambda 関数からエラーが返される場合は、CloudWatch Logs [コンソール](https://console.aws.amazon.com/cloudwatch/home#logs:)で関数のログを確認します。ログストリームの名前は、カスタムリソースの物理 ID です。これは、スタックのリソースを表示して確認できます。詳細については、「[Amazon CloudWatch ユーザーガイド](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Working-with-log-groups-and-streams.html#ViewingLogData)」の「*Logs に送信された CloudWatch ログデータを表示する*」を参照してください。

## リソースのクリーンアップ
<a name="walkthrough-lambda-backed-custom-resources-createfunction-cleanup"></a>

不要なリソースに対して課金されないよう、作成したすべてのスタックリソースをクリーンアップするために、スタックを削除します。

**スタックを削除するには**

1. CloudFormation コンソールから、**SampleCustomResourceStack** スタックを選択します。

1. [**アクション**] を選択し、[**スタックの削除**] を選択します。

1. 確認メッセージで、[**はい、削除する**] を選択します。

作成したすべてのリソースが削除されます。

Lambda-backed カスタムリソースの作成方法と使用方法について理解できたので、今度はこのチュートリアルで紹介したサンプルテンプレートとコードを使用して他のスタックや関数を作成し、実際に使ってみましょう。

## 関連情報
<a name="w2aac11c45b9c24b9c23"></a>
+ [CloudFormation カスタムリソースリファレンス](crpg-ref.md)
+ [AWS::CloudFormation::CustomResource](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-customresource.html)

# `cfn-response` モジュール
<a name="cfn-lambda-function-code-cfnresponsemodule"></a>

CloudFormation テンプレートでは、Lambda 関数をカスタムリソースのターゲットとして指定できます。`ZipFile` プロパティを使用して[関数の](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-lambda-function.html)ソースコードを指定すると、`cfn-response` モジュールをロードして、Lambda 関数からの応答をカスタムリソースに送信できます。`cfn-response` モジュールは、Lambda 関数を呼び出したカスタムリソースへの応答の送信を簡素化するライブラリです。このモジュールには `send` メソッドが含まれています。このメソッドは、Amazon S3 の署名付き URL (`ResponseURL`) を経由して、カスタムリソースに[応答オブジェクト](crpg-ref.md#crpg-ref-responses)を送信します。

`cfn-response` モジュールは、`ZipFile` プロパティを使用してソースコードを作成した場合にのみ使用できます。Amazon S3 バケットに保存されたソースコードには使用できません。バケットのコードでは、独自の関数を作成してレスポンスを送信する必要があります。

**注記**  
`send` メソッドを実行した後、Lambda 関数は終了するため、メソッドの後の記述は無視されます。

## `cfn-response` モジュールの読み込み
<a name="cfn-lambda-function-code-cfnresponsemodule-loading"></a>

Node.js 関数の場合は、`require()` 関数を使用して `cfn-response` モジュールをロードします。たとえば、次のコードでは、`cfn-response` という名前で `response` オブジェクトを作成しています。

```
var response = require('cfn-response');
```

Python の場合は、次の例に示すように、`import` ステートメントを使用して `cfnresponse` モジュールをロードします。

**注記**  
この完全インポートステートメントを使用します。インポートステートメントの他の形式では、CloudFormation では応答モジュールが含まれません。

```
import cfnresponse
```

## `send` メソッドのパラメータ
<a name="cfn-lambda-function-code-cfnresponsemodule-send-parameters"></a>

`send` メソッドで次のパラメータを使用できます。

`event`  
[カスタムリソースのリクエスト](crpg-ref.md#crpg-ref-requesttypes)に含まれるフィールド。

`context`  
関数および任意のコールバックが実行完了したとき、または Lambda 実行環境内からの情報にアクセスするときに指定できる Lambda 関数固有のオブジェクト。詳細については、『AWS Lambda デベロッパーガイド』の「[Node.js による Lambda 関数のビルド](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html)」を参照してください。

`responseStatus`  
関数が正常に完了したかどうか。このステータスを指定するには、`cfnresponse` モジュール定数を使用します。成功に実行した場合は `SUCCESS`、失敗した場合は `FAILED` を指定します。

`responseData`  
カスタムリソースの[応答オブジェクト](crpg-ref.md#crpg-ref-responses)の `Data` フィールド。データの内容は、名前と値のペアのリストです。

`physicalResourceId`  
オプション。関数を呼び出したカスタムリソースの一意の識別子。モジュールのデフォルトでは、Lambda 関数に関連付けられている Amazon CloudWatch Logs ログストリームの名前が使用されます。  
`PhysicalResourceId` に返された値は、カスタムリソース更新オペレーションを変更できます。返される値が同じであれば、通常の更新と見なされます。返された値が異なる場合には、CloudFormation は新しい方が更新用のものであると認識し、古いリソースに削除リクエストを送信します。詳細については、「[https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-cloudformation-customresource.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-cloudformation-customresource.html)」を参照してください。

`noEcho`  
オプション。`Fn::GetAtt` 関数を使用してカスタムリソースの出力を取得したときに、それをマスクするかどうかを示します。`true` に設定すると、返される値はすべてアスタリスク (\$1\$1\$1\$1\$1) でマスクされます。ただし、以下に指定した場所に保存されている情報は除きます。デフォルトでは、この値は `false` です。  
`NoEcho` 属性を使用しても、以下に保存されている情報はマスクされません。  
+ `Metadata` テンプレートセクション。CloudFormation は、`Metadata` セクションに含める情報の変換、変更、または編集を行いません。詳細については、「[メタデータ](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html)」を参照してください。
+ `Outputs` テンプレートセクション。詳細については、「[出力](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html)」を参照してください。
+ リソース定義の `Metadata` 属性。詳細については、「[`Metadata` 属性](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-attribute-metadata.html)」を参照してください。
パスワードやシークレットなどの機密情報を含めるには、これらのメカニズムを使用しないことを強くお勧めします。
機密情報をマスキングするために `NoEcho` を使用する方法の詳細については、[テンプレートに認証情報を埋め込まない](security-best-practices.md#creds) に関するベストプラクティスを参照してください。

## 例
<a name="cfn-lambda-function-code-cfnresponsemodule-examples"></a>

### Node.js
<a name="cfn-lambda-function-code-zipfile-examplenodejs"></a>

次の Node.js の例では、インラインの Lambda 関数で入力値を受け取り、その値に 5 を乗算しています。インライン関数は、パッケージを作成して Amazon S3 バケットにアップロードするのではなく、ソースコードをテンプレート内で直接指定できるため、小さな関数の場合は特に便利です。この関数では、`cfn-response` の `send` メソッドを使用して、呼び出たカスタムリソースに結果を返しています。

#### JSON
<a name="cfn-lambda-function-code-zipfile-examplenodejs.json"></a>

```
"ZipFile": { "Fn::Join": ["", [
  "var response = require('cfn-response');",
  "exports.handler = function(event, context) {",
  "  var input = parseInt(event.ResourceProperties.Input);",
  "  var responseData = {Value: input * 5};",
  "  response.send(event, context, response.SUCCESS, responseData);",
  "};"
]]}
```

#### YAML
<a name="cfn-lambda-function-code-zipfile-examplenodejs-yaml"></a>

```
ZipFile: >
  var response = require('cfn-response');
  exports.handler = function(event, context) {
    var input = parseInt(event.ResourceProperties.Input);
    var responseData = {Value: input * 5};
    response.send(event, context, response.SUCCESS, responseData);
  };
```

### Python
<a name="cfn-lambda-function-code-zipfile-examplepython"></a>

次の Python の例では、インラインの Lambda 関数で整数値を受け取り、その値に 5 を乗算しています。

#### JSON
<a name="cfn-lambda-function-code-zipfile-examplepython.json"></a>

```
"ZipFile" : { "Fn::Join" : ["\n", [
  "import json",
  "import cfnresponse",
  "def handler(event, context):",
  "   responseValue = int(event['ResourceProperties']['Input']) * 5",
  "   responseData = {}",
  "   responseData['Data'] = responseValue",
  "   cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, \"CustomResourcePhysicalID\")"
]]}
```

#### YAML
<a name="cfn-lambda-function-code-zipfile-examplepython.yaml"></a>

```
ZipFile: |
  import json
  import cfnresponse
  def handler(event, context):
    responseValue = int(event['ResourceProperties']['Input']) * 5
    responseData = {}
    responseData['Data'] = responseValue
    cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID")
```

## モジュールのソースコード
<a name="cfn-lambda-function-code-cfnresponsemodule-source"></a>

**Topics**
+ [非同期 Node.js ソースコード](#cfn-lambda-function-code-cfnresponsemodule-source-nodejs-async)
+ [Node.js ソースコード](#cfn-lambda-function-code-cfnresponsemodule-source-nodejs)
+ [Python ソースコード](#cfn-lambda-function-code-cfnresponsemodule-source-python)

### 非同期 Node.js ソースコード
<a name="cfn-lambda-function-code-cfnresponsemodule-source-nodejs-async"></a>

ハンドラーが非同期である場合の Node.js 関数のレスポンスモジュールのソースコードは次のとおりです。これを確認してモジュールの動作を理解し、独自の応答関数の実装に役立ててください。

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0

exports.SUCCESS = "SUCCESS";
exports.FAILED = "FAILED";

exports.send = function(event, context, responseStatus, responseData, physicalResourceId, noEcho) {

    return new Promise((resolve, reject) => {
        var responseBody = JSON.stringify({
            Status: responseStatus,
            Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
            PhysicalResourceId: physicalResourceId || context.logStreamName,
            StackId: event.StackId,
            RequestId: event.RequestId,
            LogicalResourceId: event.LogicalResourceId,
            NoEcho: noEcho || false,
            Data: responseData
        });

        console.log("Response body:\n", responseBody);

        var https = require("https");
        var url = require("url");

        var parsedUrl = url.parse(event.ResponseURL);
        var options = {
            hostname: parsedUrl.hostname,
            port: 443,
            path: parsedUrl.path,
            method: "PUT",
            headers: {
                "content-type": "",
                "content-length": responseBody.length
            }
        };

        var request = https.request(options, function(response) {
            console.log("Status code: " + parseInt(response.statusCode));
            resolve(context.done());
        });

        request.on("error", function(error) {
            console.log("send(..) failed executing https.request(..): " + maskCredentialsAndSignature(error));
            reject(context.done(error));
        });

        request.write(responseBody);
        request.end();
    })
}
 
function maskCredentialsAndSignature(message) {
    return message.replace(/X-Amz-Credential=[^&\s]+/i, 'X-Amz-Credential=*****')
        .replace(/X-Amz-Signature=[^&\s]+/i, 'X-Amz-Signature=*****');
}
```

### Node.js ソースコード
<a name="cfn-lambda-function-code-cfnresponsemodule-source-nodejs"></a>

ハンドラーが非同期でない場合の Node.js 関数のレスポンスモジュールのソースコードは次のとおりです。これを確認してモジュールの動作を理解し、独自の応答関数の実装に役立ててください。

```
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0
 
exports.SUCCESS = "SUCCESS";
exports.FAILED = "FAILED";

exports.send = function(event, context, responseStatus, responseData, physicalResourceId, noEcho) {

    var responseBody = JSON.stringify({
        Status: responseStatus,
        Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
        PhysicalResourceId: physicalResourceId || context.logStreamName,
        StackId: event.StackId,
        RequestId: event.RequestId,
        LogicalResourceId: event.LogicalResourceId,
        NoEcho: noEcho || false,
        Data: responseData
    });

    console.log("Response body:\n", responseBody);

    var https = require("https");
    var url = require("url");

    var parsedUrl = url.parse(event.ResponseURL);
    var options = {
        hostname: parsedUrl.hostname,
        port: 443,
        path: parsedUrl.path,
        method: "PUT",
        headers: {
            "content-type": "",
            "content-length": responseBody.length
        }
    };

    var request = https.request(options, function(response) {
        console.log("Status code: " + parseInt(response.statusCode));
        context.done();
    });

    request.on("error", function(error) {
        console.log("send(..) failed executing https.request(..): " + maskCredentialsAndSignature(error));
        context.done();
    });

    request.write(responseBody);
    request.end();
}
```

### Python ソースコード
<a name="cfn-lambda-function-code-cfnresponsemodule-source-python"></a>

Python 関数のレスポンスモジュールのソースコードは次のとおりです。

```
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
 
from __future__ import print_function
import urllib3
import json
import re

SUCCESS = "SUCCESS"
FAILED = "FAILED"

http = urllib3.PoolManager()


def send(event, context, responseStatus, responseData, physicalResourceId=None, noEcho=False, reason=None):
    responseUrl = event['ResponseURL']

    responseBody = {
        'Status' : responseStatus,
        'Reason' : reason or "See the details in CloudWatch Log Stream: {}".format(context.log_stream_name),
        'PhysicalResourceId' : physicalResourceId or context.log_stream_name,
        'StackId' : event['StackId'],
        'RequestId' : event['RequestId'],
        'LogicalResourceId' : event['LogicalResourceId'],
        'NoEcho' : noEcho,
        'Data' : responseData
    }

    json_responseBody = json.dumps(responseBody)

    print("Response body:")
    print(json_responseBody)

    headers = {
        'content-type' : '',
        'content-length' : str(len(json_responseBody))
    }

    try:
        response = http.request('PUT', responseUrl, headers=headers, body=json_responseBody)
        print("Status code:", response.status)


    except Exception as e:

        print("send(..) failed executing http.request(..):", mask_credentials_and_signature(e))
 
 
def mask_credentials_and_signature(message):
    message = re.sub(r'X-Amz-Credential=[^&\s]+', 'X-Amz-Credential=*****', message, flags=re.IGNORECASE)
    return re.sub(r'X-Amz-Signature=[^&\s]+', 'X-Amz-Signature=*****', message, flags=re.IGNORECASE)
```