サービスアカウントに直接成り代わって gcloud コマンドを実行する

# これはなんですか

- サービスアカウントキーを発行せずにサービスアカウント権限を使う方法を紹介します。
- ドキュメントを2週間くらい読み漁ってようやくたどり着いたのでメモしておきます。

# 結論

- `--impersonate-service-account=SERVICE_ACCOUNT_EMAIL` オプションを使う。
    - https://cloud.google.com/iam/docs/understanding-service-accounts?hl=ja#directly_impersonating_a_service_account
    - 記事作成時点で日本語が怪しいので注意。


# コマンドリファレンス

[こちら](https://cloud.google.com/sdk/gcloud/reference/?hl=ja#--impersonate-service-account)

> --impersonate-service-account=SERVICE_ACCOUNT_EMAIL
> 
> For this gcloud invocation, all API requests will be made as the given service account instead of the currently selected account. This is done without needing to create, download, and activate a key for the account. In order to perform operations as the service account, your currently selected account must have an IAM role that includes the iam.serviceAccounts.getAccessToken permission for the service account. The roles/iam.serviceAccountTokenCreator role has this permission or you may create a custom role. Overrides the default auth/impersonate_service_account property value for this command invocation.



# やってみる

## サービスアカウントを作成する

コンソールで作成した。gcloudで使いたい命令に合わせて適当な役割を付与する。

## サービスアカウントを利用したいユーザーにサービスアカウントトークン作成者 ( `roles/iam.serviceAccountTokenCreator` ) 役割を付与する

コンソールで設定した。ポリシー設定はこんな感じ。

```shell
$ gcloud iam service-accounts get-iam-policy compute-engine-user@project-id-xxxxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com
bindings:
- members:
  - user:username@domain.com
  role: roles/iam.serviceAccountTokenCreator
etag: BwWZ8ZWaXPY=
version: 1
```

## サービスアカウントを利用したいユーザーにService Usage ユーザー ( `roles/serviceusage.serviceUsageConsumer` ) 役割を付与する

※本来は不要。APIの有効化の際に利用するので横着して付与したのを必須と勘違いしていた。。。
コンソールで設定した。Cloud IAMポリシーはこんな感じ。

```shell
$ gcloud projects get-iam-policy project-id-xxxxxxxxxxxxxxxxxxxxx
bindings:
- members:
  - serviceAccount:compute-engine-user@project-id-xxxxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com
  - user:ownername@gmail.com
  role: roles/owner
- members:
  - user:username@domain.com
  role: roles/serviceusage.serviceUsageConsumer
etag: BwWZ8b2sW30=
version: 1
```

## gcloud コマンドのオプションに `--impersonate-service-account=SERVICE_ACCOUNT_EMAIL` を指定する。

https://cloud.google.com/sdk/gcloud/reference/?hl=ja#--impersonate-service-account

`SERVICE_ACCOUNT_EMAIL` は作成したサービスアカウントのEMailアドレスである。

色々聞かれるので `y` を選択してAPIを有効化する

```shell
$ gcloud projects get-iam-policy project-id-xxxxxxxxxxxxxxxxxxxxx --impersonate-service-account=compute-engine-user@project-id-xxxxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com
WARNING: This command is using service account impersonation. All API calls will be executed as [compute-engine-user@project-id-xxxxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com].
API [cloudresourcemanager.googleapis.com] not enabled on project
[86128378245]. Would you like to enable and retry (this will take a
few minutes)? (y/N)?  y

Enabling service [cloudresourcemanager.googleapis.com] on project [86128378245]...
WARNING: This command is using service account impersonation. All API calls will be executed as [compute-engine-user@project-id-xxxxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com].
WARNING: This command is using service account impersonation. All API calls will be executed as [compute-engine-user@project-id-xxxxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com].
Operation "operations/acf.ce0886e2-bcb4-45dd-9773-5a276fd570e4" finished successfully.
bindings:
- members:
  - serviceAccount:compute-engine-user@project-id-xxxxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com
  - user:ownername@gmail.com
  role: roles/owner
- members:
  - user:username@domain.com
  role: roles/serviceusage.serviceUsageConsumer
etag: BwWZ8b2sW30=
version: 1
```

ただ、そもそも権限が通ってない場合は以下のエラーになる。 (serviceusage.services.use権限がないって言われている + APIが無効)

```shell
$ gcloud projects get-iam-policy project-id-xxxxxxxxxxxxxxxxxxxxx --impersonate-service-account=compute-engine-user@project-id-xxxxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com
WARNING: This command is using service account impersonation. All API calls will be executed as [compute-engine-user@project-id-xxxxxxxxxxxxxxxxxxxxx.iam.gserviceaccount.com].
ERROR: (gcloud.projects.get-iam-policy) PERMISSION_DENIED: Caller does not have required permission to use project project-id-xxxxxxxxxxxxxxxxxxxxx. Grant the caller the Owner or Editor role, 
or a custom role with the serviceusage.services.use permission, by visiting https://console.developers.google.com/iam-admin/iam/project?project=project-id-xxxxxxxxxxxxxxxxxxxxx and then retry 
(propagation of new permission may take a few minutes).
- '@type': type.googleapis.com/google.rpc.Help
  links:
  - description: Google developers console
    url: https://console.developers.google.com
- '@type': type.googleapis.com/google.rpc.Help
  links:
  - description: Google developer console IAM admin
    url: https://console.developers.google.com/iam-admin/iam/project?project=project-id-xxxxxxxxxxxxxxxxxxxxx
If you want to invoke the command from a project different from the target resource project, use `--billing-project` or `billing/quota_project` property.
```

# 感想

- AWSで言う所の AssumeRole 的なことができる。鍵発行なしでAPIが叩けるので嬉しい。（多分）
- rate limitとかの罠があると思うので、高頻度の実行は考慮した方が良いと思う。
    - そもそもこの使い方は推奨されているのだろうか……

# 参考

https://medium.com/@tanujbolisetty/gcp-impersonate-service-accounts-36eaa247f87c