Measuring Gemini Enterprise Adoption with BigQuery
Generative AI is transforming enterprise operations at an unprecedented pace. With Gemini Enterprise, organizations are equipping their workforce with AI assistance and agents deeply integrated across Google Workspace, Google Cloud, and enterprise data repositories. From summarizing complex documents and drafting emails to taking actions and querying multi-source enterprise data, Gemini Enterprise delivers significant productivity gains.
However, rolling out Gemini Enterprise across thousands of employees introduces some common challenges:
- How do organizations gain deep, actionable insights into how Gemini Enterprise is actually being used?
- How to ensure governance and security for such a powerful tool?
- How to make it even more valuable?
- How to identify opportunities to build custom AI Agents that maximize organizational productivity?
Without robust telemetry, organizations face critical blind spots:
- Adoption Visibility: Which departments actively leverage Gemini Enterprise, and where are licenses sitting idle?
- Feature & Intent Patterns: What daily tasks (e.g., document summarization, code generation, data analysis) are driving employee prompts?
- Security & Compliance Governance: How do enterprise security teams audit user actions and access patterns without violating privacy?
- Agent Opportunity Identification: How can IT leaders identify high-frequency, repetitive user tasks that should be automated by custom-built AI Agents?
In this post, I will demonstrate the following:
- Deploying Gemini Enterprise app using Terraform, including various configurations and controls
- Deploying data stores to ground Gemini Enterprise answers against Enterprise data
- Configuring telemetry import into BigQuery for insights, governance and analytics
- Configuring Model Armor to protect against various attacks like jailbreaking, prompt injection and more.
Why Use Infrastructure-as-Code (Terraform) for Gemini Enterprise deployment and configuration?
When rolling out Gemini Enterprise across an enterprise Google Cloud environment, like any other GCP service, using Infrastructure-as-Code (IaC) approach helps avoid configuration drift, human errors, and operational overhead and introduces versioning and tracking for the infrastructure changes.
NOTE: Currently not all configurations and capabilities are supported via Terraform or other IaC tools, and some may need to be configured via the Cloud Console. However, Google is continuously adding support for more configurations and capabilities.
Demo Overview
In this demo, we have a fictional company called Cymbal that wants to deploy Gemini Enterprise and ground it against their Cloud SQL database and a Google Storage bucket containing various documents for their HR policies and employee information. They would like to enable Gemini Enterprise for all their employees and control the usage. They would also like to monitor the usage and identify opportunities to build custom AI Agents that maximize organizational productivity. All while maintaining responsible AI principles, user privacy and security against various AI threats and attacks.
+---------------------+ +---------------------+ +---------------------+ +-----------------------+
| Gemini Enterprise | ---> | Cloud Log Router | ---> | BigQuery Dataset | ---> | Insights & Visibility|
| + Model Armor | | | | | | |
+---------------------+ +---------------------+ +---------------------+ +-----------------------+
|
|
V
+-----------------+
| Data Stores |
| - Cloud SQL DB |
| - GCS Bucket |
+-----------------+
Data sources
First, we have a PostgreSQL database hosted on Cloud SQL with various HR information distributed across 3 tables for skills, peer feedback and performance reviews. Here is a schema of the database:

