Try it free

Create GCP connection via gcloud CLI and dtctl

  • Latest Dynatrace
  • How-to guide

Use the gcloud CLI for Google Cloud setup and dtctl for Dynatrace configuration. No wizard is required.

With the manual setup, you're responsible for keeping the resources in your Google Cloud environment up to date, for example the Dataflow template image version used for log forwarding.

Stepgcloud CLIdtctl
1. Create GCP connection in Dynatrace-Applicable
2. Enable Google Cloud APIsApplicable-
3. Create service accountApplicable-
4. Grant permissions to the service accountApplicable-
5. Grant project-level accessApplicable-
6. Grant folder-level access (optional)Applicable-
7. Grant organization-level access (optional)Applicable-
8. Grant impersonation rightsApplicable-
9. Link service account to Dynatrace-Applicable
10. Create monitoring configuration-Applicable
11. Customize monitoring (optional)-Applicable
12. Set up asset inventory feed (optional)Applicable-
13. Set up log forwarding (optional)Applicable-

Prerequisites

Install dtctl and configure it with your Dynatrace environment credentials before running any dtctl commands.

First-time setup: Activate the extension

If this is the first time you are setting up GCP Cloud Platform Monitoring in this Dynatrace environment, activate the extension before proceeding. The UI wizard does this automatically—manual setup requires an explicit activation.

dtctl create extension --hub-extension com.dynatrace.extension.da-gcp

Verify that the extension is installed:

dtctl get extension com.dynatrace.extension.da-gcp

If the extension is not active, see Troubleshoot GCP connections. To install a newer extension version later, see Update the extension.

Set the environment variables

Set these variables before running the commands. They're used throughout all examples.

# Your GCP project ID
PROJECT_ID="my-project-id"
# Your GCP project number (derived from the project ID; used for service agents and bucket names)
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
# Service account name (will be created)
CUSTOMER_SA_NAME="dynatrace-monitoring"
# Full service account email (derived from above)
CUSTOMER_SA_EMAIL="${CUSTOMER_SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
# Dynatrace connection name (holds service account credentials)
GCP_CONNECTION_NAME="my-gcp-connection"
# Dynatrace monitoring configuration name (holds metric/filter settings; references the connection)
GCP_MONITORING_NAME="my-gcp-monitoring"
# Dynatrace environment details
TENANT_ID="my-tenant-id" # Dynatrace environment ID (e.g. abc12345)
DT_INGEST_URL="https://${TENANT_ID}.da.dynatrace.com" # Asset feed and log ingest endpoints
REGION="us-central1" # GCP region for infrastructure deployment
# Monitoring configuration ID---printed by 'dtctl create gcp monitoring' in step 7
MCONFIG_ID="<monitoring-config-id>"
# Folder and organization IDs (only if you monitor at these scopes)
FOLDER_ID="<folder-id>"
ORG_ID="<organization-id>"

Retrieve your Dynatrace principal

The Dynatrace principal is required for service account impersonation. Retrieve it before starting.

dtctl get gcp connection principal

Example output:

dynatrace-abc12345@dtp-prod-gcp-auth.iam.gserviceaccount.com

Go to Settings Settings > Collect and capture > Cloud and virtualization > Google Cloud and start the connection wizard. The principal email is displayed in the setup instructions.

Set the Dynatrace principal in your environment:

DT_SERVICE_ACCOUNT="dynatrace-abc12345@dtp-prod-gcp-auth.iam.gserviceaccount.com"

Grant required permissions

The user running the setup must have these permissions:

