This page explains how to manually set up GCP monitoring in Dynatrace using:
dtctl: Manual Dynatrace configurationUse gcloud CLI for GCP setup and dtctl for Dynatrace configuration. For quick tool reference, see the table below.
| Step | gcloud CLI | dtctl |
|---|---|---|
- | ||
- | ||
- | ||
- | ||
- | ||
- | ||
- | ||
- | ||
- | ||
- |
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.
curl -X POST "${DT_TENANT_URL}/api/v2/hub/extensions2/com.dynatrace.extension.da-gcp" \--header "Authorization: Api-Token ${DT_API_TOKEN}" \--header "Accept: application/json"
DT_API_TOKEN requires hub:read and hub:write permissions.
Set these variables before running the commands. They're used throughout all examples.
# Your GCP project IDPROJECT_ID="my-project-id"# 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 — needed for asset feed and log forwardingTENANT_ID="my-tenant-id" # Dynatrace environment ID (e.g. abc12345)DT_TENANT_URL="https://${TENANT_ID}.da.dynatrace.com"REGION="us-central1" # GCP region for infrastructure deployment# Monitoring configuration ID — printed by 'dtctl create gcp monitoring' in step 7MCONFIG_ID="<monitoring-config-id>"
The Dynatrace service account 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
Set the Dynatrace service account in your environment:
DT_SERVICE_ACCOUNT="dynatrace-abc12345@dtp-prod-gcp-auth.iam.gserviceaccount.com"
The user running the setup must have these permissions:
| For | Role | Purpose |
|---|---|---|
GCP |
| Create and manage service accounts |
GCP |
| Enable Google Cloud APIs |
GCP |
| Grant IAM roles at project level |
GCP |
| Grant IAM roles at folder level (if applicable) |
GCP |
| Grant IAM roles at organization level (if applicable) |
Dynatrace | IAM user assigned the | Authenticate |
Dynatrace | API token with | Manage connections and monitoring configs (only needed for token-based |
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
Enable the required APIs in your GCP project.
| API | Purpose |
|---|---|
| Access to zones and regions information |
| Access to projects, folders, and organizations |
| Access to asset inventory |
| Access to metrics |
gcloud services enable \compute.googleapis.com \cloudresourcemanager.googleapis.com \cloudasset.googleapis.com \monitoring.googleapis.com \--project=$PROJECT_ID
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
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.
| Permission | Purpose |
|---|---|
| List availability zones |
| List regions |
| List organizations |
| List folders |
| List projects |
| List assets from a scope |
| List metric descriptors |
| Retrieve time series data |
Grant viewer permissions to each project you want to monitor.
gcloud projects add-iam-policy-binding $PROJECT_ID \--member="serviceAccount:$CUSTOMER_SA_EMAIL" \--role="roles/viewer"
For multiple projects:
for PROJECT in project-1 project-2 project-3; dogcloud projects add-iam-policy-binding $PROJECT \--member="serviceAccount:$CUSTOMER_SA_EMAIL" \--role="roles/viewer"done
Grant folder viewer permissions if you want to monitor entire folders.
gcloud resource-manager folders add-iam-policy-binding FOLDER_ID \--member="serviceAccount:$CUSTOMER_SA_EMAIL" \--role="roles/resourcemanager.folderViewer"
Grant organization viewer permissions for organization-wide monitoring.
gcloud organizations add-iam-policy-binding ORG_ID \--member="serviceAccount:$CUSTOMER_SA_EMAIL" \--role="roles/resourcemanager.organizationViewer"
This is the critical step that establishes trust. Grant the Dynatrace service account 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:
| Permission | Purpose |
|---|---|
| Allows Dynatrace to obtain short-lived access tokens by impersonating your service account |
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.
Update the Dynatrace connection with your service account details. This enables Dynatrace to use service account impersonation.
Wait a minute for IAM changes to propagate, then run:
dtctl update gcp connection \--name "$GCP_CONNECTION_NAME" \--serviceAccountId "$CUSTOMER_SA_EMAIL"
Verify the connection:
dtctl get gcp connections
Create the monitoring configuration to start collecting data from GCP.
Create with defaults (all locations, all essential 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
After initial setup, you can customize which regions and services to monitor.
List available GCP regions:
dtctl get gcp monitoring-locations
List available feature sets (services to monitor):
dtctl get gcp monitoring-feature-sets
To modify a monitoring configuration, export it as YAML, edit the file, and apply the changes.
Export the current configuration:
# Get the configuration IDdtctl get gcp monitoring# Export to YAML (replace with your configuration ID)dtctl get gcp monitoring <configuration-id> -o yaml > config.yaml
Edit config.yaml to update properties. Example structure:
objectid: 0e184789-2f81-34ed-9358-c6b4e47f0defscope: integration-gcpvalue:enabled: truedescription: my-gcp-monitoringgooglecloud:locationfiltering:- us-central1- us-east1- europe-west1projectfiltering: []folderfiltering: []# ... other propertiesfeaturesets:- compute_engine_essential- cloud_run_essential- cloud_sql_essential
Apply the updated configuration:
dtctl apply -f config.yaml
Ingest metrics from Google Cloud resources not covered by the default feature sets, or collect additional metrics beyond the recommended set.
To add custom resources, export your configuration to YAML, add the resources section under value.googlecloud, and apply. Example:
value:googlecloud:# ... existing configuration ...resources:# Auto-discovery: collect all available metrics- resourcetype: gcs_bucketautodiscoveryenabled: true# Specific metrics only- resourcetype: pubsub_topicautodiscoveryenabled: falsemetrics:- name: pubsub.googleapis.com/topic/send_request_counttype: CUSTOM- name: pubsub.googleapis.com/topic/message_sizestype: CUSTOM# Auto-discovery with exclusions- resourcetype: gce_instanceautodiscoveryenabled: trueautodiscoveryexcludemetrictype:- compute.googleapis.com/instance/disk/*
| Field | Description |
|---|---|
| Google Cloud monitored resource type (for example, |
|
|
| List of specific metrics to collect (when auto-discovery is off) |
| Metric prefixes to exclude (when auto-discovery is on) |
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
Get the project number (needed for the Pub/Sub service agent):
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
Create the Pub/Sub topic:
gcloud pubsub topics create dt-asset-feed-$MCONFIG_ID \--project=$PROJECT_ID \--message-retention-duration=3600s
Grant the Pub/Sub service agent token creator rights on the customer service account. This allows the push subscription to generate OIDC tokens:
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
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_TENANT_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=3600s \--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"
The Terraform deployment applies a JavaScript message transform that strips the priorAsset field from feed events to reduce message size and egress cost. This is not configurable via gcloud CLI. Without it, events include priorAsset, which increases message size but does not affect functionality.
Log forwarding uses the pipeline: Cloud Logging sink → Pub/Sub → Dataflow streaming job → Dynatrace Log Ingest API.
Set additional environment variables:
LOG_JOB_NAME="dynatrace-log-forwarder"DATAFLOW_IMAGE_TAG="0.0.35"TEMPLATE_BUCKET="${PROJECT_ID}-${LOG_JOB_NAME}"TEMP_BUCKET="${PROJECT_ID}-dataflow-temp"DT_LOG_INGEST_TOKEN="<your-logs.ingest-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 --project=$PROJECT_IDgcloud pubsub topics create ${LOG_JOB_NAME}-dlq --project=$PROJECT_ID
Create subscriptions:
# Main subscription (consumed by Dataflow)gcloud pubsub subscriptions create ${LOG_JOB_NAME}-sub \--topic=$LOG_JOB_NAME \--project=$PROJECT_ID \--ack-deadline=120 \--message-retention-duration=604800s# Dead-letter queue subscription (for inspection)gcloud pubsub subscriptions create ${LOG_JOB_NAME}-dlq-sub \--topic=${LOG_JOB_NAME}-dlq \--project=$PROJECT_ID \--ack-deadline=120 \--message-retention-duration=604800s# Replay subscription (filtered to non-permanent failures; consumed in-pipeline)gcloud pubsub subscriptions create ${LOG_JOB_NAME}-dlq-replay-sub \--topic=${LOG_JOB_NAME}-dlq \--project=$PROJECT_ID \--ack-deadline=120 \--message-retention-duration=604800s \--message-filter='attributes.dt-dlq-permanent = "false"'
Create a Cloud Logging sink and grant its writer identity publisher access on the main topic:
gcloud logging sinks create ${LOG_JOB_NAME}-sink \pubsub.googleapis.com/projects/${PROJECT_ID}/topics/${LOG_JOB_NAME} \--project=$PROJECT_IDSINK_WRITER=$(gcloud logging sinks describe ${LOG_JOB_NAME}-sink \--project=$PROJECT_ID --format='value(writerIdentity)')gcloud pubsub topics add-iam-policy-binding $LOG_JOB_NAME \--project=$PROJECT_ID \--member="$SINK_WRITER" \--role="roles/pubsub.publisher"
Grant the service account the required roles:
# Publish failed messages to the dead-letter topicgcloud pubsub topics add-iam-policy-binding ${LOG_JOB_NAME}-dlq \--project=$PROJECT_ID \--member="serviceAccount:$CUSTOMER_SA_EMAIL" \--role="roles/pubsub.publisher"# Subscribe to main and replay subscriptionsfor SUB in "${LOG_JOB_NAME}-sub" "${LOG_JOB_NAME}-dlq-replay-sub"; dogcloud 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 rolesfor ROLE in roles/dataflow.worker roles/logging.logWriter roles/secretmanager.secretAccessor; dogcloud 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-accessgcloud storage buckets create gs://$TEMP_BUCKET \--project=$PROJECT_ID --location=$REGION --uniform-bucket-level-accessgcloud 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}-dynatrace-api-token \--project=$PROJECT_ID \--data-file=-
Grant the Dataflow service agent roles/iam.serviceAccountUser on the customer service account:
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')DATAFLOW_AGENT="service-${PROJECT_NUMBER}@dataflow-service-account-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"}]}}EOFgcloud storage cp /tmp/dt-flex-template-spec.json \gs://$TEMPLATE_BUCKET/templates/dynatrace-log-forwarder.json
Launch the Dataflow flex template job:
JOB_NAME="${LOG_JOB_NAME}-${DATAFLOW_IMAGE_TAG//./-}"gcloud dataflow flex-template run $JOB_NAME \--project=$PROJECT_ID \--region=$REGION \--template-file-gcs-location=gs://$TEMPLATE_BUCKET/templates/dynatrace-log-forwarder.json \--service-account-email=$CUSTOMER_SA_EMAIL \--temp-location=gs://$TEMP_BUCKET/tmp \--enable-streaming-engine \--parameters="subscription=projects/${PROJECT_ID}/subscriptions/${LOG_JOB_NAME}-sub" \--parameters="dynatraceUrl=${DT_TENANT_URL}/api/gcp/pubsub/v1/logs" \--parameters="dynatraceApiTokenSecret=projects/${PROJECT_ID}/secrets/${LOG_JOB_NAME}-dynatrace-api-token/versions/latest" \--parameters="deadLetterTopic=projects/${PROJECT_ID}/topics/${LOG_JOB_NAME}-dlq" \--parameters="monitoringConfigurationId=${MCONFIG_ID}" \--parameters="replaySubscription=projects/${PROJECT_ID}/subscriptions/${LOG_JOB_NAME}-dlq-replay-sub"
Verify the job started:
gcloud dataflow jobs list \--region=$REGION \--project=$PROJECT_ID \--filter="name:$JOB_NAME"
This section provides a complete reference of all available configuration properties.
Properties are located under value.googlecloud in the YAML configuration:
| Property | Type | Description |
|---|---|---|
| list of strings | GCP regions to monitor (for example, |
| list of strings | Project IDs to monitor |
| list of strings | Folder IDs to monitor |
| list of key/value/condition | Include or exclude resources by label |
| list of key/value/condition | Include or exclude resources by tag |
| list of strings | Label keys to forward to signals |
| list of strings | Tag keys to forward to signals |
| list of objects | Custom resources and metrics to collect |
Feature sets are located under value.featuresets as a list of strings.
For a complete list of available Google Cloud regions, see the official Google Cloud documentation:
Use region codes (for example, us-central1, europe-west1, asia-east1) when configuring location filtering.
To remove the GCP monitoring setup
If you deployed log forwarding or asset inventory feed infrastructure using Terraform, run terraform destroy in that directory first to remove the Pub/Sub topics, Dataflow jobs, and related resources before deleting the Dynatrace objects below.
dtctl delete gcp monitoring $GCP_MONITORING_NAME
dtctl delete gcp connection $GCP_CONNECTION_NAME
Remove IAM bindings.
# Remove impersonation rightsgcloud iam service-accounts remove-iam-policy-binding $CUSTOMER_SA_EMAIL \--member="serviceAccount:$DT_SERVICE_ACCOUNT" \--role="roles/iam.serviceAccountTokenCreator"# Remove viewer rolegcloud projects remove-iam-policy-binding $PROJECT_ID \--member="serviceAccount:$CUSTOMER_SA_EMAIL" \--role="roles/viewer"
Delete the service account.
gcloud iam service-accounts delete $CUSTOMER_SA_EMAIL --project=$PROJECT_ID
# Delete the asset feed (use the scope where you created it)gcloud asset feeds delete dt-asset-feed-$MCONFIG_ID --project=$PROJECT_ID# Delete the Pub/Sub subscription and topicgcloud pubsub subscriptions delete dt-asset-feed-push-$MCONFIG_ID --project=$PROJECT_IDgcloud pubsub topics delete dt-asset-feed-$MCONFIG_ID --project=$PROJECT_ID
# Cancel the Dataflow jobJOB_ID=$(gcloud dataflow jobs list \--region=$REGION --project=$PROJECT_ID \--filter="name:${LOG_JOB_NAME}" \--format='value(id)' | head -1)gcloud dataflow jobs cancel $JOB_ID --region=$REGION --project=$PROJECT_ID# Delete Pub/Sub subscriptions and topicsgcloud pubsub subscriptions delete \${LOG_JOB_NAME}-sub \${LOG_JOB_NAME}-dlq-sub \${LOG_JOB_NAME}-dlq-replay-sub \--project=$PROJECT_IDgcloud pubsub topics delete $LOG_JOB_NAME ${LOG_JOB_NAME}-dlq --project=$PROJECT_ID# Delete the log sinkgcloud logging sinks delete ${LOG_JOB_NAME}-sink --project=$PROJECT_ID# Delete Secret Manager secretgcloud secrets delete ${LOG_JOB_NAME}-dynatrace-api-token --project=$PROJECT_ID# Delete GCS bucketsgcloud storage rm -r gs://$TEMPLATE_BUCKETgcloud storage rm -r gs://$TEMP_BUCKET