AWS CloudFormation の Lambda イベント通知で「Unable to validate the following destination configurations (次の送信先設定を検証できません)」というエラーを回避するにはどうすればよいですか?

所要時間3分
0

AWS CloudFormation テンプレートをデプロイすると、スタックが失敗します。その後、「Unable to validate the following destination configurations (次の送信先設定を検証できません)」というエラーが表示されます。

簡単な説明

次のリソースで AWS CloudFormation テンプレートをデプロイすると、このエラーが表示されます。

Amazon S3 は、バケットの作成時に通知設定を検証する必要があります。検証では、Lambda 関数にイベントをプッシュするアクセス許可がバケットにあるかどうかを確認します。アクセス許可リソース (このチェックに合格するために存在する必要がある) には、バケット名が必要です。これは、アクセス許可リソースはバケットに依存し、バケットはアクセス許可リソースに依存していることを示しています。

注意: 次のコード例のような DependsOn リソース属性を実装してこの問題を解決しようとすると、「Circular dependency between resources (リソース間の循環依存関係)」エラーが表示されます。

次の例は、S3 バケットリソースと Lambda アクセス許可リソースの SourceArn プロパティ間の循環依存関係を示しています。

"Resources": {
  "MyS3BucketPermission": {
    "Type": "AWS::Lambda::Permission",
    "Properties": {
      "Action": "lambda:InvokeFunction",
      ...
      ...
      "SourceArn": {
        "Ref": "MyS3Bucket"
      }
    }
  },
  "MyS3Bucket": {
    "DependsOn" : "MyS3BucketPermission",
    ...
    ...

重要: Amazon S3 イベントソースの Lambda アクセス許可リソースに SourceAccount プロパティを追加するのがベストプラクティスです。Amazon S3 の Amazon リソースネーム (ARN) にはアカウント ID が含まれていないため、プロパティを追加します。SourceArn プロパティは他のほとんどのイベントソースに適していますが、Amazon S3 イベントソースに SourceAccount プロパティを追加することを検討してください。これにより、ユーザーが削除したバケットを再作成することができなくなるので、新しいバケット所有者に Lambda 関数を呼び出すための完全なアクセス許可を付与できなくなります。

解決方法

Fn::Sub 組み込み関数をスタックパラメータで使用すると、循環依存を回避できます。また、Fn::Join を使用して文字列を結合することもできます。

次のサンプルテンプレートでは、S3 バケット名 BucketPrefixAWS::S3::Bucket および AWS::Lambda::Permission リソースのパラメータです。

注意: 次の例では、バケット名が以前に AWS アカウントで使用されていなかったことを前提としています。このコードスニペットでテンプレートを再利用する場合は、毎回別のバケットプレフィックスを指定する必要があります。

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Parameters": {
    "BucketPrefix": {
      "Type": "String",
      "Default": "test-bucket-name"
    }
  },
  "Resources": {
    "EncryptionServiceBucket": {
      "DependsOn": "LambdaInvokePermission",
      "Type": "AWS::S3::Bucket",
      "Properties": {
        "BucketName": {
          "Fn::Sub": "${BucketPrefix}-encryption-service"
        },
        "NotificationConfiguration": {
          "LambdaConfigurations": [
            {
              "Function": {
                "Fn::GetAtt": [
                  "AppendItemToListFunction",
                  "Arn"
                ]
              },
              "Event": "s3:ObjectCreated:*",
              "Filter": {
                "S3Key": {
                  "Rules": [
                    {
                      "Name": "suffix",
                      "Value": "zip"
                    }
                  ]
                }
              }
            }
          ]
        }
      }
    },
    "LambdaInvokePermission": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "FunctionName": {
          "Fn::GetAtt": [
            "AppendItemToListFunction",
            "Arn"
          ]
        },
        "Action": "lambda:InvokeFunction",
        "Principal": "s3.amazonaws.com",
        "SourceAccount": {
          "Ref": "AWS::AccountId"
        },
        "SourceArn": { 
            "Fn::Sub": "arn:aws:s3:::${BucketPrefix}-encryption-service"
        }
      }
    },
    "AppendItemToListFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Handler": "index.handler",
        "Role": {
          "Fn::GetAtt": [
            "LambdaExecutionRole",
            "Arn"
          ]
        },
        "Code": {
          "ZipFile": {
            "Fn::Join": [
              "",
              [
                "var response = require('cfn-response');",
                "exports.handler = function(event, context) {",
                " var responseData = {Value: event.ResourceProperties.List};",
                " responseData.Value.push(event.ResourceProperties.AppendedItem);",
                " response.send(event, context, response.SUCCESS, responseData);",
                "};"
              ]
            ]
          }
        },
        "Runtime": "nodejs12.x"
      }
    },
    "LambdaExecutionRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "lambda.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ]
            }
          ]
        },
        "Path": "/",
        "Policies": [
          {
            "PolicyName": "root",
            "PolicyDocument": {
              "Version": "2012-10-17",
              "Statement": [
                {
                  "Effect": "Allow",
                  "Action": [
                    "logs:*"
                  ],
                  "Resource": "arn:aws:logs:*:*:*"
                }
              ]
            }
          }
        ]
      }
    }
  }
}

テンプレートは、次の順序でリソースを作成するため、循環依存を回避します。

  1. AWS Identity and Access Management (IAM) ロール
  2. Lambda 関数
  3. Lambda アクセス許可
  4. S3 バケット

これで、Amazon S3 は通知設定を確認し、問題なくバケットを作成できます。

次の解決策を試すこともできます。

  • 通知設定なしで S3 バケットを作成し、次のスタック更新でバケットを追加します。
  • 制約の少ない Lambda アクセス許可を作成します。たとえば、SourceArn を省略することにより、特定の AWS アカウントの呼び出しを許可します。
  • スタックワークフローの最後に実行するカスタムリソースを作成します。このリソースは、他のすべてのリソースが作成された後、S3 バケットに通知設定を追加します。

関連情報

AWS CloudFormation で Lambda イベント通知を使用するときに「Unable to validate the following destination configurations (次の送信先設定を検証できません)」エラーを回避するにはどうすればよいですか?

Amazon S3 イベントで AWS Lambda を使用する

参照番号

Fn::GetAtt

AWS公式
AWS公式更新しました 4年前
コメントはありません

関連するコンテンツ