【问题标题】:How do I get projectId from GoogleCredentials?如何从 GoogleCredentials 获取 projectId?
【发布时间】:2019-05-02 21:29:24
【问题描述】:

我想使用 Python 获取我在 Google Cloud 上的所有 Dataproc 集群的列表。

我的服务帐户凭据存储在 JSON 密钥文件中,其位置由 env var GOOGLE_APPLICATION_CREDENTIALS 引用。这是我到目前为止的代码:

import os
import googleapiclient.discovery
from oauth2client.client import GoogleCredentials


def build_dataproc_service(credentials):
    return googleapiclient.discovery.build("dataproc", "v1", credentials=credentials)


def list_clusters():
    credentials = GoogleCredentials.get_application_default()
    dataproc = build_dataproc_service(credentials)
    clusters = dataproc.projects().regions().clusters().list(projectId="my-project", region="REGION").execute()
    return clusters


if __name__ == "__main__":
    list_clusters()

如您所见,我已经硬编码了 projectId ("my-project")。鉴于 projectId 存在于 JSON 密钥文件中,我希望我可以通过询问 credentials 对象的属性来获得它,但不存在这样的属性。 projectId 确实 存在嵌入在 credentials._service_account_email 字符串属性中,但从那里提取它很笨重并且感觉不对。

我认为一定有更好的方法。如何获取服务帐号所在项目的projectId?

请注意,最初我打算将此代码在 Google Compute Engine 实例上的 docker 容器中运行,但是将来有一天我可能希望在 GKE 上运行。不确定这是否会影响答案。

【问题讨论】:

    标签: python google-compute-engine google-cloud-dataproc


    【解决方案1】:

    考虑这一点的正式方式是,虽然 projectId 有时是服务帐户的属性,但 projectId 通常不是长期凭据的属性.例如,想想您在与您的 Google 帐户/电子邮件地址关联的 gcloud CLI(如果有)中使用的离线安装的个人凭据。该电子邮件身份不驻留在任何云项目中,但可用于派生 GoogleCredential 对象。

    从技术上讲,如果您想“正确地”执行此操作,则需要一个主服务帐户,该帐户有权在所有项目中拥有GET 服务帐户描述,这些项目包含您计划使用的实际服务帐户,然后调用IAM API 的 projects.serviceAccounts.get 在服务帐户电子邮件地址上,而不是在“凭据”对象上。那里的响应可以识别服务帐户所在的项目 ID。这相当于gcloud 命令:

    gcloud iam service-accounts describe my-service-account@projectid.iam.gserviceaccount.com
    

    但是,正如 Dagang 所说,从长远来看,通常会适得其反,开始假设服务帐户只会用于其所在项目的操作。特别是,虽然service account 资源本身存在于项目中,但它们通常以跨项目的方式使用。一种常见的操作模式是使用单个 GCP 项目来管理大量服务帐户,然后授予这些服务帐户对其他 GCP 项目中资源的各种细粒度访问权限。

    【讨论】:

    • 所以听起来我最好的选择是从计算引擎元数据服务器获取 projectId。这假设我的代码在 Dataproc 集群所在的同一个项目中运行,在我的情况下这是一个安全的假设。
    • 听起来是个合理的方法。
    【解决方案2】:

    我认为客户端代码不应该从 GoogleCredentials 中提取项目 ID。请参阅API doc 中的此代码 sn-p。

    from googleapiclient.discovery import build
    from oauth2client.client import GoogleCredentials
    
    credentials = GoogleCredentials.get_application_default()
    service = build('compute', 'v1', credentials=credentials)
    
    PROJECT = 'bamboo-machine-422'
    ZONE = 'us-central1-a'
    request = service.instances().list(project=PROJECT, zone=ZONE)
    response = request.execute()
    
    print(response)
    

    您也许可以从 credentials._service_account_email 推断项目 ID,但这并不可靠。此外,项目 A 中的服务帐户未绑定到该项目,它可能还具有其他项目的权限。

    【讨论】:

      【解决方案3】:

      服务帐户凭据 .json 包含 project_id,因此您可以这样做:

      # If this is running in a cloud function, then GCP_PROJECT should be defined
      if 'GCP_PROJECT' in os.environ:
          project_id = os.environ['GCP_PROJECT']
      
      # else if this is running locally then GOOGLE_APPLICATION_CREDENTIALS should be defined
      elif 'GOOGLE_APPLICATION_CREDENTIALS' in os.environ:
          with open(os.environ['GOOGLE_APPLICATION_CREDENTIALS'], 'r') as fp:
              credentials = json.load(fp)
          project_id = credentials['project_id']
      else:
          raise Exception('Failed to determine project_id')
      

      【讨论】:

        【解决方案4】:

        从 UnderlyingCredential 获取 ProjectId

        var credential = GoogleCredential.FromFile(googleCredentialPath);
        var projectId = ((Google.Apis.Auth.OAuth2.ServiceAccountCredential)credential.UnderlyingCredential).ProjectId;
        

        【讨论】:

        • 这是什么语言?
        【解决方案5】:

        在 C# 中

        var credentials = GoogleCredential.GetApplicationDefault();
        var projectId = (credentials.UnderlyingCredential as dynamic).ProjectId as string;
        

        【讨论】:

        • 我不确定您为什么要转换为动态,而不是 ServiceAccountCredential?
        猜你喜欢
        • 2020-12-01
        • 2023-03-20
        • 2017-08-31
        • 1970-01-01
        • 2020-09-23
        • 2017-11-17
        • 2022-11-08
        • 2018-07-02
        • 1970-01-01
        相关资源
        最近更新 更多