AWSに画像をアップロードする

Heroku上で扱う画像ファイルなどは、アプリがデプロイされるか、
アプリが再起動（24時間に1回自動で行われる）される度に消えてしまう仕様です。

なので、アップロードされた画像を「AWS」というサービスに保存されるようにする事で
この問題を解決していきたいと思います。

#目次
①.AWSアカウントの作成
②.AWS上のセキュリティ対策
③.S3でバケット（保存先）を作成
④.画像がS3のバケットに保存されるように設定

---

それでは早速始めていきます！

##①AWSアカウントの作成

https://aws.amazon.com/jp/
にアクセスしてサインアップします。
注意点は、アカウントの種類は「パーソナル」を選択すること。
サポートプランは無料のベーシックプランでOK
ここまできたら、実際にサインインして確認します。

##②AWS上のセキュリティ対策
3つの設定を行います。

1.二段階認証の設定
2.IAMユーザーの作成
3.git-secretsの利用

###1.二段階認証の設定

Authyというアプリケーションを利用するので、
ますは「Authy」を自分のスマホにインストールします。
初期設定が終われば、次はAWSマネジメントコンソールにアクセスします。

ヘッダーからアカウント名をクリックし、メニューの中から 「マイセキュリティ資格情報」 を選択
[![Image from Gyazo](https://i.gyazo.com/fac47cc9429d72341acc6de35c2e0817.png)](https://gyazo.com/fac47cc9429d72341acc6de35c2e0817)

モーダル画面が表示されたら「Continue to Security Credentials」 をクリック。

メニューの中の 「多要素認証（MFA）」 をクリックすると内容が展開されるので、「MFAの有効化」 をクリック
[![Image from Gyazo](https://i.gyazo.com/cd80867c79df3a8cfcb587786f2638ee.png)](https://gyazo.com/cd80867c79df3a8cfcb587786f2638ee)

モーダル画面の中から「仮想MFAデバイス」が選択されているのを確認して、続行をクリック
→次の画面で「QRコードの表示」をクリックすると、QRコードが表示されます。
→この状態で先ほどの「Authy」アプリを起動し、「Accounts」にある「＋」ボタンをタップ
→設定画面を終了すると、二段階認証用のパスワードが表示されるようになります。

ワンタイムパスワードなので、30秒経つと自動的にそのパスワードは無効になります。
このパスワードをAWSで入力します。

1つ目のパスワードを上の欄に、その次に表示されたパスワードを下の欄に入力
最後に「MFAの割り当て」をクリックして設定は完了

---

###2.IAMユーザーの作成
IAMユーザーとは、AWSのサービスの1つで、機能を制限したユーザーを作成できる機能のこと
※最初にAWSで作ったアカウントはルートユーザー

検索のフォームに「IAM」と打ち込み、出てきた検索結果からIAMのページに飛ぶ
[![Image from Gyazo](https://i.gyazo.com/7c0c028bafef6f1249919d329aa90d4f.png)](https://gyazo.com/7c0c028bafef6f1249919d329aa90d4f)

遷移先のページで個々の「IAMユーザーの作成」をクリックし、「ユーザーの管理」をクリック
[![Image from Gyazo](https://i.gyazo.com/74c54f210ab8a1f06434b422d8de711d.png)](https://gyazo.com/74c54f210ab8a1f06434b422d8de711d)

遷移先のページで「ユーザーを追加」をクリックすると下記の画面になりますので、
作成するユーザー名の入力と、「プログラムによるアクセス」にチェック
そのあとは右下の「次のステップ：アクセス権限」をクリックして進みます。
[![Image from Gyazo](https://i.gyazo.com/5a97a3d3e5ad7ff383a338e325519476.png)](https://gyazo.com/5a97a3d3e5ad7ff383a338e325519476)

「既存のポリシーを直接アタッチ」から検索窓に「amazons3」と入力し、
「AmazonS3FullAccess」に設定後、右下の「次のステップ：タグ」をクリック
[![Image from Gyazo](https://i.gyazo.com/181b5f0c6f1224d2e17bbc20ef0af0fe.png)](https://gyazo.com/181b5f0c6f1224d2e17bbc20ef0af0fe)

遷移後は何もせず「次のステップ: 確認」をクリック
最後に設定の確認をして、「ユーザーの作成」をクリック

これで「IAMユーザー」の作成が完了です。
最後に、忘れずに認証情報（.csv）をダウンロードしておきます。

---

####「IAMユーザーのパスワード」を設定

IAMのメニューから作成したIAMユーザーをクリック
→「認証情報」のタブをクリックすると以下のページに遷移するのでコンソールのパスワード欄の「管理」 をクリック
[![Image from Gyazo](https://i.gyazo.com/8e545171b9e3192a7a778f1257ab6a53.png)](https://gyazo.com/8e545171b9e3192a7a778f1257ab6a53)

「有効化」「自動生成パスワード」をクリック
[![Image from Gyazo](https://i.gyazo.com/9a84e1bc02ed1e31bd74c5ffb00c498d.png)](https://gyazo.com/9a84e1bc02ed1e31bd74c5ffb00c498d)

ルートユーザーの時と同じく、認証情報はダウンロードしておきます。

---

####作成したIAMユーザーでログイン
今作成したIAMユーザーでログインできることを確認してみます。
まずルートアカウントのサインアウトを行います。
[![Image from Gyazo](https://i.gyazo.com/4d15682fbf2c4b2937bea5ec871905a1.png)](https://gyazo.com/4d15682fbf2c4b2937bea5ec871905a1)

続いて、先ほどダウンロードしたcredential.csvを開くと下記の情報が記載されている

```
User name
Password
Console login link
```
User name と Password を入力して、Console login link のURLにアクセス

その後の画面で正常にログインできれば成功です。

---

####ルートユーザーで再度サインイン
サインイン画面で「ルートユーザーのEメールを使用したサインイン」を選択。
二段階認証を経てログインできます。

---

####IAMユーザーも二段階認証に
IAMのユーザー選択画面から先ほど作成したユーザー名を選択し、
遷移したページ先で以下の「MFAデバイスの割当」をクリック
[![Image from Gyazo](https://i.gyazo.com/b29c9211240270cce30772870e9e3551.png)](https://gyazo.com/b29c9211240270cce30772870e9e3551)

あとは ルートユーザーの時と同じ手順で二段階認証の設定を行う。

---

###3.git-secretsの利用
「git-secrets」は、誤操作でパスワードをGitHubにpushしてしまう防いでくれるツールです。

まずはターミナルから、Homebrewを経由してgit-secretsを導入。

```
% cd ~/
% brew install git-secrets
```

git-secretsが導入できたら、設定を適用したいリポジトリに移動し、git-secretsを有効化

```
% cd アプリケーション名 #開発中のアプリケーションディレクトリに移動
% git secrets --install
```
続いて、「どのようなコードのコミットを防ぐのか」を設定していきます。
この後の作業は開発中アプリケーションのディレクトリで実行します。

---

####git-secretsの条件を設定
下記のコマンドを実行することで、secret_key, access_keyなど、
アップロードしたくないAWS関連の秘密情報を一括で設定することができる

```
% git secrets --register-aws --global
```

現在のgit-secretsの設定は、下記のコマンドで確認することができる

```
% git secrets --list

secrets.providers git secrets --aws-provider
secrets.patterns [A-Z0-9]{20}
secrets.patterns ("|')?(AWS|aws|Aws)?_?(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)("|')?\s*(:|=>|=)\s*("|')?[A-Za-z0-9/\+=]{40}("|')?
secrets.patterns ("|')?(AWS|aws|Aws)?_?(ACCOUNT|account|Account)_?(ID|id|Id)?("|')?\s*(:|=>|=)\s*("|')?[0-9]{4}\-?[0-9]{4}\-?[0-9]{4}("|')?
secrets.allowed AKIAIOSFODNN7EXAMPLE
secrets.allowed wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
```

これで、「git secrets --install」を行なったリポジトリでは、
コミット時にAWSの秘密情報を含んだコードがないかチェックされるようになりました。
「うっかりパスワードをGitHubに載せてしまった」という事態を防ぐことができるので、必ず設定を行う。

---

####今後作成する全てのリポジトリにgit-secretsが適用されるように設定
特段の理由がなければ、以下のコマンドを実行して自動で適用されるようにしておく。

```
% git secrets --install ~/.git-templates/git-secrets
% git config --global init.templatedir '~/.git-templates/git-secrets'
```
---

###GitHub Desktopからgit-secretsを利用できるように設定
GitHub Desktop経由でgit secretsを利用する場合は、追加の設定をします。
この時、Github Desktopがapplicationフォルダに存在している必要があ流ので適宜移動しておく。

以下を実行

```
% sudo cp /usr/local/bin/git-secrets /Applications/GitHub\ Desktop.app/Contents/Resources/app/git/bin/git-secrets
```

上記コマンドで「No such file or directory」のエラーがでる場合は以下のコマンドを実行

```
% sudo cp /usr/local/bin/git-secrets /Applications/GitHub\ Desktop.app/Contents/Resources/git/bin/git-secrets
```

---

###③S3を利用してバケットを作成
S3で実際にデータが格納される場所のことを「バケット」と呼びます。
バケットの名前はアクセスするときのURLに使用されるため、
英数字で、まだ誰も付けたことがない名前を使う必要がある。

「ストレージ」のS3をクリック
[![Image from Gyazo](https://i.gyazo.com/0f8ad36cdfdf2d4cccabcd79f36a7f1c.png)](https://gyazo.com/0f8ad36cdfdf2d4cccabcd79f36a7f1c)

遷移後の画面で「バケットを作成」をクリックし、バケット名の入力とリージョンを設定。
リージョンは「アジアパシフィック（東京）ap-northeast-1」 を選択し「次へ」
[![Image from Gyazo](https://i.gyazo.com/dd62c15afd96ebb324500bd813cc2f85.png)](https://gyazo.com/dd62c15afd96ebb324500bd813cc2f85)

次の画面ではそのまま「次へ」

遷移後の画面で、「パブリックアクセスをすべてブロック」のチェックを外し、
新規の〜、任意の〜 にチェックを入れ、最後に上段のオレンジ色の注意喚起箇所のチェックを入れる。
「次へ」を選択し「バケットを作成」で完了です。
[![Image from Gyazo](https://i.gyazo.com/0f469ad994ea2779041573393a2713a5.png)](https://gyazo.com/0f469ad994ea2779041573393a2713a5)

---

####バケットポリシー
どのようなアクセスに対してS3への保存やデータの読み取りを許可するか決められる仕組み。
今回は、作成したIAMユーザーからのアクセスのみを許可するように設定します。

AWSのトップページから「IAM」と検索し、表示されたメニューをクリック

「ユーザー」を選択し、「ユーザー名」のところの先ほど作成したユーザー名をクリック
[![Image from Gyazo](https://i.gyazo.com/c70b19b2875a69270c10836ab915d302.png)](https://gyazo.com/c70b19b2875a69270c10836ab915d302)

IAMユーザーの情報が表示されるので、必要な情報を取得。
後ほど「ユーザーのARN」が必要にるので、一度メモアプリなどに保存しておきます。

次に、バケットポリシーの設定を行います。
IAMの時と同じようにサービス一覧からS3を選びます。先ほど作成したバケットをクリック。

「アクセス権限」をクリックし、「バケットポリシー」をクリック
表示されるコード記述欄にポリシーの入力をします。
先ほどメモしておいた 「ユーザーのARN」を①に、「作成したバケット名」を②に記述します。

```
{
   "Version": "2012-10-17",
   "Id": "Policy1544152951996",
   "Statement": [
       {
           "Sid": "Stmt1544152948221",
           "Effect": "Allow",
           "Principal": {
               "AWS": "①"
           },
           "Action": "s3:*",
           "Resource": "arn:aws:s3:::②"
       }
   ]
}
```
書けたら右上の「保存」をクリック
※保存ボタンを押した直後に「このバケットに対してパブリックアクセスの〜」と出ていれば保存成功。

---

###④画像がS3のバケットに保存されるように設定
1.投稿機能の挙動確認
2.ローカル環境の画像保存先をS3に変更
3.本番環境の画像保存先をS3に変更

####1.投稿機能の挙動確認
下記2点ができていることを確認します。
・ローカル環境で画像投稿が出来る
・画像がデータベースに保存されている

####2.ローカル環境の画像保存先をS3に変更
以下の要領で進めていきます。

a.必要なGemをインストール
b.保存先を指定
c.環境変数を設定
d.正しく保存できるか確認

####a.必要なGemをインストール
下記Gemを追記し、bundle install

```
gem "aws-sdk-s3", require: false
```

####b.保存先を指定
development.rbを編集します。
画像の保存先を「:local」→「:amazon」に変更

```config/environments/development.rb

config.active_storage.service = :local　←修正前
config.active_storage.service = :amazon　←修正後
```

storage.ymlを編集します。

```config/storage.yml
test:
 service: Disk
 root: <%= Rails.root.join("tmp/storage") %>

local:
 service: Disk
 root: <%= Rails.root.join("storage") %>

amazon:
 service: S3
 access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
 secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
 region: ap-northeast-1
 bucket: 自身のバケット名

（省略）
```

今設定した 

```
「 access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>」「secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>」 
```

の部分は環境変数を読み込むための記述です。

####c.環境変数を設定
`【MacOSがCatalina以降の場合】`
ターミナルで以下のコマンド実行

```
 vim ~/.zshrc
```
「i」を打ち込みインサートモードへ。下記のコードを貼り付けます。

```
export AWS_ACCESS_KEY_ID="ここにCSVファイルのAccess key IDの値をコピー"
export AWS_SECRET_ACCESS_KEY="ここにCSVファイルのSecret access keyの値をコピー"
```

完了したら、「escキー」→「:wq」の順で実行し、保存。
編集後は「.zshrc」を読み込み直す。

```
% source ~/.zshrc
```

`【MacOSがMojave以前の場合】`
ターミナルで以下のコマンド実行

```
 vim ~/.bash_profile
```
「i」を打ち込みインサートモードへ。下記のコードを貼り付けます。

```
export AWS_ACCESS_KEY_ID="ここにCSVファイルのAccess key IDの値をコピー"
export AWS_SECRET_ACCESS_KEY="ここにCSVファイルのSecret access keyの値をコピー"
```

完了したら、「escキー」→「:wq」の順で実行し、保存。
編集後は「.zshrc」を読み込み直す。

```
$ source ~/.bash_profile
```

####d.正しく保存出来るか確認
ローカル環境で開発中のアプリから画像投稿を行い、S3に保存されているか確認してください。
バケット名をクリックすると投稿した画像があるはずなので、投稿日時を確かめ、今投稿した画像であることを確認します。

###3.本番環境の画像保存先をS3に変更
以下の要領で進めます。

a.保存先を指定
b.Heroku上で環境変数を設定
c.正しく保存できるか確認

####a.保存先を指定
本番環境の設定ファイルを開き、画像がS3に保存されるように設定を変更します。

production.rbを編集

```config/environments/production.rb
config.active_storage.service = :local 　←修正前
config.active_storage.service = :amazon　←修正後
```

####b.Heroku上で環境変数を設定
Herokuで環境変数を扱うにはマスターキーの時と同様、「heroku config:setコマンド」を打つ必要がある。

ターミナル（開発中のアプリ）で実行

```
heroku config:set AWS_ACCESS_KEY_ID="ここにCSVファイルの「Access key ID」の値をコピー"
```

```
heroku config:set AWS_SECRET_ACCESS_KEY="ここにCSVファイルの「Secret access key」の値をコピー"
```

環境変数が正しく設定できているかを確認するためにターミナル（開発中のアプリ)で下記のコマンドを入力

```
% heroku config
```

AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY

がそれぞれ設定されていたら成功。
編集内容をコミット。（ブランチを切っている場合は必ずmasterブランチにマージする）
ターミナル（開発中のアプリ）で下記のコマンドを実行し、編集した内容をHerokuに反映。

```
% git push heroku master
```

このコマンドにより、herokuがリモートリポジトリのmasterブランチを参照することが出来る。

####c.正しく保存できるか確認
本番環境で、開発中のアプリから画像投稿を行います。
そのためには以下の方法で「Web URL」を確認する必要がある。

以下のコマンドをターミナル（開発中のアプリ）で実行することで、
Herokuにデプロイされたアプリケーションの情報を見ることができる。

```
% heroku apps:info
```

「Web URL: 〜」の部分を確認できたら公開されたページにアクセスし、画像投稿をします。
その後、AWSトップページからS3にアクセスし、バケットにファイルが保存されているか確認します。

バケット名をクリックすると投稿した画像があるはずなので、投稿日時を確かめ、今投稿した画像であることを確認します。

---

以上です。お疲れ様でした。