Skip to content

Commit

Permalink
Add google_chronicle_retrohunt resource to chronicle (GoogleCloudPlat…
Browse files Browse the repository at this point in the history
  • Loading branch information
ankitgoyal0301 authored and PerlMonker303 committed Feb 3, 2025
1 parent 1f3829d commit e920a2d
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 0 deletions.
149 changes: 149 additions & 0 deletions mmv1/products/chronicle/Retrohunt.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Copyright 2024 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: Retrohunt
description: Retrohunt is an execution of a Rule over a time range in the past.
references:
guides:
'Google SecOps Guides': 'https://cloud.google.com/chronicle/docs/secops/secops-overview'
api: 'https://cloud.google.com/chronicle/docs/reference/rest/v1alpha/projects.locations.instances.rules.retrohunts'
base_url: projects/{{project}}/locations/{{location}}/instances/{{instance}}/rules/{{rule}}/retrohunts
immutable: true
self_link: projects/{{project}}/locations/{{location}}/instances/{{instance}}/rules/{{rule}}/retrohunts/{{retrohunt}}
create_url: projects/{{project}}/locations/{{location}}/instances/{{instance}}/rules/{{rule}}/retrohunts
id_format: projects/{{project}}/locations/{{location}}/instances/{{instance}}/rules/{{rule}}/retrohunts/{{retrohunt}}
import_format:
- projects/{{project}}/locations/{{location}}/instances/{{instance}}/rules/{{rule}}/retrohunts/{{retrohunt}}
exclude_delete: true

custom_code:
decoder: 'templates/terraform/decoders/chronicle_retrohunt.go.tmpl'
post_create: 'templates/terraform/post_create/chronicle_retrohunt_id.go.tmpl'
examples:
- name: 'chronicle_retrohunt_basic'
primary_resource_id: example
min_version: 'beta'
exclude_import_test: true
skip_vcr: true
vars:
start_time: '2025-01-01T00:00:00Z'
end_time: '2025-01-01T12:00:00Z'
test_env_vars:
chronicle_id: 'CHRONICLE_ID'
test_vars_overrides:
start_time: 'time.Now().Add(time.Hour * (-12)).Format(time.RFC3339)'
end_time: 'time.Now().Add(time.Hour * (-1)).Format(time.RFC3339)'

async:
actions: [create]
type: OpAsync
operation:
full_url: 'https://{{location}}-chronicle.googleapis.com/v1beta/{{op_id}}'
timeouts:
insert_minutes: 20
update_minutes: 20
delete_minutes: 20
result:
resource_inside_response: true
exclude_sweeper: true
parameters:
- name: location
type: String
description: The location of the resource. This is the geographical region where the Chronicle instance resides, such as "us" or "europe-west2".
url_param_only: true
required: true
- name: instance
type: String
description: The unique identifier for the Chronicle instance, which is the same as the customer ID.
url_param_only: true
required: true
- name: rule
type: String
description: The Rule ID of the rule.
url_param_only: true
required: true
- name: retrohunt
type: String
description: The retrohunt ID of the Retrohunt. A retrohunt is an execution of a Rule over a time range in the past.
default_from_api: true
properties:
- name: progressPercentage
type: Double
description: Output only. Percent progress of the retrohunt towards completion, from 0.00 to 100.00.
output: true
- name: name
type: String
description: |-
The resource name of the retrohunt.
Retrohunt is the child of a rule revision. {rule} in the format below is
structured as {rule_id@revision_id}.
Format:
projects/{project}/locations/{location}/instances/{instance}/rules/{rule}/retrohunts/{retrohunt}
output: true
- name: processInterval
type: NestedObject
description: |-
Represents a time interval, encoded as a Timestamp start (inclusive) and a
Timestamp end (exclusive).
The start must be less than or equal to the end.
When the start equals the end, the interval is empty (matches no time).
When both start and end are unspecified, the interval matches any time.
required: true
properties:
- name: startTime
type: String
required: true
description: |-
Inclusive start of the interval.
- name: endTime
type: String
required: true
description: |-
Exclusive end of the interval.
- name: executionInterval
type: NestedObject
description: |-
Represents a time interval, encoded as a Timestamp start (inclusive) and a
Timestamp end (exclusive).
The start must be less than or equal to the end.
When the start equals the end, the interval is empty (matches no time).
When both start and end are unspecified, the interval matches any time.
output: true
properties:
- name: endTime
type: String
description: |-
Optional. Exclusive end of the interval.
If specified, a Timestamp matching this interval will have to be before the
end.
- name: startTime
type: String
description: |-
Optional. Inclusive start of the interval.
If specified, a Timestamp matching this interval will have to be the same
or after the start.
- name: state
type: String
description: |-
Output only. The state of the retrohunt.
Possible values:
RUNNING
DONE
CANCELLED
FAILED
output: true
24 changes: 24 additions & 0 deletions mmv1/templates/terraform/decoders/chronicle_retrohunt.go.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{{/*
The license inside this block applies to this file
Copyright 2024 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, ok := res["name"].(string)
if !ok {
log.Printf("[ERROR] 'name' not found in response")
}
parts := strings.Split(name, "/")
retrohunt_id := parts[len(parts)-1]

log.Printf("[DEBUG] Setting retrohunt to %s", retrohunt_id)

res["retrohunt"] = retrohunt_id

return res, nil
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
resource "google_chronicle_rule" "my-rule" {
provider = "google-beta"
location = "us"
instance = "{{index $.TestEnvVars "chronicle_id"}}"
deletion_policy = "FORCE"
text = <<-EOT
rule test_rule { meta: events: $userid = $e.principal.user.userid match: $userid over 10m condition: $e }
EOT
}

resource "google_chronicle_retrohunt" "{{$.PrimaryResourceId}}" {
provider = "google-beta"
location = "us"
instance = "{{index $.TestEnvVars "chronicle_id"}}"
rule = element(split("/", resource.google_chronicle_rule.my-rule.name), length(split("/", resource.google_chronicle_rule.my-rule.name)) - 1)
process_interval {
start_time = "{{index $.Vars "start_time"}}"
end_time = "{{index $.Vars "end_time"}}"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
metadata := res["metadata"].(map[string]interface{})
retrohunt := metadata["retrohunt"].(string)
parts := strings.Split(retrohunt, "/")
retrohunt_id := parts[len(parts)-1]

log.Printf("[DEBUG] Setting retrohunt id to %s", retrohunt_id)

// retrohunt value is set by API response and required to GET the connection
// it is set by extracting id from the "retrohunt" field in the response metadata rather than a field in the response
if err := d.Set("retrohunt", retrohunt_id); err != nil {
return fmt.Errorf("Error reading Retrohunt ID: %s", err)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package chronicle

import (
"encoding/json"
"fmt"
"time"

"github.com/hashicorp/terraform-provider-google/google/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
)

type ChronicleOperationWaiter struct {
Config *transport_tpg.Config
UserAgent string
Project string
tpgresource.CommonOperationWaiter
}

func (w *ChronicleOperationWaiter) QueryOp() (interface{}, error) {
if w == nil {
return nil, fmt.Errorf("Cannot query operation, it's unset or nil.")
}

region := tpgresource.GetRegionFromRegionalSelfLink(w.CommonOperationWaiter.Op.Name)

// Returns the proper get.
url := fmt.Sprintf("https://%s-chronicle.googleapis.com/v1beta/%s", region, w.CommonOperationWaiter.Op.Name)

return transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
Config: w.Config,
Method: "GET",
Project: w.Project,
RawURL: url,
UserAgent: w.UserAgent,
})
}

func createChronicleWaiter(config *transport_tpg.Config, op map[string]interface{}, project, activity, userAgent string) (*ChronicleOperationWaiter, error) {
w := &ChronicleOperationWaiter{
Config: config,
UserAgent: userAgent,
Project: project,
}
if err := w.CommonOperationWaiter.SetOp(op); err != nil {
return nil, err
}
return w, nil
}

func ChronicleOperationWaitTimeWithResponse(config *transport_tpg.Config, op map[string]interface{}, response *map[string]interface{}, project, activity, userAgent string, timeout time.Duration) error {
w, err := createChronicleWaiter(config, op, project, activity, userAgent)
if err != nil {
return err
}
if err := tpgresource.OperationWait(w, activity, timeout, config.PollInterval); err != nil {
return err
}
return json.Unmarshal([]byte(w.CommonOperationWaiter.Op.Response), response)
}

func ChronicleOperationWaitTime(config *transport_tpg.Config, op map[string]interface{}, project, activity, userAgent string, timeout time.Duration) error {
if val, ok := op["name"]; !ok || val == "" {
// This was a synchronous call - there is no operation to wait for.
return nil
}
w, err := createChronicleWaiter(config, op, project, activity, userAgent)
if err != nil {
// If w is nil, the op was synchronous.
return err
}
return tpgresource.OperationWait(w, activity, timeout, config.PollInterval)
}

0 comments on commit e920a2d

Please sign in to comment.