ForRolePurpose
GCProles/iam.serviceAccountAdminCreate and manage service accounts
GCProles/serviceusage.serviceUsageAdminEnable Google Cloud APIs
GCProles/resourcemanager.projectIamAdminGrant IAM roles at project level
GCProles/resourcemanager.folderIamAdminGrant IAM roles at folder level (if applicable)
GCProles/resourcemanager.organizationIamAdminGrant IAM roles at organization level (if applicable)
GCProles/pubsub.adminCreate Pub/Sub topics and subscriptions and set their IAM policies (steps 9 and 10, if applicable)
GCProles/cloudasset.ownerCreate the asset feed (step 9, if applicable)
GCProles/logging.configWriterCreate log sinks (step 10, if applicable)
GCProles/storage.adminCreate the Cloud Storage buckets and upload the template spec (step 10, if applicable)
GCProles/secretmanager.adminCreate the secret for the Log Ingest token (step 10, if applicable)
GCProles/dataflow.adminLaunch the Dataflow job (step 10, if applicable)
GCProles/iam.serviceAccountUserRun the Dataflow job as your service account (step 10, if applicable)
DynatraceIAM user assigned the Data-Acquisition Cloud Integrations All and Standard User policiesAuthenticate dtctl and manage Cloud Integrations settings
DynatraceAPI token with settings:objects:read, settings:objects:write, extensions:configurations:read, extensions:configurations:writeManage connections and monitoring configs (only needed for token-based dtctl authentication)

Create a GCP connection in Dynatrace

Step 1: Create a GCP connection in Dynatrace

This connection will be linked to your GCP service account later.

dtctl create gcp connection --name "$GCP_CONNECTION_NAME"

Verify the connection was created:

dtctl get gcp connections

Step 2: Enable Google Cloud APIs

Enable the required APIs in your GCP project.

APIPurpose
cloudresourcemanager.googleapis.comAccess to projects, folders, and organizations
cloudasset.googleapis.comAccess to asset inventory
monitoring.googleapis.comAccess to metrics—enabled by default on new GCP projects
pubsub.googleapis.comPub/Sub topics and subscriptions for the asset feed and log forwarding
iam.googleapis.comService account IAM and OIDC token creation
gcloud services enable \
cloudresourcemanager.googleapis.com \
cloudasset.googleapis.com \
monitoring.googleapis.com \
pubsub.googleapis.com \
iam.googleapis.com \
--project=$PROJECT_ID

If your organization restricts default-enabled APIs, manually enable monitoring.googleapis.com on each monitored project:

gcloud services enable monitoring.googleapis.com --project=MONITORED_PROJECT_ID

Step 3: Create a service account

Create a service account in your GCP project. Dynatrace will impersonate this account to access your resources.

gcloud iam service-accounts create $CUSTOMER_SA_NAME \
--display-name="Dynatrace monitoring account" \
--project=$PROJECT_ID

Verify the service account was created:

gcloud iam service-accounts list --project=$PROJECT_ID

Step 4: Grant permissions to the service account

Grant the service account the following permissions. These are the exact permissions Dynatrace requires. If your organization restricts broad predefined roles such as roles/viewer, create a custom role with these permissions instead.

PermissionPurpose
resourcemanager.organizations.getList organizations
resourcemanager.folders.getList folders
resourcemanager.projects.getList projects
cloudasset.assets.searchAllResourcesList assets from a scope
monitoring.metricDescriptors.listList metric descriptors
monitoring.timeSeries.listRetrieve time series data

Step 5: Grant project-level access

Grant the required roles on each project you want to monitor.

for ROLE in roles/monitoring.viewer roles/cloudasset.viewer roles/browser; do
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$CUSTOMER_SA_EMAIL" \
--role="$ROLE"
done

For multiple projects:

for PROJECT in project-1 project-2 project-3; do
for ROLE in roles/monitoring.viewer roles/cloudasset.viewer roles/browser; do
gcloud projects add-iam-policy-binding $PROJECT \
--member="serviceAccount:$CUSTOMER_SA_EMAIL" \
--role="$ROLE"
done
done

Step 6: Grant folder-level access Optional

Grant the required roles on each folder you want to monitor. roles/browser at folder scope lets the service account list all folders and projects within that folder.

for ROLE in roles/monitoring.viewer roles/cloudasset.viewer roles/browser; do
gcloud resource-manager folders add-iam-policy-binding FOLDER_ID \
--member="serviceAccount:$CUSTOMER_SA_EMAIL" \
--role="$ROLE"
done

Folder filtering requires roles/browser granted at folder level. Granting it only at project level is not sufficient.

Step 7: Grant organization-level access Optional

Grant the required roles at organization scope for organization-wide monitoring.

for ROLE in roles/monitoring.viewer roles/cloudasset.viewer roles/browser; do
gcloud organizations add-iam-policy-binding ORG_ID \
--member="serviceAccount:$CUSTOMER_SA_EMAIL" \
--role="$ROLE"
done

