はじめに
googleカレンダーなどのgoogleサービスにアクセスするときは、サービスアカウントのjsonやAPIキーなどの認証ファイルを準備するよう公式のリファレンスで示しています。
しかし、GCPの計算リソースとサービスアカウントとアクセススコープをうまく利用することで、認証ファイルを使うことなくgoogleカレンダーなどのgoogleサービスにアクセスできます。
今回は、認証ファイルを発行せずにGoogle Compute Engine(以下GCE)からgoogleカレンダーへアクセスする手順を示します。
手順
Google Cloud SDKのインストール
以下のリンクを参照してください。
https://cloud.google.com/sdk/install
https://cloud.google.com/sdk/docs/initializing
Google Calendar APIを有効にする
APIを有効にします。
Cloud SDKの場合は以下の通りです。
gcloud services enable calendar-json.googleapis.com
(非公開カレンダーの場合)カレンダーにアクセスするためのサービスアカウントの準備
googleカレンダーはメールアドレスでアクセス権限を制御していますが、このメールアドレスにサービスアカウントを利用します。
公開されているカレンダーを閲覧したい場合は、この作業は不要です。
サービスアカウントの発行
Cloud SDKの場合は以下の通りです。
gcloud iam service-accounts create ${SVCACCOUNT_NAME} \
--display-name ${SVCACCOUNT_NAME}
${SVCACCOUNT_NAME}
は適宜設定してください。
目的のカレンダーにアクセス権限を設定する
先に発行したサービスアカウントがアクセスできるように権限を設定します。
今回はカレンダーの情報を取得したいだけなので閲覧権限です。
サービスアカウントは${SVCACCOUNT_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
というメールアドレス形式で得られるため、アクセスしたいカレンダーの共有設定で登録します。
googleカレンダーの [設定と共有] -> [特定のユーザーとの共有] から [ユーザーの追加] を実行します。
GCEインスタンスの設定
インスタンスは既に存在するものとします。
インスタンスを作成する段階で設定したい場合はドキュメントを参照してください。
https://cloud.google.com/sdk/gcloud/reference/compute/instances/create
スコープの設定
今回の罠ポイント。
GCEインスタンスに対してgoogleカレンダーへのアクセススコープを付与する必要があります。
compute instances set-service-account
において、 --scope
オプションに**「インスタンスに既に付与されているスコープ」と「カレンダーに対するスコープ」**をカンマ区切りで記述します。
カレンダーに対するスコープは、今回についてはカレンダーへの読み取り権限があれば良いので https://www.googleapis.com/auth/calendar.readonly
になります。
必要なスコープは以下のURLを参照してください。
https://developers.google.com/calendar/auth
https://www.googleapis.com/auth/cloud-platform
が元々付与されていたインスタンスの場合は以下のようになります。
# インスタンスが停止しているタイミングで実行
gcloud compute instances set-service-account \
projects/${PROJECT_ID}/zones/${ZONE}/instances/${INSTANCE_NAME} \
--scopes https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/calendar.readonly
現在のスコープの調べ方
インスタンスに既に付与されているスコープがわからない場合は、 compute instances describe
で調べます。
gcloud compute instances describe ${INSTANCE_NAME}
インスタンスの情報が出力されるので、 scopes
を探します。
......
serviceAccounts:
- email: xxx@yyy.gserviceaccount.com
scopes:
- https://www.googleapis.com/auth/devstorage.read_only
- https://www.googleapis.com/auth/logging.write
- https://www.googleapis.com/auth/monitoring.write
- https://www.googleapis.com/auth/servicecontrol
- https://www.googleapis.com/auth/service.management.readonly
- https://www.googleapis.com/auth/trace.append
......
(非公開カレンダーの場合)サービスアカウントの設定
インスタンスにサービスアカウントを紐付けます。
公開されているカレンダーを閲覧したい場合は、この作業は不要です。
# インスタンスが停止しているタイミングで実行
gcloud compute instances set-service-account \
projects/${PROJECT_ID}/zones/${ZONE}/instances/${INSTANCE_NAME} \
--service-account ${SVCACCOUNT_NAME}@${PROJECT_ID}.iam.gserviceaccount.com
pythonスクリプトを実行
以下は、設定したGCEインスタンス上で実行します。
ライブラリのインストール
pip3 install google-api-python-client pprint
pythonスクリプトの作成
カレンダーを読み込むためのスクリプトを書いていきます。
import googleapiclient.discovery
import google.auth
import datetime
import sys
from pprint import pprint
def create_credentials():
credentials, _ = google.auth.default()
return credentials
def create_service(credentials):
return googleapiclient.discovery.build('calendar', 'v3', credentials=credentials)
def fetch_calendar_events(service, calendar_id):
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
ret = service.events().list(calendarId=calendar_id, timeMin=now,
maxResults=10, singleEvents=True,
orderBy='startTime').execute()
return ret
def main(calendar_id):
credentials = create_credentials()
service = create_service(credentials)
events = fetch_calendar_events(service, calendar_id)
pprint(events)
if __name__ == '__main__':
args = sys.argv
calendar_id = args[1]
main(calendar_id)
実行
実行します。
引数にカレンダーIDを与えてあげてください。
# python3 fetch_calendar_events.py <calendar_id>
# 公開カレンダーの例
python3 fetch_calendar_events.py go1v09pqgsfc586u5ugeco97j4@group.calendar.google.com
上記コマンドを実行すると、ほたてカレンダーの情報が表示されます。
{'accessRole': 'reader',
'defaultReminders': [],
'description': 'ほたてちゃん親衛隊から\n'
'ほたてちゃんを襲いたいチェイサーから\n'
'ほたてちゃんに喰われたい女装子から\n'
'ほたてちゃんご本人まで\n'
'\n'
'※非公式カレンダーです。\n'
'※ご利用は自己責任でお願いします。\n'
'※バグのせいで欠勤になっても責任は負いかねます。\n'
'※バグ報告や機能改善提案は @hkak03key まで',
...
}
補足
サービスアカウント
サービスアカウントとは、GCP上における仮想のユーザとみなすことができます。
AWSではIAMロールに相当するものと考えられます。
(ただし、AWSにはロールにポリシーを直接アタッチするのに対し、GCPでは「役割」に権限を定義し、サービスアカウントに対してアタッチします。)
GCPでは、実際のユーザをプロジェクトに追加し、権限を与えることができます。
一方で、何らかのシステムにおいては実際のユーザは必要ない場合がほとんどです。
このような場合に、サービスアカウントを利用します。
例えば、今回のようにサービスアカウントを紐付けることで、権限を制御することができます。
一方で、サービスアカウントは「googleサービスへのアクセスユーザ」として利用できます。
つまり、非公開のgoogleカレンダーに対して共有ユーザとしてサービスアカウントを指定すると、そのカレンダーに対してアクセスが可能になります。
スコープ
スコープについての十分な理解は無いので、今回この記事を書くにあたって理解したことを書くと、
最終的なアクセス権限 =
(
"https://www.googleapis.com/auth/cloud-platform"
AND
{IAMで定義されたservice accountが持つgcpに対する権限}
)
OR
{gcp以外のgoogleサービスへのスコープ}
となっていました。
googleカレンダーはgcp以外のgoogleサービスへのスコープなので、別途与えなければいけないようです。
おわりに
認証ファイルを使うことなくGCEからgoogleカレンダーへアクセスする手順を示しました。
AWSのiamロールに慣れきっていると、ファイルレスで認証できたほうが幸せ感ありますよね。
ただ、ぽちぽちで設定してると見通しが悪くなるのでTerraform等でIaCをやりたくなります。
ちなみに、Google Cloud Functions(以下GCF)ではスコープという概念がないようです(2020-02現在)。
「これは詰んだ...」と思いながらダメ元でGCFで試したら動いた。
GCFではscope全許可のようですね...。それはそれでいいのだろうか?と思いつつ。。
それでは、本来の目的である「ほたてbotのpython移植ミッション」を進めます。
GASはもう書きたくない!!!!!!
それでは、また次回!
余談
スコープの件でめっちゃ詰んでたんですが、GCPUGのSlackに質問投げたら1時間で解決しました。
Q. 星咲さんはどこで手間取ってたの?
— 星咲ひかる@女装子エンジニアは月収を上げて新宿に住みたい (@hkak03key) February 11, 2020
A. GCEからカレンダーにアクセスできなくて泣いてた
ちなみに質問投げたら1時間で解決した模様https://t.co/rY9U9XdESJ
で、回答してくださった@apstndbさんの マギレコ・オタク イケメンコメントはこちら。
運命を変えたいなら GCPUG Slack に来て
— _ (@apstndb) February 11, 2020
この Slack で GCP ユーザーは救われるから
というわけで、みんなもGCPUG Slackに入ろう!!!!!!!
https://gcpug.slack.com/