0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

sam localコマンドでLambdaの環境変数が読み込まれなかった原因の備忘録

0
Posted at

状況説明

template.yaml
Parameters:
  Env: # local, devなど
    Type: String
  DatabaseEndpoint: # sam local実行時に参照するDB接続先
    Type: String

Conditions:
  IsLocal: !Equals [!Ref Env, "local"] # localならtrue

Resources:
  # Lambda定義
  Function:
    Type: AWS::Serverless::Function
      Properties:
        # ~~省略~~
        Environment:
          Variables:
            DATABASE_ENDPOINT: !If
              - IsLocal
              - !Ref DatabaseEndpoint # IsLocalがtrueのとき参照
              - !GetAtt DSQL.Endpoint # IsLocalがfalseのとき参照

  # Aurora DSQL定義
  Dsql:
    Type: AWS::DSQL::Cluster
    Properties:
      DeletionProtectionEnabled: true

ローカル環境でテストするときはsamconfig.tomlからDatabaseEndpointを読み取り、dev環境では実際のDSQLクラスターのエンドポイントを参照するように設定したつもりでした。

しかしこれでsam local start-apiコマンドを実行すると、DATABASE_ENDPOINTがNULLになってしまいDBへの接続ができませんでした。

原因

原因は2つありました。

1. !GetAttは本物のリソースを参照しようとする

!GetAtt DSQL.Endpoint

この場合、実際にプロビジョニングされているDSQLのエンドポイントを見に行こうとしますが、ローカルからはアクセスできないためエラーになってしまいます。

一方で!Refは本物のリソースにアクセスできない場合はtemplate.yamlに定義されているリソース名をそのまま返すため、エラーにならないようです。

2. !Ifは最初にtrueもfalseも両方評価する

DATABASE_ENDPOINT: !If
  - IsLocal
    - !Ref DatabaseEndpoint
    - !GetAtt DSQL.Endpoint

IsLocalがtrueなので!Ref DatabaseEndpointのみ評価するのかと思いきや、なぜかfalseの方も最初に評価してしまう仕様らしいです。

注意
「SAMやCloudFormationの仕様ではなく、SAM CLIの仕様」という意味です。

結論

このような流れでDATABASE_ENDPOINTがNULLになってしまったと思われます。

回避した方法

!Ref DatabaseEndpointのみに変更しました。

template.yaml
Resources:
  # Lambda定義
  Function:
    Type: AWS::Serverless::Function
      Properties:
        # ~~省略~~
        Environment:
          Variables:
            DATABASE_ENDPOINT: !Ref DatabaseEndpoint

ローカル実行のとき

samconfig.tomlparameter_overridesで環境変数を注入します。

samconfig.toml
[default.local_start_api.parameters]
parameter_overrides = [
  "Env=local",
  "DatabaseEndpoint=example.dsql.ap-northeast-1.on.aws"
]

dev環境のとき

GitHub actionsでCI/CDデプロイを組んでいるので、workflow内で環境変数を注入します。

deploy-workflow.yaml
- name: Deploy
    run: |
      sam deploy \
        --config-env ${{ vars.ENV }} \
        --no-confirm-changeset \
        --no-fail-on-empty-changeset \
        --parameter-overrides \
          Env=${{ vars.ENV }} \
          DatabaseEndpoint=${{ secrets.DATABASE_ENDPOINT }}

これだとGitHubのEnvironmentsにDATABASE_ENDPOINTを設定する必要がありますが、しょうがないと割り切りました。

備考

実はRole参照にも!GetAttを使っていたのですが、ここはエラーも何も出ていませんでした。
sam localでは使わないので、Role設定は無視しているということなんでしょうね。

template.yaml
Resources:
  # Lambda定義
  Function:
    Type: AWS::Serverless::Function
      Properties:
        # ~~省略~~
        Environment:
          Variables:
            DATABASE_ENDPOINT: !If
              - IsLocal
              - !Ref DatabaseEndpoint
              - !GetAtt DSQL.Endpoint
        Role: !GetAtt FunctionRole.Arn # ←これ
  FunctionRole:
    Type: AWS::IAM::Role
    Properties:
    # ~~省略~~

Issue

似たような事例を報告しているissueがありました。
Bug: Unable to resolve env var when referencing FunctionUrl #4510
まだOpenではありますが、最後のコメントが2023年なので当分進展はないでしょうね・・・。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?