【ハンズオン】Flutter-for-webをS3でホストしてCloudFrontで配信する +aでCICDも！

## 前置き

### 想定読者
flutter-for-webをAWSのs3でホストしてCloudFrontを使用して配信したい方。
**+aでCICDも行って開発スピードを上げたい方**

### 目的
私が今回初めて参画したプロジェクトでフロントエンドをFlutter-for-webで実装をしておりその時の
- AWSを用いたSPAでのホスト
- codeシリーズ用いたCICD
- buildspec.ymlの書き方

上記で躓いたので共有したいと思いこの記事を書きました



## 準備

###　ハンズオンで作成する手順

1. S3の作成
1. CloudFrontの作成
1. FlutterプロジェクトをローカルでビルドしてS３にアップロードする  
**-------- ここまででS3 + CloudFrontでの配信は完了 --------**
1. IAMポリシーの作成
1. IAMロールの作成
1. GitHubと接続
1. CodePipelineの作成
1. 該当のリポジトリにbuildspec.ymlファイルを追加
1. 一旦FlutterコードをCloudFrontで確認できるか
1. 実際にCICDが行えているかを確認  
**-------- ここまででCICDの完了 --------**

## 開始！！！

### 1. S3の作成
まずはAWSマネジメントコンソールにログインして
上の検索欄でS3と検索してS３を選択してください

![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/902ea630-8eb4-35ec-4b5c-065e09aa3db9.png)

そしたら下記のような画面になるのでバケットの作成を押します
※自分は個人的に作っているものがあるのでバケット(5)と表示されていますがなにもない場合は0になります
![画像２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/a9d7e041-c444-c6e3-7eda-f07c847e4bbe.png)

バケット名を入力してそれ以外の項目はいじらずに一番下まで行きバケットの作成を押します
![画像3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/52666521-2c21-e409-8de6-41c00c1bb085.png)

そうするとバケットが作成されているのが確認できるのでS3バケットの作成は完了です！
![画像4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/b4d8e9e7-7f6e-36ed-5d32-1c883c66f538.png)

### 2. CloudFrontの作成

S3と同じ用に検索欄からCloudFrontを検索して選択してください
![画像5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/6bc2c704-e58c-9a7a-f593-6051d0d1a823.png)

ディストリビューションを作成を押します
![画像6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/325dd34e-093a-abde-b075-e45fd0450b3a.png)

`オリジンドメイン` : s3で作ったものを選択します
`名前` : オリジンドメイント同じで大丈夫です
`S3バケットアクセス` : はい、OAIを....の方を選択
`オリジンアクセスアイデンティティ` : 新しいOAIを作成を押して作成を押せばOKです
`バケットポリシー` : はい、バケットポリシーを..の方を選択
![画像7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/f575d607-2757-4e12-c91f-85be76ef24ef.png)

それ以外の項目は基本的にいじらなくて良いのですが1点だけ変更する場所があります
`デフォルトルートオブジェクト` : index.htmlに変更
そしたらディストリビューションを作成を押します
![画像8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/145d9077-aa51-0202-dd04-c1bd4ae1e1ac.png)

数分待つとステータスが`有効`になるのでこうなれば作成が完了です
![画像9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/0adc7a97-3637-1616-6c4a-bcb70794a728.png)

#### S３に適当なindex.htmlファイルを配置してCloudFrontのURLで見れるか確認してみましょう
先程作成したS3はまだ中身が空なのでアップロードボタンを押してアップロードしてみてください
![画像10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/57b4b213-b3e8-3c3b-0bb6-03b8fd1131ed.png)

※サンプルのindex.htmlファイルです

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Hello World</title>
</head>

<body>
    Hello World
