
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点によるものです。
- osモジュールのインポートが不足している。
- 24行目のpg8000.connectメソッドのssl_contextパラメータはpg8000(1.14.0)から廃止されている。
- 33行目に不要なdecodeメソッドが使われている。
- 37行目のresult変数名は誤り。("s"が足りない。)
- 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_contextThis 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名>"
}
]
}