Skip to content

Commit 0c07678

Browse files
ankitgoyal0301anoopkverma-google
authored andcommitted
Add google_chronicle_retrohunt resource to chronicle (GoogleCloudPlatform#12776)
1 parent 53fbada commit 0c07678

File tree

5 files changed

+277
-0
lines changed

5 files changed

+277
-0
lines changed
+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Copyright 2024 Google Inc.
2+
# Licensed under the Apache License, Version 2.0 (the "License");
3+
# you may not use this file except in compliance with the License.
4+
# You may obtain a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
14+
---
15+
name: Retrohunt
16+
description: Retrohunt is an execution of a Rule over a time range in the past.
17+
references:
18+
guides:
19+
'Google SecOps Guides': 'https://cloud.google.com/chronicle/docs/secops/secops-overview'
20+
api: 'https://cloud.google.com/chronicle/docs/reference/rest/v1alpha/projects.locations.instances.rules.retrohunts'
21+
base_url: projects/{{project}}/locations/{{location}}/instances/{{instance}}/rules/{{rule}}/retrohunts
22+
immutable: true
23+
self_link: projects/{{project}}/locations/{{location}}/instances/{{instance}}/rules/{{rule}}/retrohunts/{{retrohunt}}
24+
create_url: projects/{{project}}/locations/{{location}}/instances/{{instance}}/rules/{{rule}}/retrohunts
25+
id_format: projects/{{project}}/locations/{{location}}/instances/{{instance}}/rules/{{rule}}/retrohunts/{{retrohunt}}
26+
import_format:
27+
- projects/{{project}}/locations/{{location}}/instances/{{instance}}/rules/{{rule}}/retrohunts/{{retrohunt}}
28+
exclude_delete: true
29+
30+
custom_code:
31+
decoder: 'templates/terraform/decoders/chronicle_retrohunt.go.tmpl'
32+
post_create: 'templates/terraform/post_create/chronicle_retrohunt_id.go.tmpl'
33+
examples:
34+
- name: 'chronicle_retrohunt_basic'
35+
primary_resource_id: example
36+
min_version: 'beta'
37+
exclude_import_test: true
38+
skip_vcr: true
39+
vars:
40+
start_time: '2025-01-01T00:00:00Z'
41+
end_time: '2025-01-01T12:00:00Z'
42+
test_env_vars:
43+
chronicle_id: 'CHRONICLE_ID'
44+
test_vars_overrides:
45+
start_time: 'time.Now().Add(time.Hour * (-12)).Format(time.RFC3339)'
46+
end_time: 'time.Now().Add(time.Hour * (-1)).Format(time.RFC3339)'
47+
48+
async:
49+
actions: [create]
50+
type: OpAsync
51+
operation:
52+
full_url: 'https://{{location}}-chronicle.googleapis.com/v1beta/{{op_id}}'
53+
timeouts:
54+
insert_minutes: 20
55+
update_minutes: 20
56+
delete_minutes: 20
57+
result:
58+
resource_inside_response: true
59+
exclude_sweeper: true
60+
parameters:
61+
- name: location
62+
type: String
63+
description: The location of the resource. This is the geographical region where the Chronicle instance resides, such as "us" or "europe-west2".
64+
url_param_only: true
65+
required: true
66+
- name: instance
67+
type: String
68+
description: The unique identifier for the Chronicle instance, which is the same as the customer ID.
69+
url_param_only: true
70+
required: true
71+
- name: rule
72+
type: String
73+
description: The Rule ID of the rule.
74+
url_param_only: true
75+
required: true
76+
- name: retrohunt
77+
type: String
78+
description: The retrohunt ID of the Retrohunt. A retrohunt is an execution of a Rule over a time range in the past.
79+
default_from_api: true
80+
properties:
81+
- name: progressPercentage
82+
type: Double
83+
description: Output only. Percent progress of the retrohunt towards completion, from 0.00 to 100.00.
84+
output: true
85+
- name: name
86+
type: String
87+
description: |-
88+
The resource name of the retrohunt.
89+
Retrohunt is the child of a rule revision. {rule} in the format below is
90+
structured as {rule_id@revision_id}.
91+
Format:
92+
projects/{project}/locations/{location}/instances/{instance}/rules/{rule}/retrohunts/{retrohunt}
93+
output: true
94+
- name: processInterval
95+
type: NestedObject
96+
description: |-
97+
Represents a time interval, encoded as a Timestamp start (inclusive) and a
98+
Timestamp end (exclusive).
99+
100+
The start must be less than or equal to the end.
101+
When the start equals the end, the interval is empty (matches no time).
102+
When both start and end are unspecified, the interval matches any time.
103+
required: true
104+
properties:
105+
- name: startTime
106+
type: String
107+
required: true
108+
description: |-
109+
Inclusive start of the interval.
110+
- name: endTime
111+
type: String
112+
required: true
113+
description: |-
114+
Exclusive end of the interval.
115+
- name: executionInterval
116+
type: NestedObject
117+
description: |-
118+
Represents a time interval, encoded as a Timestamp start (inclusive) and a
119+
Timestamp end (exclusive).
120+
121+
The start must be less than or equal to the end.
122+
When the start equals the end, the interval is empty (matches no time).
123+
When both start and end are unspecified, the interval matches any time.
124+
output: true
125+
properties:
126+
- name: endTime
127+
type: String
128+
description: |-
129+
Optional. Exclusive end of the interval.
130+
131+
If specified, a Timestamp matching this interval will have to be before the
132+
end.
133+
- name: startTime
134+
type: String
135+
description: |-
136+
Optional. Inclusive start of the interval.
137+
138+
If specified, a Timestamp matching this interval will have to be the same
139+
or after the start.
140+
- name: state
141+
type: String
142+
description: |-
143+
Output only. The state of the retrohunt.
144+
Possible values:
145+
RUNNING
146+
DONE
147+
CANCELLED
148+
FAILED
149+
output: true
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{{/*
2+
The license inside this block applies to this file
3+
Copyright 2024 Google Inc.
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
7+
Unless required by applicable law or agreed to in writing, software
8+
distributed under the License is distributed on an "AS IS" BASIS,
9+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
See the License for the specific language governing permissions and
11+
limitations under the License.
12+
*/ -}}
13+
name, ok := res["name"].(string)
14+
if !ok {
15+
log.Printf("[ERROR] 'name' not found in response")
16+
}
17+
parts := strings.Split(name, "/")
18+
retrohunt_id := parts[len(parts)-1]
19+
20+
log.Printf("[DEBUG] Setting retrohunt to %s", retrohunt_id)
21+
22+
res["retrohunt"] = retrohunt_id
23+
24+
return res, nil
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
resource "google_chronicle_rule" "my-rule" {
2+
provider = "google-beta"
3+
location = "us"
4+
instance = "{{index $.TestEnvVars "chronicle_id"}}"
5+
deletion_policy = "FORCE"
6+
text = <<-EOT
7+
rule test_rule { meta: events: $userid = $e.principal.user.userid match: $userid over 10m condition: $e }
8+
EOT
9+
}
10+
11+
resource "google_chronicle_retrohunt" "{{$.PrimaryResourceId}}" {
12+
provider = "google-beta"
13+
location = "us"
14+
instance = "{{index $.TestEnvVars "chronicle_id"}}"
15+
rule = element(split("/", resource.google_chronicle_rule.my-rule.name), length(split("/", resource.google_chronicle_rule.my-rule.name)) - 1)
16+
process_interval {
17+
start_time = "{{index $.Vars "start_time"}}"
18+
end_time = "{{index $.Vars "end_time"}}"
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
metadata := res["metadata"].(map[string]interface{})
2+
retrohunt := metadata["retrohunt"].(string)
3+
parts := strings.Split(retrohunt, "/")
4+
retrohunt_id := parts[len(parts)-1]
5+
6+
log.Printf("[DEBUG] Setting retrohunt id to %s", retrohunt_id)
7+
8+
// retrohunt value is set by API response and required to GET the connection
9+
// it is set by extracting id from the "retrohunt" field in the response metadata rather than a field in the response
10+
if err := d.Set("retrohunt", retrohunt_id); err != nil {
11+
return fmt.Errorf("Error reading Retrohunt ID: %s", err)
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package chronicle
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"time"
7+
8+
"github.com/hashicorp/terraform-provider-google/google/tpgresource"
9+
transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
10+
)
11+
12+
type ChronicleOperationWaiter struct {
13+
Config *transport_tpg.Config
14+
UserAgent string
15+
Project string
16+
tpgresource.CommonOperationWaiter
17+
}
18+
19+
func (w *ChronicleOperationWaiter) QueryOp() (interface{}, error) {
20+
if w == nil {
21+
return nil, fmt.Errorf("Cannot query operation, it's unset or nil.")
22+
}
23+
24+
region := tpgresource.GetRegionFromRegionalSelfLink(w.CommonOperationWaiter.Op.Name)
25+
26+
// Returns the proper get.
27+
url := fmt.Sprintf("https://%s-chronicle.googleapis.com/v1beta/%s", region, w.CommonOperationWaiter.Op.Name)
28+
29+
return transport_tpg.SendRequest(transport_tpg.SendRequestOptions{
30+
Config: w.Config,
31+
Method: "GET",
32+
Project: w.Project,
33+
RawURL: url,
34+
UserAgent: w.UserAgent,
35+
})
36+
}
37+
38+
func createChronicleWaiter(config *transport_tpg.Config, op map[string]interface{}, project, activity, userAgent string) (*ChronicleOperationWaiter, error) {
39+
w := &ChronicleOperationWaiter{
40+
Config: config,
41+
UserAgent: userAgent,
42+
Project: project,
43+
}
44+
if err := w.CommonOperationWaiter.SetOp(op); err != nil {
45+
return nil, err
46+
}
47+
return w, nil
48+
}
49+
50+
func ChronicleOperationWaitTimeWithResponse(config *transport_tpg.Config, op map[string]interface{}, response *map[string]interface{}, project, activity, userAgent string, timeout time.Duration) error {
51+
w, err := createChronicleWaiter(config, op, project, activity, userAgent)
52+
if err != nil {
53+
return err
54+
}
55+
if err := tpgresource.OperationWait(w, activity, timeout, config.PollInterval); err != nil {
56+
return err
57+
}
58+
return json.Unmarshal([]byte(w.CommonOperationWaiter.Op.Response), response)
59+
}
60+
61+
func ChronicleOperationWaitTime(config *transport_tpg.Config, op map[string]interface{}, project, activity, userAgent string, timeout time.Duration) error {
62+
if val, ok := op["name"]; !ok || val == "" {
63+
// This was a synchronous call - there is no operation to wait for.
64+
return nil
65+
}
66+
w, err := createChronicleWaiter(config, op, project, activity, userAgent)
67+
if err != nil {
68+
// If w is nil, the op was synchronous.
69+
return err
70+
}
71+
return tpgresource.OperationWait(w, activity, timeout, config.PollInterval)
72+
}

0 commit comments

Comments
 (0)