ENGINEER BLOG ENGINEER BLOG
  • 公開日
  • 最終更新日

【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 buildsam 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 buildsam 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 buildsam 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して、非同期実行を実現していたようです。

この記事は私が書きました

嶋田 龍登

記事一覧

インフラからアプリのことまでなんでもやりたい、フルスタックを目指すエンジニア

嶋田 龍登

この記事を共有する

クラウドのご相談

CONTACT

クラウド導入や運用でお悩みの方は、お気軽にご相談ください。
専門家がサポートします。

サービス資料ダウンロード

DOWNLOAD

ビジネスをクラウドで加速させる準備はできていますか?
今すぐサービス資料をダウンロードして、詳細をご確認ください。