Step 8: Grant Dynatrace impersonation rights

This step establishes the impersonation trust. Grant the Dynatrace principal the roles/iam.serviceAccountTokenCreator role on your service account. This allows Dynatrace to impersonate your service account without storing long-lived credentials.

gcloud iam service-accounts add-iam-policy-binding $CUSTOMER_SA_EMAIL \
--member="serviceAccount:$DT_SERVICE_ACCOUNT" \
--role="roles/iam.serviceAccountTokenCreator"

If your organization restricts broad predefined roles, create a custom role with the single exact permission this role grants:

PermissionPurpose
iam.serviceAccounts.getAccessTokenAllows Dynatrace to obtain short-lived access tokens by impersonating your service account
Troubleshooting

If you get PERMISSION_DENIED with iam.serviceAccounts.getIamPolicy, your account is missing permissions to update IAM policy on the service account. Ask your GCP admin for the roles/iam.serviceAccountAdmin role.

Step 9: Link the service account to Dynatrace

Update the Dynatrace connection with your service account details. This enables Dynatrace to use service account impersonation.

IAM changes can take a few minutes to propagate. If the command fails with an IAM policy update error, wait a minute and run it again:

dtctl update gcp connection \
--name "$GCP_CONNECTION_NAME" \
--serviceAccountId "$CUSTOMER_SA_EMAIL"

Verify the connection:

dtctl get gcp connections

Step 10: Create the monitoring configuration

Create the monitoring configuration to start collecting data from GCP.

Create with defaults (all locations, all essential feature sets). The default selects more services than the Recommended mode in the Settings app. To choose the services yourself, add --featureSets with a comma-separated list from dtctl get gcp monitoring-feature-sets.

dtctl create gcp monitoring \
--name "$GCP_MONITORING_NAME" \
--credentials "$GCP_CONNECTION_NAME"

Enable monitoring. The service account was already linked in the previous step, so --serviceAccountId is not needed here:

dtctl enable gcp monitoring \
--name "$GCP_MONITORING_NAME"

You can combine the previous step and this one. Skip the dtctl update gcp connection call above and instead pass --serviceAccountId directly to enable—it updates the linked connection and enables monitoring in a single command:

dtctl enable gcp monitoring \
--name "$GCP_MONITORING_NAME" \
--serviceAccountId "$CUSTOMER_SA_EMAIL"

Verify the configuration:

dtctl describe gcp monitoring $GCP_MONITORING_NAME

Step 11: Customize monitoring Optional

After the initial setup, you can customize which regions, services, and custom resources and metrics Dynatrace monitors. See Update monitoring settings for the steps. For all configuration properties, see the Monitoring configuration reference.

Step 12: Set up the asset inventory feed Optional

The asset inventory feed delivers real-time topology change events from Google Cloud Asset Inventory to Dynatrace. It uses a Pub/Sub push subscription with OIDC authentication—no Dynatrace API token is required.

Enable required APIs:

gcloud services enable pubsub.googleapis.com iam.googleapis.com \
--project=$PROJECT_ID

Create the Pub/Sub topic:

gcloud pubsub topics create dt-asset-feed-$MCONFIG_ID \
--project=$PROJECT_ID \
--message-retention-duration=1200s

The Terraform deployment applies a JavaScript UDF on the Pub/Sub topic that strips priorAsset from feed events (copying it to asset on delete events so Dynatrace can identify what was removed). The gcloud CLI does not support topic-level message transforms. Without the transform, events include the full priorAsset payload, which increases message size and egress cost but does not affect functionality.

Grant the Pub/Sub service agent token creator rights on the customer service account. This allows the push subscription to generate OIDC tokens for OIDC authentication:

gcloud iam service-accounts add-iam-policy-binding $CUSTOMER_SA_EMAIL \
--member="serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com" \
--role="roles/iam.serviceAccountTokenCreator" \
--project=$PROJECT_ID

Grant the Cloud Asset service agent publisher rights on the topic. GCP does not grant this automatically—without it the asset feed cannot publish events:

# Ensure the Cloud Asset service agent exists (GCP creates it lazily).
# The beta command requires the gcloud beta component: gcloud components install beta
gcloud beta services identity create \
--service=cloudasset.googleapis.com \
--project=$PROJECT_ID
CLOUDASSET_AGENT="service-${PROJECT_NUMBER}@gcp-sa-cloudasset.iam.gserviceaccount.com"
gcloud pubsub topics add-iam-policy-binding dt-asset-feed-$MCONFIG_ID \
--project=$PROJECT_ID \
--member="serviceAccount:$CLOUDASSET_AGENT" \
--role="roles/pubsub.publisher"

Create the push subscription with OIDC authentication:

gcloud pubsub subscriptions create dt-asset-feed-push-$MCONFIG_ID \
--topic=dt-asset-feed-$MCONFIG_ID \
--project=$PROJECT_ID \
--push-endpoint="${DT_INGEST_URL}/api/gcp/assetfeed/v1/events" \
--push-auth-service-account="$CUSTOMER_SA_EMAIL" \
--push-auth-token-audience="dt:gcp:assetfeed:${TENANT_ID}:${MCONFIG_ID}" \
--push-no-wrapper \
--ack-deadline=30 \
--message-retention-duration=1200s \
--expiration-period=never \
--min-retry-delay=10s \
--max-retry-delay=120s

Create the asset feed for your monitoring scope. The --asset-types list controls which resource types trigger change events.

ASSET_TYPES="alloydb.googleapis.com/Cluster,alloydb.googleapis.com/Instance,apigee.googleapis.com/Instance,apigee.googleapis.com/Organization,appengine.googleapis.com/Application,bigquery.googleapis.com/Dataset,bigquery.googleapis.com/Model,bigquerydatatransfer.googleapis.com/TransferConfig,bigtableadmin.googleapis.com/Backup,bigtableadmin.googleapis.com/Cluster,bigtableadmin.googleapis.com/Table,cloudfunctions.googleapis.com/CloudFunction,cloudresourcemanager.googleapis.com/TagBinding,cloudtasks.googleapis.com/Queue,composer.googleapis.com/Environment,compute.googleapis.com/Autoscaler,compute.googleapis.com/BackendService,compute.googleapis.com/Disk,compute.googleapis.com/Instance,compute.googleapis.com/Interconnect,compute.googleapis.com/InterconnectAttachment,compute.googleapis.com/Network,compute.googleapis.com/Router,compute.googleapis.com/ServiceAttachment,compute.googleapis.com/VpnGateway,compute.googleapis.com/VpnTunnel,container.googleapis.com/Cluster,dataflow.googleapis.com/Job,dataproc.googleapis.com/Batch,dataproc.googleapis.com/Cluster,dataproc.googleapis.com/Job,dataproc.googleapis.com/Session,file.googleapis.com/Instance,k8s.io/Node,k8s.io/Pod,k8s.io/Service,logging.googleapis.com/LogSink,netapp.googleapis.com/Volume,pubsub.googleapis.com/Snapshot,pubsub.googleapis.com/Subscription,pubsub.googleapis.com/Topic,recaptchaenterprise.googleapis.com/Key,redis.googleapis.com/Cluster,redis.googleapis.com/Instance,run.googleapis.com/Job,run.googleapis.com/Revision,spanner.googleapis.com/Instance,storage.googleapis.com/Bucket"
gcloud asset feeds create dt-asset-feed-$MCONFIG_ID \
--project=$PROJECT_ID \
--pubsub-topic=projects/$PROJECT_ID/topics/dt-asset-feed-$MCONFIG_ID \
--content-type=resource \
--asset-types="$ASSET_TYPES"

For additional projects, a feed publishes as the Cloud Asset service agent of its own project. Grant each of these agents publisher rights on the topic, and create a feed in each project. The topic stays in the service account project:

for PROJECT in project-2 project-3; do
gcloud services enable cloudasset.googleapis.com --project=$PROJECT
gcloud beta services identity create \
--service=cloudasset.googleapis.com \
--project=$PROJECT
PN=$(gcloud projects describe $PROJECT --format='value(projectNumber)')
gcloud pubsub topics add-iam-policy-binding dt-asset-feed-$MCONFIG_ID \
--project=$PROJECT_ID \
--member="serviceAccount:service-${PN}@gcp-sa-cloudasset.iam.gserviceaccount.com" \
--role="roles/pubsub.publisher"
gcloud asset feeds create dt-asset-feed-$MCONFIG_ID \
--project=$PROJECT \
--pubsub-topic=projects/$PROJECT_ID/topics/dt-asset-feed-$MCONFIG_ID \
--content-type=resource \
--asset-types="$ASSET_TYPES"
done