</body>
</html>
```

アップロードし終わったらCloudFrontのURLにアクセスしてみます。
![画像11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/d23058b9-bfdc-d173-dbfe-5131c390b00c.png)

見れました！
![画像12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/a4ad394c-c964-8693-66b5-a4232c6bcb58.png)



### 4. IAMロールの作成
CodeBuildとCodePipelineで使用するIAMロールを作成していきます

IAMと検索してIAMを押します
![画像13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/b576c939-fda6-3c32-23f9-cac925189db2.png)

まずはIAMポリシーを作成します
左の欄からポリシーを選択してポリシーを作成を押します
![画像14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/36a1fd17-4571-d899-d146-3c2f41b66d2d.png)

CodeBuild、S3、CloudFront、CloudWatch Logsのフルアクセスをつけていきます
下記の画像はcodebuildをつけた例です。
右下の`さらにアクセス許可を追加する`からS3、CloudFront、CloudWatch Logsもつけていきます
![画像16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/da068fc2-814d-3a4c-bfb4-8a6ac305d9b4.png)

下記のようになれば正しくアクセスを追加できています
そしたら右下の次のステップタグを選択します。タグは付けなくていいのでスルーで確認をしてポリシーを作成します！
![画像17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/bad40d1a-37f1-0126-ed2b-5f6b65e86b09.png)

この感じでCodePipelineのポリシーも作成していきます
必要な権限はCodeStar Connections、s3、CodeBuild、CloudWatch Logsです


### 5. IAMロールの作成
CodeBuild、CodePipelineでつかうのでIAMユーザーではなくIAMロールを作成していきます

左のロールを選択してロールを作成を押します
![画像19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/a6a86cb6-07bd-c7a0-2277-abfc677cc606.png)

まずはCodeBuildから作成するのでCodeBuildを選択して次のステップに進みます
![画像20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/e19893d3-9b27-446f-1c33-fe141f1d3615.png)


ここで先ほど作成したCodeBuild用のポリシーを選択して次のステップに行きます
※検索欄で名前を検索することですぐに見つけることができます
![画像21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/412400ec-c350-f1d2-cf73-d51af5c5707e.png)

タグは付ける必要なく次のステップに進みロール名を入力して作成します
![画像22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/c1d64d5a-44a6-5a7b-7f28-a4a7595dd1a6.png)

この流れでCodePipelineも作成していきます
![画像23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/49d7abf8-a4d9-a202-42ad-f722872be37c.png)
**....なくない？！**
ってことでGUIからだとなぜかCodePipelineがないので一旦CodeBuildを選択して作って、後ほど編集しましょう！
※ポリシーはCodePipelineのポリシーをアタッチしてください

ロールから先程CodePipelineがなったのでCodeBuildで作成したものを選択して信頼関係の編集を押します
![画像25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/044e09b4-9185-8475-d695-3316e6de599b.png)

ServiceがCodeBuildのものになっていると思うので下記のように書き換えればOKです！
![画像26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/e1ffc8c9-a0fb-3240-3423-d354517164c8.png)

```
"Service": "codepipeline.amazonaws.com"
```


### 6. GitHubとの接続作成
検索欄でCodePipelineと検索をして左の欄の設定→接続を押します
![画像27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/44917d90-8b98-2576-af8b-b44be1e11c59.png)

GitHubを選択して接続名は任意で接続するを押します
![画像28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/796681d9-9568-d78e-2b4e-e6c233952a96.png)

新しいアプリを選択するとGitHubの画面に遷移するので接続したいGithubを選んで接続を押してください
![画像29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/60c30b63-d2ea-f817-6a95-3821ac2d9d86.png)

これでGitHubと接続できるようになりました。


### 7. CodePipelineの作成
`パイプラインを作成する`を押してパイプライン名とロールを選択して次に
※ロールは先程作ったのがあるので既存の方から選んでいきます
![画像30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/49cc47f5-acd2-6aa0-c4b9-81471d56e7c4.png)

`ソースプロバイダー` : GitHub(バージョン2)
`接続` : 入力欄をクリックすると先ほど作成した接続が表示されるのでそちらを選択
`リポジトリ名` : 自分がCICDを行いたいFlutter-for-webのリポジトリを選択します
`ブランチ名` : どのブランチにPushされたらCICDを動かすか選びます
その他の項目はいじらなくていいです
![画像31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/031d46fc-ef11-27d8-10a0-ecc1e1f05ec0.png)

`ソースプロバイダー` : AWS CodeBuild
`プロジェクト名` : CodeBuildはまだ作成してないのでプロジェクトを作成するを押します
※プロジェクトを作成するを押すと別タブで開くのでそこでCodeBuildの作成を行います
![画像32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/d4000fbb-bfd2-be4e-19af-0f777299b5c4.png)

`プロジェクト名` : 自分の好きな名前
`環境イメージ` ; マネージド型イメージ
`オペレーティングシステム` : Ubuntu
`ランタイム` : Standard
`イメージ` : aws/codebuild/standard:4.0
`環境タイプ` : Linux
`サービスロール` : ロールは先程作ったcodebuildのものを既存から選択
`ビルド仕様` : buildspecファイルを使用する
`Buildspec名` : buildspec.yml
それ以外はいじらないでcodepiplineに進むを押せば作成されます！
![画像34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/d9633cd1-4477-adc3-20bd-110337c00ab8.png)
![画像35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/356b080e-8afc-45d7-ef2f-93b24f864891.png)
![画像36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/6f2506b2-6acb-d8df-592e-2ae4c77fa554.png)

デプロイステージは使用しないのでスキップしてレビュー画面になるので一番下のCodePipelineを作成するを押します
![画像37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/084c46a3-ab90-2396-f9d9-2285f92bb222.png)

### 8. 該当のリポジトリにbuildspec.ymlファイルを追加

CICDを行いたいリポジトリのトップディレクトリ配下にbuildspec.ymlという名前でファイルを作成して下記コードを移します

```yml
version: 0.2

