AttributeError: 'tuple' object has no attribute 'authorize' - GCP Create Service Account with Workload Identity Federation

I am trying to create a service account using Python in GCP. This works fine when i've set env var GOOGLE_APPLICATION_CREDENTIALS to a JSON credentials file, and used the following code:

GoogleCredentials.get_application_default()

However the following code fails in CI - Github Actions using Workload Identity Federation:

import google
import googleapiclient.discovery
import os
from util import get_service_name

environment = os.getenv('ENVIRONMENT')

def create_service_account(requested_project_id):
    project_id = requested_project_id
    credentials = google.auth.default()

    service = googleapiclient.discovery.build(
        'iam', 'v1', credentials=credentials)

    service_account_name = f'svc-{get_service_name()}'

    service_accounts = service.projects().serviceAccounts().list(
        name='projects/' + project_id).execute()

    service_account_exists = False

    for account in service_accounts['accounts']:
        if (service_account_name in account['name']):
            service_account_exists = True
            service_account = account
            break

    if (service_account_exists == False):
        service_account = service.projects().serviceAccounts().create(
            name='projects/' + project_id,
            body={
                'accountId': service_account_name,
                'serviceAccount': {
                    'displayName': service_account_name
                }
            }).execute()
        
    print(f'{"Already Exists" if service_account_exists else "Created"} service account: ' + service_account['email'])

    return service_account

Fails with the error:

 File "/opt/hostedtoolcache/Python/3.9.0/x64/lib/python3.9/site-packages/googleapiclient/_helpers.py", line 131, in positional_wrapper
    return wrapped(*args, **kwargs)   File "/opt/hostedtoolcache/Python/3.9.0/x64/lib/python3.9/site-packages/googleapiclient/discovery.py", line 298, in build
    service = build_from_document(   File "/opt/hostedtoolcache/Python/3.9.0/x64/lib/python3.9/site-packages/googleapiclient/_helpers.py", line 131, in positional_wrapper
    return wrapped(*args, **kwargs)   File "/opt/hostedtoolcache/Python/3.9.0/x64/lib/python3.9/site-packages/googleapiclient/discovery.py", line 600, in build_from_document
    http = _auth.authorized_http(credentials)   File "/opt/hostedtoolcache/Python/3.9.0/x64/lib/python3.9/site-packages/googleapiclient/_auth.py", line 119, in authorized_http
    return credentials.authorize(build_http()) AttributeError: 'tuple' object has no attribute 'authorize'

I am using the following Github Action to authenticate with Google

- name: Authenticate to Google Cloud To Create Service Account
  uses: google-github-actions/[email protected]
  with:
    workload_identity_provider: 'projects/xxx/locations/global/workloadIdentityPools/github-actions-identity-pool/providers/github-provider'
    service_account: '[email protected]'

Can anyone help?


Solution 1:

You have two problems. This line of code is failing:

credentials = google.auth.default()

Problem 1 - Generate an Google OAuth Access Token

Change the GitHub Actions Step to:

- name: Authenticate to Google Cloud To Create Service Account
  uses: google-github-actions/[email protected]
  with:
    token_format: 'access_token'  # Your python code needs an access token
    access_token_lifetime: '300s' # make this value small but long enough to complete the job
    workload_identity_provider: 'projects/xxx/locations/global/workloadIdentityPools/github-actions-identity-pool/providers/github-provider'
    service_account: '[email protected]'

Problem 2 - Creating Credentials

This line will not work because the credentials are not available from ADC (Application Default Credentials).

credentials = google.auth.default()

Pass the access token generated by Workload Identity Federation to your program from from the GitHub Actions output:

${{ steps.auth.outputs.access_token }}

Create the credentials from the access token:

credentials = google.oauth2.credentials.Credentials(access_token)
service = googleapiclient.discovery.build('iam', 'v1', credentials=credentials)