LoginSignup
0

More than 1 year has passed since last update.

AWS CloudFormationでLamdaを排他制御するStep Functionsを構築しよう

Posted at

はじめに

AWS CloudFormationを利用してLamdaを排他制御するStep Functions構築のテンプレートのサンプルです。
API GatewayはStep Functionsを使用します。また、Step FunctionsではSQSとLambdaにて排他制御を実現します。
さらに、Lambdaは、S3アーティファクトを使用します。
API Gatewayは非同期処理となり、実行状況をポーリングで確認できるようにしています。
また、API GatewayにはCORSの設定をしています。

テンプレートの概要が分からない場合は、はじめてのAWS CloudFormationテンプレートを理解するを参考にしてください。

コードはGitHubにもあります。

今回は、akane というシステムの dev 環境を想定しています。
同じ構成で違う環境を作成する場合は、{環境名}-parameters.jsonを別途作成します。

ディレクトリ構成
akane (システム)
  ├── apigw (スタック)
  │   ├── apigw.yml (CFnテンプレート)
  │   └── dev-parameters.json (dev 環境のパラメータ)
  ├── lambda (スタック)
  │   ├── createSQSMessage (スタック)
  │   │   ├── code
  │   │   │   └── createSQSMessage.py (S3アーティファクトソース)
  │   │   ├── createSQSMessage.yml (CFnテンプレート)
  │   │   ├── delete_artifact.dev.sh (S3アーティファクト削除シェル)
  │   │   ├── dev-parameters.json (dev 環境のパラメータ)
  │   │   ├── mkzip.sh (S3アーティファクト作成シェル)
  │   │   └── upload_artifact.dev.sh (S3アーティファクトアップロードシェル)
  │   ├── getLock (スタック)
  │   │   ├── code
  │   │   │   └── getLock.py (S3アーティファクトソース)
  │   │   ├── delete_artifact.dev.sh (S3アーティファクト削除シェル)
  │   │   ├── dev-parameters.json (dev 環境のパラメータ)
  │   │   ├── getLock.yml (CFnテンプレート)
  │   │   ├── mkzip.sh (S3アーティファクト作成シェル)
  │   │   └── upload_artifact.dev.sh (S3アーティファクトアップロードシェル)
  │   ├── setLock (スタック)
  │   │   ├── code
  │   │   │   └── setLock.py (S3アーティファクトソース)
  │   │   ├── delete_artifact.dev.sh (S3アーティファクト削除シェル)
  │   │   ├── dev-parameters.json (dev 環境のパラメータ)
  │   │   ├── mkzip.sh (S3アーティファクト作成シェル)
  │   │   ├── setLock.yml (CFnテンプレート)
  │   │   └── upload_artifact.dev.sh (S3アーティファクトアップロードシェル)
  │   └── waitSecs (スタック)
  │       ├── code
  │       │   └── waitSecs.py (S3アーティファクトソース)
  │       ├── delete_artifact.dev.sh (S3アーティファクト削除シェル)
  │       ├── dev-parameters.json (dev 環境のパラメータ)
  │       ├── mkzip.sh (S3アーティファクト作成シェル)
  │       ├── upload_artifact.dev.sh (S3アーティファクトアップロードシェル)
  │       └── waitSecs.yml (CFnテンプレート)
  ├── role (スタック)
  │   ├── dev-parameters.json (dev 環境のパラメータ)
  │   └── role.yml (CFnテンプレート)
  ├── s3 (スタック)
  │   ├── all-parameters.json (all) 環境のパラメータ)
  │   └── s3.yml (CFnテンプレート)
  ├── sqs (スタック)
  │   ├── dev-parameters.json (dev 環境のパラメータ)
  │   └── sqs.yml (CFnテンプレート)
  └── step-funcs (スタック)
      ├── dev-parameters.json (dev 環境のパラメータ)
      └── step-funcs.yml (CFnテンプレート)

AWS リソース構築内容

  1. apigwスタック
    • CloudWatch ログのロール
    • API Gateway
    • 使用量プラン
    • API キー
  2. lambdaスタック
    • Lambda (createSQSMessage, getLock, setLock, waitSecs)
  3. roleスタック
    • Lambdaロール
    • Step Functionsロール
    • API Gatewayロール
  4. s3スタック
    • s3バケット (akane-all-s3-artifacts)
    • バケットポリシー (s3:GetObject)
  5. sqsスタック
    • SQSキュー
    • アクセスポリシー
  6. step-funcsスタック
    • Step Functionsステートマシン

実行環境の準備

AWS CloudFormationを動かすためのAWS CLIの設定を参考にしてください。

AWS リソース構築手順

  1. 下記を実行してスタックを作成

    ./create_stacks.sh
    
  2. 下記を実行してLambdaの動作を確認

    ./test_lambda.sh
    
  3. 下記を実行してStep Functionsの動作を確認

    ./test_step-funcs.sh
    
  4. 下記を実行してAPI Gatewayの動作を確認

    ./test_apigw.sh <URL> <API_KEY>
    
  5. 下記を実行してスタックを削除

    ./delete_stacks.sh
    

構築テンプレート

1. s3スタック

s3.yml
AWSTemplateFormatVersion: 2010-09-09
Description: S3 For Akane

# Metadata:

Parameters:
  SystemName:
    Type: String
    AllowedPattern: '[a-zA-Z0-9-]*'
  EnvType:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.

# Mappings

# Conditions

# Transform

