🤖

aws-sam-cliで独自の環境変数を扱うには?

2020/10/29に公開2

はじめに

この記事は、AWS SAMを用いてLambda関数の実装をローカルで行う際に、
ユーザ独自の環境変数をどう扱うかについてハマってしまったことをまとめた記事です。
Lamnda関数を通してTwitterAPIを用いてツイートを送信する処理を実装したのですが、ローカルでの実行時にAPIキーなどを扱う時に苦労しました。
これが正解かどうかは確かではありませんが、ローカルでの検証や実行を行う際のやり方の一つではないかと思います。

godotenvが使えない…?

今回、Lambda関数はGolangを用いて開発をしました。
Golangにおいて環境変数を読み込む時は、godotenvというオープンソースパッケージを用いて.envファイルから環境変数を読み込んでいます。

.env
HOGE_ACCESS_TOKEN=""
HOGE_ACCESS_TOKEN_SECRET=""
err := godotenv.Load()

しかし、AWS SAMを用いて開発をした場合、この.envファイルを読み込むのには少し癖があるそうです。
というのも、AWS SAMを用いて開発をした場合、SAM側はLambdaを実行している時だけDockerコンテナを立ち上げるため、通常go run main.goコマンドを使ってプログラムを実行するだけで読み込めた.envファイルが読み込めない場合がほとんどです。
※こちらについて、僕なりに.envファイルをDocker Lambdaで読み込む方法を調べましたが、良い方法を見つけることが出来ませんでした😢

環境変数を定義するJSONから読み込む

SAMのドキュメントを読んでみると、--env-varsという環境変数を読み込むためのオプションが用意されています。

スクリーンショット 2020-10-27 21.31.12.png

環境変数ファイルとは何かというのをさらに調べたところ、Lambda関数に定義されている環境変数をJSON形式で書くことによって、それらを上書きできるというものらしいです。
Lambda関数の環境変数の定義はAWSのコンソール上からも定義が出来るので、それらをJSONで管理しているものと考えて良いでしょう。

ドキュメントによると、JSONは以下のように定義が出来ます。

{
  "Your function name": {
    "TABLE_NAME": "hoge",
    "BUCKET_NAME": "fuga"
  }
}

今回私はTwitterのAPIキーをこのenv.jsonに記載しました。
なおこのJSONファイルは、.gitignoreに含めることをオススメします。リポジトリにはenv.sample.json的な名前をつけた空っぽのファイルをpushしましょう。

{
  "Your function name": {
    "TWITTER_API_KEY": "hogehoge",
    "TWITTER_SECRET_KEY": "fugafuga"
  }
}

template.yamlから定義済み環境変数を読み込む

env.jsonに定義したファイルを、今度はLambda関数に読み込ませます。
SAM templateを用いてLambda関数を生成した場合、template.yamlというファイルが生成されていると思います。

SAM templateについて

こちらのyamlファイルのParameterセクションにAPIキーのフィールドを定義します。

このフィールドには、後ほどsam deployコマンドを使ってデプロイする際に上書きされます。

Description: >
  Sample function
Params:
  APIKey:
    Type: String
  SecretKey:
    Type: String

受け取ったAPIキーを、Resourcesセクションの中でこのlambda関数の環境変数として設定をします。

Resources:
  SampleFunction:
    Type: AWS::Serverless::Function 
    Properties:
      Environment:
        Variables:
          TWITTER_API_KEY: !Ref TwitterAPIKey
          TWITTER_SECRET_KEY: !Ref TwitterSecretKey

コードから環境変数を読み込み、実行

読み込まれた環境変数は、os.Getenv()を使ってコード上から呼び出すことが可能です。

// getCredential TwitterAPIを取得する
func getCredential() *anaconda.TwitterApi {
  return anaconda.NewTwitterApiWithCredentials(
    os.Getenv("TWITTER_ACCESS_TOKEN"),
    os.Getenv("TWITTER_ACCESS_TOKEN_SECRET"),
  )
}

忘れてはならないのが、sam local invokeコマンドを使って実行する際に--env-varsオプションをつけて実行することです。

sam build; sam local invoke SampleFnuction \
	--env-vars env.json

デプロイ

さて、sam local invokeコマンドにおいてはenv.jsonから環境変数を読み込みましたが、デプロイを実行するsam deployコマンドにはそのオプションがありません😭

代わりに、--parameter-overridesオプションがあるのでそのオプションで環境変数を定義します。

今回僕は予めzshrcなどにAPIキーをexportしておき、ShellScriptから参照できるようにしました。

sam deploy --parameter-overrides APIKey=$TWITTER_API_KEY SecretKey=$TWITTER_SECRET_KEY

まとめ

godotenvを使って環境変数を読み込む方法を見つけられなかったので、JSONファイルから独自の環境変数を定義して読み込むやり方を採用しましたが、これよりも良い方法が何かしらあると思っています。

実際に、aws-sam-cliのissueにも、dotenvファイルを読み込むオプションを作らないかという提案が上がっているのを見つけました。

feat(init): load .env file at startup of sam local #1355

現状、sam-cliを使って.envファイルをローカルで読み込む方法は無さそうなので、しばらくは今回紹介したやり方で運用をしようかと思います。

参考

AWS SAM
godotenv
aws-sam-cli
AWS SAMで環境変数を扱う

Discussion

畳屋民也畳屋民也

同様の問題に遭遇して参考にさせていただきました!

SAM CLI configuration file を使えば local invoke も deploy も一つのファイルで解決しそうですよ。

https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html

以下のようなファイルを project の root directory に置いておけば、引数指定しなくても sam local invoke SampleFnuction, sam deploy だけで行けるはずです。

samconfig.toml
version=0.1

[default.local_invoke.parameters]
parameter_overrides=["TwitterAPIKey=hoge", "TwitterSecretKey=fuga"]
 
[default.deploy.parameters]
parameter_overrides=["TwitterAPIKey=hogehoge", "TwitterSecretKey=fugafuga"]
とんととんと

コメントありがとうございます!
記事を参考にしていただき、筆者としても嬉しい限りです。

samconfig.tomlファイルにparameter_overridesオプションがあったのは知りませんでした…!
これをすることで、記事内でも記載しているdeploy.shなどは不要になって、1つのファイルで完結しそうですね!
git管理をする場合は、tomlファイルにパラメータを直書きしている関係上、取り扱いには気をつけないといけないのでどちらの方法を取るかはプロジェクト毎に変わりそうですね😃