Lambdaのクロスアカウントクエリを試す
Posted On 2025-02-08
AWS Organizationを利用し、TerraformでIAMロールを設定して親アカウントのLambdaから子アカウントの関数一覧を取得する手順を解説。AssumeRoleを介してクロスアカウントアクセスを実現し、安全にLambdaリソースの情報を取得できる構成を示す。
目次
はじめに
- この記事で個人でAWS Organizationを作るのがそこまで大変でないことがわかったので、アカウント間のクエリを試してみる
- 管理側のアカウントで、子アカウントのLambda関数一覧を取得するというもの
- 正確にはOrganizationなくても作れるけど、マルチアカウントの問題多いんでやってみる
ポイント
親子アカウントにロールを作る。IAMロールのポリシーが全ての肝
- 管理アカウント側のロール
management-lambda-execution-role
- 信頼関係はシングルアカウントのときと同様に
lambda.amazonaws.com
- ログの記録用にマネージドポリシー
AWSLambdaBasicExecutionRole
を追加 - 子アカウントのロール
LambdaReaderCrossAccountRole
をsts:AssumeRole
するための権限を追加。子アカウントのAssume Roleするためのポリシーがクロスアカウントになって変わった点
- 信頼関係はシングルアカウントのときと同様に
- 子アカウントのロール
LambdaReaderCrossAccountRole
- 信頼関係は親のアカウントのロール
management-lambda-execution-role
をsts:AssumeRole
するための権限を追加。親のアカウントのロールを信頼関係として追加するというのがクロスアカウントになって変わった点 lambda:ListFunctions
の権限を追加。これはシングルアカウントのときと同様
- 信頼関係は親のアカウントのロール
- Lambdaのプログラム:子アカウントのロールをAssume Roleする
図表で書いてみる
もうちょっと整理してみる。シングルアカウントの場合
信頼関係 | ポリシー | |
---|---|---|
シングルアカウント | lambda.amazonaws.com | アプリに必要な権限(例:lambda:ListFunctions) |
クロスアカウントの場合。Lambdaは親アカウント側にデプロイしてクエリすることを想定。
信頼関係 | ポリシー | |
---|---|---|
クロスアカウント(親) | lambda.amazonaws.com | 子アカウントのロールをAssume Roleする権限(sts:AssumeRole) |
クロスアカウント(子) | 親のアカウントのロールへの信頼ポリシー | アプリに必要な権限(例:lambda:ListFunctions) |
ロール間のAssumeをくっつけてしまえばシングルアカウントと同じ。
考え方
- Lambdaを実行するのは親アカウント側だから、
lambda.amazonaws.com
への信頼関係は親側のロールで必要 - リソースを取ってくるのは子アカウント側だから、
lambda:ListFunctions
の権限は子側のロールで必要 - 親のLambdaのアプリケーションではAssume Roleするので、あとは適当にAssume Roleができるようにする
コード
Terraform
以下のようなプロバイダを定義する
# 管理アカウント用(profile: management)
provider "aws" {
region = "ap-northeast-1"
profile = "management"
}
# develop アカウント用(profile: develop)
provider "aws" {
alias = "develop"
region = "ap-northeast-1"
profile = "develop"
}
LambdaとIAMロールのデプロイ
##############################
# 管理アカウント側:Lambda 用実行ロール
##############################
resource "aws_iam_role" "lambda_role" {
name = "management-lambda-execution-role"
assume_role_policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
})
}
# Lambda 用基本実行ポリシー(CloudWatch Logs 出力用)
resource "aws_iam_role_policy_attachment" "lambda_logs" {
role = aws_iam_role.lambda_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
# developアカウント側のクロスアカウントロールへのsts:AssumeRoleを許可するポリシーを追加
resource "aws_iam_role_policy" "management_assume_develop_role_policy" {
name = "allow-assume-develop-role"
role = aws_iam_role.lambda_role.id
policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAssumeDevelopRole",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": aws_iam_role.develop_lambda_reader.arn
}
]
})
}
##############################
# develop アカウント側:クロスアカウントで Lambda 関数一覧取得を許可するロール
##############################
resource "aws_iam_role" "develop_lambda_reader" {
provider = aws.develop
name = "LambdaReaderCrossAccountRole"
# 管理アカウント側の Lambda 実行ロール (aws_iam_role.lambda_role) を信頼する
assume_role_policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": aws_iam_role.lambda_role.arn
# ※ もしクロスアカウント参照が難しい場合は、
# "AWS": "arn:aws:iam::<management_account_id>:role/management-lambda-execution-role"
# のように直接文字列で指定してください。
},
"Action": "sts:AssumeRole"
}
]
})
}
# develop 側で Lambda 関数一覧取得 (lambda:ListFunctions) を許可するポリシー
resource "aws_iam_policy" "develop_lambda_list_policy" {
provider = aws.develop
name = "DevelopLambdaListPolicy"
policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "lambda:ListFunctions",
"Resource": "*"
}
]
})
}
resource "aws_iam_role_policy_attachment" "develop_policy_attach" {
provider = aws.develop
role = aws_iam_role.develop_lambda_reader.name
policy_arn = aws_iam_policy.develop_lambda_list_policy.arn
}
##############################
# Lambda のソースコード ZIP 化(同ディレクトリ内の lambda_function.py を利用)
##############################
data "archive_file" "lambda_zip" {
type = "zip"
source_file = "${path.module}/lambda_function.py"
output_path = "${path.module}/lambda_function.zip"
}
##############################
# 管理アカウント側:Lambda 関数作成
##############################
resource "aws_lambda_function" "list_develop_functions" {
function_name = "list_develop_functions"
role = aws_iam_role.lambda_role.arn
handler = "lambda_function.lambda_handler"
runtime = "python3.12"
filename = data.archive_file.lambda_zip.output_path
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
# develop アカウント側のクロスアカウント用ロール ARN を環境変数として渡す
environment {
variables = {
DEVELOP_ROLE_ARN = aws_iam_role.develop_lambda_reader.arn
}
}
}
Lambdaのコード
アプリケーション内でAssume Roleしているのが特徴。この仕組みさえ知ってしまえばそこまで難しくないと思う。
import boto3
import json
import os
def lambda_handler(event, context):
# STS クライアント作成
sts_client = boto3.client('sts')
# 環境変数から develop アカウント側のロール ARN を取得
develop_role_arn = os.environ.get('DEVELOP_ROLE_ARN')
if not develop_role_arn:
return {
'statusCode': 500,
'body': json.dumps('DEVELOP_ROLE_ARN 環境変数が設定されていません')
}
# develop アカウント側のロールを assume する
try:
assumed_role = sts_client.assume_role(
RoleArn=develop_role_arn,
RoleSessionName="LambdaAssumeRoleSession"
)
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps(f'AssumeRole エラー: {str(e)}')
}
credentials = assumed_role['Credentials']
# develop アカウント(ap-northeast-1)用の Lambda クライアント作成
lambda_client = boto3.client(
'lambda',
region_name='ap-northeast-1',
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken']
)
# Lambda 関数一覧を取得
try:
response = lambda_client.list_functions()
functions = [func['FunctionName'] for func in response.get('Functions', [])]
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps(f'list_functions エラー: {str(e)}')
}
return {
'statusCode': 200,
'body': json.dumps(functions)
}
結果
親側でLambdaを実行すると以下のように表示されるはず。関数名は適当に。
Response:
{
"statusCode": 200,
"body": "[\"LambdaFunction1\", \"LambdaFunction2\"]"
}
所感
- 作ってみたらだいぶわかった
Shikoan's ML Blogの中の人が運営しているサークル「じゅ~しぃ~すくりぷと」の本のご案内
技術書コーナー
北海道の駅巡りコーナー