動機
GCPで動作するアプリケーションを作っていると、アプリケーションの管理者=プロジェクトの管理者 であることが多いと思います。開発フェーズか運用フェーズかでそこに割り当てられている人は変わったとしても、この関係は変わりないのが理想と思います。
であれば、プログラムでプロジェクト管理者のアカウントを判断できれば、「アプリケーション管理者のアカウント」をアプリケーションで保持する必要ながなくなって便利です。
プロジェクト管理者のアカウントをコードで取得する手段を探してみたところ、Goで発見しましたので紹介します。
コード
サービスの名称と機能からIdentity and Access Managementあたりかと あたりをつけて探し始めて、たどり着いたのはResource Managerでした。プロジェクト管理者のアカウントを取得するには、以下の様に複数のAPIを経由します。
import (
"context"
"os"
"strings"
"google.golang.org/api/cloudresourcemanager/v1"
)
...
// context.Contextを取得
context := context.Background()
// Cloud Resource Managerサービスを取得
crmservice, err := cloudresourcemanager.NewService(context) // (1)Resource Managerサービスを取得
if err != nil {
// エラー処理
return
}
// プロジェクトのIAMポリシーを取得
projid := os.Getenv("GOOGLE_CLOUD_PROJECT") // (2)プロジェクトIDを取得
request := new(cloudresourcemanager.GetIamPolicyRequest)
iampolicy, err := crmservice.Projects.GetIamPolicy(projid, request).Do() // (3)IAMポリシーを取得
if err != nil {
// エラー処理
return
}
// このプロジェクトの管理者に含まれているか確認
adminuser := false
check_end:
for _, binding := range iampolicy.Bindings { // (4)各ポリシー
for _, member := range binding.Members { // (5)各メンバ
if strings.Contains(member, email) { // (6)Eメールアドレスで判断
adminuser = true
break check_end
}
}
}
// adminuser == tureならプロジェクト管理者
- Cloud Resource Managerサービスを取得します。
- 環境変数GOOGLE_CLOUD_PROJECTからプロジェクトIDを取得しています。Cloud Run/Goではこれで取得できましたが、その他のサービスでは同様に取得できるか分かりませんので、確認してください。
- プロジェクトのIdentity and Access Managerポリシーを取得します。メソッドチェインの最後のDo()メソッドが実行結果としてPolicyを返してくれます。
- Bindingsプロパティがポリシーにバインドされているプリンシパルの配列です。
- 各プリンシパルは複数のメンバーを含んでいます。このメンバーには個人アカウントだけではなく、サービスアカウントなども含まれます。
- Eメールアドレスでプロジェクト管理者かどうかを判断しています。個人アカウントの場合は先頭に"user:"が付いているので、Eメールアドレスを含んでいるか否かで判断しています。
ライブラリ
今回使ったライブラリのパッケージは以下。
一方でGoogle公式サイトには以下も載っています。
cloud.google.com/go/resourcemanager/apiv2
後者のライブラリは実装されている機能が少なすぎて、今回のように実装できません。というかAPI仕様を見る限り、ほとんど使い物にならないのではないかと思います。
GoのResource Managerに限らず、どのライブラリを使えばいいのか、こんなの見つけたけど使って大丈夫なのかと、迷うことがよくありますが、今回は以上の実装で目的を果たせました。また前者のライブラリは機能も十分そろっていそうなので、その他の用途にも使えると思います。