Then, we have a Google Storage bucket with various HR policies and employee information documents, such as employee handbook, the company’s policies and earnings information.
Deploying Gemini Enterprise Infrastructure with Terraform
Let’s walk through the key Terraform resources to set up for this demo.
1. Provider Configuration & API Enablement
First, set up your Terraform provider and enable the required Google Cloud APIs.
resource "google_project_service" "required_apis" {
for_each = var.required_apis
service = each.value
project = var.project_id
disable_on_destroy = false
disable_dependent_services = false
}
# Add a delay after enabling APIs to ensure they propagate
resource "time_sleep" "api_propagation" {
depends_on = [google_project_service.required_apis]
create_duration = "30s"
}
In the terraform.tfvars we provide a set of required APIs as follows
required_apis = [
"storage.googleapis.com",
"eventarc.googleapis.com",
"logging.googleapis.com",
"aiplatform.googleapis.com",
"discoveryengine.googleapis.com"
]
2. Creating the Gemini Enterprise App and data stores
Create a google_discovery_engine_search_engine resource for the app and a google_discovery_engine_data_store resource for the main data store.
Let’s first create our data stores.
NOTE: Gemini Enterprise provides lots of connectors to 3rd party systems like Jira, Salesforce, Sharepoint, and more. However, for this demo, we are using Cloud SQL and Google Storage.
NOTE: In an enterprise deployment, you would want to set a scheduled import for your data sources. At the moment, the only way to do that periodic import is using the console. In this demo, we will use the API within Terraform to do a one-time import.
Cloud Storage data store
We already have a bucket with all the documents we need so we will create a data store first and set the content_config as CONTENT_REQUIRED to indicate unstructured data.
resource "google_discovery_engine_data_store" "gemini_search_store" {
location = lower(var.ge_region)
project = var.project_id
data_store_id = "storage-${random_id.ge_suffix.hex}"
display_name = "Cymbal Storage Data Store"
industry_vertical = var.ge_industry_vertical
content_config = "CONTENT_REQUIRED"
solution_types = [local.solution_type]
create_advanced_site_search = false
depends_on = [time_sleep.api_propagation]
timeouts {
create = "20m"
update = "15m"
delete = "20m"
}
}
Then, we will trigger the import using an API call with the bucket URL.
resource "null_resource" "import_gcs_data" {
depends_on = [google_discovery_engine_data_store.gemini_search_store]
provisioner "local-exec" {
command = <<EOT
curl -X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
"https://discoveryengine.googleapis.com/v1/projects/${var.project_id}/locations/${var.ge_region}/collections/default_collection/dataStores/${google_discovery_engine_data_store.gemini_search_store.data_store_id}/branches/0/documents:import" \
-d '{
"gcsSource": {
"inputUris": ["gs://cloud-samples-data/gen-app-builder/search/alphabet-investor-pdfs/*.pdf"],
"dataSchema": "content"
},
"reconciliationMode": "INCREMENTAL"
}'
EOT
}
}
Cloud SQL data store
We also have the Cloud SQL database, so we will create a data store for it and an import job for each table we want to import.
resource "google_discovery_engine_data_store" "gemini_sql_store" {
for_each = var.cloud_sql_config != null ? var.cloud_sql_config : {}
location = lower(var.ge_region)
project = var.project_id
data_store_id = "sql-${replace(lower(coalesce(each.value.cloud_sql_table, each.key)), "_", "-")}-${random_id.ge_suffix.hex}"
display_name = "Cymbal SQL Data Store - ${coalesce(each.value.cloud_sql_table, each.key)}"
industry_vertical = var.ge_industry_vertical
content_config = "NO_CONTENT"
solution_types = [local.solution_type]
create_advanced_site_search = false
depends_on = [time_sleep.api_propagation]
timeouts {
create = "20m"
update = "15m"
delete = "20m"
}
}
Next, we need to create an import job for each table we want to import.
NOTE: For this demo, we are using a
null_resourceto trigger the import using an API call. In a production environment, you would want to use a scheduled import to keep your data in sync.
resource "null_resource" "import_sql_data" {
for_each = var.cloud_sql_config != null ? var.cloud_sql_config : {}
depends_on = [google_discovery_engine_data_store.gemini_sql_store, time_sleep.iam_propagation]
provisioner "local-exec" {
command = <<EOT
curl -X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json" \
"https://discoveryengine.googleapis.com/v1/projects/${var.project_id}/locations/${var.ge_region}/collections/default_collection/dataStores/${google_discovery_engine_data_store.gemini_sql_store[each.key].data_store_id}/branches/0/documents:import" \
-d '{
"cloudSqlSource": {
"projectId": "${var.project_id}",
"instanceId": "${coalesce(each.value.cloud_sql_instance, each.key)}",
"databaseId": "${each.value.cloud_sql_database}",
"tableId": "${each.value.cloud_sql_table}",
"gcsStagingDir": "gs://${google_storage_bucket.ge_staging_bucket.name}/staging/"
},
"reconciliationMode": "INCREMENTAL",
"autoGenerateIds": true
}'
EOT
}
}
Create the Gemini Enterprise App
The next step is to create the Gemini Enterprise app and link it to the data stores we created above.
resource "google_discovery_engine_search_engine" "gemini_search_engine" {
engine_id = "${local.name_prefix}-engine-${random_id.ge_suffix.hex}"
collection_id = "default_collection"
project = var.project_id
location = var.ge_region
display_name = "Gemini Enterprise App ${local.name_prefix}"
industry_vertical = var.ge_industry_vertical
data_store_ids = concat([google_discovery_engine_data_store.gemini_search_store.data_store_id], values(google_discovery_engine_data_store.gemini_sql_store)[*].data_store_id)
app_type = var.ge_app_type
common_config {
company_name = var.company_name
}
features = {
for k, v in var.ge_search_features :
replace(k, "_", "-") => (v ? "FEATURE_STATE_ON" : "FEATURE_STATE_OFF")
if v != null
}
search_engine_config {
search_tier = var.ge_search_tier
search_add_ons = var.ge_search_addons
required_subscription_tier = var.ge_subscription_tier
}
knowledge_graph_config {}
depends_on = [time_sleep.api_propagation]
}
These are the variables that we will provide to this resource. You can see we are configuring the various options in Gemini Enterprise, for example, we enabled image generation but disabled video generation (we will see this in the console once deployed).
ge_app_type = "APP_TYPE_INTRANET"
company_name = "Cymbal"
ge_region = "global"
ge_subscription_tier = "SUBSCRIPTION_TIER_SEARCH_AND_ASSISTANT"
ge_search_features = {
agent_sharing_without_admin_approval = false
disable_agent_sharing = true
disable_image_generation = false
disable_onedrive_upload = true
disable_video_generation = true
model_selector = true
notebook_lm = true
no_code_agent_builder = true
agent_gallery = true
personalization_memory = true
prompt_gallery = true
}
3. BigQuery deployment and log routing
Next, we need to create a BigQuery dataset to store the logs and a log sink to route the logs from Cloud Logging to BigQuery. We can do this using the following Terraform resources. This will create a BigQuery dataset and two log sinks. The first sink will route the streaming user activity and gen ai logs to BigQuery and the second sink will route the audit logs to BigQuery.
resource "google_bigquery_dataset" "ge_telemetry" {
project = var.project_id
dataset_id = "ge_telemetry_${random_id.ge_suffix.hex}"
location = var.bq_region
description = "This dataset will be used to analyze Gemini Enterprise usage and adoption"
delete_contents_on_destroy = true
}
resource "google_logging_project_sink" "ge_streaming_logs" {
name = "ge-enterprise-streaming-logs-sink"
project = var.project_id
destination = "bigquery.googleapis.com/projects/${var.project_id}/datasets/${google_bigquery_dataset.ge_telemetry.dataset_id}"
unique_writer_identity = true
filter = <<-EOT
logName="projects/${var.project_id}/logs/discoveryengine.googleapis.com%2Fgemini_enterprise_user_activity" OR
logName="projects/${var.project_id}/logs/discoveryengine.googleapis.com%2Fgen_ai.user.message" OR
logName="projects/${var.project_id}/logs/discoveryengine.googleapis.com%2Fgen_ai.choice"
EOT
}
resource "google_logging_project_sink" "ge_audit_logs" {
name = "ge-enterprise-audit-logs-sink"
project = var.project_id
destination = "bigquery.googleapis.com/projects/${var.project_id}/datasets/${google_bigquery_dataset.ge_telemetry.dataset_id}"
unique_writer_identity = true
filter = <<-EOT
logName:"projects/${var.project_id}/logs/cloudaudit.googleapis.com" AND
protoPayload.serviceName="discoveryengine.googleapis.com"
EOT
}
4. Configuring IAM Permissions to allow all resources to securely talk to each other
resource "google_project_iam_member" "ge_user" {
project = var.project_id
role = "roles/discoveryengine.agentspaceUser"
member = "user:${var.admin_email}"
}
resource "google_bigquery_dataset_iam_member" "streaming_sink_bq_writer" {
project = var.project_id
dataset_id = google_bigquery_dataset.ge_telemetry.dataset_id
role = "roles/bigquery.dataEditor"
member = google_logging_project_sink.ge_streaming_logs.writer_identity
}
resource "google_bigquery_dataset_iam_member" "audit_sink_bq_writer" {
project = var.project_id
dataset_id = google_bigquery_dataset.ge_telemetry.dataset_id
role = "roles/bigquery.dataEditor"
member = google_logging_project_sink.ge_audit_logs.writer_identity
}
resource "google_storage_bucket_iam_member" "cloudsql_sa_storage_admin" {
count = var.cloud_sql_config != null ? 1 : 0
bucket = google_storage_bucket.ge_staging_bucket.name
role = "roles/storage.admin"
member = "serviceAccount:${var.cloud_sql_sa}"
}
resource "google_project_service_identity" "discovery_engine_sa" {
provider = google-beta
project = var.project_id
service = "discoveryengine.googleapis.com"
}
resource "google_project_iam_member" "discovery_engine_cloudsql_viewer" {
project = var.project_id
role = "roles/cloudsql.viewer"
member = google_project_service_identity.discovery_engine_sa.member
}
resource "google_storage_bucket_iam_member" "discovery_engine_storage_admin" {
bucket = google_storage_bucket.ge_staging_bucket.name
role = "roles/storage.admin"
member = google_project_service_identity.discovery_engine_sa.member
}
resource "time_sleep" "iam_propagation" {
count = var.cloud_sql_config != null ? 1 : 0
depends_on = [
google_storage_bucket_iam_member.cloudsql_sa_storage_admin,
google_project_iam_member.discovery_engine_cloudsql_viewer,
google_storage_bucket_iam_member.discovery_engine_storage_admin
]
create_duration = "60s"
}
5. Deployment time
terraform init
terraform plan
terraform apply
Now, let’s look at the console and see what got deployed.
The Gemini enterprise App was deployed successfully.

