Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 256 additions & 0 deletions mmv1/products/chronicle/NativeDashboard.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
# Copyright 2026 Google Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

---
name: NativeDashboard
description: A configuration for a native dashboard within a Google SecOps (Chronicle) instance.
references:
guides:
'Google SecOps Guides': 'https://cloud.google.com/chronicle/docs/secops/secops-overview'
api: 'https://cloud.google.com/chronicle/docs/reference/rest/v1beta/projects.locations.instances.nativeDashboards'
base_url: projects/{{project}}/locations/{{location}}/instances/{{instance}}/nativeDashboards
self_link: 'projects/{{project}}/locations/{{location}}/instances/{{instance}}/nativeDashboards/{{dashboard_id}}'
create_url: projects/{{project}}/locations/{{location}}/instances/{{instance}}/nativeDashboards
id_format: 'projects/{{project}}/locations/{{location}}/instances/{{instance}}/nativeDashboards/{{dashboard_id}}'
import_format:
- projects/{{project}}/locations/{{location}}/instances/{{instance}}/nativeDashboards/{{dashboard_id}}

update_mask: true
update_verb: PATCH

min_version: 'beta'
examples:
- name: chronicle_nativedashboard_basic
primary_resource_id: my_basic_dashboard
min_version: 'beta'
ignore_read_extra:
- "dashboard_user_data.0.last_viewed_time"
vars:
dashboard_name: 'dashboard'
dashboard_description: 'dashboard_description'
test_env_vars:
chronicle_id: 'CHRONICLE_ID'

autogen_status: TmF0aXZlRGFzaGJvYXJk
parameters:
- name: location
type: String
required: true
immutable: true
url_param_only: true
description: The location of the Chronicle instance.
- name: instance
type: String
required: true
immutable: true
url_param_only: true
description: The ID of the Chronicle instance.

properties:
- name: name
type: String
output: true
description: The full resource name of the dashboard.

- name: dashboardId
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is dashboardId not user-settable? It should be per https://google.aip.dev/133#user-specified-ids

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is set in the BE only.

type: String
output: true
immutable: true
custom_flatten: 'templates/terraform/custom_flatten/id_from_name.tmpl'
description: The unique ID of the Dashboard.

- name: access
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For both the access and type fields, consider changing the type from String to Enum and formally listing the enum_values. This allows Terraform to catch typos or invalid values locally before even making an API call, improving the user experience.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these values going to be expanded in the future? We tend to prefer strings is the set of values is mutable, but enums if it's relatively static. For example, if you're planning to add several other values here in the next few years, it tends to be easier for users if they don't have to upgrade provider versions to be able to use them

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the properties to enum type that are very unlikely to be updated in the future and rest are kept as strings only.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the change hasn't been pushed to github, can you please check?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this has been updated and only the filterOperator values has been kept as ENUM.

type: String
description: |-
The access level of the dashboard.
Possible values:
DASHBOARD_PRIVATE
DASHBOARD_PUBLIC

- name: createTime
type: String
output: true
description: The creation time of the dashboard.

- name: createUserId
type: String
output: true
description: The ID of the user who created the dashboard.

- name: dashboardUserData
type: NestedObject
default_from_api: true
description: User-specific data and preferences for the dashboard.
properties:
- name: isPinned
type: Boolean
description: Whether the dashboard is pinned by the user.
- name: lastViewedTime
type: String
output: true
description: The time when this dashboard was last viewed.

- name: definition
type: NestedObject
flatten_object: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, why flatten object

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our BE expects the update mask to be more granular such as "definition.chartLayout" instead of only "definition. By flattening it, we can get the update mask at the child level.

description: |-
The definition of the dashboard including filters, layout, and chart
configurations.
properties:
- name: charts
type: Array
description: A list of charts included in the dashboard definition.
item_type:
type: NestedObject
properties:
- name: chartLayout
type: NestedObject
description: The visual layout parameters of this chart within the dashboard.
properties:
- name: startX
type: Integer
send_empty_value: true
description: The starting X coordinate.
- name: spanX
type: Integer
required: true
description: The number of columns the chart spans.
- name: startY
type: Integer
send_empty_value: true
description: The starting Y coordinate.
- name: spanY
type: Integer
required: true
description: The number of rows the chart spans.
- name: dashboardChart
type: String
description: The resource name of the associated DashboardChart.
- name: filtersIds
type: Array
item_type:
type: String
description: List of dashboard filter IDs applied to this chart.

