Compute Engineのデフォルト設定だとGoogle Cloud Storage(GCS)のアップロードが失敗する話

# 結論
* Compute EngineがGoogle CloudのAPIを利用するには以下2つの権限設定が必要
　1. IAM(サービスアカウント)　
　　→Compute Engineのデフォルト(Compute Engine default service account)はほぼ全権限の編集者ロールがついている
　2. アクセスコントロール　★こちらの設定変更が必要
　　→デフォルトではGCSの読み込み権限のみ

* ついでに、設定変更後にはキャッシュ削除が必要な場合あり

## やろうとしたこと、やったこと
Google Cloud(GCP)のCompute Engine(GCE)上で、Youtube APIにて取得したJSONファイルをGoogle Cloud Storage(GCS)にアップロードするシェルを作ろうとした。

バケットはコンソール上で作成。GCSの操作を行えるコマンドの「gsutil」でバケット表示(読み込み操作)はできるものの、ファイルアップロード(書き込み操作)が失敗する。

```:【読み込み操作】
$ gsutil ls gs://バケット名
gs://バケット名　　　→バケットの表示(読み取り)は可能
```

```:【書き込み操作】
$ gsutil cp アップロード対象ファイル gs://バケット名
Copying file://アップロード対象ファイル [Content-Type=text/plain]...
AccessDeniedException: 403 Access denied.  　　　→バケットへのファイルアップロード(書き込み)は不可
```

## 原因
GCEの「アクセスコントロール」設定ができていなかった。

アクセスコントロールについては、[Google Cloud公式ドキュメント(サービス アカウント)](https://cloud.google.com/compute/docs/access/service-accounts?hl=ja)に以下の説明がある。

> アクセス スコープは、インスタンスの権限を指定するレガシーな方法です。アクセス スコープはセキュリティ メカニズムではなく、gcloud ツールまたはクライアント ライブラリからのリクエストで使用されるデフォルトの OAuth スコープを定義するものです。これは、gRPC や SignBlob API など、OAuth を介して認証を受けないリクエストには影響しないことに注意してください。

GCEのアクセス制御は、上記の「アクセス スコープ」と「IAM(サービスアカウント)」を利用して行う。
サービスアカウントについては教科書やGoogle資料でよく見かける印象であるが、アクセス スコープについてはあまり説明を見かけた記憶がない。
そのため設定をアクセス スコープ設定をデフォルトのままにしてVMを作成していたのだが、アクセス スコープ設定がデフォルトだとGoogle Cloud Storage(GCS)への読み込み権限しか付いていないという落とし穴(フールプルーフな作り?)が存在する。

↓GCEのデフォルトアクセス制御設定
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3311265/44d54935-901e-95fb-767b-6176c4f8a408.png">

↓「APIごとにアクセス権を設定」を押してアクセススコープのデフォルト設定を見てみると、GCS(ストレージ)について「読み取りのみ」の権限となっている
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3311265/a1cc8a21-4a41-4d5b-a8ff-0115eb1864f8.png">

↓GCS(ストレージ)のアクセススコープを「読み取り/書き込み」の権限に変更する
※アクセススコープ設定はVMの作成時にも、作成後にも設定が可能。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3311265/cc262461-c807-5398-8e17-392ed80893ff.png">


これによりファイルアップロードが成功することが確認できた。
```:【書き込み操作】
$ gsutil cp アップロード対象ファイル gs://バケット名
Copying file://memo.txt [Content-Type=text/plain]...
- [1 files][  1.7 KiB/  1.7 KiB]                                                
Operation completed over 1 objects/1.7 KiB.  →バケットへのファイルアップロード(書き込み)が成功 
```
  
## サービスアカウントとの関連性(補足)
上記のアクセススコープによるAPIの許可と、サービスアカウントによるAPIの許可の両方があることで、GCE上でGCSへのファイルアップロードが成功する。
IAM(サービスアカウント)とアクセス スコープはより制約が厳しい方が適用されるとのことで、下記のG-gen様のブログにわかりやすく整理されている。
→[[参考]G-gen様公式ブログ「改めて Google Cloud (GCP) のサービスアカウントとVMのAPIアクセススコープを確認する。」](https://blog.g-gen.co.jp/entry/vm-api-iam)※G-gen様のブログにはとてもお世話になっています。

## アクセススコープとサービスアカウントの使い分け(補足)
使い方はわかったものの、アクセス制御に関する機能が２つあり冗長なように感じる。
実際にはどのような使い分けをすべきであるのかというと公式ドキュメントを何点か見たところ、
以下の設定が推奨されているように読み取れた。

__アクセススコープは「すべてのCloud APIに完全アクセス権を許可」し、
IAM(サービスコントロール)できめ細かい設定を実施する。__

以下等に関連する記載がある。

[Google Cloud公式ドキュメント(サービス アカウントの使用に関するベスト プラクティス)](https://cloud.google.com/iam/docs/best-practices-service-accounts?hl=ja#access-scopes)
>アクセス スコープに依存するのではなく、専用のサービス アカウントを作成し、きめ細かい許可ポリシーを使用して、サービス アカウントがアクセスできるリソースを制限します。

[Google Cloud公式ドキュメント(新しいインスタンスをサービス アカウントとして走行するように設定する)](https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances?hl=ja#using)
>通常は、cloud-platform アクセス スコープを設定してほとんどの Cloud APIs に対するアクセスを許可してから、関連する IAM ロールのみをサービス アカウントに付与できます。

[Google Cloud公式ドキュメント(サービス アカウント)](https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances?hl=ja#using)
>インスタンスで完全な cloud-platform アクセス スコープを設定し、IAM ロールを使用してサービス アカウントのアクセス権を管理することをおすすめします。


※ただ、それならばあまり意識することが少ないアクセススコープのデフォルトは「すべてのCloud APIに完全アクセス権を許可」とし、設定として有名なサービスアカウントのデフォルトを「閲覧者」などにしておいてほしいとは思う。

## はまったこと(ちょっと重要)
VM作成後にアクセス スコープを設定しなおした後にも、何度やってもファイルアップロードが「AccessDeniedException: 403 Access denied」で失敗する状態のままとなる事象が発生した。
かと思ったら特に何もしていないのに急にアップロードが成功するようになり、いろいろ設定変更し直したりしても解消しなかったのだが、どうやら __キャッシュが悪さをしている__ ようであった。

__「~/.gsutil」__ というのがgsutilコマンドのキャッシュフォルダのようで、こちらをフォルダごと削除するとアップロードが成功するようになった。
もし設定変更後にうまくいかない場合はこの対応をしてみてほしい。

## 最後に(所感)
資格勉強等机上で学んだつもりでも実機を触るとちょっとしたことでもつまづくのだが、そのトラブルがエンジニアを成長させるのだと前向きに捉えて頑張っていかねばなと再認識した。
また、今回エンジニア人生10年目で初めてQiitaに投稿したが、単に作業をするだけでなくブログに自身の体験を言葉でまとめるというのはかなり苦行で、先人達の残してくれた情報のありがたさについても再認識することができた。ただ、経験した内容を言語化するという行為は自身にとっても有益であると感じたので、可能な限りブログを書くという行為を続けていければと思う。