Cloud FunctionsなどでGoogle APIを定期実行する際のTips

今季のアニメはウマ娘とフリーレンを見ます。
アニメを見る時間といったものも最近ようやく作れるようになってきました・・・子育てイソガシイムズカシイ。（先日、子供が1歳になりました。）

# はじめに
業務でGoogle Cloud上のプロジェクトの一覧を定期的に取得したい、といった内容があり、それをCloud Functionsのスケジュール実行で実現しました。
実装するにあたってやったことをまとめておきます。

Google Cloud、こういった内容の大体は公式のドキュメントにまとまってはいるのですが、いつもいろんなページに飛んでしまうのでまとめておきたい、という意図があります。

# 最初にまとめ
Google APIを実行したい場合はサービスアカウントに対して権限を付与すればよい。
ローカルで実行したい場合はサービスアカウントキーを発行すればよい。
スケジュール実行は Cloud Schedulerで Pub/Sub経由でCloud Functionsをキックする。

# 前提
- Google Cloudのプロジェクト管理についてある程度理解している

# API
プロジェクトの一覧を取得するには、[Resource Manager](https://cloud.google.com/resource-manager/docs?hl=ja)で取得が可能です。
API公開されているため、それを利用してプログラムで自動化することも可能です。

# サービスアカウント
[認証情報の自動検出](https://cloud.google.com/docs/authentication/production?hl=ja#automatically) には以下のように書かれています。
>アプリケーションが Google Cloud 環境内で実行されていて、その環境にサービス アカウントが接続されている場合、アプリケーションはそのサービス アカウントの認証情報を取得できます。その後、アプリケーションはこの認証情報を使用して Google Cloud APIs を呼び出すことができます。
> Compute Engine、Google Kubernetes Engine、App Engine、Cloud Run、Cloud Functions など、さまざまな Google Cloud サービスのリソースにサービス アカウントを接続できます。これは、認証情報を手動で提供するよりも便利で安全なため、この方法をおすすめします。

以上から、今回のケースのようなCloud Functionsで実行する場合はサービスアカウントを接続する方法が良いようです。

## ローカル実行
スクリプトを書いている時は、ローカルで試しながらやりたいケースが多いと思います。
そのような場合はサービスアカウントキーを取得すればよいようです。

> アプリケーションには Google Cloud クライアント ライブラリを使用することをおすすめします。Google Cloud クライアント ライブラリでは、アプリケーションのデフォルト認証情報（ADC）というライブラリを使用して、サービス アカウントの認証情報を自動的に検索します。サービス アカウントの認証情報は次の順序で検索されます。
> 環境変数 GOOGLE_APPLICATION_CREDENTIALS が設定されている場合、ADC では、変数が示すサービス アカウント キーまたは構成ファイルを使用します。

サービスアカウントを作成し、JSONキーファイルをダウンロードします。
その後、以下のコマンドを実行して環境変数を設定します。
環境変数が設定された状態でスクリプトを実行することでサービスアカウントの認証を行うことができます。

```
export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/service-account-file.json"
```

しかし、[サービス アカウント キーを管理するためのベスト プラクティス](https://cloud.google.com/iam/docs/best-practices-for-managing-service-account-keys?hl=ja)にもあるように、キーの発行はセキュリティ上のリスクがあります。
ローカルでの検証が済んだ際にはキーを無効化・削除することを忘れないようにしましょう。
また、AWSなど別のパブリッククラウド、オンプレミス等で運用する場合は[Workload Identity 連携](https://cloud.google.com/iam/docs/workload-identity-federation?hl=ja)の利用が推奨されています。

## Google Cloud クライアント ライブラリ
上記でも触れていますが、Google Cloudクライアントライブラリを使用することで、認証をスムーズに行うことができます。
今回はPythonで書きましたので、 [google-cloud-resource-manager](https://github.com/googleapis/google-cloud-python/tree/main/packages/google-cloud-resource-manager)を使用しました。
Githubではサンプルも公開されているので、ローカルでの検証がスムーズに行えました。
Googleの公式ドキュメントからのリンクが見当たらなかったが、なぜだろう…。

## SAに割り当てるロール
話を戻して、サービスアカウント（SA）についてです。
サービスアカウントに対して権限を割り当てることで、サービスアカウントに関連付けられている各リソースもその権限に則った操作を行うことができます。

今回の用途は「Google Cloud上のプロジェクトの一覧を定期的に取得したい」というものです。
サービスアカウントに割り当てる権限（ロール）は、事前定義されたロールを利用することが可能です。
（カスタムしたロールを利用することも可能です。）

[事前定義ロールの使用](https://cloud.google.com/resource-manager/docs/access-control-folders?hl=ja#using_predefined_roles)から、[フォルダ閲覧者](https://cloud.google.com/resource-manager/docs/access-control-folders?hl=ja#resourcemanager.folderViewer)が適切なロールのようです。
> フォルダの取得、リソース内のフォルダとプロジェクトの一覧表示ができる権限を付与します。

### ロールを割り当てるプロジェクトについて
各プロジェクトのIAMコンソールにて、サービスアカウントのプリンシパルを指定し、任意のロールを割り当てることでサービスアカウントに権限を割り当てることが可能です。
ただ、上記の「フォルダ閲覧者」はプロジェクトのIAMでは設定できませんでした。
これはそのプロジェクトより下の階層がないため、リソース内にフォルダとプロジェクトが存在しないからだと思われます。

そのため、プロジェクトの一覧を取得したい最も上の階層のフォルダに移動します。
![スクリーンショット 2023-10-04 18.24.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3328969/6b021eb6-696a-88bc-d93e-ce272b4ff094.png)

そのフォルダに移動した状態で、作成したサービスアカウントに対して「フォルダ閲覧者」のロールを割り当てます。
フォルダに移動したあとのコンソールでは、プリンシパルの入力画面で予測変換で出てきませんが、直接サービスアカウントのプリンシパル名を入力することで設定できます。

プロジェクトでIAMロールを検索したもの。
![スクリーンショット 2023-10-04 18.23.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3328969/9a768d12-fc4a-4561-6738-cc2cae94d4bc.png)

フォルダでIAMロールを検索したもの。
フォルダに関連した事前定義ロールが確認できます。
![スクリーンショット 2023-10-04 18.22.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3328969/367dd6b9-9226-6199-3b2c-3244da2b461a.png)

今回、Cloud Functionsを作成したプロジェクトも、一覧化するプロジェクトに含まれています。
ツリー図にすると以下のようになります。

```
top_folder ←このフォルダ上で、サービスアカウントに「フォルダ閲覧者」のロールを割り当てる
├folder_a
│└project_a
├folder_b
│└project_b
├folder_c
│└project_c
└ops_folder
 └ops_project ←このアカウントにCloud Functionsをデプロイしている
```

※rootフォルダへの操作権限等があることが前提です。

このように、上位のフォルダの操作権限を持つものは、下の階層のフォルダ・プロジェクトへの操作も容易に行なえます。
だからこそ、最小限の権限を適切に割り当てることが大切です。

# スケジュール実行
Cloud Functionsのスケジュール実行は、[Cloud Scheduler](https://cloud.google.com/scheduler?hl=ja)で実現が可能です。
Cloud Functionsは、httpリクエストや、[Pub/Sub](https://cloud.google.com/functions/docs/calling/pubsub?hl=ja)をトリガーに実行できます。

流れとしては以下のようになります。
+ Pub/SubトピックをトリガーとするようCloud Functionsをデプロイ（pub/subサブスクリプションの登録）
+ Cloud SchedulerのターゲットをPub/Subトピックにする

以上で、Cloud Functionsのスケジュール実行が可能です。

# おわりに
Google Cloudを利用している中で、このようにサービスアカウントを含んだ設定というのはよくあるようケースではあると思いますが、毎回いろんなページを調べ直していたのでこの機会にまとめられてよかったです。


# Appendix
- [Github - google-cloud-python](https://github.com/googleapis/google-cloud-python)
- [Google Cloud - 認証のスタートガイド](https://cloud.google.com/docs/authentication/getting-started?hl=ja)
- [Cloud Scheduler - cron ジョブをスケジュールして実行する](https://cloud.google.com/scheduler/docs/schedule-run-cron-job?hl=ja)