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を見つけました。
まだ対応してないリソース等もあるみたいですが勉強目的だとすごく有効的なツールだなと思いました!
皆さんもぜひ使ってみてください、ここまで読んでくださりありがとうございました。