この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、虎塚です。
システムのログ収集、監視、アラート通知、アクション実行などにAWS Lambdaを使っていると、LambdaとLambdaをチェーンして使いたいことがあります。さらに、複数のアカウントで発生したイベントを1つのアカウントに集めるようなときには、Lambdaから別のAWSアカウントのLambdaを呼び出したいこともあります。
この記事では、AWS Lambdaから別アカウントのAWS Lambdaを呼びだす方法を説明します。
概要
本記事で設定手順を説明するAWS構成を次の図に示します。
アカウント000000000000のAWS Lambda (caller) が、アカウント111111111111のAWS Lambda (callee) を呼び出します。
動作確認のために、1つ目のAWS Lambdaの前にKinesis Streamを配置しました。また、2つ目のAWS Lambdaは、Lambda関数内の処理で、実行中のEC2インスタンスを停止します。
それぞれのLambdaに必要な権限を次の図に示します。
次の2点がポイントです。
- 呼び出される側のLambdaのリソースポリシーで、アカウント000000000000による操作を許可します。
クロスアカウント呼び出しの許可をリソースポリシーで与えるので、呼び出される側のLambdaの実行ロールで、アカウント000000000000を信頼ポリシーに含める必要はありません。
以降にチュートリアルを用意しましたので、ピンとこない場合は、実際にお試しいただければと思います。
設定手順
Lambdaから別アカウントのLambdaを呼び出す構成を作成します。
- 2つのAWSアカウント000000000000と、111111111111を利用できるものとします。
- Kinesis Stream, EC2, AWS LambdaなどのAWSリソースは、すべて東京リージョンに作成するものとします。
基本的にAWS CLIで作業します。コマンド実行時に、対象のアカウントを間違えないように注意してください。手順では、呼び出し側アカウントに対するコマンドに--profile caller
、呼び出される側アカウントに対するコマンドに--profile callee
をつけます。
1. Kinesis Streamの作成
アカウント0000000000でKinesis Streamを作成します。
次のコマンドで、シャードを1個だけ持つKinesis Stream (my-stream) を作成します。
aws kinesis create-stream --profile caller\
--stream-name my-stream \
--shard-count 1
作成されたことを確認します。
aws kinesis describe-stream --profile caller\
--stream-name my-stream
ストリームのARNは、ステップ2.4で使うので控えておきます。
2. 呼び出し側のLambdaの作成
アカウント0000000000でAWS Lambda (caller) を作成します。
2.1. デプロイパッケージの作成
ローカルで、次のPythonスクリプトをテキストファイルに保存します。
caller.py
from __future__ import print_function
import base64
import boto3
import json
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def create_function_arn(data):
region = data['region']
account = data['accountId']
function_name = data['functionName']
return 'arn:aws:lambda:' + region + ':' + account + ':function:' + function_name
def lambda_handler(event, context):
logger.info(event)
for record in event['Records']:
data = json.loads(base64.b64decode(record['kinesis']['data']))
function_arn = create_function_arn(data)
params = {
'instanceId': data['parameters']['instanceId']
}
response = boto3.client('lambda').invoke(
ClientContext = 'string',
FunctionName = function_arn,
InvocationType = 'Event',
LogType = 'Tail',
Payload = json.dumps(params)
)
logger.info(response)
このスクリプトでは、Kinesisからデータを受け取って、呼び出される側のLambda (callee) を特定し、パラメータとしてインスタンスIDを渡して呼び出す処理をします。
デプロイパッケージを作成する環境として、EC2インスタンスを起動します。このインスタンスは、アカウント000000000000とアカウント111111111111のどちらで起動しても構いません。今回は、次のAMIを利用しました。
- Amazon Linux AMI 2016.09.1 (HVM), SSD Volume Type - ami-56d4ad31
ec2-userのホームディレクトリに、Pythonスクリプトをローカルからコピーします。
scp -i ~/.ssh/key.pem ./caller.py ec2-user@XXX.XXX.XXX.XXX:
EC2インスタンスにSSHログインします。
このAMIに初期インストールされているPythonは2.7.12で、pipが含まれているので、pipのインストールを省略できます。
必要なパッケージをインストールします。
sudo yum install python27-devel python27-pip gcc
パッケージ作成の作業環境を整えます。
virtualenv ~/caller
source ~/caller/bin/activate
boto3はAWS上の実行環境で利用できるので、ローカルデバッグで利用したいときだけインストールします。
pip install boto3
デプロイパッケージ (caller.zip) に関連ファイルを含めます。関連ファイルがない場合は、スキップします (コマンドを実行してしまっても問題ありません)。
cd $VIRTUAL_ENV/lib/python2.7/site-packages
zip -r9 ~/caller.zip *
cd $VIRTUAL_ENV/lib64/python2.7/site-packages
zip -r9 ~/caller.zip *
デプロイパッケージにスクリプトを含めます。
cd ~
zip -g caller.zip caller.py
EC2インスタンスからログアウトして、作成したデプロイパッケージをローカルにダウンロードしておきます。
scp -i ~/.ssh/key.pem ec2-user@XXX.XXX.XXX.XXX:caller.zip .
このEC2インスタンスは、ステップ3.1でもう一度使うので、起動したままにしておきます。
2.2. Lambdaの実行ロールの作成
まず、IAMロールの信頼ポリシーを、ローカルにファイルで作成します。
TrustPolicyForLambda.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
次に、上の信頼ポリシーを指定して、IAMロールを作成します。
aws iam create-role --profile caller \
--role-name caller-role \
--assume-role-policy-document file://TrustPolicyForLambda.json
戻り値に含まれるロールのARNは、ステップ2.3で使うので控えておきます。
最後に、作成したIAMロールにアクセスポリシーを追加します。
# Lambdaの実行ログの出力を許可します。一般的なLambdaの実行ロールの権限です。
aws iam attach-role-policy --profile caller \
--role-name caller-role \
--policy-arn arn:aws:iam::aws:policy/AWSLambdaExecute
# Kinesis Streamからデータの取得を許可します。
aws iam attach-role-policy --profile caller \
--role-name caller-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaKinesisExecutionRole
# Lambdaの呼び出しを許可します。calleeの呼び出しに必要です。
aws iam attach-role-policy --profile caller \
--role-name caller-role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaRole
2.3. デプロイパッケージのアップロード
ステップ2.1で作成したデプロイパッケージのzipファイルを指定して、Lambda関数を作成します。
aws lambda create-function --profile caller \
--function-name caller \
--runtime python2.7 \
--role arn:aws:iam::000000000000:role/caller-role \
--handler caller.lambda_handler \
--zip-file fileb://caller.zip
2.4. イベントソースにKinesis Streamを設定
ステップ1で作成したKinesis Stream (my-stream) を、Lambdaのイベントソースに設定します。これによって、Kinesis Streamにデータが入ると、Lambda (caller) が起動するようになります。
aws lambda create-event-source-mapping --profile caller \
--event-source-arn arn:aws:kinesis:ap-northeast-1:000000000000:stream/my-stream \
--function-name caller \
--starting-position LATEST
3. 呼び出される側のLambdaの作成
アカウント111111111111でAWS Lambda (callee) を作成します。
3.1. デプロイパッケージの作成
ローカルで次のPythonスクリプトをテキストファイルに保存します。
callee.py
from __future__ import print_function
import boto3
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
logger.info(event)
instance = event['instanceId']
response = boto3.client('ec2').stop_instances(
InstanceIds=[
instance,
]
)
ステップ2.1で使ったインスタンスに上のスクリプトをコピーして、前回と同じ手順でデプロイパッケージを作成します。
scp -i ~/.ssh/key.pem ./callee.py ec2-user@XXX.XXX.XXX.XXX:
EC2インスタンスにSSHログインして、パッケージ作成の作業環境を整えます。
virtualenv ~/callee
source ~/callee/bin/activate
デプロイパッケージ (callee.zip) に関連ファイルを含めます。
cd $VIRTUAL_ENV/lib/python2.7/site-packages
zip -r9 ~/callee.zip *
cd $VIRTUAL_ENV/lib64/python2.7/site-packages
zip -r9 ~/callee.zip *
デプロイパッケージにスクリプトを含めます。
cd ~
zip -g callee.zip callee.py
EC2インスタンスからログアウトして、作成したデプロイパッケージをローカルにダウンロードしておきます。
scp -i ~/.ssh/key.pem ec2-user@XXX.XXX.XXX.XXX:callee.zip .
このEC2インスタンスは、停止して削除してしまって構いません。
3.2. Lambdaの実行ロールの作成
アカウント111111111111でIAMロールを作成します。
まず、IAMロールの信頼ポリシーを、ローカルにファイルで作成します。
TrustPolicyForLambda.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
ステップ2.2で作成した信頼ポリシーと同じように、Lambdaサービスロールとして必要な設定を定義します。
次に、上の信頼ポリシーを指定して、IAMロールを作成します。
aws iam create-role --profile callee \
--role-name callee-role \
--assume-role-policy-document file://TrustPolicyForLambdaAndCallerAccount.json
戻り値に含まれるロールのARNは、ステップ3.3で使うので控えておきます。
最後に、作成したIAMロールにアクセスポリシーを追加します。
# Lambdaの実行ログの出力を許可します。一般的なLambdaの実行ロールの権限です。
aws iam attach-role-policy --profile callee \
--role-name callee-role \
--policy-arn arn:aws:iam::aws:policy/AWSLambdaExecute
# EC2の操作を許可します。今回はEC2を停止させるために追加します。
aws iam attach-role-policy --profile callee \
--role-name callee-role \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess
3.3. デプロイパッケージのアップロード
前のステップで作成したデプロイパッケージのzipファイルを指定して、Lambda関数を作成します。
aws lambda create-function --profile callee \
--function-name callee \
--runtime python2.7 \
--role arn:aws:iam::111111111111:role/callee-role \
--handler callee.lambda_handler \
--zip-file fileb://callee.zip
3.4. 権限の追加
アカウント000000000000からこのLambda関数を呼び出せるように、Lambdaのリソースポリシーに権限を追加します。
aws lambda add-permission --profile callee \
--function-name callee \
--statement-id AllowActionFromCaller \
--principal 000000000000 \
--action lambda:InvokeFunction
これで設定が完了しました。
動作確認
Lambda (caller) から、クロスアカウントでLambda (callee) を呼び出せることを確認します。
1. 対象インスタンスの起動確認
Lambda (callee) が停止させるEC2インスタンス (i-12345678901234567) を、アカウント111111111111の東京リージョンに起動します。i-12345678901234567が起動していることを確認します。
aws ec2 describe-instances --profile callee \
--instance-id i-12345678901234567 \
| jq '.Reservations[].Instances[].State.Name'
"running"
2. Kinesisへデータ投入
Kinesis Stream (my-stream) に投入するデータを、ローカルにファイルで作成します。
sample_event.json
{
"accountId" : "111111111111",
"region" : "ap-northeast-1",
"functionName" : "callee",
"parameters" : {
"instanceId" : "i-12345678901234567"
}
}
上のデータには、呼び出される側のLambdaを特定するための情報 (アカウントID、Lambdaが配置されたリージョン、Lambda関数名) と、処理対象のインスタンスIDが含まれています。
次のコマンドで、Kinesis Streamにデータを投入します。
aws kinesis put-record --profile caller \
--stream-name my-stream \
--data file://sample_event.json \
--partition-key test
3. Lambdaのログ確認
AWS Management Consoleにログインして、CloudWatch LogsでLambdaのログを確認します。
- アカウント000000000000の/aws/lambda/callerロググループ
- アカウント111111111111の/aws/lambda/calleeロググループ
エラーが出力されていないことを確認します。
4. インスタンスの確認
EC2インスタンスが停止されたことを確認します。
aws ec2 describe-instances --profile callee \
--instance-id i-12345678901234567 \
| jq '.Reservations[].Instances[].State.Name'
"stopped"
おわりに
LambdaからLambdaをクロスアカウントで呼び出すための設定方法を説明しました。アカウントをまたいで複数のLambdaを繋ぎたいときに、この記事がお役に立てば幸いです。
なお、実際にこういった構成で環境を作る場合は、Lambdaに与える権限を適切に絞りましょう。(たとえば、今回の設定例でいうと、calleeの実行ロールに与える権限は、EC2のフルアクセスよりも停止だけのほうが望ましいです)
それでは、また。