Resources:
  # S3 Bucket作成
  akaneS3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      AccessControl: Private
      BucketName: !Sub
        - ${SystemName}-${EnvType}-s3-artifacts
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-s3-artifacts
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
  # S3 BucketPolicy作成
  akaneS3BucketPolicy:
    Type: AWS::S3::BucketPolicy
    DependsOn: akaneS3Bucket
    Properties:
      Bucket: !Ref akaneS3Bucket
      PolicyDocument:
        Statement:
          - Action:
              - s3:GetObject
            Effect: Allow
            Resource: !Join
              - ''
              - - 'arn:aws:s3:::'
                - !Ref akaneS3Bucket
                - /*
            Principal:
              AWS: '*'

Outputs:
  akaneS3Bucket:
    Value: !Ref akaneS3Bucket
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-s3-artifacts
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
all-parameters.json
{
    "Parameters": [
        {
            "ParameterKey": "SystemName",
            "ParameterValue": "akane"
        },
        {
            "ParameterKey": "EnvType",
            "ParameterValue": "all"
        }
    ]
}

2. roleスタック

role.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Role For Akane

# Metadata:

Parameters:
  SystemName:
    Type: String
    AllowedPattern: '[a-zA-Z0-9-]*'
  EnvType:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.

# Mappings

# Conditions

# Transform

Resources:
  # Lambda Role 作成
  akaneRoleForLambda:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub
        - ${SystemName}-${EnvType}-role-lambda-${AWS::Region}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Description: !Sub
        - ${SystemName}-${EnvType}-role-lambda-${AWS::Region}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      MaxSessionDuration: 3600
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSQSFullAccess
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: '/'
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-role-lambda-${AWS::Region}
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType

  # StepFunctions Role 作成
  akaneRoleForStepFunctions:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub
        - ${SystemName}-${EnvType}-role-step-funcs-${AWS::Region}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Description: !Sub
        - ${SystemName}-${EnvType}-role-step-funcs-${AWS::Region}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      MaxSessionDuration: 3600
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - !Join
                  - ''
                  - - 'states.'
                    - !Ref AWS::Region
                    - '.amazonaws.com'
            Action:
              - sts:AssumeRole
      Path: '/'
      Policies:
        - PolicyName: LambdaInvokeFunction
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action: lambda:InvokeFunction
                Resource: !Join
                  - ''
                  - - 'arn:aws:lambda:'
                    - !Ref AWS::Region
                    - ':'
                    - !Ref AWS::AccountId
                    - ':function:'
                    - !Ref SystemName
                    - '-'
                    - !Ref EnvType
                    - '-*'
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-role-step-funcs-${AWS::Region}
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType

  # ApiGateway Role 作成
  akaneRoleForApiGateway:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub
        - ${SystemName}-${EnvType}-role-apigw-${AWS::Region}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Description: !Sub
        - ${SystemName}-${EnvType}-role-apigw-${AWS::Region}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      MaxSessionDuration: 3600
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AWSStepFunctionsFullAccess
        - arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - apigateway.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: '/'
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-role-apigw-${AWS::Region}
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType

Outputs:
  akaneRoleForLambdaArn:
    Value: !GetAtt akaneRoleForLambda.Arn
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-role-lambda-arn-${AWS::Region}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
  akaneRoleForStepFunctionsArn:
    Value: !GetAtt akaneRoleForStepFunctions.Arn
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-role-step-funcs-arn-${AWS::Region}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
  akaneRoleForApiGatewayArn:
    Value: !GetAtt akaneRoleForApiGateway.Arn
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-role-apigw-arn-${AWS::Region}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
dev-parameters.json
{
    "Parameters": [
        {
            "ParameterKey": "SystemName",
            "ParameterValue": "akane"
        },
        {
            "ParameterKey": "EnvType",
            "ParameterValue": "dev"
        }
    ],
    "Capabilities": [
        "CAPABILITY_NAMED_IAM"
    ]
}

3. sqsスタック

sqs.yml
AWSTemplateFormatVersion: 2010-09-09
Description: SQS For Akane

# Metadata:

Parameters:
  SystemName:
    Type: String
    AllowedPattern: '[a-zA-Z0-9-]*'
  EnvType:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.

# Mappings

# Conditions

# Transform

Resources:
  akaneSQSQueue:
    Type: AWS::SQS::Queue
    Properties:
      DelaySeconds: 0
      MaximumMessageSize: 262144
      MessageRetentionPeriod: 1209600
      QueueName: !Sub
        - ${SystemName}-${EnvType}-sqs-lock
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      ReceiveMessageWaitTimeSeconds: 0
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-sqs-lock
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
      VisibilityTimeout: 660

  akaneSQSQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
        - !Ref akaneSQSQueue
      PolicyDocument:
        Version: 2012-10-17
        Id: __default_policy_ID
        Statement:
          - Sid: __owner_statement
            Action: SQS:*
            Effect: Allow
            Resource: !GetAtt akaneSQSQueue.Arn
            Principal:
              AWS: !Join
                - ''
                - - 'arn:aws:iam::'
                  - !Ref AWS::AccountId
                  - ':root'
          - Sid: __sender_statement
            Action: SQS:SendMessage
            Effect: Allow
            Resource: !GetAtt akaneSQSQueue.Arn
            Principal:
              AWS:
                Fn::ImportValue: !Sub
                  - ${SystemName}-${EnvType}-role-lambda-arn-${AWS::Region}
                  - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
          - Sid: __receiver_statement
            Action:
              - SQS:ChangeMessageVisibility
              - SQS:DeleteMessage
              - SQS:ReceiveMessage
            Effect: Allow
            Resource: !GetAtt akaneSQSQueue.Arn
            Principal:
              AWS:
                Fn::ImportValue: !Sub
                  - ${SystemName}-${EnvType}-role-lambda-arn-${AWS::Region}
                  - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}

Outputs:
  akaneSQSQueueName:
    Value: !GetAtt akaneSQSQueue.QueueName
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-sqs-lock-queue-name
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
dev-parameters.json
{
    "Parameters": [
        {
            "ParameterKey": "SystemName",
            "ParameterValue": "akane"
        },
        {
            "ParameterKey": "EnvType",
            "ParameterValue": "dev"
        }
    ]
}

4. lambdaスタック (waitSecs)

lambda.yml(waitSecs)
AWSTemplateFormatVersion: 2010-09-09
Description: Lambda For Akane

# Metadata:

Parameters:
  SystemName:
    Type: String
    AllowedPattern: '[a-zA-Z0-9-]*'
  EnvType:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.
  ArtifactS3Bucket:
    Type: String
  LambdaName:
    Type: String
  LambdaMemorySize:
    Type: Number
  LambdaRuntime:
    Type: String

# Mappings

# Conditions

# Transform

Resources:
  # Lambda作成
  akaneLambdaWaitSecs:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: !Ref ArtifactS3Bucket
        S3Key: !Sub
          - code-lambda-${LambdaName}.zip
          - {LambdaName: !Ref LambdaName}
      Description: !Sub
        - ${SystemName}-${EnvType}-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
      FunctionName: !Sub
        - ${SystemName}-${EnvType}-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
      Handler: !Sub
        - ${LambdaName}.handler
        - {LambdaName: !Ref LambdaName}
      MemorySize: !Ref LambdaMemorySize
      Role:
        Fn::ImportValue: !Sub
          - ${SystemName}-${EnvType}-role-lambda-arn-${AWS::Region}
          - { SystemName: !Ref SystemName, EnvType: !Ref EnvType }
      Runtime: !Ref LambdaRuntime
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-lambda-${LambdaName}
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
      Timeout: 900
      TracingConfig:
        Mode: PassThrough

Outputs:
  akaneLambdaWaitSecs:
    Value: !Ref akaneLambdaWaitSecs
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}

  akaneLambdaWaitSecsArn:
    Value: !GetAtt akaneLambdaWaitSecs.Arn
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-lambda-arn-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
dev-parameters.json
{
    "Parameters": [
        {
            "ParameterKey": "SystemName",
            "ParameterValue": "akane"
        },
        {
            "ParameterKey": "EnvType",
            "ParameterValue": "dev"
        },
        {
            "ParameterKey": "ArtifactS3Bucket",
            "ParameterValue": "akane-all-s3-artifacts"
        },
        {
            "ParameterKey": "LambdaName",
            "ParameterValue": "waitSecs"
        },
        {
            "ParameterKey": "LambdaMemorySize",
            "ParameterValue": "128"
        },
        {
            "ParameterKey": "LambdaRuntime",
            "ParameterValue": "python3.8"
        }
    ]
}

5. lambdaスタック (getLock)

lambda.yml(getLock)
AWSTemplateFormatVersion: 2010-09-09
Description: Lambda For Akane

# Metadata:

Parameters:
  SystemName:
    Type: String
    AllowedPattern: '[a-zA-Z0-9-]*'
  EnvType:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.
  ArtifactS3Bucket:
    Type: String
  LambdaName:
    Type: String
  LambdaMemorySize:
    Type: Number
  LambdaRuntime:
    Type: String
  VisibilityTimeout:
    Type: Number
  RetryWaitSecs:
    Type: Number

# Mappings

# Conditions

# Transform

Resources:
  # Lambda作成
  akaneLambdaGetLock:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: !Ref ArtifactS3Bucket
        S3Key: !Sub
          - code-lambda-${LambdaName}.zip
          - {LambdaName: !Ref LambdaName}
      Description: !Sub
        - ${SystemName}-${EnvType}-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
      Environment:
        Variables:
          SQS_QUEUE_NAME:
            Fn::ImportValue: !Sub
              - ${SystemName}-${EnvType}-sqs-lock-queue-name
              - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
          VISIBILITY_TIMEOUT: !Ref VisibilityTimeout
          RETRY_WAIT_SECS: !Ref RetryWaitSecs
      FunctionName: !Sub
        - ${SystemName}-${EnvType}-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
      Handler: !Sub
        - ${LambdaName}.handler
        - {LambdaName: !Ref LambdaName}
      MemorySize: !Ref LambdaMemorySize
      Role:
        Fn::ImportValue: !Sub
          - ${SystemName}-${EnvType}-role-lambda-arn-${AWS::Region}
          - { SystemName: !Ref SystemName, EnvType: !Ref EnvType }
      Runtime: !Ref LambdaRuntime
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-lambda-${LambdaName}
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
      Timeout: 900
      TracingConfig:
        Mode: PassThrough

Outputs:
  akaneLambdaGetLock:
    Value: !Ref akaneLambdaGetLock
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}

  akaneLambdaGetLockArn:
    Value: !GetAtt akaneLambdaGetLock.Arn
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-lambda-arn-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
dev-parameters.json
{
    "Parameters": [
        {
            "ParameterKey": "SystemName",
            "ParameterValue": "akane"
        },
        {
            "ParameterKey": "EnvType",
            "ParameterValue": "dev"
        },
        {
            "ParameterKey": "ArtifactS3Bucket",
            "ParameterValue": "akane-all-s3-artifacts"
        },
        {
            "ParameterKey": "LambdaName",
            "ParameterValue": "getLock"
        },
        {
            "ParameterKey": "LambdaMemorySize",
            "ParameterValue": "128"
        },
        {
            "ParameterKey": "LambdaRuntime",
            "ParameterValue": "python3.8"
        },
        {
            "ParameterKey": "VisibilityTimeout",
            "ParameterValue": "60"
        },
        {
            "ParameterKey": "RetryWaitSecs",
            "ParameterValue": "30"
        }
    ]
}

6. lambdaスタック (setLock)

lambda.yml(setLock)
AWSTemplateFormatVersion: 2010-09-09
Description: Lambda For Akane

# Metadata:

Parameters:
  SystemName:
    Type: String
    AllowedPattern: '[a-zA-Z0-9-]*'
  EnvType:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.
  ArtifactS3Bucket:
    Type: String
  LambdaName:
    Type: String
  LambdaMemorySize:
    Type: Number
  LambdaRuntime:
    Type: String
  LambdaNameToGetLock:
    Type: String
  SQSMessageBody:
    Type: String

# Mappings

# Conditions

# Transform

Resources:
  # Lambda作成
  akaneLambdaSetLock:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: !Ref ArtifactS3Bucket
        S3Key: !Sub
          - code-lambda-${LambdaName}.zip
          - {LambdaName: !Ref LambdaName}
      Description: !Sub
        - ${SystemName}-${EnvType}-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
      Environment:
        Variables:
          SQS_QUEUE_NAME:
            Fn::ImportValue: !Sub
              - ${SystemName}-${EnvType}-sqs-lock-queue-name
              - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
          LAMBDA_NAME_TO_GET_LOCK: !Ref LambdaNameToGetLock
          SQS_MESSAGE_BODY: !Ref SQSMessageBody
      FunctionName: !Sub
        - ${SystemName}-${EnvType}-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
      Handler: !Sub
        - ${LambdaName}.handler
        - {LambdaName: !Ref LambdaName}
      MemorySize: !Ref LambdaMemorySize
      Role:
        Fn::ImportValue: !Sub
          - ${SystemName}-${EnvType}-role-lambda-arn-${AWS::Region}
          - { SystemName: !Ref SystemName, EnvType: !Ref EnvType }
      Runtime: !Ref LambdaRuntime
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-lambda-${LambdaName}
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
      Timeout: 900
      TracingConfig:
        Mode: PassThrough

Outputs:
  akaneLambdaSetLock:
    Value: !Ref akaneLambdaSetLock
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}

  akaneLambdaSetLockArn:
    Value: !GetAtt akaneLambdaSetLock.Arn
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-lambda-arn-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
dev-parameters.json
{
    "Parameters": [
        {
            "ParameterKey": "SystemName",
            "ParameterValue": "akane"
        },
        {
            "ParameterKey": "EnvType",
            "ParameterValue": "dev"
        },
        {
            "ParameterKey": "ArtifactS3Bucket",
            "ParameterValue": "akane-all-s3-artifacts"
        },
        {
            "ParameterKey": "LambdaName",
            "ParameterValue": "setLock"
        },
        {
            "ParameterKey": "LambdaMemorySize",
            "ParameterValue": "128"
        },
        {
            "ParameterKey": "LambdaRuntime",
            "ParameterValue": "python3.8"
        },
        {
            "ParameterKey": "LambdaNameToGetLock",
            "ParameterValue": "getLock"
        },
        {
            "ParameterKey": "SQSMessageBody",
            "ParameterValue": "ExclusionControlLock"
        }
    ]
}

7. lambdaスタック (createSQSMessage)

lambda.yml(createSQSMessage)
AWSTemplateFormatVersion: 2010-09-09
Description: Lambda For Akane

# Metadata:

Parameters:
  SystemName:
    Type: String
    AllowedPattern: '[a-zA-Z0-9-]*'
  EnvType:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.
  ArtifactS3Bucket:
    Type: String
  LambdaName:
    Type: String
  LambdaMemorySize:
    Type: Number
  LambdaRuntime:
    Type: String
  SQSMessageBody:
    Type: String

# Mappings

# Conditions

# Transform

Resources:
  # Lambda作成
  akaneLambdaCreateSQSMessage:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket: !Ref ArtifactS3Bucket
        S3Key: !Sub
          - code-lambda-${LambdaName}.zip
          - {LambdaName: !Ref LambdaName}
      Description: !Sub
        - ${SystemName}-${EnvType}-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
      Environment:
        Variables:
          SQS_QUEUE_NAME:
            Fn::ImportValue: !Sub
              - ${SystemName}-${EnvType}-sqs-lock-queue-name
              - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
          SQS_MESSAGE_BODY: !Ref SQSMessageBody
      FunctionName: !Sub
        - ${SystemName}-${EnvType}-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
      Handler: !Sub
        - ${LambdaName}.handler
        - {LambdaName: !Ref LambdaName}
      MemorySize: !Ref LambdaMemorySize
      Role:
        Fn::ImportValue: !Sub
          - ${SystemName}-${EnvType}-role-lambda-arn-${AWS::Region}
          - { SystemName: !Ref SystemName, EnvType: !Ref EnvType }
      Runtime: !Ref LambdaRuntime
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-lambda-${LambdaName}
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
      Timeout: 900
      TracingConfig:
        Mode: PassThrough

  # スケジュール 作成
  akaneEventsRule:
    Type: AWS::Events::Rule
    Properties:
      Description: !Sub
        - ${SystemName}-${EnvType}-rule-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
      Name: !Sub
        - ${SystemName}-${EnvType}-rule-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
      ScheduleExpression: "rate(24 hours)"
      State: ENABLED
      Targets:
        - Arn: !GetAtt akaneLambdaCreateSQSMessage.Arn
          Id: !Sub
            - ${SystemName}-${EnvType}-rule-lambda-${LambdaName}
            - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}

  # LambdaPermission 作成
  akaneLambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !GetAtt akaneLambdaCreateSQSMessage.Arn
      Principal: events.amazonaws.com
      SourceArn: !GetAtt akaneEventsRule.Arn

Outputs:
  akaneLambdaCreateSQSMessage:
    Value: !Ref akaneLambdaCreateSQSMessage
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-lambda-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName }

  akaneLambdaCreateSQSMessageArn:
    Value: !GetAtt akaneLambdaCreateSQSMessage.Arn
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-lambda-arn-${LambdaName}
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType, LambdaName: !Ref LambdaName}
dev-parameters.json
{
    "Parameters": [
        {
            "ParameterKey": "SystemName",
            "ParameterValue": "akane"
        },
        {
            "ParameterKey": "EnvType",
            "ParameterValue": "dev"
        },
        {
            "ParameterKey": "ArtifactS3Bucket",
            "ParameterValue": "akane-all-s3-artifacts"
        },
        {
            "ParameterKey": "LambdaName",
            "ParameterValue": "createSQSMessage"
        },
        {
            "ParameterKey": "LambdaMemorySize",
            "ParameterValue": "128"
        },
        {
            "ParameterKey": "LambdaRuntime",
            "ParameterValue": "python3.8"
        },
        {
            "ParameterKey": "SQSMessageBody",
            "ParameterValue": "ExclusionControlLock"
        }
    ]
}

8. step-funcsスタック

step-funcs.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Step Functions For Akane

# Metadata:

Parameters:
  SystemName:
    Type: String
    AllowedPattern: '[a-zA-Z0-9-]*'
  EnvType:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.
  StepFuncsName:
    Type: String

# Mappings

# Conditions

# Transform

Resources:
  akanewaitSecsStepFunctionsStateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      StateMachineName: !Sub
        - ${SystemName}-${EnvType}-step-funcs-${StepFuncsName}
        - { SystemName: !Ref SystemName, EnvType: !Ref EnvType, StepFuncsName: !Ref StepFuncsName }
      DefinitionString: !Sub
        - |
            {
              "Comment": "${StepFuncsName}",
              "StartAt": "getLock",
              "States": {
                "getLock": {
                  "Type": "Task",
                  "Resource": "${LambdaArnToGetLock}",
                  "ResultPath": "$.getLock",
                  "Next": "checkLock"
                },
                "checkLock": {
                  "Type" : "Choice",
                  "Choices": [
                    {
                      "Variable": "$.getLock.isSucceededLock",
                      "NumericEquals": 0,
                      "Next": "retryWaitToGetLock"
                    },
                    {
                      "Variable": "$.getLock.isSucceededLock",
                      "NumericEquals": 1,
                      "Next": "waitSecs"
                    }
                  ],
                  "Default": "retryWaitToGetLock"
                },
                "retryWaitToGetLock": {
                  "Type": "Wait",
                  "SecondsPath": "$.getLock.retryWaitSecs",
                  "Next": "getLock"
                },
                "waitSecs": {
                  "Type": "Task",
                  "Resource": "${LambdaArnToWaitSecs}",
                  "ResultPath": "$.waitSecs",
                  "Next": "setLock"
                },
                "setLock": {
                  "Type": "Task",
                  "Resource": "${LambdaArnToSetLock}",
                  "ResultPath": "$.setLock",
                  "End": true
                }
              }
            }
        - StepFuncsName: !Ref StepFuncsName
          LambdaArnToWaitSecs:
            Fn::ImportValue: !Sub
              - ${SystemName}-${EnvType}-lambda-arn-waitSecs
              - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
          LambdaArnToGetLock:
            Fn::ImportValue: !Sub
              - ${SystemName}-${EnvType}-lambda-arn-getLock
              - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
          LambdaArnToSetLock:
            Fn::ImportValue: !Sub
              - ${SystemName}-${EnvType}-lambda-arn-setLock
              - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      RoleArn:
        Fn::ImportValue: !Sub
          - ${SystemName}-${EnvType}-role-step-funcs-arn-${AWS::Region}
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      StateMachineType: STANDARD
      LoggingConfiguration:
        IncludeExecutionData: false
        Level: 'OFF'
      Tags:
        - Key: Name
          Value: !Sub
            - ${SystemName}-${EnvType}-step-funcs-${StepFuncsName}
            - { SystemName: !Ref SystemName, EnvType: !Ref EnvType, StepFuncsName: !Ref StepFuncsName }
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType

Outputs:
  akanewaitSecsStepFunctionsStateMachine:
    Value: !Ref akanewaitSecsStepFunctionsStateMachine
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-step-funcs-${StepFuncsName}
        - { SystemName: !Ref SystemName, EnvType: !Ref EnvType, StepFuncsName: !Ref StepFuncsName }
dev-parameters.json
{
    "Parameters": [
        {
            "ParameterKey": "SystemName",
            "ParameterValue": "akane"
        },
        {
            "ParameterKey": "EnvType",
            "ParameterValue": "dev"
        },
        {
            "ParameterKey": "StepFuncsName",
            "ParameterValue": "waitSecs"
        }
    ]
}

9. apigwスタック

apigw.yml
AWSTemplateFormatVersion: 2010-09-09
Description: API Gateway For Akane

# Metadata:

Parameters:
  SystemName:
    Type: String
    AllowedPattern: '[a-zA-Z0-9-]*'
  EnvType:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.
  EnvTypeCommon:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.
  APIGatewayStage:
    Type: String

# Mappings

# Conditions

# Transform

Resources:
  # ApiGateway CloudWatchRole 作成
  akaneCloudWatchRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - apigateway.amazonaws.com
            Action: sts:AssumeRole
      Description: !Sub
        - ${SystemName}-${EnvTypeCommon}-role-apigw-cloudwatch-${AWS::Region}
        - {SystemName: !Ref SystemName, EnvTypeCommon: !Ref EnvTypeCommon}
      ManagedPolicyArns:
        -  arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs
      Path: /
      RoleName: !Sub
        - ${SystemName}-${EnvTypeCommon}-role-apigw-cloudwatch-${AWS::Region}
        - {SystemName: !Ref SystemName, EnvTypeCommon: !Ref EnvTypeCommon}
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvTypeCommon}-role-apigw-cloudwatch-${AWS::Region}
          - {SystemName: !Ref SystemName, EnvTypeCommon: !Ref EnvTypeCommon}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvTypeCommon
          Value: !Ref EnvTypeCommon
  akaneApiGatewayAccount:
    Type: AWS::ApiGateway::Account
    Properties:
      CloudWatchRoleArn: !GetAtt akaneCloudWatchRole.Arn

  # ApiGateway 作成
  akaneApiGatewayRestApi:
    Type: AWS::ApiGateway::RestApi
    DependsOn: akaneApiGatewayAccount
    Properties:
      Name: !Sub
        - ${SystemName}-${EnvType}-apigw
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Description: !Sub
        - ${SystemName}-${EnvType}-apigw
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      EndpointConfiguration:
        Types:
          - REGIONAL
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-apigw
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType

  # WaitSecs
  akaneApiGatewayResourceForWaitSecs:
    Type: AWS::ApiGateway::Resource
    Properties:
      RestApiId: !Ref akaneApiGatewayRestApi
      PathPart: 'waitsecs'
      ParentId: !GetAtt akaneApiGatewayRestApi.RootResourceId

  # WaitSecs: POST
  akaneApiGatewayMethodPostForWaitSecs:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref akaneApiGatewayRestApi
      ResourceId: !Ref akaneApiGatewayResourceForWaitSecs
      HttpMethod: POST
      AuthorizationType: NONE
      ApiKeyRequired: true
      RequestParameters: {}
      RequestModels: {}
      MethodResponses:
        - ResponseModels: {}
          ResponseParameters:
            method.response.header.Access-Control-Allow-Headers: true
            method.response.header.Access-Control-Allow-Methods: true
            method.response.header.Access-Control-Allow-Origin: true
          StatusCode: '200'
      Integration:
        CacheNamespace: !Ref akaneApiGatewayResourceForWaitSecs
        Credentials:
          Fn::ImportValue: !Sub
            - ${SystemName}-${EnvType}-role-apigw-arn-${AWS::Region}
            - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        IntegrationHttpMethod: POST
        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'"
              method.response.header.Access-Control-Allow-Origin: "'*'"
            ResponseTemplates:
              application/json: "{\"taskid\": $input.json('$.executionArn')}"
            StatusCode: '200'
        PassthroughBehavior: WHEN_NO_TEMPLATES
        RequestParameters: {}
        RequestTemplates:
          application/json: !Sub
            - |
              {
                "input": "$util.escapeJavaScript($input.json('$'))",
                "stateMachineArn": "${stateMachineArn}"
              }
            - stateMachineArn:
                Fn::ImportValue: !Sub
                  - ${SystemName}-${EnvType}-step-funcs-waitSecs
                  - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        TimeoutInMillis: 29000
        Type: AWS
        Uri: !Sub arn:aws:apigateway:${AWS::Region}:states:action/StartExecution

  # Institution: OPTIONS
  akaneApiGatewayMethodOptionsForWaitSecs:
    Type: AWS::ApiGateway::Method
    DependsOn: akaneApiGatewayMethodPostForWaitSecs
    Properties:
      RestApiId: !Ref akaneApiGatewayRestApi
      ResourceId: !Ref akaneApiGatewayResourceForWaitSecs
      HttpMethod: OPTIONS
      AuthorizationType: NONE
      ApiKeyRequired: true
      RequestParameters: {}
      RequestModels: {}
      MethodResponses:
        - ResponseModels:
            application/json: 'Empty'
          ResponseParameters:
            method.response.header.Access-Control-Allow-Headers: true
            method.response.header.Access-Control-Allow-Methods: true
            method.response.header.Access-Control-Allow-Origin: true
          StatusCode: '200'
      Integration:
        CacheNamespace: !Ref akaneApiGatewayResourceForWaitSecs
        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,OPTIONS'"
              method.response.header.Access-Control-Allow-Origin: "'*'"
            ResponseTemplates: {}
            StatusCode: '200'
        PassthroughBehavior: NEVER
        RequestParameters: {}
        RequestTemplates:
          application/json: "{\"statusCode\": 200}"
        TimeoutInMillis: 29000
        Type: MOCK

  # Status
  akaneApiGatewayResourceForStatus:
    Type: AWS::ApiGateway::Resource
    DependsOn: akaneApiGatewayMethodOptionsForWaitSecs
    Properties:
      RestApiId: !Ref akaneApiGatewayRestApi
      PathPart: 'status'
      ParentId: !GetAtt akaneApiGatewayRestApi.RootResourceId

  # Status: POST
  akaneApiGatewayMethodPostForStatus:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref akaneApiGatewayRestApi
      ResourceId: !Ref akaneApiGatewayResourceForStatus
      HttpMethod: POST
      AuthorizationType: NONE
      ApiKeyRequired: true
      RequestParameters:
        method.request.header.task_id: true
      RequestModels: {}
      MethodResponses:
        - ResponseModels: {}
          ResponseParameters:
            method.response.header.Access-Control-Allow-Headers: true
            method.response.header.Access-Control-Allow-Methods: true
            method.response.header.Access-Control-Allow-Origin: true
          StatusCode: '200'
      Integration:
        CacheNamespace: !Ref akaneApiGatewayResourceForStatus
        Credentials:
          Fn::ImportValue: !Sub
            - ${SystemName}-${EnvType}-role-apigw-arn-${AWS::Region}
            - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        IntegrationHttpMethod: POST
        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'"
              method.response.header.Access-Control-Allow-Origin: "'*'"
            ResponseTemplates:
              application/json: "{\"status\": $input.json('$.status'), \"result\": $input.json('$.output')}"
            StatusCode: '200'
        PassthroughBehavior: WHEN_NO_TEMPLATES
        RequestParameters: {}
        RequestTemplates:
          application/json: "{\"executionArn\": $input.json('$.taskid')}"
        TimeoutInMillis: 29000
        Type: AWS
        Uri: !Sub arn:aws:apigateway:${AWS::Region}:states:action/DescribeExecution

  # Status: OPTIONS
  akaneApiGatewayMethodOptionsForStatus:
    Type: AWS::ApiGateway::Method
    DependsOn: akaneApiGatewayMethodPostForStatus
    Properties:
      RestApiId: !Ref akaneApiGatewayRestApi
      ResourceId: !Ref akaneApiGatewayResourceForStatus
      HttpMethod: OPTIONS
      AuthorizationType: NONE
      ApiKeyRequired: true
      RequestParameters: {}
      RequestModels: {}
      MethodResponses:
        - ResponseModels:
            application/json: 'Empty'
          ResponseParameters:
            method.response.header.Access-Control-Allow-Headers: true
            method.response.header.Access-Control-Allow-Methods: true
            method.response.header.Access-Control-Allow-Origin: true
          StatusCode: '200'
      Integration:
        CacheNamespace: !Ref akaneApiGatewayResourceForStatus
        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,OPTIONS'"
              method.response.header.Access-Control-Allow-Origin: "'*'"
            ResponseTemplates: {}
            StatusCode: '200'
        PassthroughBehavior: NEVER
        RequestParameters: {}
        RequestTemplates:
          application/json: "{\"statusCode\": 200}"
        TimeoutInMillis: 29000
        Type: MOCK

  # ApiGateway Deployment 作成
  akaneApiGatewayDeployment:
    Type: AWS::ApiGateway::Deployment
    DependsOn: akaneApiGatewayMethodOptionsForStatus
    Properties:
      Description: !Sub
        - ${SystemName}-${EnvType}-apigw-deployment
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      RestApiId: !Ref akaneApiGatewayRestApi
  akaneApiGatewayStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      DeploymentId: !Ref akaneApiGatewayDeployment
      Description: !Sub
        - ${SystemName}-${EnvType}-apigw-stage
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      MethodSettings:
        - CacheDataEncrypted: false
          CachingEnabled: false
          DataTraceEnabled: true # CloudWatch ログを有効化
          HttpMethod: '*'
          LoggingLevel: ERROR
          MetricsEnabled: false # 詳細 CloudWatch メトリクスを有効化
          ResourcePath: '/*'
      RestApiId: !Ref akaneApiGatewayRestApi
      StageName: !Ref APIGatewayStage
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-apigw-stage
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
      TracingEnabled: false # X-Ray トレースの有効化

  # ApiGateway APIキー 作成
  akaneApiGatewayKey:
    Type: AWS::ApiGateway::ApiKey
    Properties:
      Name: !Sub
        - ${SystemName}-${EnvType}-apigw-key
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Description: !Sub
        - ${SystemName}-${EnvType}-apigw-key
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Enabled: true
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-apigw-key
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
  akaneApiGatewayUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    DependsOn: akaneApiGatewayStage
    Properties:
      ApiStages:
        - ApiId: !Ref akaneApiGatewayRestApi
          Stage: !Ref APIGatewayStage
      Description: !Sub
        - ${SystemName}-${EnvType}-apigw-usage-plan
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      UsagePlanName: !Sub
        - ${SystemName}-${EnvType}-apigw-usage-plan
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-apigw-usage-plan
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
  akaneApiGatewayUsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    Properties:
      KeyId: !Ref akaneApiGatewayKey
      KeyType: API_KEY
      UsagePlanId: !Ref akaneApiGatewayUsagePlan
dev-parameters.json
{
    "Parameters": [
        {
            "ParameterKey": "SystemName",
            "ParameterValue": "akane"
        },
        {
            "ParameterKey": "EnvType",
            "ParameterValue": "dev"
        },
        {
            "ParameterKey": "EnvTypeCommon",
            "ParameterValue": "all"
        },
        {
            "ParameterKey": "APIGatewayStage",
            "ParameterValue": "v1"
        }
    ],
    "Capabilities": [
        "CAPABILITY_NAMED_IAM"
    ]
}

10. 実行ファイル

create_stacks.sh
#!/bin/sh

cd `dirname $0`

SYSTEM_NAME=akane

create_stack () {
    ENV_TYPE=$1
    STACK_NAME=$2
    aws cloudformation create-stack \
    --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME} \
    --template-body file://./${SYSTEM_NAME}/${STACK_NAME}/${STACK_NAME}.yml \
    --cli-input-json file://./${SYSTEM_NAME}/${STACK_NAME}/${ENV_TYPE}-parameters.json

    aws cloudformation wait stack-create-complete \
    --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME}
}

create_stack all s3
create_stack dev role
create_stack dev sqs
./create_lambda_stacks.sh
create_stack dev step-funcs
create_stack dev apigw

exit 0
test_lambda.sh
#!/bin/sh

cd `dirname $0`

echo " --- \033[0;33m TEST LAMBDA \033[0;39m --- "

LAMBDA_NAME=akane-dev-lambda-waitMinutes
OUTPUT_FILE=response.json

aws lambda invoke --function-name ${LAMBDA_NAME} --log-type Tail ${OUTPUT_FILE} --query 'LogResult'  --output text |  base64 -D

cat ${OUTPUT_FILE}

exit 0
test_step-funcs.sh
#!/bin/sh

cd `dirname $0`

./create_sqs_message.sh

echo " --- \033[0;33m TEST STEP_FUNCS \033[0;39m --- "

STEP_FUNCS_NAME=akane-dev-step-funcs-waitSecs
SLEEP_SEC=3

get_step_funcs_result() {
  RESPONSE=$1
  ARN_ID=`echo ${RESPONSE} | jq -r '.executionArn'`

  if [ "${ARN_ID}" == "null" ]; then
    echo ${RESPONSE}
    exit 1
  fi
  echo ${ARN_ID}
  step_funcs_status=$(aws stepfunctions describe-execution --execution-arn ${ARN_ID} | jq -r '.status')
  echo " --- \033[0;35m STEP FUNCTIONS STATUS \033[0;39m --- "
  while [ ${step_funcs_status} == "RUNNING" ] ; do
    echo "     \033[0;33m${step_funcs_status} \033[0;39m "
    sleep ${SLEEP_SEC}
    step_funcs_status=$(aws stepfunctions describe-execution --execution-arn ${ARN_ID} | jq -r '.status')
  done
  if [ $step_funcs_status == "SUCCEEDED" ]; then
    echo -e "\033[0;32m  ${step_funcs_status}\033[0;39m"
    aws stepfunctions describe-execution --execution-arn ${ARN_ID} | jq -r '.output'
  else
    echo -e "\033[0;31m  ${step_funcs_status}\033[0;39m"
  fi
}

STATE_MACHINE_ARN=`aws stepfunctions list-state-machines --query 'stateMachines' | jq -r ".[] | select(.name == \"${STEP_FUNCS_NAME}\") | .stateMachineArn"`

response=`aws stepfunctions start-execution --state-machine-arn ${STATE_MACHINE_ARN}`

get_step_funcs_result "${response}"

exit 0
test_apigw.sh
#!/bin/sh

set -e

if [ $# -ne 2 ]; then
    echo " --- \033[0;31m [ERROR]: The following arguments are required \033[0;39m --- "
    echo "      \033[0;33m$0 <URL> <API_KEY>\033[0;39m"
    echo "      \033[0;33m<URL> Example: https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/v1\033[0;39m"
    exit 1
fi

cd `dirname $0`

./create_sqs_message.sh

echo " --- \033[0;33m TEST APIGW \033[0;39m --- "

SLEEP_SEC=3

get_apigw_result() {
  RESPONSE=$1
  ARN_ID=`echo ${RESPONSE} | jq -r '.taskid'`

  if [ "${ARN_ID}" == "null" ]; then
    echo ${RESPONSE}
    exit 1
  fi
  echo ${ARN_ID}

  step_funcs_status=`curl -s -X POST -H 'Content-Type: application/json' \
    -H "x-api-key: ${API_KEY}" \
    -d "{\"taskid\": \"${ARN_ID}\"}" \
    "${URL}/status" | jq -r '.status'`
  echo " --- \033[0;35m STEP FUNCTIONS STATUS \033[0;39m --- "
  while [ ${step_funcs_status} == "RUNNING" ] ; do
    echo "     \033[0;33m${step_funcs_status} \033[0;39m "
    sleep ${SLEEP_SEC}
    step_funcs_status=`curl -s -X POST -H 'Content-Type: application/json' \
      -H "x-api-key: ${API_KEY}" \
      -d "{\"taskid\": \"${ARN_ID}\"}" \
      "${URL}/status" | jq -r '.status'`
  done
  if [ $step_funcs_status == "SUCCEEDED" ]; then
    echo -e "\033[0;32m  ${step_funcs_status}\033[0;39m"
    curl -s -X POST -H 'Content-Type: application/json' \
      -H "x-api-key: ${API_KEY}" \
      -d "{\"taskid\": \"${ARN_ID}\"}" \
      "${URL}/status" | jq -r '.result'
  else
    echo -e "\033[0;31m  ${step_funcs_status}\033[0;39m"
  fi
}

URL=$1
API_KEY=$2
WAIT_SECS=15

response=`curl -s -X POST -H 'Content-Type: application/json' \
-H "x-api-key: ${API_KEY}" \
-d "{\"wait_secs\": \"${WAIT_SECS}\"}" \
"${URL}/waitsecs"`
echo ${response}

get_apigw_result "${response}"

exit 0
delete_stacks.sh
#!/bin/sh

cd `dirname $0`

SYSTEM_NAME=akane

delete_stack () {
    ENV_TYPE=$1
    STACK_NAME=$2
    aws cloudformation delete-stack \
    --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME}

    aws cloudformation wait stack-delete-complete \
    --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME}
}

delete_stack dev apigw
delete_stack dev step-funcs
./delete_lambda_stacks.sh
delete_stack dev sqs
delete_stack dev role
delete_stack all s3

exit 0

4. アーティファクト関連ファイル

mkzip.sh
#!/bin/sh

cd `dirname $0`

ENV_TYPE=dev
LAMBDA_NAME=$(cat ${ENV_TYPE}-parameters.json | jq -r '.Parameters[] | select(.ParameterKey == "LambdaName").ParameterValue')

FILE=../code-lambda-${LAMBDA_NAME}.zip

cd ./code

ls . |  grep -v -E ".gitignore|${LAMBDA_NAME}.py" | xargs rm -rf

rm -f ${FILE}
zip -r ${FILE} ./*

exit 0
upload_artifact.dev.sh
#!/bin/sh

cd `dirname $0`

BASENAME=$(basename $0)
FILENAME=${BASENAME%.*}
ENV_TYPE=${FILENAME##*.}

BUCKET_NAME=$(cat ${ENV_TYPE}-parameters.json | jq -r '.Parameters[] | select(.ParameterKey == "ArtifactS3Bucket").ParameterValue')

ARTIFACT_NAME=code-lambda-$(cat ${ENV_TYPE}-parameters.json | jq -r '.Parameters[] | select(.ParameterKey == "LambdaName").ParameterValue')

aws s3 cp ${ARTIFACT_NAME}.zip s3://${BUCKET_NAME}/${ARTIFACT_NAME}.zip

exit 0
delete_artifact.dev.sh
#!/bin/sh

cd `dirname $0`

BASENAME=$(basename $0)
FILENAME=${BASENAME%.*}
ENV_TYPE=${FILENAME##*.}

BUCKET_NAME=$(cat ${ENV_TYPE}-parameters.json | jq -r '.Parameters[] | select(.ParameterKey == "ArtifactS3Bucket").ParameterValue')

ARTIFACT_NAME=code-lambda-$(cat ${ENV_TYPE}-parameters.json | jq -r '.Parameters[] | select(.ParameterKey == "LambdaName").ParameterValue')

aws s3 rm s3://${BUCKET_NAME}/${ARTIFACT_NAME}.zip

exit 0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0