- name: filters
type: Array
description: Global filters defined for the dashboard.
item_type:
type: NestedObject
properties:
- name: chartIds
type: Array
item_type:
type: String
description: The IDs of charts that this filter applies to.
- name: dataSource
type: String
description: |-
The data source for the filter.
Possible values:
UDM, ENTITY, INGESTION_METRICS, RULE_DETECTIONS, RULESETS, GLOBAL,
IOC_MATCHES, RULES, SOAR_CASES, SOAR_PLAYBOOKS, SOAR_CASE_HISTORY,
DATA_TABLE, INVESTIGATION, INVESTIGATION_FEEDBACK
- name: displayName
type: String
description: The display name of the filter.
- name: fieldPath
type: String
send_empty_value: true
description: The UDM field path being filtered.
- name: filterOperatorAndFieldValues
type: Array
description: The specific operator and value set for the filter.
item_type:
type: NestedObject
properties:
- name: fieldValues
type: Array
item_type:
type: String
description: |-
The values for the modifier. All operators should have a single
value other than 'IN' and 'BETWEEN'.
- name: filterOperator
type: Enum
enum_values:
- 'EQUAL'
- 'NOT_EQUAL'
- 'IN'
- 'GREATER_THAN'
- 'GREATER_THAN_OR_EQUAL_TO'
- 'LESS_THAN'
- 'LESS_THAN_OR_EQUAL_TO'
- 'BETWEEN'
- 'PAST'
- 'IS_NULL'
- 'IS_NOT_NULL'
- 'STARTS_WITH'
- 'ENDS_WITH'
- 'DOES_NOT_STARTS_WITH'
- 'DOES_NOT_ENDS_WITH'
- 'NOT_IN'
- 'CONTAINS'
- 'DOES_NOT_CONTAIN'
description: The operator to apply to the field.
- name: id
type: String
description: The unique ID of the filter.
- name: isMandatory
type: Boolean
default_value: false
description: Whether the filter is mandatory for the dashboard consumer.
- name: isStandardTimeRangeFilter
type: Boolean
description: Whether the filter is a standard time range filter.
- name: isStandardTimeRangeFilterEnabled
type: Boolean
description: Whether the standard time range filter is currently enabled.

- name: fingerprint
type: String
output: true
description: The server-generated fingerprint of the dashboard definition.

- name: description
type: String
description: A description of the dashboard.

- name: displayName
type: String
required: true
description: The display name/title of the dashboard visible to users.

- name: etag
type: String
output: true
description: |-
Server-computed checksum for optimistic concurrency control,
sent on update and delete requests.

- name: type
type: String
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same ENUM comment.

description: |-
The type of dashboard.
Possible values:
CURATED, PRIVATE, PUBLIC, CUSTOM, MARKETPLACE

- name: updateTime
type: String
output: true
description: The time when the dashboard was last edited.

- name: updateUserId
type: String
output: true
description: The ID of the user who last edited the dashboard.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
resource "google_chronicle_native_dashboard" "my_basic_dashboard" {
provider = google-beta
location = "us"
instance = "{{index $.TestEnvVars "chronicle_id"}}"
display_name = "{{index $.Vars "dashboard_name"}}"
description = "{{index $.Vars "dashboard_description"}}"
access = "DASHBOARD_PRIVATE"
type = "CUSTOM"

filters {
id = "GlobalTimeFilter"
display_name = "Global Time Filter"
data_source = "GLOBAL"
is_standard_time_range_filter = true
is_standard_time_range_filter_enabled = true
filter_operator_and_field_values {
filter_operator = "PAST"
field_values = ["1", "DAY"]
}
}
}
Loading
Loading