As the commenters have pointed out, this isn't trivial.

But, I'm always up for some gcloud-bashing ;-)

Your code sample suggests you want the answer in PowerShell which I don't have and hope you don't mind pointers (what follows is incomplete) in bash:

Does:

  • Project ID
  • Account name
  • Enabled|Disabled
  • Email
  • Keys
  • Creation (timestamp)

Doesn't:

  • Role assignments
  • Last used (audit logs?)
PROJECTS=$(gcloud projects list --format="value(projectId)")

for PROJECT in ${PROJECTS}
do
  echo "Project: ${PROJECT}"
  # Extracts ACCOUNT_ID, EMAIL (==ACCOUNT_ID@...), DISABLED
  ROBOTS=$(\
    gcloud iam service-accounts list \
    --project=${PROJECT} \
    --format="csvno-heading,email,email.split(\"@\").slice(0),disabled)")
  for ROBOT in ${ROBOTS}
  do
    # Parse results
    IFS=, read ENCODED_NAME EMAIL ACCOUNT_ID DISABLED <<< ${ROBOT}
    NAME=$(echo -e ${ENCODED_NAME} | base64 --decode)
    echo "  Service Account: ${NAME}"
    echo "    Disabled: ${DISABLED}"
    echo "    Email: ${EMAIL}"
    # Keys
    KEYS=$(\
        gcloud iam service-accounts keys list \
        --iam-account=${EMAIL} \
        --project=${PROJECT} \
        --format="value(name.scope(keys))")
    for KEY in ${KEYS}
    do
      echo "    Key: ${KEY}"
    done
    # Creation (Only searches back 30-days!)
    FILTER=""\
"logName=\"projects/${PROJECT}/logs/cloudaudit.googleapis.com%2Factivity\" "\
"resource.type=\"service_account\" "\
"protoPayload.methodName=\"google.iam.admin.v1.CreateServiceAccount\" "\
"protoPayload.request.account_id=\"${ACCOUNT_ID}\" "

    LOG=$(\
        gcloud logging read "${FILTER}" \
        --project=${PROJECT} \
        --format=json \
        --freshness=30d \
        --format="value(timestamp)")
    echo "    Created: ${LOG}"
  done
done

