2025/03/29

サーバーレスアーキテクチャ

一般的なサーバーレスアーキテクチャ構成
(サーバーレスアーキテクチャというのは、サーバーがないというわけではなく、サーバーを意識しなくてもよいアーキテクチャのこと。)
CloudFormationで作成してみると、こんな具合。
```
AWSTemplateFormatVersion: 2010-09-09
Description: The base template for creating stack.
# -------------------------
# Metadata 
# -------------------------
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Common Settings.
        Parameters:
          - Cost
          - Domain
      - Label:
          default: Local Settings.
        Parameters:
          - ACMArn

# -------------------------
# <<< Parameters 
# -------------------------
Parameters: 
  Cost:
    Type: String

  Domain:
    Type: String

  ACMArn:
    Type: String
    Description: Used in CloudFront.

# -------------------------
# Resources
# -------------------------
Resources:
  # -------------------------
  # フロントエンドセクション
  # -----

  # OAIを作成
  OAI:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: !Ref AWS::StackName

  # 静的Web用バケット
  S3Bucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: svlswebbucket
      WebsiteConfiguration:
        IndexDocument: index.html
        ErrorDocument: error.html
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # OAI用バケットポリシー
  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref S3Bucket
      PolicyDocument:
        Statement:
          - Sid: 1
            Effect: Allow
            Action:
              - s3:GetObject
            Resource: !Sub arn:aws:s3:::${S3Bucket}/*
            Principal: 
              AWS: !Sub
                - arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${OAIId}
                - OAIId: {'Fn::ImportValue': 'svls:oai:id'}

  # CloudFrontを使用
  Distribution:
    Type: "AWS::CloudFront::Distribution"
    Properties:
      DistributionConfig: 
        Aliases: 
          - "www.naopapa.xyz"
        Origins: 
          - 
            ConnectionAttempts: 3
            ConnectionTimeout: 10
            DomainName: !ImportValue "svls:s3:rdn"
            Id: "S3Origin"
            OriginPath: ""
            S3OriginConfig: 
              OriginAccessIdentity: !Sub
                - "origin-access-identity/cloudfront/${OAIId}"
                - OAIId: {'Fn::ImportValue': 'svls:oai:id'}
        DefaultCacheBehavior: 
          AllowedMethods: 
            - "HEAD"
            - "GET"
          CachedMethods: 
            - "HEAD"
            - "GET"
          Compress: false
          DefaultTTL: 86400
          ForwardedValues: 
            Cookies: 
                Forward: "none"
            QueryString: false
          MaxTTL: 31536000
          MinTTL: 0
          SmoothStreaming: false
          TargetOriginId: "S3Origin"
          ViewerProtocolPolicy: "redirect-to-https"
        Comment: !Sub "${AWS::StackName} distribution"
        PriceClass: "PriceClass_All"
        Enabled: true
        ViewerCertificate: 
          AcmCertificateArn: !Ref ACMArn
          #! CloudFrontDefaultCertificate: false 
          MinimumProtocolVersion: "TLSv1.2_2021"
          SslSupportMethod: "sni-only"
        Restrictions: 
          GeoRestriction: 
            RestrictionType: "none"
        HttpVersion: "http1.1"
        DefaultRootObject: "index.html"
        IPV6Enabled: true
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # -------------------------
  # 認証セクション
  # -----

  # ログ記録用のLambda関数にアタッチするロール
  AfterAuthRole:
    Type: "AWS::IAM::Role"
    Properties:
      Path: "/"
      RoleName: svls-lambda-auth-role
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument: 
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      Policies:
        - PolicyName: "CognitoAfterAuthPolicy"
          PolicyDocument: 
            Version: "2012-10-17"
            Statement: 
              - Effect: "Allow"
                Action:
                  - "dynamodb:PutItem"
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource: "*"
      PermissionsBoundary: !Sub 'arn:aws:iam::${AWS::AccountId}:policy/DevelopUserBoundary'

  # ログ記録用のLambda関数
  AfterAuthFunc:
    Type: AWS::Lambda::Function
    DependsOn: AfterAuthRole
    Properties:
      Runtime: python3.12
      Role: !GetAtt AfterAuthRole.Arn
      Handler: index.handler
      FunctionName: svls-auth-func
      Code:
        ZipFile: | 
          import boto3
          import json
          from datetime import datetime
          
          # define the DynamoDB table that Lambda will connect to
          tableName = "svls-tbl-login"
          
          dynamo = boto3.client('dynamodb')
          
          def handler(event, context):
              # Send post authentication data to Cloudwatch logs
              print ("Authentication successful")
              print (json.dumps(event))
              
              email = event['request']['userAttributes']['email']
              date_s = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
          
              item = {
                "id": { 'S': date_s }, 
                "login": { 'S': email }
              }
              itemJSON = json.dumps(item)
              print('itemJSON=' + itemJSON)    
              dynamo.put_item(TableName=tableName, Item=item)
          
              # Return to Amazon Cognito
              return event
          
      Timeout: 3
      MemorySize: 128
      EphemeralStorage: 
        Size: 512
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # Cognitoユーザープール
  UserPool:
    Type: "AWS::Cognito::UserPool"
    Properties:
      UserPoolName: svls-user-pool
      UsernameAttributes:
        - email
      #! AliasAttributes: 
      #!   - email
      AutoVerifiedAttributes:
        - email
      UserAttributeUpdateSettings:
        AttributesRequireVerificationBeforeUpdate:
         - email
      Schema:
        - Name: email
          AttributeDataType: String
          Mutable: false
          Required: true
      UsernameConfiguration: 
        CaseSensitive: false
      MfaConfiguration: "OFF"
      EmailConfiguration: 
        EmailSendingAccount: "COGNITO_DEFAULT"
      AccountRecoverySetting:
        RecoveryMechanisms:
          - Name: verified_email
            Priority: 1
      AdminCreateUserConfig:
        AllowAdminCreateUserOnly: false
      VerificationMessageTemplate: 
        DefaultEmailOption: "CONFIRM_WITH_CODE"
      LambdaConfig: 
        PostAuthentication: !ImportValue 'svls:lambda:auth:arn'
      UserPoolTags:
        Cost: !Ref Cost

  # Cognitoユーザープールクライアント
  UserPoolClient:
    Type: "AWS::Cognito::UserPoolClient"
    DependsOn: UserPool
    Properties:
      ClientName: svls-apl-client
      GenerateSecret: false
      UserPoolId: !Ref UserPool
      ExplicitAuthFlows: 
        - ALLOW_REFRESH_TOKEN_AUTH
        - ALLOW_USER_SRP_AUTH

  # CognitoIDプール(Cognito Federated Identities)
  IdentityPool:
    Type: "AWS::Cognito::IdentityPool"
    DependsOn: UserPoolClient
    Properties:
      AllowClassicFlow: false
      IdentityPoolName: svls-id-pool
      AllowUnauthenticatedIdentities: false
      CognitoIdentityProviders: 
        - ClientId: !Ref UserPoolClient
          ProviderName: !GetAtt UserPool.ProviderName

  # role for authorized access to AWS resources
  CognitoAuthorizedRole:
    Type: "AWS::IAM::Role"
    DependsOn: IdentityPool
    Properties:
      RoleName: "svls-role-cog-idpool"
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal: 
              Federated: "cognito-identity.amazonaws.com"
            Action: 
              - "sts:AssumeRoleWithWebIdentity"
            Condition:
              StringEquals: 
                "cognito-identity.amazonaws.com:aud": !Ref IdentityPool
              "ForAnyValue:StringLike":
                "cognito-identity.amazonaws.com:amr": authenticated
      Policies:
        - PolicyName: "CognitoAuthorizedPolicy"
          PolicyDocument: 
            Version: "2012-10-17"
            Statement: 
              - Effect: "Allow"
                Action:
                  - "cognito-identity:GetCredentialsForIdentity"
                Resource: "*"
      PermissionsBoundary: !Sub 'arn:aws:iam::${AWS::AccountId}:policy/DevelopUserBoundary'
  
  # Attach role to the Identity Pool
  IdentityPoolRoleMapping:
    Type: "AWS::Cognito::IdentityPoolRoleAttachment"
    DependsOn: 
      - IdentityPool
      - CognitoAuthorizedRole
    Properties:
      IdentityPoolId: !Ref IdentityPool
      Roles:
        authenticated: !GetAtt CognitoAuthorizedRole.Arn
      
  # CognitoユーザープールへLambda関数の実行を許可
  LambdaPermission:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !ImportValue 'svls:lambda:auth:arn'
      Principal: "cognito-idp.amazonaws.com"
      SourceArn: !ImportValue 'svls:cognito:userpool:arn'

  # -------------------------
  # REST APIセクション
  # -----

  # API Gatewayを作成
  RestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: SvlsRestApi
      EndpointConfiguration: 
        Types: 
         - REGIONAL
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # Resource
  ApiGatewayResource:
    Type: "AWS::ApiGateway::Resource"
    Properties:
      RestApiId: !Ref RestApi
      PathPart: "SvlsPost"
      ParentId: !GetAtt RestApi.RootResourceId

  # メソッド認可にCognitoユーザープール認証を使用
  Authorizer:
    Type: AWS::ApiGateway::Authorizer
    Properties:
      Name: CognitoAuthorizer
      RestApiId: !ImportValue 'svls:restapi:id'
      Type: COGNITO_USER_POOLS
      IdentitySource: method.request.header.Authorization
      ProviderARNs: 
       - !ImportValue 'svls:cognito:userpool:arn'

  # SNSにアタッチするロール
  SNSRole:
    Type: "AWS::IAM::Role"
    Properties:
      Path: "/"
      RoleName: svls-sns-role
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument: 
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service:
                - sns.amazonaws.com
      Policies:
        - PolicyName: "SvlsSNSPolicy"
          PolicyDocument: 
            Version: "2012-10-17"
            Statement: 
              - Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                  - "logs:PutMetricFilter"
                  - "logs:PutRetentionPolicy"
                Resource: "*"
      PermissionsBoundary: !Sub 'arn:aws:iam::${AWS::AccountId}:policy/DevelopUserBoundary'

  # SNSトピックを作成
  SNSTopic:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: ''
      TopicName: svls-topic
      DeliveryStatusLogging:
       - Protocol: sqs
         SuccessFeedbackSampleRate: 100
         SuccessFeedbackRoleArn: !GetAtt SNSRole.Arn
         FailureFeedbackRoleArn: !GetAtt SNSRole.Arn
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # 投稿用のLambda関数にアタッチするロール
  SNSPublishRole:
    Type: "AWS::IAM::Role"
    Properties:
      Path: "/"
      RoleName: svls-lambda-publish-role
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument: 
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      Policies:
        - PolicyName: "CognitoAfterAuthPolicy"
          PolicyDocument: 
            Version: "2012-10-17"
            Statement: 
              - Effect: "Allow"
                Action:
                  - "sns:Publish"
                Resource: !ImportValue 'svls:topic:arn'
              - Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource: "*"
      PermissionsBoundary: !Sub 'arn:aws:iam::${AWS::AccountId}:policy/DevelopUserBoundary'

  # 投稿用のLambda関数を作成
  SNSPublishFunc:
    Type: AWS::Lambda::Function
    DependsOn: SNSPublishRole
    Properties:
      Runtime: python3.12
      Role: !GetAtt SNSPublishRole.Arn
      Handler: index.handler
      FunctionName: svls-publish-func
      Environment:
        Variables: 
          TOPIC_ARN: !ImportValue 'svls:topic:arn'
      Code:
        ZipFile: | 
          import json
          import boto3
          import os
          
          TOPICARN = os.environ['TOPIC_ARN']
          client = boto3.client('sns')
          
          def handler(event, context):
              # Access the form data from the event
              userMsg = event['message']
          
              # Do something with the data (e.g., store it in a database)
              dict = {
                'default': 'default message',
                'sqs': {
                  'entry': {
                      'message': userMsg,
                  }
                }
              }
              dict["sqs"] = json.dumps(dict["sqs"])
              messageJSON = json.dumps(dict)
              request = {
                 'TargetArn': TOPICARN,
                 'Message': messageJSON,
                 'MessageStructure': 'json',
                 'MessageAttributes': {
                     'event_type': {
                         'DataType': 'String',
                         'StringValue': 'entry',
                     },
                 },
              }
              response = client.publish(**request)
          
              # Return a response
              return {
                  'statusCode': 200,
                  'body': 'Message(' + userMsg + ') published successfully!'
              }
          
      Timeout: 3
      MemorySize: 128
      EphemeralStorage: 
        Size: 512
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # ApiGatewayをLambda関数のトリガーに追加
  LambdaPermission:
    Type: "AWS::Lambda::Permission"
    Properties:
      Action: "lambda:InvokeFunction"
      FunctionName: !ImportValue 'svls:lambda:publish:arn'
      Principal: "apigateway.amazonaws.com"
      SourceArn: !Sub
        - "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApiId}/*/POST/SvlsPost"
        - RestApiId: {'Fn::ImportValue': 'svls:restapi:id'}

  # 投稿用POSTメソッド
  ApiGatewayPostMethod:
    Type: "AWS::ApiGateway::Method"
    Properties:
      RestApiId: !ImportValue 'svls:restapi:id'
      ResourceId: !ImportValue 'svls:restapi:resourceid'
      HttpMethod: POST
      AuthorizationType: COGNITO_USER_POOLS
      AuthorizerId: !ImportValue 'svls:authorizer:id'
      ApiKeyRequired: false
      RequestParameters: {}
      MethodResponses: 
        - 
          ResponseModels: 
            "application/json": "Empty"
          ResponseParameters: 
            "method.response.header.Access-Control-Allow-Origin": false
          StatusCode: "200"
      Integration: 
        CacheNamespace: !ImportValue 'svls:restapi:resourceid'
        ContentHandling: "CONVERT_TO_TEXT"
        IntegrationHttpMethod: "POST"
        IntegrationResponses: 
          - 
            ResponseParameters: 
                "method.response.header.Access-Control-Allow-Origin": "'*'"
            SelectionPattern: ""
            StatusCode: "200"
        PassthroughBehavior: WHEN_NO_MATCH
        TimeoutInMillis: 29000
        Type: "AWS"
        Uri: !Sub
          - "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunctionArn}/invocations"
          - LambdaFunctionArn: {'Fn::ImportValue': 'svls:lambda:publish:arn'}

  # OPTIONSメソッド、CORS絡みだったような
  ApiGatewayOptionsMethod:
    Type: "AWS::ApiGateway::Method"
    Properties:
      RestApiId: !ImportValue 'svls:restapi:id'
      ResourceId: !ImportValue 'svls:restapi:resourceid'
      HttpMethod: "OPTIONS"
      AuthorizationType: "NONE"
      ApiKeyRequired: false
      RequestParameters: {}
      MethodResponses: 
        - 
          ResponseModels: 
              "application/json": "Empty"
          ResponseParameters: 
              "method.response.header.Access-Control-Allow-Headers": false
              "method.response.header.Access-Control-Allow-Methods": false
              "method.response.header.Access-Control-Allow-Origin": false
          StatusCode: "200"
      Integration: 
          CacheNamespace: !ImportValue 'svls:restapi:resourceid'
          IntegrationResponses: 
            - 
              ResponseParameters: 
                  "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
                  "method.response.header.Access-Control-Allow-Methods": "'POST,GET,OPTIONS'"
                  "method.response.header.Access-Control-Allow-Origin": "'*'"
              ResponseTemplates: {}
              SelectionPattern: ""
              StatusCode: "200"
          PassthroughBehavior: "WHEN_NO_MATCH"
          RequestTemplates: 
              "application/json": "{\"statusCode\": 200}"
          TimeoutInMillis: 29000
          Type: "MOCK"

  # APIのデプロイ
  ApiDeployment:
    Type: "AWS::ApiGateway::Deployment"
    Properties:
      RestApiId: !ImportValue 'svls:restapi:id'

  # APIのステージを作成
  ApiStage:
    Type: "AWS::ApiGateway::Stage"
    Properties:
      StageName: "prod"
      DeploymentId: !ImportValue 'svls:restapi:deployment:id'
      RestApiId: !ImportValue 'svls:restapi:id'
      CacheClusterEnabled: false
      CacheClusterSize: "0.5"
      TracingEnabled: false
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # APIのカスタムドメインを追加
  CustomDomain:
    Type: "AWS::ApiGateway::DomainName"
    Properties:
      DomainName: !Sub 'api.${Domain}'
      EndpointConfiguration: 
        Types: 
          - "REGIONAL"
      RegionalCertificateArn: !Ref ACMArn
      SecurityPolicy: "TLS_1_2"
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # APIをカスタムドメインへマッピング
  ApiGatewayBasePathMapping:
    Type: "AWS::ApiGateway::BasePathMapping"
    Properties:
      BasePath: !ImportValue 'svls:restapi:stage'
      DomainName: !ImportValue 'svls:restapi:domainname'
      RestApiId: !ImportValue 'svls:restapi:id'
      Stage: !ImportValue 'svls:restapi:stage'

  # -------------------------
  # バックエンドセクション
  # -----

  # 標準キューを作成
  SQSQueue:
    Type: "AWS::SQS::Queue"
    Properties:
      DelaySeconds: "0"
      MaximumMessageSize: "262144"
      MessageRetentionPeriod: "7200"
      ReceiveMessageWaitTimeSeconds: "0"
      VisibilityTimeout: "30"
      QueueName: "SvlsQueue"
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # リソースポリシーを設定
  SQSQueuePolicy:
    Type: "AWS::SQS::QueuePolicy"
    Properties:
      PolicyDocument: !Sub 
        - |
          {   "Version": "2012-10-17",
              "Statement": [
                  { 
                      "Effect": "Allow",
                      "Principal": {
                          "AWS": "*"
                      },
                      "Action": "SQS:SendMessage",
                      "Resource": "${SQSQueue.Arn}",
                      "Condition": {
                          "ArnLike": {
                              "aws:SourceArn": "${TopicArn}"
                          }
                      }
                  }
              ]
            }
        - TopicArn: !ImportValue 'svls:topic:arn'
      Queues: 
        - !Sub "https://sqs.${AWS::Region}.amazonaws.com/${AWS::AccountId}/${SQSQueue.QueueName}"

  # キューをSNSサブスクリプションに登録
  SNSSubscription:
    Type: "AWS::SNS::Subscription"
    Properties:
      TopicArn: !ImportValue 'svls:topic:arn'
      Endpoint: !ImportValue 'svls:sqs:arn'
      Protocol: "sqs"
      RawMessageDelivery: "false"
      Region: !Ref AWS::Region

  # 投稿処理用のLambda関数にアタッチするロール
  LambdaConsumerRole:
    Type: "AWS::IAM::Role"
    Properties:
      Path: "/"
      RoleName: svls-lambda-consumer-role
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument: 
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      Policies:
        - PolicyName: "CognitoAfterAuthPolicy"
          PolicyDocument: 
            Version: "2012-10-17"
            Statement: 
              - Effect: "Allow"
                Action:
                  - "dynamodb:DeleteItem"
                  - "dynamodb:GetItem"
                  - "dynamodb:PutItem"
                  - "dynamodb:Query"
                  - "dynamodb:Scan"
                  - "dynamodb:UpdateItem"
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource: "*"
      ManagedPolicyArns: 
      - "arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole"
      PermissionsBoundary: !Sub 'arn:aws:iam::${AWS::AccountId}:policy/DevelopUserBoundary'
      
  # 投稿登録前処理用のLambda関数
  PreProcFunc:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.10
      Role: !GetAtt LambdaConsumerRole.Arn
      Handler: index.handler
      FunctionName: svls-preproc-func
      Code: 
        ZipFile: | 
          import json

          def handler(event, context):
              userMsg = event['message']
              print("userMsg=" + userMsg)
              
              convMsg = userMsg.title()
              print("convMsg=" + convMsg)
          
              return {
                  'message': convMsg
              }

      Timeout: 3
      MemorySize: 128
      EphemeralStorage: 
        Size: 512
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # 投稿登録用のLambda関数
  DBInsFunc:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.10
      Role: !GetAtt LambdaConsumerRole.Arn
      Handler: index.handler
      FunctionName: svls-dbins-func
      Code: 
        ZipFile: |
          import boto3
          import json
          from datetime import datetime
          
          # define the DynamoDB table that Lambda will connect to
          tableName = "svls-tbl-message"
          
          # create the DynamoDB resource
          # dynamo = boto3.resource('dynamodb')
          # create the DynamoDB client
          dynamo = boto3.client('dynamodb')
          
          print('Loading function')
          
          def handler(event, context):
              msg = event['message']
              date_s = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
          
              item = {
                "id": { 'S': date_s }, 
                "message": { 'S': msg }
              }
              itemJSON = json.dumps(item)
              print('itemJSON=' + itemJSON)
              
              dynamo.put_item(TableName=tableName, Item=item)
          
              return {
                  'itemJSON': itemJSON
              }
      Timeout: 3
      MemorySize: 128
      EphemeralStorage: 
        Size: 512
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # ステートマシンにアタッチするロール
  SQSConsumerRole:
    Type: "AWS::IAM::Role"
    Properties:
      Path: "/"
      RoleName: svls-statemechine-role
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument: 
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service:
                - states.amazonaws.com
      Policies:
        - PolicyName: "CognitoAfterAuthPolicy"
          PolicyDocument: 
            Version: "2012-10-17"
            Statement: 
              - Effect: "Allow"
                Action:
                  - "lambda:InvokeFunction"
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - "xray:PutTraceSegments"
                  - "xray:PutTelemetryRecords"
                  - "xray:GetSamplingRules"
                  - "xray:GetSamplingTargets"
                Resource: "*"
      PermissionsBoundary: !Sub 'arn:aws:iam::${AWS::AccountId}:policy/DevelopUserBoundary'

  # ステートマシンを作成
  StateMachine:
    Type: "AWS::StepFunctions::StateMachine"
    Properties:
      StateMachineName: "SvlsStateMachine"
      DefinitionString: !Sub 
        - |
          {
            "Comment": "Statemachine has two tasks with Lambda functions.",
            "StartAt": "Lambda preProc",
            "States": {
              "Lambda preProc": {
                "Type": "Task",
                "Resource": "arn:aws:states:::lambda:invoke",
                "OutputPath": "$.Payload",
                "Parameters": {
                  "Payload.$": "$",
                  "FunctionName": "${PreProcFuncArn}:$LATEST"
                },
                "Retry": [
                  {
                    "ErrorEquals": [
                      "Lambda.ServiceException",
                      "Lambda.AWSLambdaException",
                      "Lambda.SdkClientException",
                      "Lambda.TooManyRequestsException"
                    ],
                    "IntervalSeconds": 1,
                    "MaxAttempts": 3,
                    "BackoffRate": 2
                  }
                ],
                "Next": "Lambda DBIns"
              },
              "Lambda DBIns": {
                "Type": "Task",
                "Resource": "arn:aws:states:::lambda:invoke",
                "OutputPath": "$.Payload",
                "Parameters": {
                  "Payload.$": "$",
                  "FunctionName": "${DBInsFuncArn}:$LATEST"
                },
                "Retry": [
                  {
                    "ErrorEquals": [
                      "Lambda.ServiceException",
                      "Lambda.AWSLambdaException",
                      "Lambda.SdkClientException",
                      "Lambda.TooManyRequestsException"
                    ],
                    "IntervalSeconds": 1,
                    "MaxAttempts": 3,
                    "BackoffRate": 2
                  }
                ],
                "End": true
              }
            }
          }
        - PreProcFuncArn: !ImportValue 'svls:lambda:preproc:arn'
          DBInsFuncArn: !ImportValue 'svls:lambda:dbins:arn'
      RoleArn: !GetAtt SQSConsumerRole.Arn
      StateMachineType: "STANDARD"
      LoggingConfiguration: 
        IncludeExecutionData: false
        Level: "OFF"
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # ステートマシンのARNをSSMパラメータへ保持
  Parameter:
    Type: AWS::SSM::Parameter
    Properties:
      Name: SvlsStateMachineArn
      Type: String
      Value: !ImportValue 'svls:statemachine:arn'
      Description: "SSM Parameter for using by lambda function"
      Tags: 
        Cost: !Ref Cost

  # SQSからトリガーするLambda関数にアタッチするロール
  SQSTriggerRole:
    Type: "AWS::IAM::Role"
    Properties:
      Path: "/"
      RoleName: svls-lambda-sqstrigger-role
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument: 
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - sts:AssumeRole
            Principal:
              Service:
                - lambda.amazonaws.com
      Policies:
        - PolicyName: "CognitoAfterAuthPolicy"
          PolicyDocument: 
            Version: "2012-10-17"
            Statement: 
              - Effect: "Allow"
                Action:
                  - "ssm:GetParameter"
                  - "states:StartExecution"
                Resource: "*"
              - Effect: "Allow"
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource: "*"
      ManagedPolicyArns: 
        - "arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole"
      PermissionsBoundary: !Sub 'arn:aws:iam::${AWS::AccountId}:policy/DevelopUserBoundary'

 # SQSからトリガーするLambda関数
  SQSTriggerFunc:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.12
      Role: !GetAtt SQSTriggerRole.Arn
      Handler: index.handler
      FunctionName: svls-sqstrigger-func
      Code: 
        ZipFile: |
          import json
          import boto3
          
          TARGET_ARN = ''
          stepfunctions = boto3.client('stepfunctions')
          ssm = boto3.client("ssm")
          
          def handler(event, context):
              parsedBody = json.loads(event['Records'][0]['body'])
              print("parsedBody:", parsedBody)
          
              message = parsedBody['Message']
              parsedMessage = json.loads(message)
              print("parsedMessage:", parsedMessage)
          
              entry = parsedMessage["entry"]
              print('entry:', entry)
              
              userMsg = entry["message"]
              print('USERMSG=:', userMsg)
          
              response = ssm.get_parameter(
                  Name = "SvlsStateMachineArn"
              )
              val = response["Parameter"]["Value"]
              print('ssmParam=' + val)
          
              response = stepfunctions.start_execution(
                  input=json.dumps(entry),
                  stateMachineArn=val
              )
          
      Timeout: 3
      MemorySize: 128
      EphemeralStorage: 
        Size: 512
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # SQSイベントにLambda関数をマッピング
  LambdaEventSourceMapping:
    Type: "AWS::Lambda::EventSourceMapping"
    Properties:
      BatchSize: 10
      EventSourceArn: !ImportValue 'svls:sqs:arn'
      FunctionName: !ImportValue 'svls:lambda:sqstrigger:arn'
      Enabled: true
      MaximumBatchingWindowInSeconds: 0
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # 投稿用テーブルを作成
  MessageTable:
    Type: "AWS::DynamoDB::Table"
    Properties:
      AttributeDefinitions: 
        - 
          AttributeName: "id"
          AttributeType: "S"
      TableName: "svls-tbl-message"
      KeySchema: 
        - 
          AttributeName: "id"
          KeyType: "HASH"
      ProvisionedThroughput: 
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
      Tags:
        - Key: Cost
          Value: !Ref Cost

  # ログイン履歴用テーブルを作成
  LoginTable:
    Type: "AWS::DynamoDB::Table"
    Properties:
      AttributeDefinitions: 
        - 
          AttributeName: "id"
          AttributeType: "S"
      TableName: "svls-tbl-login"
      KeySchema: 
        - 
          AttributeName: "id"
          KeyType: "HASH"
      ProvisionedThroughput: 
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
      Tags:
        - Key: Cost
          Value: !Ref Cost
```

0 件のコメント:

コメントを投稿

人気の投稿