phases:
  install:
    runtime-versions:
      android: 29
      java: corretto8
  pre_build:
    commands:
      - echo Pre Build started
      - git clone https://github.com/flutter/flutter.git -b stable
      - export PATH="$PATH:`pwd`/flutter/bin"
      - flutter precache
      - flutter doctor
  build:
    commands:
      - flutter build web
  post_build:
    commands:
      - aws s3 sync build/web/ s3://{s3の名前} --exact-timestamps
      - echo "cloudfront cache delete"
      - aws cloudfront create-invalidation --distribution-id {CloudFrontのID} --path "/*"
      - echo "complete"
```

上記コードでS3の名前とCloudFrontのIDを入力するところがありますがそれの確認方法は

**S3の名前**
![画像38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/931c7f03-fa46-c1fa-a4f3-2a70db1916f8.png)

**CloudFrontのID**
![画像40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/f7c80462-80f3-3cfd-231b-7f844834db46.png)

### 9. 一旦FlutterコードをCloudFrontで確認できるか

こちらにサンプルのFlutter-for-webコードがあるのでcloneしてきてください
https://github.com/Masaki-G/flutter-for-web-sample

CloneできたらすべてのファイルをS3にアップロードします
![画像41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/11afc0d6-b0ef-ab39-93a0-4545e46e34ae.png)

アップロードできたら下記のようになります
![画像43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/3e40b4cd-ae15-9e5a-3699-22f4c01ec3ee.png)

CloudFrontのURLにアクセスして下記のようになれば成功です！
![画像44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/ebd5f2b1-f435-dc80-0efd-03810866bb1b.png)

最後にアップロードしてファイルは全て削除しておきます！

### 10. 実際にCICDが行えているかを確認  

下記で設定したリポジトリの対象のブランチになにか更新を行うとCICDが走るので更新してみてください
![画像31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/031d46fc-ef11-27d8-10a0-ecc1e1f05ec0.png)

CodePipelineにアクセスするとCICDが走ってるが確認できます
![画像45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501320/b7250f6e-f37d-0967-b189-16e4472c776a.png)

Buildが終わった後にS3を見てみるとBuildされた後のファイル群が保存されていると思うのでCloudFrontのURLで確認できると思います！


## 最後に
自分もVueなどのSPAでCICD構成は何度かやったことがあるのですが
Flutter-for-webをやるのは初だったのでbuildspec.ymlの記述が少し手こずりました...w
Flutter-for-webでも結構VueやReactなどと遜色ない感じがするので是非チャレンジしてみてください！