Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically get current Service Account on GCP

Is there a way to programmatically access the email of the currently used Service Account on a GCP instance when no GOOGLE_APPLICATION_CREDENTIALS is set? (ie. when using the default Service Account)

I've looked through the GCP documentation, but the only resource I found can't be used with the default Service Account when no GOOGLE_APPLICATION_CREDENTIALS is set. I know that it is possible to do so using gcloud (see this SO question or documentation), however these solutions aren't applicable when running on a ContainerOptimisedOS. I've spent a couple of weeks back and forth with the GCP support team, but they concluded with not being able to help me and redirected me to Stack Overflow for help.

like image 218
Voy Avatar asked Dec 30 '25 00:12

Voy


2 Answers

The solution of John works great, on any language without any external library. However, it works only in Google Cloud environment, when a metadata server is deployed. You can't perform this test on your computer.

I propose just bellow a piece of Python code (with Google OAuth library, but it works in other languages that have this library) to ask the library the current credential. If the credential is a service account (from GOOGLE_APPLICATION_CREDENTIALS on your computer, the ADC (Application Default Credential) or from the metadata server), you have the email printed, else you have a warning message because you use your user account credential:

import google.auth

credentials, project_id = google.auth.default()

if hasattr(credentials, "service_account_email"):
    print(credentials.service_account_email)
else:
    print("WARNING: no service account credential. User account credential?")

Note that if the default service account is used this method will print default instead of the entire email address.

The Go version:

ctx := context.Background()
credential,err := google.FindDefaultCredentials(ctx)
content := map[string]interface{}{}

json.Unmarshal(credential.JSON, &content)
if content["client_email"] != nil {
    fmt.Println(content["client_email"])
} else {
    fmt.Println("WARNING: no service account credential. User account credential?")
}
like image 142
guillaume blaquiere Avatar answered Dec 31 '25 17:12

guillaume blaquiere


Just adding to the accepted answer. As stated in the answer this seems to return "default":

import google.auth

credentials, project_id = google.auth.default()
# returns "default"
print(credentials.service_account_email) 

I found to get the email name of the GSA currently active (via the metadata api) I had to manually refresh first:

import google.auth
import google.auth.transport.requests

credentials, project_id = google.auth.default()
request = google.auth.transport.requests.Request()
credentials.refresh(request=request)
# returns "[email protected]"
print(credentials.service_account_email) 

I'm using workload ID. I think maybe there is a race condition and I'm trying to read the service_account_email property before the creds get initialized for the first time when the pod starts.

The _retrieve_info() function is called when refresh() is called and it appears this is the function that grabs the email name.

If I had my script sleep for a few seconds on start up, I wonder if service_account_email would eventually be populated with the email name of the GSA.

like image 44
red888 Avatar answered Dec 31 '25 19:12

red888