GCP permissions
What access does ZopNight need on GCP?
ZopNight needs 39 read permissions on GCP, most of them satisfied by Cloud Asset Inventory returning a whole project in one call. The 113 write permissions cover scheduling and remediation only, and are granted separately. The cloudasset.googleapis.com API must be enabled or discovery silently returns nothing.
aiplatform
| Action | Access | Purpose |
|---|---|---|
| aiplatform.batchPredictionJobs.list | discovery | |
| aiplatform.customJobs.list | discovery | |
| aiplatform.deploymentResourcePools.list | discovery | |
| aiplatform.hyperparameterTuningJobs.list | discovery | |
| aiplatform.models.list | discovery | |
| aiplatform.nasJobs.list | discovery | |
| aiplatform.notebookExecutionJobs.list | discovery | |
| aiplatform.pipelineJobs.list | discovery | |
| aiplatform.trainingPipelines.list | discovery | |
| aiplatform.tuningJobs.list | discovery | |
| aiplatform.endpoints.deploy | scheduling | |
| aiplatform.endpoints.undeploy | scheduling |
artifactregistry
| Action | Access | Purpose |
|---|---|---|
| artifactregistry.dockerimages.list | discovery | |
| artifactregistry.repositories.list | discovery |
bigquery
| Action | Access | Purpose |
|---|---|---|
| bigquery.jobs.create | cost | |
| bigquery.tables.getData | cost |
cloudasset
| Action | Access | Purpose |
|---|---|---|
| cloudasset.assets.searchAllResources | discovery |
cloudsql
| Action | Access | Purpose |
|---|---|---|
| cloudsql.backupRuns.list | discovery | |
| cloudsql.databases.list | discovery; provisioning | |
| cloudsql.instances.get | remediation; provisioning | |
| cloudsql.instances.list | discovery | |
| cloudsql.databases.create | provisioning | |
| cloudsql.instances.create | provisioning | |
| cloudsql.instances.delete | provisioning | |
| cloudsql.instances.export | provisioning | |
| cloudsql.instances.import | provisioning | |
| cloudsql.instances.update | scheduling; provisioning |
compute
| Action | Access | Purpose |
|---|---|---|
| compute.autoscalers.get | scheduling | |
| compute.disks.get | provisioning | |
| compute.instances.get | remediation; provisioning | |
| compute.instances.list | discovery; provisioning | |
| compute.addresses.create | provisioning | |
| compute.addresses.delete | provisioning | |
| compute.autoscalers.create | scheduling | |
| compute.autoscalers.delete | scheduling | |
| compute.autoscalers.update | scheduling | |
| compute.backendServices.create | provisioning | |
| compute.backendServices.delete | provisioning | |
| compute.backendServices.update | provisioning | |
| compute.disks.delete | remediation | |
| compute.disks.resize | provisioning | |
| compute.firewalls.create | provisioning | |
| compute.firewalls.delete | provisioning | |
| compute.firewalls.update | provisioning | |
| compute.forwardingRules.create | provisioning | |
| compute.forwardingRules.delete | provisioning | |
| compute.globalAddresses.create | provisioning | |
| compute.globalAddresses.delete | provisioning | |
| compute.globalForwardingRules.create | provisioning | |
| compute.globalForwardingRules.delete | provisioning | |
| compute.globalOperations.get | provisioning | |
| compute.healthChecks.create | provisioning | |
| compute.healthChecks.delete | provisioning | |
| compute.instanceGroupManagers.create | scheduling; provisioning | |
| compute.instanceGroupManagers.delete | provisioning | |
| compute.instanceGroups.create | provisioning | |
| compute.instanceGroups.delete | provisioning | |
| compute.instanceGroups.update | provisioning | |
| compute.instances.create | provisioning | |
| compute.instances.delete | provisioning | |
| compute.instances.getGuestAttributes | provisioning | |
| compute.instances.osAdminLogin | deployments | |
| compute.instances.osLogin | deployments | |
| compute.instances.setMachineType | remediation; provisioning | |
| compute.instances.start | scheduling; provisioning | |
| compute.instances.stop | scheduling; provisioning | |
| compute.instanceTemplates.create | scheduling; provisioning | |
| compute.instanceTemplates.delete | scheduling; provisioning | |
| compute.networks.create | provisioning | |
| compute.networks.delete | provisioning | |
| compute.networks.updatePolicy | provisioning | |
| compute.regionOperations.get | provisioning | |
| compute.routers.create | provisioning | |
| compute.routers.delete | provisioning | |
| compute.routers.update | provisioning | |
| compute.snapshots.delete | remediation | |
| compute.sslCertificates.create | provisioning | |
| compute.sslCertificates.delete | provisioning | |
| compute.subnetworks.create | provisioning | |
| compute.subnetworks.delete | provisioning | |
| compute.targetHttpProxies.create | provisioning | |
| compute.targetHttpProxies.delete | provisioning | |
| compute.targetHttpsProxies.create | provisioning | |
| compute.targetHttpsProxies.delete | provisioning | |
| compute.urlMaps.create | provisioning | |
| compute.urlMaps.delete | provisioning | |
| compute.zoneOperations.get | remediation; provisioning |
container
| Action | Access | Purpose |
|---|---|---|
| container.clusters.get | discovery | |
| container.clusters.list | discovery | |
| container.nodePools.get | remediation | |
| container.clusters.create | provisioning | |
| container.clusters.delete | provisioning | |
| container.clusters.update | scheduling | |
| container.cronJobs.update | namespace_scheduling | |
| container.daemonSets.update | namespace_scheduling | |
| container.deployments.list | scheduling | |
| container.deployments.update | namespace_scheduling | |
| container.horizontalPodAutoscalers.create | namespace_scheduling | |
| container.horizontalPodAutoscalers.delete | namespace_scheduling | |
| container.operations.get | remediation | |
| container.podDisruptionBudgets.create | namespace_scheduling | |
| container.podDisruptionBudgets.delete | namespace_scheduling | |
| container.statefulSets.update | namespace_scheduling | |
| container.thirdPartyObjects.create | namespace_scheduling | |
| container.thirdPartyObjects.delete | namespace_scheduling |
dataproc
| Action | Access | Purpose |
|---|---|---|
| dataproc.clusters.list | discovery | |
| dataproc.jobs.list | discovery | |
| dataproc.clusters.delete | remediation | |
| dataproc.operations.get | remediation |
dns
| Action | Access | Purpose |
|---|---|---|
| dns.managedZones.get | provisioning | |
| dns.managedZones.list | provisioning | |
| dns.resourceRecordSets.list | provisioning | |
| dns.changes.create | provisioning | |
| dns.managedZones.create | provisioning | |
| dns.managedZones.delete | provisioning | |
| dns.resourceRecordSets.create | provisioning | |
| dns.resourceRecordSets.delete | provisioning | |
| dns.resourceRecordSets.update | provisioning |
iam
| Action | Access | Purpose |
|---|---|---|
| iam.serviceAccountKeys.list | discovery | |
| iam.serviceAccounts.actAs | provisioning | |
| iam.serviceAccounts.create | provisioning | |
| iam.serviceAccounts.delete | provisioning | |
| iam.serviceAccounts.setIamPolicy | provisioning |
iap
| Action | Access | Purpose |
|---|---|---|
| iap.tunnelInstances.accessViaIAP | deployments |
logging
| Action | Access | Purpose |
|---|---|---|
| logging.logEntries.list | deployments |
monitoring
| Action | Access | Purpose |
|---|---|---|
| monitoring.timeSeries.list | recommendations; deployments |
notebooks
| Action | Access | Purpose |
|---|---|---|
| notebooks.instances.delete | remediation | |
| notebooks.instances.start | scheduling | |
| notebooks.instances.stop | scheduling |
osconfig
| Action | Access | Purpose |
|---|---|---|
| osconfig.inventories.get | recommendations | |
| osconfig.patchJobs.exec | observability | |
| osconfig.patchJobs.get | observability |
redis
| Action | Access | Purpose |
|---|---|---|
| redis.instances.create | provisioning | |
| redis.instances.delete | provisioning | |
| redis.instances.update | scheduling |
resourcemanager
| Action | Access | Purpose |
|---|---|---|
| resourcemanager.projects.setIamPolicy | provisioning |
run
| Action | Access | Purpose |
|---|---|---|
| run.services.get | remediation | |
| run.services.update | scheduling |
secretmanager
| Action | Access | Purpose |
|---|---|---|
| secretmanager.secrets.create | deployments | |
| secretmanager.secrets.delete | deployments | |
| secretmanager.versions.access | deployments | |
| secretmanager.versions.add | deployments |
servicenetworking
| Action | Access | Purpose |
|---|---|---|
| servicenetworking.operations.get | provisioning | |
| servicenetworking.services.addPeering | provisioning |
storage
| Action | Access | Purpose |
|---|---|---|
| storage.buckets.get | remediation; provisioning | |
| storage.buckets.getIamPolicy | provisioning | |
| storage.objects.list | provisioning | |
| storage.buckets.create | provisioning | |
| storage.buckets.delete | provisioning | |
| storage.buckets.setIamPolicy | provisioning | |
| storage.buckets.update | remediation | |
| storage.objects.delete | provisioning |
Cloud Asset Inventory does the reading
GCP’s permission model is finer-grained than the other two, so the count reads oddly: 39 read permissions against 113 write. That is not a lopsided grant. It reflects that GCP splits mutation into many narrow permissions while bulk reads go through a single API.
Discovery runs almost entirely through Cloud Asset Inventory, which returns the whole
project inventory in one call. That is why the read side is small: one API does the work that
takes dozens of Describe* calls on AWS.
Service account or OAuth?
- Service account: a JSON key file. Works headless, survives unattended, and is the right choice for automation. The key is a stored secret you own.
- OAuth: a three-step Google consent flow, tokens encrypted at rest. Nothing long-lived is stored, but it cannot be switched to in place, because it needs interactive consent from a human. Choose it deliberately, not by default.
Billing is a separate grant either way: on GCP, billing data lives at billing-account scope, not project scope, so the one-click billing upgrade available on AWS does not apply here.
The Cloud Asset API has to be enabled
If cloudasset.googleapis.com is not enabled on the project, discovery returns nothing at all.
Not an error, just nothing. This is the single most common cause of a GCP account that connects
successfully and then shows an empty inventory.
What the write permissions cover
scheduling (start and stop Compute Engine, GKE node pools, Cloud SQL, Cloud Run, Memorystore),
remediation (acting on a finding), namespace_scheduling (scaling GKE workloads to zero and
back), and provisioning/deployments for ZopDay. Nothing reads object contents or table rows.
Cloud SQL is worth calling out: stopping an instance is a patch to its activationPolicy, not a
stop API, so the permission you need is cloudsql.instances.update.
GCP permissions: common questions
Why only 39 read permissions on GCP but 113 write?
Because GCP splits mutation into many narrow permissions while bulk reads go through a single API. Discovery runs almost entirely through Cloud Asset Inventory, so roles/cloudasset.viewer does the work that takes dozens of Describe calls on AWS. It is not a lopsided grant, just a different shape.
Service account key or OAuth?
A service account JSON key for anything automated: it works headless and survives unattended, and the key is a stored secret you own. OAuth stores nothing long-lived but needs interactive human consent, which is why it cannot be switched to in place on an existing account.
What permission does ZopNight need to stop a Cloud SQL instance?
cloudsql.instances.update, covered by roles/cloudsql.editor. Cloud SQL has no stop API; stopping an instance is a patch to its activationPolicy, so the grant is an update rather than a stop.