Replace FOLDER_ID with your numeric folder ID (for example, 178078973866).

ASSET_TYPES="alloydb.googleapis.com/Cluster,alloydb.googleapis.com/Instance,apigee.googleapis.com/Instance,apigee.googleapis.com/Organization,appengine.googleapis.com/Application,bigquery.googleapis.com/Dataset,bigquery.googleapis.com/Model,bigquerydatatransfer.googleapis.com/TransferConfig,bigtableadmin.googleapis.com/Backup,bigtableadmin.googleapis.com/Cluster,bigtableadmin.googleapis.com/Table,cloudfunctions.googleapis.com/CloudFunction,cloudresourcemanager.googleapis.com/TagBinding,cloudtasks.googleapis.com/Queue,composer.googleapis.com/Environment,compute.googleapis.com/Autoscaler,compute.googleapis.com/BackendService,compute.googleapis.com/Disk,compute.googleapis.com/Instance,compute.googleapis.com/Interconnect,compute.googleapis.com/InterconnectAttachment,compute.googleapis.com/Network,compute.googleapis.com/Router,compute.googleapis.com/ServiceAttachment,compute.googleapis.com/VpnGateway,compute.googleapis.com/VpnTunnel,container.googleapis.com/Cluster,dataflow.googleapis.com/Job,dataproc.googleapis.com/Batch,dataproc.googleapis.com/Cluster,dataproc.googleapis.com/Job,dataproc.googleapis.com/Session,file.googleapis.com/Instance,k8s.io/Node,k8s.io/Pod,k8s.io/Service,logging.googleapis.com/LogSink,netapp.googleapis.com/Volume,pubsub.googleapis.com/Snapshot,pubsub.googleapis.com/Subscription,pubsub.googleapis.com/Topic,recaptchaenterprise.googleapis.com/Key,redis.googleapis.com/Cluster,redis.googleapis.com/Instance,run.googleapis.com/Job,run.googleapis.com/Revision,spanner.googleapis.com/Instance,storage.googleapis.com/Bucket"
gcloud asset feeds create dt-asset-feed-$MCONFIG_ID \
--folder=FOLDER_ID \
--billing-project=$PROJECT_ID \
--pubsub-topic=projects/$PROJECT_ID/topics/dt-asset-feed-$MCONFIG_ID \
--content-type=resource \
--asset-types="$ASSET_TYPES"

Replace ORG_ID with your numeric organization ID.

ASSET_TYPES="alloydb.googleapis.com/Cluster,alloydb.googleapis.com/Instance,apigee.googleapis.com/Instance,apigee.googleapis.com/Organization,appengine.googleapis.com/Application,bigquery.googleapis.com/Dataset,bigquery.googleapis.com/Model,bigquerydatatransfer.googleapis.com/TransferConfig,bigtableadmin.googleapis.com/Backup,bigtableadmin.googleapis.com/Cluster,bigtableadmin.googleapis.com/Table,cloudfunctions.googleapis.com/CloudFunction,cloudresourcemanager.googleapis.com/TagBinding,cloudtasks.googleapis.com/Queue,composer.googleapis.com/Environment,compute.googleapis.com/Autoscaler,compute.googleapis.com/BackendService,compute.googleapis.com/Disk,compute.googleapis.com/Instance,compute.googleapis.com/Interconnect,compute.googleapis.com/InterconnectAttachment,compute.googleapis.com/Network,compute.googleapis.com/Router,compute.googleapis.com/ServiceAttachment,compute.googleapis.com/VpnGateway,compute.googleapis.com/VpnTunnel,container.googleapis.com/Cluster,dataflow.googleapis.com/Job,dataproc.googleapis.com/Batch,dataproc.googleapis.com/Cluster,dataproc.googleapis.com/Job,dataproc.googleapis.com/Session,file.googleapis.com/Instance,k8s.io/Node,k8s.io/Pod,k8s.io/Service,logging.googleapis.com/LogSink,netapp.googleapis.com/Volume,pubsub.googleapis.com/Snapshot,pubsub.googleapis.com/Subscription,pubsub.googleapis.com/Topic,recaptchaenterprise.googleapis.com/Key,redis.googleapis.com/Cluster,redis.googleapis.com/Instance,run.googleapis.com/Job,run.googleapis.com/Revision,spanner.googleapis.com/Instance,storage.googleapis.com/Bucket"
gcloud asset feeds create dt-asset-feed-$MCONFIG_ID \
--organization=ORG_ID \
--billing-project=$PROJECT_ID \
--pubsub-topic=projects/$PROJECT_ID/topics/dt-asset-feed-$MCONFIG_ID \
--content-type=resource \
--asset-types="$ASSET_TYPES"

