Serverless FrameworkでResourceNotFoundExceptionを解消する

2023.03.13

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

アノテーションの髙嶋です。

私はAWSへのデプロイにはServerless Frameworkを使用しています。
今回はデプロイ時にResourceNotFoundExceptionが発生してしまったので、それを解消した話を書きます。

発生事象

今回の実行環境は下記のとおりです。

Python:3.9.16
node:v12.22.12
Serverless Framework:3.28.1

Serverless Frameworkのymlは下記のように記述していました。

serverless.yml

service: lambda-test
provider:
  name: aws
  runtime: python3.9
  region: ap-northeast-1
functions:
  testHandler:
    handler: functions/handler.lambda_handler
    package:
      patterns:
        - /functions/**
resources:
  Resources:
    ResourceBasePermission:
      Type: AWS::Lambda::Permission
      Properties:
        Action: lambda:InvokeFunction
        FunctionName: lambda-test-dev-testHandler
        Principal: s3.amazonaws.com
        SourceArn: arn:aws:s3:::${env:BUCKET_NAME}
        SourceAccount: ${env:AWS_ID}

※ResourceBasePermissionでLambda関数に対して、S3バケットへのPermissionを設定する。

slsのデプロイコマンドを実行したところ、ResourceNotFoundExceptionが発生し、失敗してしまいました。
・コマンド

sls deploy

※事前に環境変数としてBUCKET_NAMEAWS_IDを設定してください

・エラーメッセージ

Error:
CREATE_FAILED: ResourceBasePermission (AWS::Lambda::Permission)
Function not found: arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:lambda-test-dev-testHandler (Service: AWSLambdaInternal; Status Code: 404; Error Code: ResourceNotFoundException; Request ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx; Proxy: null)

発生原因

前述したエラーメッセージとCloudFormationのログを確認したところ、Lambda関数の作成より前にLambda関数に対するResourceBasePermissionが実行されていました。
Lambda関数がまだないということで404エラーが発生してしまったようです。

解決方法

Lambda関数が作成されたあとにPermission設定が実行されるよう、実行順の制御を追加する必要があります。
後述した修正を実施することで、エラーが解消することを確認しました。

FunctionNameを参照にする(暗黙的な制御)

「発生事象」で記述したymlファイルでは、FunctionNameを実際に作成される名前で指定していました。 そこを!Refで指定することで、CloudFormationでの実行順が暗黙的に制御されるようになります。

serverless.yml(修正後)

・
・
resources:
  Resources:
    ResourceBasePermission:
      Type: AWS::Lambda::Permission
      Properties:
        Action: lambda:InvokeFunction
        FunctionName: !Ref TestHandlerLambdaFunction
        Principal: s3.amazonaws.com
        SourceArn: arn:aws:s3:::${env:BUCKET_NAME}
        SourceAccount: ${env:AWS_ID}

実行制御に関しては下記を参照ください。
ユーザーガイド(DependsOn 属性)

また、Lambda関数を参照する場合、ymlで記述した名称を基にServerless Frameworkのルールに則った値で設定する必要があります。
今回の場合はtestHandlerTestHandlerLambdaFunctionとなります。

名称のルールに関しては下記を参照ください。
AWS Infrastructure Resources

DependsOnを記述する(明示的な制御)

DependsOnを記述することで、実行順を明示的に制御することもできます。

serverless.yml(修正後)

・
・
resources:
  Resources:
    ResourceBasePermission:
      Type: AWS::Lambda::Permission
      Properties:
        Action: lambda:InvokeFunction
        FunctionName: lambda-test-dev-testHandler
        Principal: s3.amazonaws.com
        SourceArn: arn:aws:s3:::${env:BUCKET_NAME}
        SourceAccount: ${env:AWS_ID}
      DependsOn: 
        - TestHandlerLambdaFunction

ただ、こちらの方法だとfunctionsで定義するLambda関数名が変更になると、FunctionNameの修正も必要になります。
なので、前述したFunctionNameを参照にする方が暗黙的に制御される、かつ、修正箇所を少なくできる、でいいかなと考えます。

今回得た教訓

実は今回のエラーが発生したのは本番リリース時でした。。

開発環境の構築時は

  1. Lambda関数を作成するデプロイを実施
  2. Permissionの記述を追加してデプロイを実施

というように2段階で実施していました。

そのため、すでにLambda関数が存在する状態で実施していたためエラーは発生せず、実行順の制御が必要なことに気付けていませんでした。

なので、本番環境へのリリースと同じ手順を開発環境でも試すこと!を改めて認識しました。

アノテーション株式会社について

アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。