LambdaからAurora(PostgreSQL)へIAM認証を使用した接続で失敗する原因と対処について

テクニカルサポートノート サービス名:Lambda, Aurora PostgreSQL
2021.01.23

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

困っていた内容

LambdaからAurora(PostgreSQL)へIAM認証を使用した接続をするために、下記AWSブログを参考にしたが、エラーが発生してうまくいかない。
▼発生するエラー
{
  "status": "Error",
  "message": "connect() got an unexpected keyword argument 'ssl'"
}
なお、今回の環境は下記バージョンで確認を行っております。
・Lambdaのランタイム: Python 3.7
・Aurora(PostgreSQL): 10.14

どうすればいいか?

基本的にブログの内容通りに進めていただいて構いません。
ただし、最後のサンプルコードには誤りと廃止されたパラメータが使われている箇所があり、そのままコピペ実行してもうまくいきません。具体的には下記5点によるものです。
  1. osモジュールのインポートが不足している。
  2. 24行目のpg8000.connectメソッドのssl_contextパラメータはpg8000(1.14.0)から廃止されている。
  3. 33行目に不要なdecodeメソッドが使われている。
  4. 37行目のresult変数名は誤り。("s"が足りない。)
  5. 39行目と41行目のダブルクォーテーションに誤った文字が使われている。
2点目の内容について補足します。
GitHubにあるpg8000のチェンジログを確認しますと、sslパラメータが廃止された点について記載されています。また、APIドキュメントには代わりとなるssl_contextパラメータが記載されていますので、こちらを使用して修正版のコードを書いてみたいと思います。※:ブログが公開された後に変更が行われた様です。

The ssl.wrap_socket function is deprecated, so we now give the user the option of using a default SSLContext or to pass in a custom one. This is a backwardly incompatible change.

ssl_context
This governs SSL encryption for TCP/IP sockets. It can have three values:
・None, meaning no SSL (the default)
・True, means use SSL with an ssl.SSContext created using ssl.create_default_context()
・An instance of ssl.SSContext which will be used to create the SSL connection.

ということで、修正版のコードが下記です。

import os
import boto3
import pg8000

def lambda_handler(event, context):
     try:
         # rds のサービス名で低レベルクライアントを作成する
         client = boto3.client("rds")
         # 環境変数を読み取り、DB EndPoint を取得する
         DBEndPoint = os.environ.get("DBEndPoint")
         # 環境変数を読み取り、データベース名を取得する
         DatabaseName = os.environ.get("DatabaseName")
         # 環境変数を読み取り、データベースにアクセスできるデータベースユーザー名を取得します。
         DBUserName = os.environ.get("DBUserName")
         # IAM 認証情報を使用してデータベースに接続するために使用する認証トークンを生成します。
         password = client.generate_db_auth_token(
             DBHostname=DBEndPoint, Port=5432, DBUsername=DBUserName
         )
         # パスワードとして生成されたトークンを使用してサーバーとの接続を確立する
         conn = pg8000.connect(
             host=DBEndPoint,
             user=DBUserName,
             database=DatabaseName,
             password=password,
             ssl_context=True,
         )
         # カーソルオブジェクトのインスタンス化
         cursor = conn.cursor()
         # 実行されるクエリ
         query = "SELECT CURRENT_DATABASE()"
         # 接続されたデータベースでクエリ/コマンドを実行する
         cursor.execute(query)
         # 列名を取得する
         columns = [str(desc[0]) for desc in cursor.description]
         results = []
         # 結果セットから dict の行を作成します。
         for res in cursor:
             results.append(dict(zip(columns, res)))
         # 結果を返す
         return {"status": "Success", "results": results}
     except Exception as e:
         return {"status": "Error", "message": str(e)}

 

うまくいくと、Lambdaの実行結果として下記内容が記録されるはずです。
{
  "status": "Success",
  "results": [
    {
      "current_database": "<接続したDatabase名>"
    }
  ]
}