AWS Application Composerとは?
AWS Application ComposerとはAWSリソースのサーバーレスアプリケーションの設計とデプロイメントを簡素化するためのビジュアルツールです。
公式サイトはこちら
ビジュアルデザインの直感的な操作と自動生成されるテンプレートにより、開発プロセスが簡素化され、スケーラブルで安定したサーバーレスアプリケーションの構築が容易になります。
またApplication Composer自体の追加料金は発生しない(デプロイ等してリソースを作成すればそのリソース分の料金はかかります)のでライトに使用できますね。
今回はこの Application Composerを使ってtemplate.yamlの構成を視覚的に確認してみたいと思います。
実際に使ってみた
今回は既存のtemplate.yamlをAWS Application Composerに読み込ませてみます。
コード(長いので折りたたんでいます、クリックしてください)
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: >
SAM Template for an API Gateway with multiple Lambda functions, multiple S3 buckets, DynamoDB table, and CORS settings.
Globals:
Function:
Timeout: 30
Resources:
MyApi:
Type: 'AWS::Serverless::Api'
Properties:
Name: MyApi
StageName: Prod
Auth:
DefaultAuthorizer: CognitoAuthorizer
Authorizers:
CognitoAuthorizer:
UserPoolArn: !Sub arn:aws:cognito-idp:ap-northeast-1:123456789012:userpool/ap-northeast-1_7mtCnXmWT
Cors:
AllowMethods: "'OPTIONS,GET,POST'"
AllowHeaders: "'Content-Type,Authorization'"
AllowOrigin: "'*'"
# S3 Buckets
MyS3Bucket1:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: my-s3-bucket1-${AWS::AccountId}
AccessControl: Private
MyS3Bucket2:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: my-s3-bucket2-${AWS::AccountId}
AccessControl: Private
MyS3Bucket3:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: my-s3-bucket3-${AWS::AccountId}
AccessControl: Private
MyS3Bucket4:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: my-s3-bucket4-${AWS::AccountId}
AccessControl: Private
# DynamoDB Table
MyDynamoDBTable:
Type: 'AWS::DynamoDB::Table'
Properties:
TableName: MyDynamoDBTable
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
# Lambda Functions
MyLambdaFunction1:
Type: 'AWS::Serverless::Function'
Properties:
Handler: index.handler1
Runtime: python3.12
CodeUri: .
Environment:
Variables:
S3_BUCKET: !Ref MyS3Bucket1
DYNAMO_TABLE: !Ref MyDynamoDBTable
Policies:
- S3ReadPolicy:
BucketName: !Ref MyS3Bucket1
- S3WritePolicy:
BucketName: !Ref MyS3Bucket1
- DynamoDBCrudPolicy:
TableName: !Ref MyDynamoDBTable
Events:
GetResource1:
Type: Api
Properties:
Path: /resource1
Method: get
RestApiId: !Ref MyApi
MyLambdaFunction2:
Type: 'AWS::Serverless::Function'
Properties:
Handler: index.handler2
Runtime: python3.12
CodeUri: .
Environment:
Variables:
S3_BUCKET: !Ref MyS3Bucket2
DYNAMO_TABLE: !Ref MyDynamoDBTable
Policies:
- S3ReadPolicy:
BucketName: !Ref MyS3Bucket2
- S3WritePolicy:
BucketName: !Ref MyS3Bucket2
- DynamoDBCrudPolicy:
TableName: !Ref MyDynamoDBTable
Events:
GetResource2:
Type: Api
Properties:
Path: /resource2
Method: get
RestApiId: !Ref MyApi
MyLambdaFunction3:
Type: 'AWS::Serverless::Function'
Properties:
Handler: index.handler3
Runtime: python3.12
CodeUri: .
Environment:
Variables:
S3_BUCKET: !Ref MyS3Bucket3
DYNAMO_TABLE: !Ref MyDynamoDBTable
Policies:
- S3ReadPolicy:
BucketName: !Ref MyS3Bucket3
- S3WritePolicy:
BucketName: !Ref MyS3Bucket3
- DynamoDBCrudPolicy:
TableName: !Ref MyDynamoDBTable
Events:
GetResource3:
Type: Api
Properties:
Path: /resource3
Method: get
RestApiId: !Ref MyApi
MyLambdaFunction4:
Type: 'AWS::Serverless::Function'
Properties:
Handler: index.handler4
Runtime: python3.12
CodeUri: .
Environment:
Variables:
S3_BUCKET: !Ref MyS3Bucket4
DYNAMO_TABLE: !Ref MyDynamoDBTable
Policies:
- S3ReadPolicy:
BucketName: !Ref MyS3Bucket4
- S3WritePolicy:
BucketName: !Ref MyS3Bucket4
- DynamoDBCrudPolicy:
TableName: !Ref MyDynamoDBTable
Events:
GetResource4:
Type: Api
Properties:
Path: /resource4
Method: get
RestApiId: !Ref MyApi
# IAM Role for Lambda
MyLambdaRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: MyLambdaPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: 'arn:aws:logs:*:*:*'
- Effect: Allow
Action:
- cognito-idp:ListUsers
Resource: '*'
Outputs:
ApiUrl:
Description: "URL for the API"
Value: !Sub "https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod"
S3Bucket1Name:
Description: "Name of the S3 bucket 1"
Value: !Ref MyS3Bucket1
S3Bucket2Name:
Description: "Name of the S3 bucket 2"
Value: !Ref MyS3Bucket2
S3Bucket3Name:
Description: "Name of the S3 bucket 3"
Value: !Ref MyS3Bucket3
S3Bucket4Name:
Description: "Name of the S3 bucket 4"
Value: !Ref MyS3Bucket4
DynamoDBTableName:
Description: "Name of the DynamoDB Table"
Value: !Ref MyDynamoDBTable
 