Notes

  • Service Account creation times -- IIUC -- can only be obtained through audit logs (CreateServiceAccount[Key]). One challenge with this is having to search back through the project's (entire) history to find these.
  • Only user-created Service Accounts are created (during the project's lifetime). Google-maintained account e.g. App Engine will not be found.
  • The script searches the logs for each account. This is inefficient and it would be better to search the logs once for all accounts and then merge the results.
  • Role assignments is difficult because of inheritance. A naive solution would get the IAM policy for each Project but this is insufficient as it doesn't cover Organizational|Folder permissions nor does it include resource-specific bindings.
  • I actually don't know how to grep the logs for last auth times and assume this is available through audit logs
  • Apologies for the gnarly base64 encoding of the account's displayName. This is to avoid over-eager parsing of (the majority of) names that include spaces. There's likely a better approach.
Answer from DazWilkin on Stack Overflow
🌐
Google Cloud
cloud.google.com › google cloud sdk › gcloud iam service-accounts keys list
gcloud iam service-accounts keys list | Google Cloud SDK | Google Cloud Documentation
Skip to main content · Console · English · Deutsch · Español – América Latina · Français · Português – Brasil · 中文 – 简体 · 日本語 · 한국어
🌐
Google Cloud
cloud.google.com › security › iam › list and get service account keys
List and get service account keys | Identity and Access Management (IAM) | Google Cloud
Click Keys. The Google Cloud console displays a list of keys for the service account. Execute the gcloud iam service-accounts keys list command to list service account keys.
🌐
Reddit
reddit.com › r/googlecloud › service account keys - observations
r/googlecloud on Reddit: Service account keys - observations
February 8, 2020 -

Thought I share some of my observations about service account keys. I did all this quick testing in GCP not QwikiLabs.

A service account created using web console looks like this. Note "No keys" (also the creation date is blank uhmm.. bug?)

IAM > Service Accounts

However when listed in CLI the key is there:

me@cloudshell:~ (tg-project1)$ gcloud iam service-accounts keys list --iam-account=sa1-693@tg-project1.iam.gserviceaccount.com
KEY_ID                                    CREATED_AT            EXPIRES_AT
4af846b08ffd89ca141804910a782cea1d99ff20  2020-02-08T07:42:44Z  2022-02-28T21:12:54Z
me@cloudshell:~ (tg-project1)$

Note the key seems to have a lieftime of ~2 years and 20 days

Google says in their documentation that they rotate the keys every two weeks (link), which does seem to be the case, since a different service account I had created months ago has a near-week-old key:

me@cloudshell:~ (tg-project1)$ gcloud iam service-accounts keys list --iam-account=tg-project1@appspot.gserviceaccount.com

KEY_ID CREATED_AT EXPIRES_AT

edc07c4a4721131cdfae14687562def66637552f 2020-02-02T15:38:29Z 2022-02-11T13:32:41Z

me@cloudshell:~ (tg-project1)$

IF you are prepping to the #ACE exam:

  • Service accounts always have keys. GCP console does not always list them.

  • Default (GCP-generated) service account keys have a lifetime of ~2yrs but they are rotated every 2 weeks.

It gets interesting when we create a new key for the service account:

me@cloudshell:~ (tg-project1)$ gcloud iam service-accounts keys create ~/sa1-693.json --iam-account=sa1-693@tg-project1.iam.gserviceaccount.com

created key [dd9b1d062013b394c5745aa6de52cb338ef62503] of type [json] as [/home/me/sa1-693.json] for [sa1-693@tg-project1.iam.gserviceaccount.com]

me@cloudshell:~ (tg-project1)$ gcloud iam service-accounts keys list --iam-account=sa1-693@tg-project1.iam.gserviceaccount.com

KEY_ID CREATED_AT EXPIRES_AT

dd9b1d062013b394c5745aa6de52cb338ef62503 2020-02-08T08:10:59Z 9999-12-31T23:59:59Z

4af846b08ffd89ca141804910a782cea1d99ff20 2020-02-08T07:42:44Z 2022-02-28T21:12:54Z

me@cloudshell:~ (tg-project1)$

  • The old key still exists, at least after a couple of minutes.

  • The new key has the expiration date of Dec 31st 9999 :-P

Tagging: #ACE #AssociateCloudEngineer

🌐
Fig
fig.io › manual › gcloud › iam › service-accounts › keys
gcloud iam service-accounts keys | Fig
gcloud iam service-accounts keys list · gcloud iam service-accounts keys upload · gcloud iam service-accounts list · gcloud iam service-accounts remove-iam-policy-binding · gcloud iam service-accounts set-iam-policy · gcloud iam service-accounts sign-blob ·
🌐
Fig
fig.io › manual › gcloud › iam › service-accounts
gcloud iam service-accounts | Fig
gcloud iam service-accounts keys list · gcloud iam service-accounts keys upload · gcloud iam service-accounts list · gcloud iam service-accounts remove-iam-policy-binding · gcloud iam service-accounts set-iam-policy · gcloud iam service-accounts sign-blob ·
Top answer
1 of 2
6

As the commenters have pointed out, this isn't trivial.

But, I'm always up for some gcloud-bashing ;-)

Your code sample suggests you want the answer in PowerShell which I don't have and hope you don't mind pointers (what follows is incomplete) in bash:

Does:

  • Project ID
  • Account name
  • Enabled|Disabled
  • Email
  • Keys
  • Creation (timestamp)

Doesn't:

  • Role assignments
  • Last used (audit logs?)
PROJECTS=$(gcloud projects list --format="value(projectId)")

for PROJECT in ${PROJECTS}
do
  echo "Project: ${PROJECT}"
  # Extracts ACCOUNT_ID, EMAIL (==ACCOUNT_ID@...), DISABLED
  ROBOTS=$(\
    gcloud iam service-accounts list \
    --project=${PROJECT} \
    --format="csvno-heading,email,email.split(\"@\").slice(0),disabled)")
  for ROBOT in ${ROBOTS}
  do
    # Parse results
    IFS=, read ENCODED_NAME EMAIL ACCOUNT_ID DISABLED <<< ${ROBOT}
    NAME=$(echo -e ${ENCODED_NAME} | base64 --decode)
    echo "  Service Account: ${NAME}"
    echo "    Disabled: ${DISABLED}"
    echo "    Email: ${EMAIL}"
    # Keys
    KEYS=$(\
        gcloud iam service-accounts keys list \
        --iam-account=${EMAIL} \
        --project=${PROJECT} \
        --format="value(name.scope(keys))")
    for KEY in ${KEYS}
    do
      echo "    Key: ${KEY}"
    done
    # Creation (Only searches back 30-days!)
    FILTER=""\
"logName=\"projects/${PROJECT}/logs/cloudaudit.googleapis.com%2Factivity\" "\
"resource.type=\"service_account\" "\
"protoPayload.methodName=\"google.iam.admin.v1.CreateServiceAccount\" "\
"protoPayload.request.account_id=\"${ACCOUNT_ID}\" "

    LOG=$(\
        gcloud logging read "${FILTER}" \
        --project=${PROJECT} \
        --format=json \
        --freshness=30d \
        --format="value(timestamp)")
    echo "    Created: ${LOG}"
  done
done

Notes

  • Service Account creation times -- IIUC -- can only be obtained through audit logs (CreateServiceAccount[Key]). One challenge with this is having to search back through the project's (entire) history to find these.
  • Only user-created Service Accounts are created (during the project's lifetime). Google-maintained account e.g. App Engine will not be found.
  • The script searches the logs for each account. This is inefficient and it would be better to search the logs once for all accounts and then merge the results.
  • Role assignments is difficult because of inheritance. A naive solution would get the IAM policy for each Project but this is insufficient as it doesn't cover Organizational|Folder permissions nor does it include resource-specific bindings.
  • I actually don't know how to grep the logs for last auth times and assume this is available through audit logs
  • Apologies for the gnarly base64 encoding of the account's displayName. This is to avoid over-eager parsing of (the majority of) names that include spaces. There's likely a better approach.
2 of 2
0

Taking @DazWilin's excellent script, with a few mods it generates a Service Account Register or CSV file of all the service accounts, including their descriptions and keys. Removing the log scrape speeds it up.

#! /bin/bash
# Requires permission to list projects, list service accounts, view keys
if [ $# -lt 1 ]
then
  echo "usage:  $0 csv_output_file"
  exit
fi

gcloud projects list --format="value(projectId)" --sort-by=projectId
OUTFILE=$1
FILTER='prefix'
PROJECTS=$(gcloud projects list --format="value(projectId)" --filter="${FILTER}")

echo "Project,ServiceAccountName,Account Name,Email,Description,key_id,key_created_at,key_expires_at" > $OUTFILE

for PROJECT in ${PROJECTS}
do
  echo "Project: ${PROJECT}"
  # Extracts ACCOUNT_ID, EMAIL (==ACCOUNT_ID@...), DISABLED, DESCRIPTION
  ROBOTS=$(\
    gcloud iam service-accounts list \
    --project=${PROJECT} \
     --format="csvno-heading,email,email.split(\"@\").slice(0),disabled,description.encode(\"base64\"))")

  #echo $ROBOTS
  for ROBOT in ${ROBOTS}
  do
    # Parse results
    IFS=, read ENCODED_NAME EMAIL ACCOUNT_ID DISABLED ENCODED_DESCR<<< ${ROBOT}
    NAME=$(echo -e ${ENCODED_NAME} | base64 --decode)
    DESCR=$(echo -e ${ENCODED_DESCR} | base64 --decode)
    echo "  Service Account: ${NAME}"
    echo "    Disabled: ${DISABLED}"
    echo "    Email: ${EMAIL}"
    echo "    Descr: ${DESCR}"
    RESPONSE=$(\
        gcloud iam service-accounts keys list \
        --iam-account=${EMAIL} \
        --project=${PROJECT} \
        --format="csvno-heading,validAfterTime,validBeforeTime)" \
        )
    IFS=$'\n' rows=($RESPONSE)
    for row in "${rows[@]}"
    do
        echo "$PROJECT,$NAME, $ACCOUNT_ID,$EMAIL,$DESCR,$row" >> $OUTFILE
        # IFS=$',' args=($row)
        # keyname=args[0]
        # created=args[1]
        # expires=args[2]
    done
  done
done

Find elsewhere
🌐
Fig
fig.io › manual › gcloud › iam › service-accounts › list
gcloud iam service-accounts list | Fig
List all of a project's service accounts · Made with ❤️ in San Francisco Copyright © 2026 Hercules Labs Inc
🌐
Fig
fig.io › manual › gcloud › iam › service-accounts › keys › list
gcloud iam service-accounts keys list | Fig
List the keys for a service account · Made with ❤️ in San Francisco Copyright © 2024 Hercules Labs Inc
🌐
GitHub
gist.github.com › youssefguenoun › 6c590b70de9d13fc226751342c2bf574
gcp gcloud cheat sheet · GitHub
gcloud iam service-accounts keys list --iam-account=vault-admin@<project_id>.iam.gserviceaccount.com gcloud projects get-iam-policy <project_id> gcloud iam service-accounts list gcloud iam service-accounts get-iam-policy <sa_email> # get the ...
🌐
Google Cloud
cloud.google.com › google cloud sdk › gcloud iam service-accounts keys create
gcloud iam service-accounts keys create | Google Cloud SDK | Google Cloud Documentation
Skip to main content · Console · English · Deutsch · Español – América Latina · Français · Português – Brasil · 中文 – 简体 · 日本語 · 한국어
🌐
helpmanual.io
helpmanual.io › man1 › gcloud_iam_service-accounts_keys_list
gcloud_iam_service-accounts_keys_list • man page
Run $ gcloud help for details. To list all user-managed keys created before noon on July 19th, 2015 (to perform key rotation, for example), run: $ gcloud iam service-accounts keys list \ --iam-account my-iam-account@somedomain.com --managed-by user \ --created-before 2015-07-19T12:00:00Z
🌐
Google Cloud
cloud.google.com › application development › google cloud sdk › gcloud iam service-accounts keys
gcloud iam service-accounts keys | Google Cloud SDK
Skip to main content · English · Deutsch · Español – América Latina · Français · Português – Brasil · 中文 – 简体 · 日本語 · 한국어 · Console
🌐
Google
docs.cloud.google.com › security › iam › method: projects.serviceaccounts.keys.list
Method: projects.serviceAccounts.keys.list | Identity and Access Management (IAM) | Google Cloud Documentation
May 21, 2025 - Lists every ServiceAccountKey for a service account · GET https://iam.googleapis.com/v1/{name=projects/*/serviceAccounts/*}/keys
🌐
breadNET
documentation.breadnet.co.uk › cloud › gcp › list-all-service-account-keys
List all service account keys - breadNET Documentation
SELECT SPLIT(resource.data.name, '/')[1] AS PROJECT, SPLIT(resource.data.name, '/')[3] AS email, SPLIT(resource.data.name, '/')[5] AS keyId, resource.data.validAfterTime AS creationTime FROM iam_googleapis_com_ServiceAccountKey WHERE resource.data.keyType = "USER_MANAGED" ORDER BY creationTime ASC ... gcloud projects list --format="value(project_id)" | while read -r project_id; do gcloud iam service-accounts list --project=$project_id --format='value(email)' | while read -r sa_email; do echo "Service Account: $sa_email" gcloud iam service-accounts keys list --iam-account=$sa_email --managed-by=user; done done
🌐
Medium
pnatraj.medium.com › how-to-run-gcloud-command-line-using-a-service-account-f39043d515b9
How to run gcloud command line using a service account | by Nataraj | Medium
July 30, 2018 - gcloud iam service-accounts list --filter gcpcmdlineuser@someproject.gserviceaccount.com · 3) Download the service account key ·
🌐
Cuberoot31
cuberoot31.com › posts › how to use self-made service account key with expiration date on google cloud platform
How to Use Self-made Service Account Key with Expiration Date on Google Cloud Platform | Blog by Alex Danilin
November 23, 2021 - First let’s get a list of all keys associated with the service account: gcloud iam service-accounts keys list --iam-account={service-account-id}@{gcp-project-id}.iam.gserviceaccount.com · The result will be something like this: 9999-12-31T23:59:59Z means here that the expiration date for this key was not set.