この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
先日Lambda関数からData API for Redshift(以降「DataAPI」)クエリの実行を行ってみました。
サブネットもセキュリティグループも考慮不要!Lambda関数からData API for Redshiftでクエリ実行してみた。
(以降、「前回の記事」)
今回はそこから少し発展させて、Lambda関数から別アカウントのVPCにあるRedshiftにクエリを投げてみます。 DataAPIはIAMによる認証でクエリが投げられるサービスですので、 AWSアカウントが違う場合でも、IAMによる設定を適切に行えばクエリを投げることが可能になります!
構成は簡単ですが、図にするとこんな感じです。
Redshiftがあるアカウントを「Redshiftアカウント」、 Lambda関数があるアカウントを「Lambdaアカウント」と呼称します。
なお、前回の記事がある前提で基本的な説明は省略していますので、 この記事の前に前回の記事をご参照頂ければと思います。
Lambda関数(暫定)を用意
アクセスを許可するLambdaアカウント側のIAMロールを確定させるためにLambda関数を作成します。 一旦前回の記事のLambda関数コードを貼り付けておきます。 もちろんこのまま実行しても失敗します。
RedshiftアカウントにIAMロールを作成する
Redshiftアカウントに、Redshiftにアクセス可能なIAMロールを作成します。 Lambda関数からこのロールにAssumeRoleを行うことによって、 別アカウントのRedshiftへの接続を可能にします。
「別のAWSアカウント」を選び、LambdaアカウントのIDを入力。
このロールがRedshiftにアクセスするので「AmazonRedshiftFullAccess」を選択する。
タグは任意で。
ロールの名前を入力。
ロールが作られました。このロールの引き受けができるリソースを上記で作成したLambda関数に限定したいので、 「信頼関係の編集」をクリック。
Principal
のAWS
の部分に、Lambda関数にアタッチされているIAMロールを指定します。
RedshiftアカウントのIAMロールの設定は完了です。 続いて、接続元のLambda関数にアタッチされたロールに上記IAMロールへのAssumeRoleを許可します。
以上でIAM周りの設定は完了です。
Lambda関数の修正
Lambda関数のコードに、RedshiftアカウントのIAMロールにAssumeRoleする処理を入れなくてはいけません。
とは言っても、以下のようにsession
を作成するようにするだけです。
import json
import time
import boto3
from boto3.session import Session
# Redshift接続情報
CLUSTER_NAME='cluster_name'
DATABASE_NAME='sample_db'
DB_USER='dbuser'
ROLE_ARN='arn:aws:iam::10XXXXXXXXXX:role/AllowRedshiftDataApiCrossAccount'
# 実行するSQLを設定
sql = '''
SELECT * FROM public.sample_table;
'''
def create_session():
sts_client = boto3.client('sts', endpoint_url='https://sts.ap-northeast-1.amazonaws.com')
credentials = sts_client.assume_role(RoleArn=ROLE_ARN,
RoleSessionName='ResfhitDataAPI')
accesskey = credentials['Credentials']['AccessKeyId']
secretkey = credentials['Credentials']['SecretAccessKey']
session_token = credentials['Credentials']['SessionToken']
session = Session(aws_access_key_id=accesskey,
aws_secret_access_key=secretkey,
aws_session_token=session_token)
return session
def lambda_handler(event, context):
# Redshiftにクエリを投げる。非同期なのですぐ返ってくる
session = create_session()
data_client = session.client('redshift-data')
result = data_client.execute_statement(
ClusterIdentifier=CLUSTER_NAME,
Database=DATABASE_NAME,
DbUser=DB_USER,
Sql=sql,
)
# 実行IDを取得
id = result['Id']
#print('id = {}'.format(id))
# クエリが終わるのを待つ
statement = ''
status = ''
while status != 'FINISHED' and status != 'FAILED' and status != 'ABORTED':
statement = data_client.describe_statement(Id=id)
#print(statement)
status = statement['Status']
time.sleep(1)
# 結果の表示
if status == 'FINISHED':
if int(statement['ResultSize']) > 0:
# select文等なら戻り値を表示
statement = data_client.get_statement_result(Id=id)
print(json.dumps(statement['Records']))
else:
# 戻り値がないものはFINISHだけ出力して終わり
print('QUERY FINSHED')
elif status == 'FAILED':
# 失敗時
print('QUERY FAILED\n{}'.format(statement))
elif status == 'ABORTED':
# ユーザによる停止時
print('QUERY ABORTED: The query run was stopped by the user.')
session
を作ってsession.client('redshift-data')
することによって、
そこで作られるインスタンスはRedshiftアカウント内でのロール権限で動作します。
あとは前回同様、クエリを流して終了を待てば結果を得ることができます。
まとめ
DataAPIを使用することで、別アカウントのRedshiftにもLambdaからクエリを投げることができました!
アカウントAのRedshiftからデータを取り出してアカウントBのRedshiftにデータを入れる などの処理が単独のLambda関数で実行可能となり、構成がかなりシンプルになります。