Step 13: Set up log forwarding Optional

Log forwarding uses the pipeline: Cloud Logging sink → Pub/Sub → Dataflow streaming job → Dynatrace Log Ingest API.

Create a platform token with the data-acquisition:logs:ingest scope and note the value—you need it below as DT_LOG_INGEST_TOKEN.

Set additional environment variables. These names match the resources the Terraform bundle creates, so both deployment methods produce identical resource names:

LOG_JOB_NAME="dt-logs" # base name for all log-forwarding resources
DATAFLOW_IMAGE_TAG="1.0.3"
TEMPLATE_BUCKET="${PROJECT_NUMBER}-logs-${MCONFIG_ID}"
TEMP_BUCKET="${PROJECT_NUMBER}-logs-temp-${MCONFIG_ID}"
DT_LOG_INGEST_TOKEN="<your-platform-token>"

Enable required APIs:

gcloud services enable \
logging.googleapis.com \
dataflow.googleapis.com \
storage.googleapis.com \
secretmanager.googleapis.com \
--project=$PROJECT_ID

Create Pub/Sub topics (main + dead-letter queue):

gcloud pubsub topics create ${LOG_JOB_NAME}-${MCONFIG_ID} --project=$PROJECT_ID
gcloud pubsub topics create ${LOG_JOB_NAME}-dlq-${MCONFIG_ID} --project=$PROJECT_ID

Create subscriptions:

# Main subscription (consumed by Dataflow)
gcloud pubsub subscriptions create ${LOG_JOB_NAME}-sub-${MCONFIG_ID} \
--topic=${LOG_JOB_NAME}-${MCONFIG_ID} \
--project=$PROJECT_ID \
--ack-deadline=120 \
--message-retention-duration=604800s \
--expiration-period=never
# Dead-letter queue subscription (for inspection)
gcloud pubsub subscriptions create ${LOG_JOB_NAME}-dlq-sub-${MCONFIG_ID} \
--topic=${LOG_JOB_NAME}-dlq-${MCONFIG_ID} \
--project=$PROJECT_ID \
--ack-deadline=120 \
--message-retention-duration=604800s \
--expiration-period=never
# Replay subscription (filtered to non-permanent failures; consumed in-pipeline)
gcloud pubsub subscriptions create ${LOG_JOB_NAME}-dlq-replay-sub-${MCONFIG_ID} \
--topic=${LOG_JOB_NAME}-dlq-${MCONFIG_ID} \
--project=$PROJECT_ID \
--ack-deadline=120 \
--message-retention-duration=604800s \
--expiration-period=never \
--message-filter='attributes.dt-dlq-permanent = "false"'

--expiration-period=never keeps the subscriptions from expiring when they are inactive. The Terraform bundle creates its subscriptions the same way.

Create a Cloud Logging sink for this project and grant its writer identity publisher access on the main topic. To forward the logs of other projects, create a sink in each of them with the same destination—see Create log sinks:

gcloud logging sinks create ${LOG_JOB_NAME}-sink-${MCONFIG_ID} \
pubsub.googleapis.com/projects/${PROJECT_ID}/topics/${LOG_JOB_NAME}-${MCONFIG_ID} \
--project=$PROJECT_ID
SINK_WRITER=$(gcloud logging sinks describe ${LOG_JOB_NAME}-sink-${MCONFIG_ID} \
--project=$PROJECT_ID --format='value(writerIdentity)')
gcloud pubsub topics add-iam-policy-binding ${LOG_JOB_NAME}-${MCONFIG_ID} \
--project=$PROJECT_ID \
--member="$SINK_WRITER" \
--role="roles/pubsub.publisher"

