> For the complete documentation index, see [llms.txt](https://cortex-docs.paloaltonetworks.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cortex-docs.paloaltonetworks.com/google-cloud-platform-manual-onboarding/gcp-manual-onboarding/gcp-manual-onboarding-guide-organization-scope/phase-2-platform-service-accounts.md).

# Phase 2: Platform service accounts

## Phase 2: Platform service accounts

This phase creates the main service account used by Cortex to monitor your GCP resources.

Note: All GCP resources that support labels must include the following labels: `managed_by=paloaltonetworks` Any additional labels you configured in the GCP onboarding wizard in Cortex.

To add a label to a GCP resource, use the label flag:

{% code overflow="wrap" %}

```
--labels managed_by=paloaltonetworks,<your_key>=<your_value>
```

{% endcode %}

### 2.1 Create the Platform Service Account (Required)

Create a service account in the host project that Cortex will use as the primary identity for accessing your resources.

{% code overflow="wrap" %}

```bash
gcloud iam service-accounts create <PLATFORM_SA_NAME> \
  --display-name="Cortex Platform Service Account" \
  --project=<HOST_PROJECT_ID>
```

{% endcode %}

Where:

* `<PLATFORM_SA_NAME>` is your chosen service account ID (e.g., cortex-platform)
* `<HOST_PROJECT_ID>` is the GCP host project ID where service accounts are created

### 2.2 Grant predefined managed roles to the Platform Service Account (Required)

Module: Assets Discovery

Bind the following predefined roles to the platform service account at the organization level:

```bash
gcloud organizations add-iam-policy-binding <ORG_ID> \
  --member="serviceAccount:<PLATFORM_SA_NAME>@<HOST_PROJECT_ID>.iam.gserviceaccount.com" \
  --role="roles/cloudfunctions.viewer"
gcloud organizations add-iam-policy-binding <ORG_ID> \
  --member="serviceAccount:<PLATFORM_SA_NAME>@<HOST_PROJECT_ID>.iam.gserviceaccount.com" \
  --role="roles/container.clusterViewer"
gcloud organizations add-iam-policy-binding <ORG_ID> \
  --member="serviceAccount:<PLATFORM_SA_NAME>@<HOST_PROJECT_ID>.iam.gserviceaccount.com" \
  --role="roles/iam.organizationRoleViewer"
gcloud organizations add-iam-policy-binding <ORG_ID> \
  --member="serviceAccount:<PLATFORM_SA_NAME>@<HOST_PROJECT_ID>.iam.gserviceaccount.com" \
  --role="roles/resourcemanager.folderViewer"
gcloud organizations add-iam-policy-binding <ORG_ID> \
  --member="serviceAccount:<PLATFORM_SA_NAME>@<HOST_PROJECT_ID>.iam.gserviceaccount.com" \
  --role="roles/storage.objectViewer"
gcloud organizations add-iam-policy-binding <ORG_ID> \
  --member="serviceAccount:<PLATFORM_SA_NAME>@<HOST_PROJECT_ID>.iam.gserviceaccount.com" \
  --role="roles/firebaserules.viewer"
gcloud organizations add-iam-policy-binding <ORG_ID> \
  --member="serviceAccount:<PLATFORM_SA_NAME>@<HOST_PROJECT_ID>.iam.gserviceaccount.com" \
  --role="roles/viewer"
```

Where:

* `<PLATFORM_SA_NAME>` is the Platform Service Account name
* `<HOST_PROJECT_ID>` is the GCP host project ID where service accounts are created
* `<ORG_ID>` is the numeric ID of the organization to onboard

After running the last command, the following prompt is displayed:

```
[1]
EXPRESSION=((resource.name.extract("snapshots/ end)").startsWith("cortex-scan-")
|| resource.name.extract("disks/ end)").startsWith("cortex-scan-")) &&
resource.service == "compute.googleapis.com"), TITLE=scoped-permission-condition
[2] None
[3] Specify a new condition
The policy contains bindings with conditions, so specifying a condition is
required when adding a binding. Please specify a condition.:
```

Choose "None" by entering 2.

### 2.3 Create the Custom Role and grant it to the Platform Service Account (Required)

Module: Assets Discovery

Create a custom role at the organization level with granular permissions not covered by the built-in roles.

#### 2.3.1 Create the Custom Role

```bash
gcloud iam roles create <CLOUD_VIEWER_ROLE_ID> \
  --organization=<ORG_ID> \
  --title="Cortex Platform Cloud Viewer" \
  --description="Custom role for Cortex Platform Cloud. Contains granular additional permissions not covered by built-in roles." \
  --stage=GA \
  --permissions="accesscontextmanager.accessLevels.list,accesscontextmanager.accessPolicies.list,accesscontextmanager.servicePerimeters.list,bigquery.tables.get,clientauthconfig.clients.listWithSecrets,resourcemanager.tagKeys.list,cloudsecurityscanner.scans.list,cloudtasks.queues.list,compute.reservations.getIamPolicy,deploymentmanager.deployments.getIamPolicy,firebaserules.rulesets.get,iam.workforcePools.list,iam.workloadIdentityPoolProviders.list,iam.workloadIdentityPools.list,logging.cmekSettings.get,networkservices.meshes.getIamPolicy,pubsub.subscriptions.getIamPolicy,pubsub.topics.getIamPolicy,run.services.list,run.jobs.list,run.jobs.getIamPolicy,cloudscheduler.jobs.list,baremetalsolution.instances.list,baremetalsolution.networks.list,baremetalsolution.nfsshares.list,baremetalsolution.volumes.list,baremetalsolution.luns.list,analyticshub.dataExchanges.list,analyticshub.listings.getIamPolicy,analyticshub.listings.list,notebooks.locations.list,notebooks.schedules.list,composer.imageversions.list,datamigration.connectionprofiles.list,datamigration.connectionprofiles.getIamPolicy,datamigration.conversionworkspaces.list,datamigration.conversionworkspaces.getIamPolicy,datamigration.migrationjobs.list,datamigration.migrationjobs.getIamPolicy,datamigration.privateconnections.list,datamigration.privateconnections.getIamPolicy,aiplatform.batchPredictionJobs.list,aiplatform.nasJobs.list,resourcemanager.projects.list,resourcemanager.folders.get,resourcemanager.folders.getIamPolicy,resourcemanager.organizations.get,resourcemanager.organizations.getIamPolicy,serviceusage.services.use,storage.buckets.getIamPolicy,storage.buckets.get,storage.buckets.list,storage.buckets.listEffectiveTags,storage.buckets.listTagBindings,storage.objects.getIamPolicy"
```

Where `<CLOUD_VIEWER_ROLE_ID>` is your chosen role ID (e.g., CortexCloudViewer).

#### 2.3.2 Grant the Custom Role to the Platform Service Account

Bind at the organization level:

```bash
gcloud organizations add-iam-policy-binding <ORG_ID> \
  --member="serviceAccount:<PLATFORM_SA_NAME>@<HOST_PROJECT_ID>.iam.gserviceaccount.com" \
  --role="organizations/<ORG_ID>/roles/<CLOUD_VIEWER_ROLE_ID>"
```

Where:

* `<ORG_ID>` is the numeric ID of the organization to onboard
* `<PLATFORM_SA_NAME>` is the Platform Service Account name
* `<HOST_PROJECT_ID>` is the GCP host project ID where service accounts are created
* `<CLOUD_VIEWER_ROLE_ID>` is your chosen role ID (e.g., CortexCloudViewer)

### 2.4 Grant the Service Account Token Creator role to the Outpost Service Account (Required)

Allow the Outpost Service Account (provided by Cortex) to impersonate the Platform Service Account:

{% code overflow="wrap" %}

```bash
gcloud iam service-accounts add-iam-policy-binding <PLATFORM_SA_NAME>@<HOST_PROJECT_ID>.iam.gserviceaccount.com \
  --member="serviceAccount:<OUTPOST_SERVICE_ACCOUNT_EMAIL>" \
  --role="roles/iam.serviceAccountTokenCreator"
```

{% endcode %}

Where:

* `<PLATFORM_SA_NAME>` is the Platform Service Account name
* `<HOST_PROJECT_ID>` is the GCP host project ID where service accounts are created
* `<OUTPOST_SERVICE_ACCOUNT_EMAIL>` is the value listed in the identifiers JSON file

### 2.5 Agentless Disk Scanning (ADS) (Optional)

Module: ADS

Execute the steps in this section if you enabled Agentless Disk Scanning in the GCP onboarding wizard in Cortex.

#### 2.5.1 Create the ADS Connector Role

Create at the organization level:

```bash
gcloud iam roles create <ADS_CONNECTOR_ROLE_ID> \
  --organization=<ORG_ID> \
  --title="ADS Connector Role" \
  --permissions="compute.snapshots.get,compute.snapshots.create,compute.snapshots.delete,compute.snapshots.setLabels,compute.disks.create,compute.disks.get,compute.disks.delete,compute.disks.setLabels,compute.images.get"
```

Where:

* `<ADS_CONNECTOR_ROLE_ID>` is your chosen role ID (e.g., CortexAdsConnector)
* `<ORG_ID>` is the numeric ID of the organization to onboard

#### 2.5.2 Grant the ADS Connector role to the Platform Service Account

Grant the ADS Connector role to the Platform Service Account at the organization level and apply an IAM condition to restrict permissions to resources prefixed with cortex-scan-\*:

```bash
gcloud organizations add-iam-policy-binding <ORG_ID> \
  --member="serviceAccount:<PLATFORM_SA_NAME>@<HOST_PROJECT_ID>.iam.gserviceaccount.com" \
  --role="organizations/<ORG_ID>/roles/<ADS_CONNECTOR_ROLE_ID>" \
  --condition='title=scoped-permission-condition,expression=((resource.name.extract("snapshots/{end}").startsWith("cortex-scan-") || resource.name.extract("disks/{end}").startsWith("cortex-scan-")) && resource.service == "compute.googleapis.com")'
```

Where:

* `<ORG_ID>` is the numeric ID of the organization to onboard
* `<PLATFORM_SA_NAME>` is the Platform Service Account name
* `<HOST_PROJECT_ID>` is the GCP host project ID where service accounts are created
* `<ADS_CONNECTOR_ROLE_ID>` is your chosen role ID (e.g., CortexAdsConnector)

#### 2.5.3 Create the ADS Outpost role

Create at the organization level:

{% code overflow="wrap" %}

```bash
gcloud iam roles create <ADS_OUTPOST_ROLE_ID> \
  --organization=<ORG_ID> \
  --title="ADS Outpost Role" \
  --permissions="compute.snapshots.useReadOnly"
```

{% endcode %}

Where:

* `<ADS_OUTPOST_ROLE_ID>` is your chosen role ID (e.g., CortexAdsOutpost)
* `<ORG_ID>` is the numeric ID of the organization to onboard

#### 2.5.4 Grant the ADS Outpost role to the Outpost Service Account

Grant the ADS Outpost role to the Outpost Service Account at the organization level and apply an IAM condition to restrict permissions to resources prefixed with cortex-scan-\*:

```bash
gcloud organizations add-iam-policy-binding <ORG_ID> \
  --member="serviceAccount:<OUTPOST_SERVICE_ACCOUNT_EMAIL>" \
  --role="organizations/<ORG_ID>/roles/<ADS_OUTPOST_ROLE_ID>" \
  --condition='title=scoped-permission-condition,expression=((resource.name.extract("snapshots/{end}").startsWith("cortex-scan-") || resource.name.extract("disks/{end}").startsWith("cortex-scan-")) && resource.service == "compute.googleapis.com")'
```

Where:

* `<ORG_ID>` is the numeric ID of the organization to onboard
* `<OUTPOST_SERVICE_ACCOUNT_EMAIL>` is the value listed in the identifiers JSON file
* `<ADS_OUTPOST_ROLE_ID>` is your chosen role ID (e.g., CortexAdsOutpost)

### 2.6 Automation (Optional)

Module: AUTOMATION

Execute the steps in this section if you enabled Automation in the GCP onboarding wizard in Cortex.

#### 2.6.1 Create the Automation role

Create at the organization level:

```bash
gcloud iam roles create <AUTOMATION_ROLE_ID> \
  --organization=<ORG_ID> \
  --title="Automation Role" \
  --permissions="cloudasset.assets.searchAllResources,compute.firewalls.create,compute.firewalls.get,compute.firewalls.list,compute.firewalls.update,compute.images.get,compute.instanceGroups.get,compute.instances.get,compute.instances.list,compute.instances.setLabels,compute.instances.setMetadata,compute.instances.setServiceAccount,compute.instances.setTags,compute.instances.start,compute.instances.stop,compute.networks.create,compute.networks.get,compute.networks.list,compute.networks.updatePolicy,compute.regions.get,compute.snapshots.get,compute.snapshots.list,compute.subnetworks.get,compute.subnetworks.list,compute.subnetworks.setPrivateIpGoogleAccess,compute.subnetworks.update,compute.zones.get,container.clusters.get,container.clusters.list,container.clusters.update,resourcemanager.projects.getIamPolicy,resourcemanager.projects.setIamPolicy,storage.buckets.get,storage.buckets.getIamPolicy,storage.buckets.getIpFilter,storage.buckets.list,storage.buckets.setIamPolicy,storage.buckets.update,storage.objects.getIamPolicy,storage.objects.list,bigquery.datasets.get,bigquery.datasets.getIamPolicy,bigquery.datasets.update,bigquery.datasets.setIamPolicy"
```

Where:

* `<AUTOMATION_ROLE_ID>` is your chosen name for the role (e.g. CortexAutomation)
* `<ORG_ID>` is the numeric ID of the organization to onboard

#### 2.6.2 Grant the Automation role to the Platform Service Account

Bind at the organization level:

```bash
gcloud organizations add-iam-policy-binding <ORG_ID> \
  --member="serviceAccount:<PLATFORM_SA_NAME>@<HOST_PROJECT_ID>.iam.gserviceaccount.com" \
  --role="organizations/<ORG_ID>/roles/<AUTOMATION_ROLE_ID>"
```

Where:

* `<ORG_ID>` is the numeric ID of the organization to onboard
* `<PLATFORM_SA_NAME>` is the Platform Service Account name
* `<HOST_PROJECT_ID>` is the GCP host project ID where service accounts are created
* `<AUTOMATION_ROLE_ID>` is your chosen name for the role (e.g. CortexAutomation)

### 2.7 DSPM: Platform Service Account (Optional)

Module: DSPM

Execute the steps in this section if you enabled Data Security Posture Management (DSPM) in the GCP onboarding wizard in Cortex.

#### 2.7.1 Create the DSPM Connector role

Create at the organization level:

```bash
gcloud iam roles create <DSPM_CONNECTOR_ROLE_ID> \
  --organization=<ORG_ID> \
  --title="DSPM Connector Role" \
  --permissions="cloudsql.backupRuns.create,cloudsql.backupRuns.delete,cloudsql.backupRuns.get,cloudsql.backupRuns.list,bigtable.backups.create,bigtable.backups.delete,bigtable.backups.get,bigtable.backups.list,bigtable.clusters.get,bigtable.clusters.list,bigtable.instances.get,bigtable.instances.list,bigtable.tables.get,bigtable.tables.list"
```

Where:

* `<DSPM_CONNECTOR_ROLE_ID>` is your chosen name for the role (e.g. CortexDspmConnector)
* `<ORG_ID>` is the numeric ID of the organization to onboard

#### 2.7.2 Grant the DSPM Connector role to the Platform Service Account

Bind at the organization level:

```bash
gcloud organizations add-iam-policy-binding <ORG_ID> \
  --member="serviceAccount:<PLATFORM_SA_NAME>@<HOST_PROJECT_ID>.iam.gserviceaccount.com" \
  --role="organizations/<ORG_ID>/roles/<DSPM_CONNECTOR_ROLE_ID>"
```

Where:

* `<ORG_ID>` is the numeric ID of the organization to onboard
* `<PLATFORM_SA_NAME>` is the Platform Service Account name
* `<HOST_PROJECT_ID>` is the GCP host project ID where service accounts are created
* `<DSPM_CONNECTOR_ROLE_ID>` is your chosen name for the role (e.g. CortexDspmConnector)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://cortex-docs.paloaltonetworks.com/google-cloud-platform-manual-onboarding/gcp-manual-onboarding/gcp-manual-onboarding-guide-organization-scope/phase-2-platform-service-accounts.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
