From 5a9a3c27c4d8a36865b7f0b758c01a056f9eeb09 Mon Sep 17 00:00:00 2001 From: Govind Singh Date: Tue, 26 May 2026 13:12:44 +0000 Subject: [PATCH 1/2] feat: Add Terraform support for Chronicle Parser and ParserExtension --- mmv1/products/chronicle/Parser.yaml | 317 ++++++++++++++++++ mmv1/products/chronicle/ParserExtension.yaml | 199 +++++++++++ .../examples/chronicle_parser_basic.tf.tmpl | 7 + .../chronicle_parserextension_basic.tf.tmpl | 7 + 4 files changed, 530 insertions(+) create mode 100644 mmv1/products/chronicle/Parser.yaml create mode 100644 mmv1/products/chronicle/ParserExtension.yaml create mode 100644 mmv1/templates/terraform/examples/chronicle_parser_basic.tf.tmpl create mode 100644 mmv1/templates/terraform/examples/chronicle_parserextension_basic.tf.tmpl diff --git a/mmv1/products/chronicle/Parser.yaml b/mmv1/products/chronicle/Parser.yaml new file mode 100644 index 000000000000..e2b9a4e989b0 --- /dev/null +++ b/mmv1/products/chronicle/Parser.yaml @@ -0,0 +1,317 @@ +# 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: Parser +description: Description +references: + guides: + 'Custom parsers overview': 'https://cloud.google.com/chronicle/docs/detection/custom-parsers-overview' + api: 'https://cloud.google.com/chronicle/docs/reference/rest/v1beta/projects.locations.instances.logTypes.parsers' +base_url: projects/{{project}}/locations/{{location}}/instances/{{instance}}/logTypes/{{logtype}}/parsers +self_link: projects/{{project}}/locations/{{location}}/instances/{{instance}}/logTypes/{{logtype}}/parsers/{{parser}} +create_url: projects/{{project}}/locations/{{location}}/instances/{{instance}}/logTypes/{{logtype}}/parsers +update_mask: true +update_verb: PATCH +import_format: + - projects/{{project}}/locations/{{location}}/instances/{{instance}}/logTypes/{{logtype}}/parsers/{{parser}} +autogen_status: UGFyc2Vy +autogen_async: false +examples: + - name: 'chronicle_parser_basic' + primary_resource_id: example + test_env_vars: + chronicle_id: 'CHRONICLE_ID' +parameters: + - name: location + type: String + required: true + description: Resource ID segment making up resource `name`. It identifies the resource within its parent collection as described in https://google.aip.dev/122. + immutable: true + url_param_only: true + - name: instance + type: String + required: true + description: Resource ID segment making up resource `name`. It identifies the resource within its parent collection as described in https://google.aip.dev/122. + immutable: true + url_param_only: true + - name: logtype + type: String + required: true + description: Resource ID segment making up resource `name`. It identifies the resource within its parent collection as described in https://google.aip.dev/122. + immutable: true + url_param_only: true + - name: parser + type: String + output: true + custom_flatten: 'templates/terraform/custom_flatten/id_from_name.tmpl' + description: Output only. The server-generated ID of the parser. +properties: + - name: cbn + type: String + description: |- + if the parser is built using config + documentation: + https://cloud.google.com/chronicle/docs/preview/parser-extensions/parsing-overview + - name: changelogs + type: NestedObject + description: Changelogs of a parser. + properties: + - name: entries + type: Array + description: all the changelog of a parser. + item_type: + type: NestedObject + properties: + - name: changeMessage + type: String + description: The changelog message. + - name: createTime + type: String + description: Time at which changelog was created. + - name: deleted + type: Boolean + description: |- + Flag whether the entry is added or deleted. + This will be true in case of rollback and false in case of upgrade. + - name: parserVersion + type: String + description: The parser version for which the changelog is created. + - name: createTime + type: String + description: Time at which parser was created. + output: true + - name: creator + type: NestedObject + output: true + description: Information about the creator of the parser. + properties: + - name: author + type: String + description: The name of the author, who created this parser. + output: true + - name: customer + type: String + description: |- + The customer who created it, This can represent the partner as well. + In case of prebuilt parser this will be empty. + output: true + - name: source + type: String + description: |- + The source of the parser. + Possible values: + GOOGLE + CUSTOM_GOOGLE_OVERRIDE + PARTNER + CUSTOMER + output: true + - name: dynamicParsingConfig + type: String + description: Dynamic parsing config applied over the parser, if any. + output: true + - name: lowCode + type: NestedObject + description: Message to represent LowCodeParser. + properties: + - name: fieldExtractors + type: NestedObject + description: A representation of a parser extension as a set of field extractors. + properties: + - name: appendRepeatedFields + type: Boolean + description: |- + Whether to append repeated fields or not. + When false, repeated fields will be replaced. + - name: extractors + type: Array + description: List of FieldExtractors. + item_type: + type: NestedObject + properties: + - name: destinationPath + type: String + description: |- + Path in generated event which is to be populated. This is required if the + FieldExtractor is used to specify the parser extension. + - name: fieldPath + type: String + description: |- + Field path could be a json path, xml path or csv column name + depending on log format. It refers to a section or substring in raw log. + This is required if the FieldExtractor is used to specify the parser + extension. + - name: preconditionOp + type: String + description: |- + Operator used for precondition. + Possible values: + EQUALS + NOT_EQUALS + - name: preconditionPath + type: String + description: |- + Precondition path could be a json path, xml path or csv column name + depending on log format. It refers to a section or substring in raw log. + - name: preconditionValue + type: String + description: Precondition value. + - name: value + type: String + description: Value to be mapped to the destination path directly. + - name: logFormat + type: String + description: |- + Possible values: + JSON + CSV + XML + - name: preprocessConfig + type: NestedObject + description: PreProcessConfig holds the GROK expression to extract the syslog header. + properties: + - name: grokRegex + type: String + description: |- + GROK Regex to extract the structured part of the log. + syntax documentation: + www.elastic.co/guide/en/logstash/current/plugins-filters-grok.html + - name: target + type: String + description: |- + Target field name for the structured part of the log. + This should match a SEMANTIC identifier from the grok expression. + - name: transformedCbnSnippet + type: String + description: CBN snippet generated from field extractors. + output: true + - name: log + type: String + description: The log used to create this low code parser in the UI. + - name: name + type: String + description: name of the parser resource. + output: true + - name: parserExtension + type: String + description: Extension applied over the parser, if any. + output: true + - name: releaseStage + type: String + description: |- + The release stage of the parser + After internal validations the prebuilt parser will directly start as + Release Candidate. The release_stage of prebuilt parsers are changed + after every release cycle: + The prebuilt Release Candidate parser is promoted as Release parser. + The existing prebuilt Release parser is moved to Rollback state. + and existing prebuilt rollback parser is moved to Archived. + In case of custom parser: + When the customer submits a validation passed custom parser it starts as + Release state. + And existing one is moved to Rollback stage. + And the existing rollback is moved to Archived. + In case a release or release candidate parser is found faulty, + the parser is marked FAULTY, + if it is release parser then rollback candidate is moved to release. + Possible values: + RELEASE + RELEASE_CANDIDATE + ROLLBACK_CANDIDATE + ARCHIVED + FAULTY + ARCHIVED_IN_USE + output: true + - name: state + type: String + description: |- + The state of the parser + Possible values: + ACTIVE + INACTIVE + output: true + - name: type + type: String + description: |- + The type of the parser + Possible values: + CUSTOM + PREBUILT + output: true + - name: validatedOnEmptyLogs + type: Boolean + description: |- + Flag to bypass parser validation when no logs are found. + If enabled, the parser won't be be rejected during the validation + phase when no logs are found. + - name: validationReport + type: String + description: The Validation report generated during parser validation. + output: true + - name: validationSkipped + type: Boolean + description: |- + If true, bypasses parser validation. + If enabled, the parser won't be rejected during the validation + phase and validation will be skipped. + - name: validationStage + type: String + description: |- + The validation stage of the parser + When a customer submits a new parser for validation, it starts with a + new stage. + When parser is picked for validation, it changes to Validation state. + If validation failed it is marked as failed, and + existing failed is moved to delete_candidate stage. + If passes it is moved to passed stage. + If customer opts to submit it, the parser is moved to Release State. + Possible values: + NEW + VALIDATING + PASSED + FAILED + DELETE_CANDIDATE + INTERNAL_ERROR + VALIDATION_SKIPPED + output: true + - name: versionInfo + type: NestedObject + description: |- + ParserVersionInfo gives the version information of the parser and related + properties like pinned etc. + properties: + - name: autoUpgradeDisabled + type: Boolean + required: true + description: |- + Signifies if the parser is disabled for auto upgrade. If true, the parser + will not be upgraded by the auto upgrade process. + - name: latestParser + type: String + description: |- + The resource name of latest Parser for this logtype. + Format: + projects/{project}/locations/{region}/instances/{instance}/logTypes/{log_type}/parsers/{parser}/{id} + output: true + - name: latestParserVersion + type: String + description: The version for the latest available stable version of the parser. + output: true + - name: rollbackAvailable + type: Boolean + description: Signifies if rollback is available for this parser version. + output: true + - name: version + type: String + description: The version of the parser. + output: true diff --git a/mmv1/products/chronicle/ParserExtension.yaml b/mmv1/products/chronicle/ParserExtension.yaml new file mode 100644 index 000000000000..2cfacfcb02a0 --- /dev/null +++ b/mmv1/products/chronicle/ParserExtension.yaml @@ -0,0 +1,199 @@ +# 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: ParserExtension +description: Description +references: + guides: + 'Custom parsers overview': 'https://cloud.google.com/chronicle/docs/detection/custom-parsers-overview' + api: 'https://cloud.google.com/chronicle/docs/reference/rest/v1beta/projects.locations.instances.logTypes.parserExtensions' +base_url: projects/{{project}}/locations/{{location}}/instances/{{instance}}/logTypes/{{logtype}}/parserExtensions +immutable: true +self_link: projects/{{project}}/locations/{{location}}/instances/{{instance}}/logTypes/{{logtype}}/parserExtensions/{{parserextension}} +create_url: projects/{{project}}/locations/{{location}}/instances/{{instance}}/logTypes/{{logtype}}/parserExtensions +import_format: + - projects/{{project}}/locations/{{location}}/instances/{{instance}}/logTypes/{{logtype}}/parserExtensions/{{parserextension}} +autogen_status: UGFyc2VyRXh0ZW5zaW9u +autogen_async: false +examples: + - name: 'chronicle_parserextension_basic' + primary_resource_id: example + test_env_vars: + chronicle_id: 'CHRONICLE_ID' +parameters: + - name: location + type: String + required: true + description: Resource ID segment making up resource `name`. It identifies the resource within its parent collection as described in https://google.aip.dev/122. + immutable: true + url_param_only: true + - name: instance + type: String + required: true + description: Resource ID segment making up resource `name`. It identifies the resource within its parent collection as described in https://google.aip.dev/122. + immutable: true + url_param_only: true + - name: logtype + type: String + required: true + description: Resource ID segment making up resource `name`. It identifies the resource within its parent collection as described in https://google.aip.dev/122. + immutable: true + url_param_only: true + - name: parserextension + type: String + output: true + custom_flatten: 'templates/terraform/custom_flatten/id_from_name.tmpl' + description: Output only. The server-generated ID of the parser extension. +properties: + - name: cbnSnippet + type: String + description: Parser config could be a cbn snippet. + - name: createTime + type: String + description: The time the parser extension was created. + output: true + - name: dynamicParsing + type: NestedObject + description: A representation of a parser extension as dynamic parsing config. + properties: + - name: optedFields + type: Array + description: List of fields to be parsed. + item_type: + type: NestedObject + properties: + - name: path + type: String + description: Path of the log field. + - name: sampleValue + type: String + description: Sample value of the log field. + - name: extensionValidationReport + type: String + output: true + description: |- + The latest extension + validation report for this extension. + - name: fieldExtractors + type: NestedObject + description: A representation of a parser extension as a set of field extractors. + properties: + - name: appendRepeatedFields + type: Boolean + description: |- + Whether to append repeated fields or not. + When false, repeated fields will be replaced. + - name: extractors + type: Array + description: List of FieldExtractors. + item_type: + type: NestedObject + properties: + - name: destinationPath + type: String + description: |- + Path in generated event which is to be populated. This is required if the + FieldExtractor is used to specify the parser extension. + - name: fieldPath + type: String + description: |- + Field path could be a json path, xml path or csv column name + depending on log format. It refers to a section or substring in raw log. + This is required if the FieldExtractor is used to specify the parser + extension. + - name: preconditionOp + type: String + description: |- + Operator used for precondition. + Possible values: + EQUALS + NOT_EQUALS + - name: preconditionPath + type: String + description: |- + Precondition path could be a json path, xml path or csv column name + depending on log format. It refers to a section or substring in raw log. + - name: preconditionValue + type: String + description: Precondition value. + - name: value + type: String + description: Value to be mapped to the destination path directly. + - name: logFormat + type: String + description: |- + Possible values: + JSON + CSV + XML + - name: preprocessConfig + type: NestedObject + description: PreProcessConfig holds the GROK expression to extract the syslog header. + properties: + - name: grokRegex + type: String + description: |- + GROK Regex to extract the structured part of the log. + syntax documentation: + www.elastic.co/guide/en/logstash/current/plugins-filters-grok.html + - name: target + type: String + description: |- + Target field name for the structured part of the log. + This should match a SEMANTIC identifier from the grok expression. + - name: transformedCbnSnippet + type: String + description: CBN snippet generated from field extractors. + output: true + - name: lastLiveTime + type: String + output: true + description: The time the config was last serving live traffic. + - name: log + type: String + description: Raw log used to assist the user in creation of augmentation. + - name: name + type: String + description: |- + Format: + projects/{project}/locations/{location}/instances/{instance}/logTypes/{logtype}/parserExtensions/{parserExtension} + output: true + - name: state + type: String + description: |- + The state of the parser extension + Possible values: + NEW + VALIDATING + LIVE + REJECTED + INTERNAL_ERROR + VALIDATED + ARCHIVED + VALIDATION_SKIPPED + output: true + - name: stateLastChangedTime + type: String + output: true + description: The time the config state was last changed. + - name: validationReport + type: String + description: The validation report generated during extension validation. + output: true + - name: validationSkipped + type: Boolean + description: |- + Flag to bypass parser extension validation. + If enabled, the parser extension won't be rejected during the validation + phase and validation will be skipped. diff --git a/mmv1/templates/terraform/examples/chronicle_parser_basic.tf.tmpl b/mmv1/templates/terraform/examples/chronicle_parser_basic.tf.tmpl new file mode 100644 index 000000000000..a9564e622926 --- /dev/null +++ b/mmv1/templates/terraform/examples/chronicle_parser_basic.tf.tmpl @@ -0,0 +1,7 @@ +resource "google_chronicle_parser" "{{$.PrimaryResourceId}}" { + location = "us" + instance = "{{index $.TestEnvVars "chronicle_id"}}" + logtype = "WINDOWS_DHCP" # Static standard prebuilt log type + validation_skipped = true + cbn = "ZHVtbXkgcGFyc2VyIGNvbmZpZw==" # Base64 encoded "dummy parser config" +} diff --git a/mmv1/templates/terraform/examples/chronicle_parserextension_basic.tf.tmpl b/mmv1/templates/terraform/examples/chronicle_parserextension_basic.tf.tmpl new file mode 100644 index 000000000000..3bcf08e1ec10 --- /dev/null +++ b/mmv1/templates/terraform/examples/chronicle_parserextension_basic.tf.tmpl @@ -0,0 +1,7 @@ +resource "google_chronicle_parser_extension" "{{$.PrimaryResourceId}}" { + location = "us" + instance = "{{index $.TestEnvVars "chronicle_id"}}" + logtype = "WINDOWS_DHCP" # Static standard prebuilt log type + validation_skipped = true + cbn_snippet = "ZHVtbXkgZXh0ZW5zaW9uIHNuaXBwZXQ=" # Base64 encoded "dummy extension snippet" +} From 81e8cb1da39f04949182a5538cd2463832e81d0b Mon Sep 17 00:00:00 2001 From: Govind Singh Date: Wed, 27 May 2026 09:14:15 +0000 Subject: [PATCH 2/2] test: add thorough acceptance tests and parallelize log types for Chronicle --- mmv1/products/chronicle/Parser.yaml | 12 +++++++++++ mmv1/products/chronicle/ParserExtension.yaml | 14 +++++++++++++ .../examples/chronicle_parser_full.tf.tmpl | 21 +++++++++++++++++++ .../chronicle_parserextension_basic.tf.tmpl | 2 +- .../chronicle_parserextension_full.tf.tmpl | 16 ++++++++++++++ 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 mmv1/templates/terraform/examples/chronicle_parser_full.tf.tmpl create mode 100644 mmv1/templates/terraform/examples/chronicle_parserextension_full.tf.tmpl diff --git a/mmv1/products/chronicle/Parser.yaml b/mmv1/products/chronicle/Parser.yaml index e2b9a4e989b0..5661cfc78932 100644 --- a/mmv1/products/chronicle/Parser.yaml +++ b/mmv1/products/chronicle/Parser.yaml @@ -32,6 +32,18 @@ examples: primary_resource_id: example test_env_vars: chronicle_id: 'CHRONICLE_ID' + ignore_read_extra: + - 'state' + - 'validation_report' + - 'validation_stage' + - name: 'chronicle_parser_full' + primary_resource_id: example + test_env_vars: + chronicle_id: 'CHRONICLE_ID' + ignore_read_extra: + - 'state' + - 'validation_report' + - 'validation_stage' parameters: - name: location type: String diff --git a/mmv1/products/chronicle/ParserExtension.yaml b/mmv1/products/chronicle/ParserExtension.yaml index 2cfacfcb02a0..ca73eb8190f9 100644 --- a/mmv1/products/chronicle/ParserExtension.yaml +++ b/mmv1/products/chronicle/ParserExtension.yaml @@ -31,6 +31,20 @@ examples: primary_resource_id: example test_env_vars: chronicle_id: 'CHRONICLE_ID' + ignore_read_extra: + - 'state' + - 'state_last_changed_time' + - 'validation_report' + - 'extension_validation_report' + - name: 'chronicle_parserextension_full' + primary_resource_id: example + test_env_vars: + chronicle_id: 'CHRONICLE_ID' + ignore_read_extra: + - 'state' + - 'state_last_changed_time' + - 'validation_report' + - 'extension_validation_report' parameters: - name: location type: String diff --git a/mmv1/templates/terraform/examples/chronicle_parser_full.tf.tmpl b/mmv1/templates/terraform/examples/chronicle_parser_full.tf.tmpl new file mode 100644 index 000000000000..d0a3121d0d7c --- /dev/null +++ b/mmv1/templates/terraform/examples/chronicle_parser_full.tf.tmpl @@ -0,0 +1,21 @@ +resource "google_chronicle_parser" "{{$.PrimaryResourceId}}" { + location = "us" + instance = "{{index $.TestEnvVars "chronicle_id"}}" + logtype = "LINUX_DHCP" # Unique log type to prevent CI parallel test collisions + validated_on_empty_logs = false + validation_skipped = true + + low_code { + log = "ZHVtbXkgbG9nIGJ5dGVz" # Base64 encoded "dummy log bytes" + field_extractors { + log_format = "JSON" + extractors { + field_path = "$.ip" + destination_path = "udm.principal.ip" + precondition_op = "EQUALS" + precondition_path = "$.event" + precondition_value = "login" + } + } + } +} diff --git a/mmv1/templates/terraform/examples/chronicle_parserextension_basic.tf.tmpl b/mmv1/templates/terraform/examples/chronicle_parserextension_basic.tf.tmpl index 3bcf08e1ec10..e506fad0bccb 100644 --- a/mmv1/templates/terraform/examples/chronicle_parserextension_basic.tf.tmpl +++ b/mmv1/templates/terraform/examples/chronicle_parserextension_basic.tf.tmpl @@ -1,7 +1,7 @@ resource "google_chronicle_parser_extension" "{{$.PrimaryResourceId}}" { location = "us" instance = "{{index $.TestEnvVars "chronicle_id"}}" - logtype = "WINDOWS_DHCP" # Static standard prebuilt log type + logtype = "CISCO_DHCP" # Unique log type to prevent CI parallel test collisions validation_skipped = true cbn_snippet = "ZHVtbXkgZXh0ZW5zaW9uIHNuaXBwZXQ=" # Base64 encoded "dummy extension snippet" } diff --git a/mmv1/templates/terraform/examples/chronicle_parserextension_full.tf.tmpl b/mmv1/templates/terraform/examples/chronicle_parserextension_full.tf.tmpl new file mode 100644 index 000000000000..42057ca37a86 --- /dev/null +++ b/mmv1/templates/terraform/examples/chronicle_parserextension_full.tf.tmpl @@ -0,0 +1,16 @@ +resource "google_chronicle_parser_extension" "{{$.PrimaryResourceId}}" { + location = "us" + instance = "{{index $.TestEnvVars "chronicle_id"}}" + logtype = "AKAMAI_DHCP" # Unique log type to prevent CI parallel test collisions + validation_skipped = true + log = "ZHVtbXkgbG9n" + + field_extractors { + log_format = "JSON" + extractors { + field_path = "$.user" + destination_path = "udm.principal.user.userid" + value = "static-override" + } + } +}