Grant the service account the required roles:

# Publish failed messages to the dead-letter topic
gcloud pubsub topics add-iam-policy-binding ${LOG_JOB_NAME}-dlq-${MCONFIG_ID} \
--project=$PROJECT_ID \
--member="serviceAccount:$CUSTOMER_SA_EMAIL" \
--role="roles/pubsub.publisher"
# Subscribe to main and replay subscriptions
for SUB in "${LOG_JOB_NAME}-sub-${MCONFIG_ID}" "${LOG_JOB_NAME}-dlq-replay-sub-${MCONFIG_ID}"; do
gcloud pubsub subscriptions add-iam-policy-binding $SUB \
--project=$PROJECT_ID \
--member="serviceAccount:$CUSTOMER_SA_EMAIL" \
--role="roles/pubsub.subscriber"
gcloud pubsub subscriptions add-iam-policy-binding $SUB \
--project=$PROJECT_ID \
--member="serviceAccount:$CUSTOMER_SA_EMAIL" \
--role="roles/pubsub.viewer"
done
# Project-level roles
for ROLE in roles/dataflow.worker roles/logging.logWriter roles/secretmanager.secretAccessor; do
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:$CUSTOMER_SA_EMAIL" \
--role="$ROLE"
done

Create GCS buckets (Dataflow flex template spec and temp storage):

gcloud storage buckets create gs://$TEMPLATE_BUCKET \
--project=$PROJECT_ID --location=$REGION --uniform-bucket-level-access
gcloud storage buckets create gs://$TEMP_BUCKET \
--project=$PROJECT_ID --location=$REGION --uniform-bucket-level-access
gcloud storage buckets add-iam-policy-binding gs://$TEMP_BUCKET \
--member="serviceAccount:$CUSTOMER_SA_EMAIL" \
--role="roles/storage.objectAdmin"
gcloud storage buckets add-iam-policy-binding gs://$TEMPLATE_BUCKET \
--member="serviceAccount:$CUSTOMER_SA_EMAIL" \
--role="roles/storage.objectViewer"

Store the Log Ingest token in Secret Manager:

echo -n "$DT_LOG_INGEST_TOKEN" | \
gcloud secrets create ${LOG_JOB_NAME}-api-token-${MCONFIG_ID} \
--project=$PROJECT_ID \
--data-file=-

Grant the Dataflow service agent roles/iam.serviceAccountUser on the customer service account:

DATAFLOW_AGENT="service-${PROJECT_NUMBER}@dataflow-service-producer-prod.iam.gserviceaccount.com"
gcloud iam service-accounts add-iam-policy-binding $CUSTOMER_SA_EMAIL \
--member="serviceAccount:$DATAFLOW_AGENT" \
--role="roles/iam.serviceAccountUser" \
--project=$PROJECT_ID

Upload the Dataflow flex template spec to GCS:

cat > /tmp/dt-flex-template-spec.json <<EOF
{
"image": "docker.io/dynatrace/dynatrace-gcp-dataflow-template:${DATAFLOW_IMAGE_TAG}",
"sdkInfo": {"language": "JAVA"},
"metadata": {
"name": "Dynatrace Log Forwarder",
"description": "Reads messages from a Pub/Sub subscription and forwards them to the Dynatrace log ingest endpoint.",
"parameters": [
{"name": "subscription", "label": "Pub/Sub Subscription", "isOptional": false},
{"name": "dynatraceUrl", "label": "Dynatrace Log Ingest URL", "isOptional": false},
{"name": "dynatraceApiTokenSecret", "label": "Dynatrace API Token Secret", "isOptional": false},
{"name": "deadLetterTopic", "label": "Dead-Letter Pub/Sub Topic", "isOptional": false},
{"name": "monitoringConfigurationId", "label": "Monitoring Configuration ID", "isOptional": false},
{"name": "windowSeconds", "label": "Window Duration (seconds)", "isOptional": true, "defaultValue": "10"},
{"name": "batchSize", "label": "Batch Size", "isOptional": true, "defaultValue": "1000"},
{"name": "maxBatchBytes", "label": "Max Batch Bytes", "isOptional": true, "defaultValue": "10485760"},
{"name": "maxReplayAttempts", "label": "Max Replay Attempts", "isOptional": true, "defaultValue": "5"},
{"name": "replaySubscription", "label": "Replay Subscription", "isOptional": true},
{"name": "replayBatchSize", "label": "Replay Batch Size", "isOptional": true, "defaultValue": "50"}
]
}
}
EOF
gcloud storage cp /tmp/dt-flex-template-spec.json \
gs://$TEMPLATE_BUCKET/templates/${LOG_JOB_NAME}.json

