紙一重の積み重ね

35歳のエンジニアがなれる最高の自分を目指して、学んだことをこつこつ情報発信するブログです。

【CloudFormation】S3からLambdaを起動させるスタック実行時に Unable to validate the following destination configurations エラーが発生したときの対処法

f:id:yokoyantech:20181207175720p:plain

はじめに

はじめてCloudFormationでS3のイベントでLambdaを発火させるスタックを作成したら失敗したのでメモ。

やりたいこと

  • S3にPUTしたタイミングでLambdaを起動したい
  • 上記をCloudFormationのテンプレートで作成したい

発生したエラー1

CloudFormationでスタック実行時に以下のエラーが発生

CreateS3Bucket
Unable to validate the following destination configurations (Service: Amazon S3; Status Code: 400; Error Code: InvalidArgument;

原因

  • Type: AWS::Lambda::Permissionがない
    • Lambda 関数を呼び出す S3 バケットのアクセス許可がなかった

対策

  • テンプレートにLambdaInvokePermissionを追加。
  • S3バケット作成のテンプレートに、DependsOnを追加。
Resources:
  # ----------------------------------------------------- #
  # Lambda関数を呼び出すS3バケットのアクセス許可
  # ----------------------------------------------------- #
  LambdaInvokePermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      FunctionName: !GetAtt hogehogeLambdaFunction.Arn
      Action: 'lambda:InvokeFunction'
      Principal: S3.amazonaws.com
      SourceAccount: !Ref 'AWS::AccountId'
      SourceArn: !GetAtt CreateS3Bucket.Arn

  CreateS3Bucket:
    Type: 'AWS::S3::Bucket'
    DependsOn:
      - LambdaInvokePermission

参考情報

aws.amazon.com

発生したエラー2

  • スタック実行時のymlのチェックでエラー発生。
テンプレートにエラーがあります。: Circular dependency between resources: [LambdaInvokePermission, CreateS3Bucket, S3BucketPolicy]

原因

  • 循環参照になっているため。(鶏が先か卵が先か状態)

対策

  • 循環参照を解消するためには、Fn::Join を使う

この問題が発生した場合は、Fn::Join 組み込み関数とスタックパラメータを使用して循環依存を回避できます。

aws.amazon.com

なぜFn::Joinを使うと循環参照が解消するのかが疑問だったが、

同様の機能の Fn::Sub 関数も参照してください。

とのことで、Fn::Sub関数を見ると納得できた。Fn::Subおよび、Fn::Joinはスタックを作成(更新)するまで使用できない値を含むコマンドを実行できるようだ。スタック完了前に作成中のArnなどを返却するイメージと理解した。

テンプレートで、スタックを作成または更新するまで使用できない値を含むコマンドまたは出力を作成するために、この関数を使用できます。

docs.aws.amazon.com

Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "input your environment(staging or production)"
        Parameters:
          - ENV

Resources:
  # ----------------------------------------------------- #
  # Lambda関数を呼び出すS3バケットのアクセス許可
  # ----------------------------------------------------- #
  LambdaInvokePermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      FunctionName: !GetAtt Scraper.Arn
      Action: 'lambda:InvokeFunction'
      Principal: s3.amazonaws.com
      SourceAccount: !Ref 'AWS::AccountId'
      # 循環参照を回避するために !GetAtt CreateS3Bucket.Arnではなく、Fn::Join を使用する
      # 参考:https://aws.amazon.com/jp/premiumsupport/knowledge-center/unable-validate-circular-dependency-cloudformation/
      SourceArn: !Join
        - ''
        - - 'arn:aws:s3:::'
          - !Sub "MyBucketName-${ENV}"

  # ----------------------------------------------------- #
  # S3
  # ----------------------------------------------------- #
  # クローリング結果を配置するS3バケット
  CreateS3Bucket:
    Type: 'AWS::S3::Bucket'
    DependsOn:
      - LambdaInvokePermission
    Properties:
      BucketName: !Sub "MyBucketName-${ENV}"
      # 以下略

まとめ

CloudFormationは非常に便利なので、今後もがしがし使っていきたいと思います!