The curious cases of the `deleted:serviceaccount`
- 3 minutes read - 469 wordsWhile testing Firestore export and import yesterday and checking the IAM permissions on a Cloud Storage Bucket, I noticed some Member (member
) values (I think Google refers to these as Principals) were logical but unfamiliar to me:
deleted:serviceAccount:{email}?uid={uid}
I was using gsutil iam get gs://${BUCKET}
because I’d realized (and this is another useful lesson) that, as I’ve been creating daily test projects, I’ve been binding each project’s Firestore Service Account (service-{project-number}@gcp-sa-firestore.iam.gserviceaccount.com
) to a Bucket owned by another Project but I hadn’t been deleting the binding when I deleted the Project.
As a result, I had a myriad of legacy bindings on the Cloud Storage Bucket but the list included unexpected deleted:serviceAccount:...?uid=...
entries.
It was immediately clear that the deleted:
prefix was being applied to those Service Acounts but had been deleted but, the Bucket’s Policy also included Bindings for not Service Accounts that should have been present and weren’t prefixed deleted:
.
ASIDE I use
jq
for JSON parsing and, in the examples, that follow, I’ll show thejq
commands that I used to filter the data.
I filter the Bucket’s Policy by the Role roles/storage.admin
:
FILTER="
.bindings[]
|select(.role==\"roles/storage.admin\").members[]
"
gsutil iam get gs://${BUCKET} \
| jq -r "${FILTER}"
This was yielding something of the form:
deleted:serviceAccount:service-{number-01}@gcp-sa-firestore.iam.gserviceaccount.com?uid={uid-01}
deleted:serviceAccount:service-{number-02}@gcp-sa-firestore.iam.gserviceaccount.com?uid={uid-02}
deleted:serviceAccount:service-{number-03}@gcp-sa-firestore.iam.gserviceaccount.com?uid={uid-03}
serviceAccount:service-{number-04}@gcp-sa-firestore.iam.gserviceaccount.com
serviceAccount:service-{number-05}@gcp-sa-firestore.iam.gserviceaccount.com
serviceAccount:service-{number-05}@gcp-sa-firestore.iam.gserviceaccount.com
There were many more entries but the above example suffices.
I knew that the Policy should only accurately include today’s Project:
NUMBER=$(
gcloud projects describe ${PROJECT} \
--format="value(projectNumber)")
Let’s say that today’s Project Number (NUMBER
) is {number-04}
.
Given that deleted:
denotes deleted Service Accounts, why were Service Accounts for {number-05}
and {number-06}
etc. present?
I realized that are owned by Projects that are (pending) deletion, gcloud projects delete
that have not yet expunged. Per Shutting down (deleting) projects, there is a 30-day window during which you can gcloud projects undelete
. During this period, Service Accounts aren’t entirely deleted (not deleted:
) either (in case they are recovered).
Could I corroborate this? Yes
gcloud projects list \
--filter='lifecycleState:DELETE_REQUESTED' \
--format="value(projectNumber)"
Yielded:
{number-05}
{number-06}
So, the Projects in my account that are pending deletion, with the not-yet-deleted Serivce Accounts are retained on the Cloud Storage Bucket’s Policy as serviceAccount:...
while the Service Accounts that are irrecoverably (?) deleted because their owning Projects have been expunged (deleted more than 30 days’ ago) are identified as deleted:serviceAccount:...
I was left with one issue, how to tidy up the Bucket’s Policy to remove the deleted:serviceAccount
entries. For the Service Accounts in Project’s pending deletion, the command is as usual gsutil iam ch -d ${EMAIL} gs://${BUCKET}
But for the Service Accounts that are expunged, you actually need to use the fully-qualified (and undocumented!?) member
value, i.e.:
gsutil iam ch \
-d deleted:serviceAccount:${EMAIL}?uid={UID} \
gs://${BUCKET}
NOTE I continue to be unclear on the meaning of the value of
uid
that appears in thesedeleted:
member entries.