Launch the Dataflow flex template job:

JOB_NAME="${LOG_JOB_NAME}-${DATAFLOW_IMAGE_TAG//./-}-${MCONFIG_ID}"
gcloud dataflow flex-template run $JOB_NAME \
--project=$PROJECT_ID \
--region=$REGION \
--template-file-gcs-location=gs://$TEMPLATE_BUCKET/templates/${LOG_JOB_NAME}.json \
--service-account-email=$CUSTOMER_SA_EMAIL \
--temp-location=gs://$TEMP_BUCKET/tmp \
--enable-streaming-engine \
--max-workers=4 \
--parameters="subscription=projects/${PROJECT_ID}/subscriptions/${LOG_JOB_NAME}-sub-${MCONFIG_ID}" \
--parameters="dynatraceUrl=${DT_INGEST_URL}/api/gcp/pubsub/v1/logs" \
--parameters="dynatraceApiTokenSecret=projects/${PROJECT_ID}/secrets/${LOG_JOB_NAME}-api-token-${MCONFIG_ID}/versions/latest" \
--parameters="deadLetterTopic=projects/${PROJECT_ID}/topics/${LOG_JOB_NAME}-dlq-${MCONFIG_ID}" \
--parameters="monitoringConfigurationId=${MCONFIG_ID}" \
--parameters="replaySubscription=projects/${PROJECT_ID}/subscriptions/${LOG_JOB_NAME}-dlq-replay-sub-${MCONFIG_ID}"

--max-workers caps autoscaling, so the job can't scale out without limit when the Dynatrace endpoint is unavailable and messages pile up. The Terraform bundle uses the same default of 4. Raise the value if you forward high log volumes.

Verify the job started:

gcloud dataflow jobs list \
--region=$REGION \
--project=$PROJECT_ID \
--filter="name:$JOB_NAME"

Monitoring configuration references

Configuration properties

Properties are located under value.googlecloud in the YAML configuration:

PropertyTypeDescription
locationfilteringlist of stringsGCP regions to monitor (for example, us-central1, europe-west1)
projectfilteringlist of stringsProject IDs to monitor
folderfilteringlist of stringsFolder IDs to monitor
observabilityscopesenabledbooleanCollect metrics only from scoping projects (true) instead of all visible projects (false, default). Equivalent to the Metric collection scope setting in the Settings app. When true, Dynatrace auto-detects scoping projects.
labelfilteringlist of key/value/conditionInclude or exclude resources by label
tagfilteringlist of key/value/conditionInclude or exclude resources by tag
labelenrichmentlist of stringsLabel keys to forward to signals
tagenrichmentlist of stringsTag keys to forward to signals
resourceslist of objectsCustom resources and metrics to collect

Feature sets are located under value.featuresets as a list of strings.

Available locations

For a complete list of available Google Cloud regions, see the official Google Cloud documentation:

  • Google Cloud locations—overview of all regions and zones
  • Compute Engine regions and zones—detailed region codes and descriptions

Use region codes (for example, us-central1, europe-west1, asia-east1) when configuring location filtering.

What's next?

Once steps are complete, data collection starts:

  • Metrics collection starts within the next five-minute polling interval, plus Google's metric ingest delay.
  • Topology (resource inventory) appears within about 15 minutes, once the initial Cloud Asset Inventory scrape completes. After that, changes stream in near real time through the asset inventory feed (if configured), and Dynatrace runs a full scrape every hour.

View your GCP data in Clouds Clouds, or query and correlate signals in Notebooks Notebooks.

If metrics or topology are still missing after 15 minutes, see Troubleshoot GCP connections.

Related tags
Infrastructure Observability