We can see the video generation feature is disabled as intended

The data stores as well have been created

An import job starts immediately for both the bucket and SQL database

After a few minutes the import jobs are completed and we can start querying the data.

We can also see the BigQuery datasets have been created.

Generating some sample responses
Let’s try a couple of prompts to start generating some telemetry



Using BigQuery to analyze the telemetry
In this demo, we will interact with BigQuery using two methods; chatting with Gemini inside BigQuery and writing SQL queries directly.
Scenario 1: Understanding the usage of the currently deployed agents
We will write a SQL query to get that information
SELECT COALESCE(jsonPayload.request.userevent.agentspaceinfo.agentspacepagetype, 'Core Assistant / General') AS agent_space,
COUNT(*) AS total_interactions,
COUNT(DISTINCT jsonPayload.useriamprincipal) AS unique_users,
MIN(timestamp) AS first_interaction,
MAX(timestamp) AS last_interaction
FROM `<project_id>.ge_telemetry_032280a7.discoveryengine_googleapis_com_gemini_enterprise_user_activity_20260803`
WHERE COALESCE(jsonPayload.request.userevent.agentspaceinfo.agentspacepagetype, '') != 'home'
GROUP BY 1
ORDER BY total_interactions DESC;

We can see that currently we only have a couple of built-in agents and we can see their current usage based on the sample prompts I ran.
Scenario 2: Classifying User Intents to Find Data Gaps
In this example, I have asked Gemini inside BigQuery to classify the queries users are asking and clarify the intent of the query. I took the query it generated and ran it manually.

