🌊

LambdaでParameterStoreから値を取ってくる時のアレコレ

2022/12/31に公開

はじめに

こんばんわ。

みなさんAWS Lambdaは利用されていますでしょうか。
イベントドリブンなアーキテクチャや簡易的なバッチ処理として利用されている方も多いと思います。

そのLambda関数の中でLINE APIなどAPIを呼び出しする処理を入れる場合、
APIキーだったり、UserIDだったり、パスワードだったりとシークレットな情報が必要になることがありますが、それらの情報はどうやって取得されていますか?

一般的にはSSMのParameter Storeだったり、SecretsManagerなどから取得しますよね。
(もちろんコードにべた書きなんてしてない・・・ですよね…?)

私は前者(Parameter Store)から取得することが多いのですが、その際スコシダケ…ハマった部分があったので記事として残しておきます。

SSMから値を取得するAPIについて

Parameter Storeから値を取得する場合、以下のAPIを利用します。

取得する値が単一の場合

https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_GetParameter.html

取得する値が複数の場合

https://docs.aws.amazon.com/systems-manager/latest/APIReference/API_GetParameters.html

実際にやってみる

それでは実際にやっていきます。
今回はPython3.9を利用し、ライブラリとしてboto3を利用します。

下準備

下準備として適当なキー(sampleparams1とsampleparams2)と値をParameter Storeに入れ込んでおきます。

単一の値の取得

まずは単一の値を取得する場合をやってみます。
今回は取得してきた値を表示するのみのシンプルな実装にします。

import boto3

def lambda_hundler(event, context):
    ssm = boto3.client("ssm")
    response = ssm.get_parameter(
        Name = "sampleparams"
    )
    sample = response["Parameter"]["Value"]
    print(sample)

詳しくはAPI Referenceを参照頂ければと思いますが、get_parameterの引数はNameが必須で、
もう1つの引数であるWithDecryptionは取得対象のパラメータがSecureStringになっている(暗号化されている)場合に利用します。
今回はStringで格納しているため指定していません。

Lambda関数ができたので上記のPythonファイルをビルドして・・・
※今回は以下の通り、ローカルにモジュールファイルをインストールしzip化しています

Lambdaにデプロイしましょう。
※ハンドラの更新やIAMロールの設定も忘れずに

デプロイが完了したら、テストイベントを発行して動きを見てみましょう。

お、ちゃんととれてますね。よかったよかった。

複数の値の取得

では、次は複数の値をParameterStoreから取得してみたいと思います。
コードは先程とそんなに変わらず、使用する関数をget_parametersに変更し、
引数のNamesにリストで取得したい値のキーを指定します。
今回は2つの値を取得しますが試しにsampleparams2->sampleparams1の順番で表示したいので、その順番でNamesに記載していきます。

import boto3
def lambda_hundler(event, context):
    ssm = boto3.client("ssm")
    response = ssm.get_parameters(
        Names = [
            "sampleparams2",
            "sampleparams1"
        ]
    )
    sample2 = response["Parameters"][0]["Value"]
    sample1 = response["Parameters"][1]["Value"]

    print(f"sample2の値は{sample2}だよ~")
    print(f"sample1の値は{sample1}だよ~")

先のようにビルド→デプロイしてテストイベントを発行してみましょう。

アレアレっ、なんか想定した結果とは異なりますね・・・。
sample2の値がsampleparameter-1に、sample1の値がsampleparameter-2にという結果になってしまっています。

ssm get-parametersの注意点

そうなんです。
ここが注意点で、複数の値を取得するget-parametersはNamesに指定したリストの要素順に結果が返ってくるとは限らないのです。
(≒想定通りに返ってくることもあれば返ってこないこともある)

なので上記のようにresponseのParameters配下の配列番号で明示的に記述してしまうと意図した動作にならない場合があります。
※時にはAPIキーとユーザIDとが逆になっていてAPIエラーが発生することもあります。(1敗)

この問題の回避方法

ではこの問題を回避するにはどうすればいいでしょうか。

1つの方法としては、以下のようにresponseのParameters配下にある Nameキー をもとにif文で判別して代入するといった記述にすることで解決することができます。
私は1つのLambda関数で取得してくる値がそこまで多くないのでこういった記述の仕方をしています。(もっとよい記述方法があればそちらで・・・)

import boto3
def lambda_hundler(event, context):
    ssm = boto3.client("ssm")
    response = ssm.get_parameters(
        Names = [
            "sampleparams2",
            "sampleparams1"
        ]
    )
    for i in response["Parameters"]:
        if i["Name"] == "sampleparams2":
            sample2 = i["Value"]
        else:
            sample1 = i["Value"]

    print(f"sample2の値は{sample2}だよ~")
    print(f"sample1の値は{sample1}だよ~")

上記の通り修正したので再デプロイしましょう。
デプロイが完了したら再度テストイベントを発行しましょう!

お!今回は無事に想定したような表示がされていそうです。めでたしめでたし。

まとめ

というわけで今回は、LambdaからSSM ParameterStoreの値を取得する方法と複数の値を取得するうえでのちょっとした注意点をご紹介しました。

今回は取得してくる値がStringだったのですが、これがSecureStringになると、
Lambdaの実行ロールに対して、[kms:Decrypt]を追加で許可する必要があります。
また関数の引数をName(Names)のみにした場合、取得してきた値は暗号化されていますので、
復号化をした状態で取得をしたい場合は別途、引数でWithDecryption=Trueを設定してあげましょう。

ここまで読んでくださりありがとうございました~。
それではみなさん良いお年を。

Discussion