かなり長くて分かりづらいですね(笑)
このコードを読ませてみましょう。
コンソールからApplication Composerのホーム画面に飛び、プロジェクトの作成をクリックします

メニューから開く→テンプレートファイルを選択します

先ほどのtemplate.yamlを読み込ませてあげるとリソースの構成が確認できます

想定通り、4つのLambdaに各S3バケットが一つずつ接続されて、DynamoDBは全ての関数に接続されています。
なぜかDynamoDBがS3バケットの間に挟まっちゃっているので少し調整しました

個人的にはこちらの方が見やすいと感じました。
よく見るとAPI GatewayのAuthorizerがどこにもつながってなくて「!」とエラーが出ています。
こちらをCognitoのユーザープールと接続してみましょう。
右側のリソースから「cognito」と検索しCognito UserPoolをドラッグさせてみましょう。

Authorizorの点とUser Poolの点を繋ぎ合わせたら修正完了です。

修正すると、テンプレートも更新されていました!

このテンプレートファイルはメニューから「テンプレートファイルを保存」で入手できます。
修正したコード(長いので折りたたんでいます、クリックしてください)
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: |
SAM Template for an API Gateway with multiple Lambda functions, multiple S3 buckets, DynamoDB table, and CORS settings.
Globals:
Function:
Timeout: 30
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
Name: MyApi
StageName: Prod
Auth:
DefaultAuthorizer: CognitoAuthorizer
Authorizers:
CognitoAuthorizer:
UserPoolArn: !GetAtt UserPool.Arn
Cors:
AllowMethods: '''OPTIONS,GET,POST'''
AllowHeaders: '''Content-Type,Authorization'''
AllowOrigin: '''*'''
# S3 Buckets
MyS3Bucket1:
Type: AWS::S3::Bucket
Properties:
BucketName: my-s3-bucket1-${AWS::AccountId}
AccessControl: Private
MyS3Bucket2:
Type: AWS::S3::Bucket
Properties:
BucketName: my-s3-bucket2-${AWS::AccountId}
AccessControl: Private
MyS3Bucket3:
Type: AWS::S3::Bucket
Properties:
BucketName: my-s3-bucket3-${AWS::AccountId}
AccessControl: Private
MyS3Bucket4:
Type: AWS::S3::Bucket
Properties:
BucketName: my-s3-bucket4-${AWS::AccountId}
AccessControl: Private
# DynamoDB Table
MyDynamoDBTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: MyDynamoDBTable
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 5
WriteCapacityUnits: 5
# Lambda Functions
MyLambdaFunction1:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler1
Runtime: python3.12
CodeUri: .
Environment:
Variables:
S3_BUCKET: !Ref MyS3Bucket1
DYNAMO_TABLE: !Ref MyDynamoDBTable
Policies:
- S3ReadPolicy:
BucketName: !Ref MyS3Bucket1
- S3WritePolicy:
BucketName: !Ref MyS3Bucket1
- DynamoDBCrudPolicy:
TableName: !Ref MyDynamoDBTable
Events:
GetResource1:
Type: Api
Properties:
Path: /resource1
Method: get
RestApiId: !Ref MyApi
MyLambdaFunction2:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler2
Runtime: python3.12
CodeUri: .
Environment:
Variables:
S3_BUCKET: !Ref MyS3Bucket2
DYNAMO_TABLE: !Ref MyDynamoDBTable
Policies:
- S3ReadPolicy:
BucketName: !Ref MyS3Bucket2
- S3WritePolicy:
BucketName: !Ref MyS3Bucket2
- DynamoDBCrudPolicy:
TableName: !Ref MyDynamoDBTable
Events:
GetResource2:
Type: Api
Properties:
Path: /resource2
Method: get
RestApiId: !Ref MyApi
MyLambdaFunction3:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler3
Runtime: python3.12
CodeUri: .
Environment:
Variables:
S3_BUCKET: !Ref MyS3Bucket3
DYNAMO_TABLE: !Ref MyDynamoDBTable
Policies:
- S3ReadPolicy:
BucketName: !Ref MyS3Bucket3
- S3WritePolicy:
BucketName: !Ref MyS3Bucket3
- DynamoDBCrudPolicy:
TableName: !Ref MyDynamoDBTable
Events:
GetResource3:
Type: Api
Properties:
Path: /resource3
Method: get
RestApiId: !Ref MyApi
MyLambdaFunction4:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler4
Runtime: python3.12
CodeUri: .
Environment:
Variables:
S3_BUCKET: !Ref MyS3Bucket4
DYNAMO_TABLE: !Ref MyDynamoDBTable
Policies:
- S3ReadPolicy:
BucketName: !Ref MyS3Bucket4
- S3WritePolicy:
BucketName: !Ref MyS3Bucket4
- DynamoDBCrudPolicy:
TableName: !Ref MyDynamoDBTable
Events:
GetResource4:
Type: Api
Properties:
Path: /resource4
Method: get
RestApiId: !Ref MyApi
# IAM Role for Lambda
MyLambdaRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: MyLambdaPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- Effect: Allow
Action:
- cognito-idp:ListUsers
Resource: '*'
UserPool:
Type: AWS::Cognito::UserPool
Properties:
AdminCreateUserConfig:
AllowAdminCreateUserOnly: false
AliasAttributes:
- email
- preferred_username
UserPoolName: !Sub ${AWS::StackName}-UserPool
Outputs:
ApiUrl:
Description: URL for the API
Value: !Sub https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/Prod
S3Bucket1Name:
Description: Name of the S3 bucket 1
Value: !Ref MyS3Bucket1
S3Bucket2Name:
Description: Name of the S3 bucket 2
Value: !Ref MyS3Bucket2
S3Bucket3Name:
Description: Name of the S3 bucket 3
Value: !Ref MyS3Bucket3
S3Bucket4Name:
Description: Name of the S3 bucket 4
Value: !Ref MyS3Bucket4
DynamoDBTableName:
Description: Name of the DynamoDB Table
Value: !Ref MyDynamoDBTable
最後に
自分はtemplateを作成するのが苦手で、何かいい方法がないかと探した際にApplication Composerを見つけました。
まだ対応してないリソース等もあるみたいですが勉強目的だとすごく有効的なツールだなと思いました!
皆さんもぜひ使ってみてください、ここまで読んでくださりありがとうございました。