We can see that BigQuery has nicely categorized the prompts into different categories and we can see the most common categories based on the sample prompts I ran. This is very helpful for us to understand what are the gaps we have in our data and how we can make everyone more productive by enriching that data or create agents to automate those tasks.
Scenario 3: Finding Repeat Workflow Automation Candidates for new Agents
After sending lots of prompts and after we have a good understanding of the current usage and the types of queries users are asking, we can start looking for opportunities to create new agents that can automate those tasks. I have asked BigQuery to analyze the current usage and find opportunities where we can create agents based on the asks.


Scenario 4: Security insights using Model Armor
I have enabled Model Armor for Gemini Enterprise which is a Google Cloud service that enhances the security and safety of your AI applications by proactively screening the prompts and responses given by the Gemini Enterprise assistant. This helps protect against various risks and ensures responsible AI practices.


Conclusion
Deploying Gemini Enterprise gives employees powerful generative AI assistance, enterprise-ready agents and productivity gains, but capitalizing on its full potential requires enterprise-grade observability, security and strategic optimization. Diving deeper into your current implementation can provide deep insights into active users, department usage trends, license ROI and identify opportunities to create new agents that can automate specific tasks.
Resources
- Gemini Enterprise docs
- Terraform resources for data sources
- Terraform deployment for Gemini Enterprise blog post
- Gemini Enterprise and BigQuery
- Model armor docs
You May Also Like
Building a portfolio website with an AI assistant
Building a Portfolio Website with an AI Assistant I’ve always …
Deploying LLMs on Kubernetes
There is a growing demand in enterprises to deploy AI models and …
Self-Hosting LLMs on Kubernetes: Serving LLMs using vLLM
Series map Introduction How LLMs and GPUs work? GPU optimization …
