- 公開日
- 最終更新日
【SAM】Connectorを使ってIAMポリシーを楽に自動生成する
この記事を共有する
はじめに
パーソルサーバーワークスの嶋田です。
SAMを使ってサーバレスアプリケーションを手軽に開発することができますが、その中でも AWS::Serverless::Connector はぜひとも押さえておきたいです。
AWS::Serverless::Connector を活用することで、開発のテンポが悪くなるユーザー体験を低減できます。
具体的には、IAMポリシーの記述のことです。
本ブログでは、SAMの特徴である AWS::Serverless::Connector がなんたるかをご紹介します。
SAMプロジェクトのセットアップ
sam initして、以下の通りプロジェクトの初期化を行います。
$ sam init
You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.
Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1
Choose an AWS Quick Start application template
1 - Hello World Example
2 - Data processing
3 - Hello World Example with Powertools for AWS Lambda
4 - Multi-step workflow
5 - Scheduled task
6 - Standalone function
7 - Serverless API
8 - Infrastructure event management
9 - Lambda Response Streaming
10 - GraphQLApi Hello World Example
11 - Full Stack
12 - Lambda EFS example
13 - Serverless Connector Hello World Example
14 - Multi-step workflow with Connectors
15 - DynamoDB Example
16 - Machine Learning
Template: 1
Use the most popular runtime and package type? (python3.13 and zip) [y/N]: N
Which runtime would you like to use?
1 - dotnet8
2 - dotnet6
3 - go (provided.al2)
4 - go (provided.al2023)
5 - graalvm.java11 (provided.al2)
6 - graalvm.java17 (provided.al2)
7 - java21
8 - java17
9 - java11
10 - java8.al2
11 - nodejs22.x
12 - nodejs20.x
13 - nodejs18.x
14 - python3.9
15 - python3.8
16 - python3.13
17 - python3.12
18 - python3.11
19 - python3.10
20 - ruby3.3
21 - ruby3.2
22 - rust (provided.al2)
23 - rust (provided.al2023)
Runtime: 11
What package type would you like to use?
1 - Zip
2 - Image
Package type: 1
Based on your selections, the only dependency manager available is npm.
We will proceed copying the template using npm.
Select your starter template
1 - Hello World Example
2 - Hello World Example TypeScript
Template: 1
Would you like to enable X-Ray tracing on the function(s) in your application? [y/N]:
Would you like to enable monitoring using CloudWatch Application Insights?
For more info, please view https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/cloudwatch-application-insights.html [y/N]:
Would you like to set Structured Logging in JSON format on your Lambda functions? [y/N]:
Project name [sam-app]: sam-connector-tutorial
-----------------------
Generating application:
-----------------------
Name: sam-connector-tutorial
Runtime: nodejs22.x
Architectures: x86_64
Dependency Manager: npm
Application Template: hello-world
Output Directory: .
Configuration file: sam-connector-tutorial/samconfig.toml
Next steps can be found in the README file at sam-connector-tutorial/README.md
Commands you can use next
=========================
[*] Create pipeline: cd sam-connector-tutorial && sam pipeline init --bootstrap
[*] Validate SAM template: cd sam-connector-tutorial && sam validate
[*] Test Function in the Cloud: cd sam-connector-tutorial && sam sync --stack-name {stack-name} --watch
SAM CLI update available (1.136.0); (1.133.0 installed)
To download: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html
ワーキングディレクトリの変更
プロジェクトのルートディレクトリに移動します。
$ cd sam-connector-tutorial/
ディレクトリ構造
今回のテーマで扱うディレクトリ構造は以下の通りです。
$ tree . ├── README.md ├── events │ └── event.json ├── hello-world-from │ ├── app.mjs │ ├── package.json │ └── tests │ └── unit │ └── test-handler.mjs ├── hello-world-to │ ├── app.mjs │ ├── package.json │ └── tests │ └── unit │ └── test-handler.mjs ├── samconfig.toml └── template.yaml 7 directories, 10 files
sam initした際に自動生成される hello-worldディレクトリを複製して、fromとtoに改名しています。
hello-world-fromとhello-world-toの中身
複製したディレクトリ内にあるapp.mjsを以下の通り編集します。
// /hello-world-from/app.mjs
export const lambdaHandler = async (event, context) => {
const response = {
statusCode: 200,
body: JSON.stringify({
message: 'hello world from',
})
};
return response;
};
// /hello-world-to/app.mjs
export const lambdaHandler = async (event, context) => {
const response = {
statusCode: 200,
body: JSON.stringify({
message: 'hello world to',
})
};
return response;
};
SAMテンプレート
Lambda関数を2つ作成して、デプロイ時に関数URLがOutputされるようにします。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: sam-connector-tutorial
Globals:
Function:
Timeout: 3
Resources:
HelloWorldFunctionFrom:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world-from/
Environment:
Variables:
HELLO_WORLD_LAMBDA_NAME: !Ref HelloWorldFunctionTo
Handler: app.lambdaHandler
Runtime: nodejs22.x
Architectures:
- x86_64
FunctionUrlConfig:
AuthType: NONE
HelloWorldFunctionTo:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world-to/
Handler: app.lambdaHandler
Runtime: nodejs22.x
Architectures:
- x86_64
FunctionUrlConfig:
AuthType: NONE
Outputs:
HelloWorldFunctionFromUrl:
Value: !GetAtt HelloWorldFunctionFromUrl.FunctionUrl
HelloWorldFunctionToUrl:
Value: !GetAtt HelloWorldFunctionToUrl.FunctionUrl
Environmentは今使いませんが、あとで使うためテンプレートに記述しています。
build & deployの実施
sam build と sam deploy を実行します。
$ sam build && sam deploy
Starting Build use cache
==================長くなるのでビルドログは省略==================
Build Succeeded
Built Artifacts : .aws-sam/build
Built Template : .aws-sam/build/template.yaml
Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided
Managed S3 bucket: aws-sam-cli-managed-default-samclisourcebucket-0jsdlxcilt06
A different default S3 bucket can be set in samconfig.toml
Or by specifying --s3-bucket explicitly.
Uploading to d87616d8d8b78394047b40016c09df39 800013 / 800013 (100.00%)
Uploading to 2b085849564ee63f05f3c09caa95fcb7 800011 / 800011 (100.00%)
Deploying with following values
===============================
Stack name : sam-connector-tutorial
Region : ap-northeast-1
Confirm changeset : True
Disable rollback : False
Deployment s3 bucket : aws-sam-cli-managed-default-samclisourcebucket-0jsdlxcilt06
Capabilities : ["CAPABILITY_IAM"]
Parameter overrides : {}
Signing Profiles : {}
Initiating deployment
=====================
Uploading to 3b5ed25ea71cc1bbfc32e9b750775dc4.template 1169 / 1169 (100.00%)
Waiting for changeset to be created..
CloudFormation stack changeset
-----------------------------------------------------------------------------------------------------
Operation LogicalResourceId ResourceType Replacement
-----------------------------------------------------------------------------------------------------
* Modify HelloWorldFunctionFrom AWS::Lambda::Function False
* Modify HelloWorldFunctionTo AWS::Lambda::Function False
-----------------------------------------------------------------------------------------------------
Changeset created successfully. arn:aws:cloudformation:ap-northeast-1:123456789101:changeSet/samcli-deploy1743516711/d5cc977b-54eb-41f2-9dbc-52dc98ad699e
Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]: y
2025-04-01 14:12:21 - Waiting for stack create/update to complete
CloudFormation events from stack operations (refresh every 5.0 seconds)
-----------------------------------------------------------------------------------------------------
ResourceStatus ResourceType LogicalResourceId ResourceStatusReason
-----------------------------------------------------------------------------------------------------
UPDATE_IN_PROGRESS AWS::CloudFormation::St sam-connector-tutorial User Initiated
ack
UPDATE_IN_PROGRESS AWS::Lambda::Function HelloWorldFunctionFrom -
UPDATE_IN_PROGRESS AWS::Lambda::Function HelloWorldFunctionTo -
UPDATE_COMPLETE AWS::Lambda::Function HelloWorldFunctionFrom -
UPDATE_COMPLETE AWS::Lambda::Function HelloWorldFunctionTo -
UPDATE_COMPLETE_CLEANUP AWS::CloudFormation::St sam-connector-tutorial -
_IN_PROGRESS ack
UPDATE_COMPLETE AWS::CloudFormation::St sam-connector-tutorial -
ack
-----------------------------------------------------------------------------------------------------
CloudFormation outputs from deployed stack
--------------------------------------------------------------------------------------------------------
Outputs
--------------------------------------------------------------------------------------------------------
Key HelloWorldFunctionFromUrl
Description -
Value https://bd2hjjzpbm432ogigmsg6dqs7y0xuttz.lambda-url.ap-northeast-1.on.aws/
Key HelloWorldFunctionToUrl
Description -
Value https://sqwuoc75heipera6v5cmbyvw5u0osfum.lambda-url.ap-northeast-1.on.aws/
--------------------------------------------------------------------------------------------------------
Successfully created/updated stack - sam-connector-tutorial in ap-northeast-1
ビルドとデプロイの実行が完了したら、curlでLambdaが実行可能か確認します。
$ curl https://bd2hjjzpbm432ogigmsg6dqs7y0xuttz.lambda-url.ap-northeast-1.on.aws/
{"message":"hello world from"}
$ curl https://sqwuoc75heipera6v5cmbyvw5u0osfum.lambda-url.ap-northeast-1.on.aws/
{"message":"hello world to"}
上記のように結果が表示されていればここまでの作業は問題なしです。
hello-world-from から hello-world-to を呼び出す
hello-world-fromのapp.mjsを以下のように書き換えます。
// /hello-world-from/app.mjs
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda'
const lambdaClient = new LambdaClient({})
export const lambdaHandler = async (event, context) => {
try {
const command = new InvokeCommand({
FunctionName: process.env.HELLO_WORLD_LAMBDA_NAME
})
const result = await lambdaClient.send(command)
const json = JSON.parse(Buffer.from(result.Payload, 'base64').toString('utf8'))
const response = {
statusCode: 200,
body: JSON.stringify({
message: json.body.message,
})
};
return response;
} catch (error) {
console.error("Exception:", error)
const response = {
statusCode: 400,
body: JSON.stringify({
message: error.message,
})
};
return response;
}
};
書き換えたら再度 sam build と sam deploy を実行します。
※2度目の作業のため、ログは割愛します
$ sam build && sam deploy
hello-world-fromの関数URLにcurlを実行します。
$ curl https://bd2hjjzpbm432ogigmsg6dqs7y0xuttz.lambda-url.ap-northeast-1.on.aws/
{"message":"User: arn:aws:sts::123456789101:assumed-role/sam-connector-tutorial-HelloWorldFunctionFromRole-WqXVb7EgqmUE/sam-connector-tutorial-HelloWorldFunctionFrom-VyLUTXHJl63a is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:ap-northeast-1:123456789101:function:sam-connector-tutorial-HelloWorldFunctionTo-e0xtRUPa2PgR because no identity-based policy allows the lambda:InvokeFunction action"}
because no identity-based policy allows the lambda:InvokeFunction action
InvokeFunctionをAllowする必要がありますね。ここまではお約束だと思います。
このあと、IAM PolicyとIAM Roleの定義をテンプレート内で記述して、デプロイ..は今回しません。
SAMにはもっと便利なものがあります。今回の主役 AWS::Serverless::Connector の登場です。
AWS::Serverless::Connector をテンプレートに記述する
SAMテンプレートを以下のように書き換えます。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: sam-connector-tutorial
Globals:
Function:
Timeout: 3
Resources:
HelloWorldFunctionFrom:
Type: AWS::Serverless::Function
# ここから新しく追加
Connectors:
ConnectorsTutorial:
Properties:
Destination:
Id: HelloWorldFunctionTo
Permissions:
- Write
# ここまで新しく追加
Properties:
CodeUri: hello-world-from/
Environment:
Variables:
HELLO_WORLD_LAMBDA_NAME: !Ref HelloWorldFunctionTo
Handler: app.lambdaHandler
Runtime: nodejs22.x
Architectures:
- x86_64
FunctionUrlConfig:
AuthType: NONE
HelloWorldFunctionTo:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello-world-to/
Handler: app.lambdaHandler
Runtime: nodejs22.x
Architectures:
- x86_64
FunctionUrlConfig:
AuthType: NONE
Outputs:
# ここから新しく追加
HelloWorldFunctionFromRoleName:
Value: !Ref HelloWorldFunctionFromRole
# ここまで新しく追加
HelloWorldFunctionFromUrl:
Value: !GetAtt HelloWorldFunctionFromUrl.FunctionUrl
HelloWorldFunctionToUrl:
Value: !GetAtt HelloWorldFunctionToUrl.FunctionUrl
書き換えたら再度 sam build と sam deploy を実行します。
※ログは割愛
$ sam build && sam deploy
なお、AWS::Serverless::Connector の記述は以下のようにも書けるようです。
MyConnector:
Type: AWS::Serverless::Connector
Properties:
Source:
Id: MyFunction
Destination:
Id: MyTable
Permissions:
- Read
- Write
関数URLにcurlを実行する
$ curl https://bd2hjjzpbm432ogigmsg6dqs7y0xuttz.lambda-url.ap-northeast-1.on.aws/
{"message":"hello world to"}
hello world to と表示されていることから、hello-world-toのLambdaを呼び出せていることが伺えます。
なお、Write ではなく Read で最初試してみましたが、以下のようなエラーが出力されました。
Error: Failed to create changeset for the stack: sam-connector-tutorial, ex: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state: For expression "Status" we matched expected path: "FAILED" Status: FAILED. Reason: Transform AWS::Serverless-2016-10-31 failed with: Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [HelloWorldFunctionFromConnectorsTutorial] is invalid. Unsupported 'Permissions' provided for connector from AWS::Lambda::Function to AWS::Lambda::Function; valid values are: Write.
メッセージを要約すると、AWS::Lambda::Function に対しては Write のみ許可されている。とのことです。親切なメッセージかと思います。
AWS::Serverless::Connector 補足
今回はLambdaからLambdaをInvokeするシナリオでDEMOしていますが、DynamoDBなどの他のAWSサービスに対しても有効なようです。仔細が気になる方はドキュメントをご確認ください。
※全てのAWSリソースをConnectorによってカバーできるわけではないです
なお、内部の動き的には以下のリソースが生成されるようです。
AWS::Serverless::Connectorを指定すると生成されるAWS CloudFormationリソース
実際にどんな権限がついているのかを確認する
SAMテンプレートのOutputsにRoleの名前を出すよう記述しているので、Roleの名前を指定してアタッチされているPolicyを取得します。
$ aws iam list-attached-role-policies --role-name sam-connector-tutorial-HelloWorldFunctionFromRole-WqXVb7EgqmUE
{
"AttachedPolicies": [
{
"PolicyName": "AWSLambdaBasicExecutionRole",
"PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
},
{
"PolicyName": "sam-connector-tutorial-HelloWorldFunctionFromConnectorsTutorialPolicy-BM6QDyIJTq3w",
"PolicyArn": "arn:aws:iam::123456789101:policy/sam-connector-tutorial-HelloWorldFunctionFromConnectorsTutorialPolicy-BM6QDyIJTq3w"
}
]
}
PolicyArnを控えたら、以下のコマンドを実行します。
$ aws iam get-policy-version --policy-arn arn:aws:iam::123456789101:policy/sam-connector-tutorial-HelloWorldFunctionFromConnectorsTutorialPolicy-BM6QDyIJTq3w --version-id v1 --query "PolicyVersion.Document" --output json
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"lambda:InvokeAsync",
"lambda:InvokeFunction"
],
"Resource": [
"arn:aws:lambda:ap-northeast-1:123456789101:function:sam-connector-tutorial-HelloWorldFunctionTo-e0xtRUPa2PgR"
],
"Effect": "Allow"
}
]
}
しっかりポリシーが付与されていますね。
おわりに
最後に、sam deleteを忘れずに実行して後片付けをしましょう。
$ sam delete
Are you sure you want to delete the stack sam-connector-tutorial in the region ap-northeast-1 ? [y/N]: y
Do you want to delete the template file 108b3c0252fc1977934c0e0775994d45.template in S3? [y/N]: y
- Deleting S3 object with key 7969a25eeb294597da07a7e2d1e77d2b
- Deleting S3 object with key 2b085849564ee63f05f3c09caa95fcb7
- Deleting S3 object with key 108b3c0252fc1977934c0e0775994d45.template
- Deleting Cloudformation stack sam-connector-tutorial
Deleted successfully
InvokeFunctionだけあれば今回のシナリオでは十分なため、細かいことを言えば最小権限の原則にはなっていないと思いますが、実装の容易さからして、テンプレートを書いたことのない人であっても動かすまでのハードルは下がるのではないでしょうか。
ここまでお付き合いいただきまして、ありがとうございました。
おまけ
InvokeAsyncについて
InvokeAsyncアクションは初めてみかけたんですが、どうやら非推奨になっているようです。
昔はこのアクションをAllowして、非同期実行を実現していたようです。
この記事は私が書きました
嶋田 龍登
記事一覧インフラからアプリのことまでなんでもやりたい、フルスタックを目指すエンジニア