はじめての AWS Amplify - ①初回設定を5分で終わらせるCloudFormationテンプレート

# はじめに

``AWS Amplify Console`` は、 **静的ウェブサイトを簡単かつ迅速にホスティングすることのできるAWSサービス** です。Webアプリの構築やデプロイを行うための **CI/CDワークフローも提供される** ことから、React、Angular、Vue、Ember などの **シングルページアプリケーション**  (SPA) を **簡単にデプロイ** することができます。また、``AWS CodeCommit`` などの **コードリポジトリと接続することで、コードコミットのたびにデプロイを実行** することもできます。

[![thumbnail](http://image.slidesharecdn.com/20200520awsblackbeltamplifya-200528074448/95/slide-1-1024.jpg)](https://www.slideshare.net/AmazonWebServicesJapan/20200520-aws-black-belt-online-seminar-aws-amplify-234663231)

``Amplify Console`` を使い始めるにあたっては、コードリポジトリとの接続やブランチの作成などの **各種セットアップが必要** となります。もちろん、AWSのコンソール画面（AWS Management Console）から、これらのセットアップを行うことも可能ですが、本記事では **「Amplify Console のセットアップをできるだけ簡単に終わらせたい」** という想いから、CloudFormationテンプレートを用いて、 **これらのセットアップをクリック1つで完了** させようと思います。


# TL;DR

以下の ``CloudFormation`` テンプレートを実行することで、**AWS Amplify Console と AWS CodeCommit を用いたCI/CDワークフローをお手軽に実現**します。下にあるボタンをクリックすると、自身のAWSアカウント（Asia Pacific Tokyo - ap-northeast-1）で、この``CloudFormation``テンプレートを実行することが可能となります。

[![cloudformation-launch-stack](https://raw.githubusercontent.com/eijikominami/aws-cloudformation-templates/master/images/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=ap-northeast-1#/stacks/create/review?stackName=Amplify&templateURL=https://eijikominami.s3-ap-northeast-1.amazonaws.com/aws-cloudformation-templates/amplify/template.yaml) 

作成されるAWSリソースとそのアーキテクチャ図はこちら。

![](https://raw.githubusercontent.com/eijikominami/aws-cloudformation-templates/master/images/architecture-amplify.png)

AWS Amplify Consoleは、 [複数のコードリポジトリに対応](https://docs.aws.amazon.com/ja_jp/amplify/latest/userguide/getting-started.html)していますが、本記事では CloudFormation を使って簡単に環境構築が可能な ``AWS CodeCommit`` をコードリポジトリとして使用しています。

# アーキテクチャ 

このテンプレートの設定内容は以下の通りです。なお、以下のYAMLコードは、 [aws-cloudformation-templates/amplify - GitHub](https://github.com/eijikominami/aws-cloudformation-templates/blob/master/amplify/README_JP.md) で公開しているCloudFormationテンプレートから、本記事用に一部抜粋したものです。本記事作成用に一部改変を行なっていること、またリポジトリの最新のコードを常に反映している訳ではないことをご了承ください。詳細は、GitHubの [当該リポジトリ](https://github.com/eijikominami/aws-cloudformation-templates/) をご覧ください。

+ [README(EN) - aws-cloudformation-templates/amplify](https://github.com/eijikominami/aws-cloudformation-templates/blob/master/amplify/README.md)
+ [README(JP) - aws-cloudformation-templates/amplify](https://github.com/eijikominami/aws-cloudformation-templates/blob/master/amplify/README_JP.md)

## AWS CodeCommit

**リポジトリ名を指定** して、Gitリポジトリを作成します。また、 ``AWS CodeCommit`` で発生した全てのイベントをトリガとして、 ``Amazon SNS`` に **通知が送信** されます。

```yaml
Parameters:
  DomainName:
    Type: String
    AllowedPattern: .+
    Description: Custom domain name for your Amplify Console application [required]
  RepositoryName:
    Type: String
    AllowedPattern: .+
    Description: Repository name on CodeCommit [required]

Resources:
  CodeCommit:
    Type: 'AWS::CodeCommit::Repository'
    Properties: 
      RepositoryDescription: !Ref DomainName
      RepositoryName: !Ref RepositoryName
      Triggers:
        - DestinationArn: !Ref SNSTopic
          Events:
            - all
          Name: Notify all events to SNS
```

## Amazon SNS

``AWS CodeCommit`` や ``AWS Amplify`` から送信された通知を受け取る ``Amazon SNS`` **トピック** を作成します。

```yaml
Resources:
  SNSTopic:
    Type: 'AWS::SNS::Topic'
    Properties:
      DisplayName: !Sub SNSTopic-createdby-${AWS::StackName}
      TopicName: !Sub SNSTopic-createdby-${AWS::StackName}
```

[AWS Serverless Application Repository](https://aws.amazon.com/jp/serverless/serverlessrepo/) にある [**sns-topic テンプレート**](https://serverlessrepo.aws.amazon.com/applications/arn:aws:serverlessrepo:us-east-1:172664222583:applications~sns-topic)  を組み込むことで、SNSトピックを作成することも可能です。この方法の場合、 **作成したSNSトピックに関連する CloudWatch アラームも同時に作成** することができます。

```yaml
Resources:
  SNSTopic:
    Type: 'AWS::Serverless::Application'
    Properties:
      Location:
        ApplicationId: arn:aws:serverlessrepo:us-east-1:172664222583:applications/sns-topic
        SemanticVersion: 1.0.18
      Parameters:
        TopicName: !Sub SNSTopic-createdby-${AWS::StackName}
```

## AWS Amplify Console

### IAM Role の作成

``AWS Amplify Console`` で使用する ``IAM Role`` を作成します。

```yaml
Resources:
  IAMRoleForAmplify:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: amplify.amazonaws.com 
            Action: 'sts:AssumeRole'
      Description: Allows Amplify Backend Deployment to access AWS resources on your behalf.
      ManagedPolicyArns:
          - arn:aws:iam::aws:policy/AdministratorAccess
      RoleName: !Sub '${AWS::StackName}-AmplifyBackend-${AWS::Region}'
```

### AWS Amplify Console の作成

``AWS Amplify Console`` を作成し、先ほど作成した ``CodeCommit`` を **リポジトリに指定** します。また、``master`` と ``develop`` の2つの **ブランチを作成** します。

```yaml
Parameters:
  DomainName:
    Type: String
    AllowedPattern: .+
    Description: Custom domain name for your Amplify Console application [required]
  RepositoryName:
    Type: String
    AllowedPattern: .+
    Description: Repository name on CodeCommit [required]

Resources:
  AmplifyConsole:
    Type: 'AWS::Amplify::App'
    Properties:
      AutoBranchCreationConfig:
        EnableAutoBranchCreation: true
        EnableAutoBuild: true
      Description: !Ref DomainName
      IAMServiceRole: !GetAtt IAMRoleForAmplify.Arn
      Name: !Ref RepositoryName
      Repository: !GetAtt CodeCommit.CloneUrlHttp
  AmplifyBranchDev:      
    Type: 'AWS::Amplify::Branch'
    Properties: 
      AppId: !GetAtt AmplifyConsole.AppId
      BranchName: develop
      Description: develop
      EnableAutoBuild: true
      EnvironmentVariables: 
        - Name: USER_BRANCH
          Value: dev
  AmplifyBranchProd:      
    Type: 'AWS::Amplify::Branch'
    Properties: 
      AppId: !GetAtt AmplifyConsole.AppId
      BranchName: master
      Description: master
      EnableAutoBuild: true
      EnvironmentVariables: 
        - Name: USER_BRANCH
          Value: prod
```

### カスタムドメインの指定

Amplify Console では、ブランチごとにデプロイが実行され、個別のURLが付与されます。また、これらのURLを **カスタムドメインに差し替える** ことが可能で、 **指定したドメインのSSL証明書の作成から紐付けまでを自動で行う** ことができます。下の例では、 ``master`` ブランチのURLに、自身が指定したカスタムドメインを紐付けています。

```yaml
Parameters:
  DomainName:
    Type: String
    AllowedPattern: .+
    Description: Custom domain name for your Amplify Console application [required]

Resources:
  AmplifyDomainProd:
    Type: 'AWS::Amplify::Domain'
    Properties: 
      AppId: !GetAtt AmplifyConsole.AppId
      DomainName: !Sub ${DomainName}
      SubDomainSettings: 
        - BranchName: !GetAtt AmplifyBranchProd.BranchName
          Prefix: ''
```

## Amazon EventBridge

``AWS Amplify Console`` から送信された通知を ``Amazon SNS`` にルーティングする、``Amazon EventBridge`` **イベントルール** を作成します。下の例では、デプロイのステータスが、``SUCCEED``, ``FAILED``, ``STARTED`` であった場合のみ、通知を ``Amazon SNS`` にルーティングします。

```yaml
  EventBridgeForAmplify:
    Type: 'AWS::Events::Rule'
    Properties: 
      Description: !Sub Rule for Amplify created by ${AWS::StackName}.
      EventPattern:
        source:
          - aws.amplify
        detail-type: 
          - Amplify Deployment Status Change
        detail:
          appId: 
            - !GetAtt AmplifyConsole.AppId
          jobStatus:
            - SUCCEED
            - FAILED
            - STARTED
      Name: !Sub ${AWS::StackName}-Amplify
      State: ENABLED
      Targets:
        # SNSトピックを直接作成した場合
        - Arn: !Ref SNSTopic
        # SNSトピックを AWS Serverless Application Repository から作成した場合
        - Arn: !GetAtt SNSTopic.Outputs.SNSTopicArn
```

以上で、``AWS Amplify Console`` と ``AWS CodeCommit`` を連携して、 CI/CDワークフローを作成することができました。

# CodeCommit から Amplify Console へのトリガ設定

~~ただ、 **上記のままだと AWS CodeCommit へのコードのプッシュをトリガにした、AWS Amplify Console のデプロイは実行されません** 。なぜなら、上記の CloudFormationテンプレートには、 **AWS CodeCommit から AWS Amplify Console へのトリガ設定が含まれていない** ためです。CloudFormationテンプレートで、このトリガ設定を記述しようとすると、AWS Amplify と AWS CodeCommit の両リソースの間で **循環参照** が発生してしまいます。~~

（**2020年7月24日追記**）
上記の CloudFormation テンプレートを実行し、Amplify の設定内で CodeCommit のリポジトリを指定した時点で、 **AWS CodeCommit から AWS Amplify Console へのトリガ設定が自動で作成** されます。したがいまして、 **通常であれば下記の設定を行う必要はありません** 。トリガ設定を手動で削除してしまった場合など、 **トリガを設定し直す必要がある場合のみ下記の手順をお試しください** 。

### 1. 手動でトリガを設定する

AWSのコンソール画面（AWS Management Console）からトリガを **手動追加** します。指定する項目は、「**トリガ名**」、「**イベントの種類**」、「**SNSトピック名** ( = arn:aws:sns:{region}:{account_id}:amplify_codecommit_topic )」、「**カスタムデータ** ( = Amlify Console アプリケーションID)」です。

![amplify_codecommit_topic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51599/627b7977-9dec-d927-01ea-ff21867c22d6.png)

この AWS Amplify アプリケーションID は、 **アプリケーション作成後でないと指定できない** ため、 **最初の CloudFormationデプロイが完了した後** に手動で入力する必要があります。

### 2.アプリケーションIDを指定できるテンプレートに変更する

スマートさには欠けますが、上述した循環参照を避けるために、**AWS CodeCommit 上で指定する AWS Amplify アプリケーションID を手動で指定** します。そのために、上のCloudFormationテンプレートに少し修正を加えます。

```yaml
Parameters:
  AmplifyConsoleAppId:
    Type: String
    Default: ''
    Description: Amplify Console application id this template created
  DomainName:
    Type: String
    AllowedPattern: .+
    Description: Custom domain name for your Amplify Console application [required]
  RepositoryName:
    Type: String
    AllowedPattern: .+
    Description: Repository name on CodeCommit [required]

Conditions:
  CreateCodeCommitTrigger: !Not [ !Equals [ !Ref AmplifyConsoleAppId, ''] ]

Resources:
  CodeCommit:
    Type: 'AWS::CodeCommit::Repository'
    Properties: 
      RepositoryDescription: !Ref DomainName
      RepositoryName: !Ref RepositoryName
      Triggers:
        - DestinationArn: !Ref SNSTopic
          Events:
            - all
          Name: Notify all events to SNS
        - !If
           - CreateCodeCommitTrigger
           - DestinationArn: !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:amplify_codecommit_topic
             CustomData: !Ref AmplifyConsoleAppId
             Events:
              - all
             Name: !Sub AmplifyTrigger-${AmplifyConsoleAppId}
           - !Ref AWS::NoValue
```

``AmplifyConsoleAppId`` パラメータを手動で指定することでトリガを指定することができますが、この AWS Amplify アプリケーションID は、 **アプリケーション作成後でないと指定できない** ため、 **最初の CloudFormationデプロイ は空白のままで実行** し、AWS Amplify アプリケーションID を指定した上で、 **再度CloudFormationデプロイを実行** する必要があります。

以上で、**AWS CodeCommit へのコードプッシュのトリガで、AWS Amplify Console のデプロイが実行される、CI/CDワークフローを構築することができました** 。