Skip to content

Commit ecd85ce

Browse files
supporting cws policies_list
1 parent ed91bec commit ecd85ce

6 files changed

+305
-13
lines changed

datadog/fwprovider/framework_provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ var Resources = []func() resource.Resource{
7070
NewWebhookCustomVariableResource,
7171
NewLogsCustomDestinationResource,
7272
NewTenantBasedHandleResource,
73+
NewCSMThreatsPoliciesListResource,
7374
NewCSMThreatsPolicyResource,
7475
NewCSMThreatsMultiPolicyAgentRuleResource,
7576
}
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
package fwprovider
2+
3+
import (
4+
"context"
5+
6+
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV2"
7+
"github.com/hashicorp/terraform-plugin-framework/diag"
8+
"github.com/hashicorp/terraform-plugin-framework/path"
9+
"github.com/hashicorp/terraform-plugin-framework/resource"
10+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
11+
"github.com/hashicorp/terraform-plugin-framework/types"
12+
13+
"github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils"
14+
)
15+
16+
var (
17+
_ resource.ResourceWithConfigure = &csmThreatsPoliciesListResource{}
18+
_ resource.ResourceWithImportState = &csmThreatsPoliciesListResource{}
19+
)
20+
21+
type csmThreatsPoliciesListResource struct {
22+
api *datadogV2.CSMThreatsApi
23+
auth context.Context
24+
}
25+
26+
type csmThreatsPoliciesListModel struct {
27+
ID types.String `tfsdk:"id"`
28+
Entries []csmThreatsPoliciesListEntryModel `tfsdk:"entries"`
29+
}
30+
31+
type csmThreatsPoliciesListEntryModel struct {
32+
PolicyID types.String `tfsdk:"policy_id"`
33+
Name types.String `tfsdk:"name"`
34+
Priority types.Int64 `tfsdk:"priority"`
35+
}
36+
37+
func NewCSMThreatsPoliciesListResource() resource.Resource {
38+
return &csmThreatsPoliciesListResource{}
39+
}
40+
41+
func (r *csmThreatsPoliciesListResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) {
42+
response.TypeName = "csm_threats_policies_list"
43+
}
44+
45+
func (r *csmThreatsPoliciesListResource) Configure(_ context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) {
46+
providerData := request.ProviderData.(*FrameworkProvider)
47+
r.api = providerData.DatadogApiInstances.GetCSMThreatsApiV2()
48+
r.auth = providerData.Auth
49+
}
50+
51+
func (r *csmThreatsPoliciesListResource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) {
52+
resource.ImportStatePassthroughID(ctx, path.Root("id"), request, response)
53+
}
54+
55+
func (r *csmThreatsPoliciesListResource) Schema(_ context.Context, _ resource.SchemaRequest, response *resource.SchemaResponse) {
56+
response.Schema = schema.Schema{
57+
Description: "Provides a Datadog CSM Threats policies API resource.",
58+
Attributes: map[string]schema.Attribute{
59+
"id": utils.ResourceIDAttribute(),
60+
},
61+
Blocks: map[string]schema.Block{
62+
"entries": schema.SetNestedBlock{
63+
Description: "A set of policies that belong to this list/batch. All non-listed policies get deleted.",
64+
NestedObject: schema.NestedBlockObject{
65+
Attributes: map[string]schema.Attribute{
66+
"policy_id": schema.StringAttribute{
67+
Description: "The ID of the policy to manage (from `csm_threats_policy`).",
68+
Required: true,
69+
},
70+
"priority": schema.Int64Attribute{
71+
Description: "The priority of the policy in this list.",
72+
Required: true,
73+
},
74+
"name": schema.StringAttribute{
75+
Description: "Optional name. If omitted, fallback to the policy_id as name.",
76+
Optional: true,
77+
Computed: true,
78+
},
79+
},
80+
},
81+
},
82+
},
83+
}
84+
}
85+
86+
func (r *csmThreatsPoliciesListResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) {
87+
var plan csmThreatsPoliciesListModel
88+
response.Diagnostics.Append(request.Plan.Get(ctx, &plan)...)
89+
if response.Diagnostics.HasError() {
90+
return
91+
}
92+
93+
plan.ID = types.StringValue("policies_list")
94+
95+
updatedEntries, err := r.applyBatchPolicies(ctx, plan.Entries, &response.Diagnostics)
96+
if err != nil {
97+
return
98+
}
99+
100+
plan.Entries = updatedEntries
101+
response.Diagnostics.Append(response.State.Set(ctx, &plan)...)
102+
}
103+
104+
func (r *csmThreatsPoliciesListResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) {
105+
var state csmThreatsPoliciesListModel
106+
response.Diagnostics.Append(request.State.Get(ctx, &state)...)
107+
if response.Diagnostics.HasError() {
108+
return
109+
}
110+
111+
if state.ID.IsUnknown() || state.ID.IsNull() || state.ID.ValueString() == "" {
112+
response.State.RemoveResource(ctx)
113+
return
114+
}
115+
116+
listResponse, httpResp, err := r.api.ListCSMThreatsAgentPolicies(r.auth)
117+
if err != nil {
118+
if httpResp != nil && httpResp.StatusCode == 404 {
119+
response.State.RemoveResource(ctx)
120+
return
121+
}
122+
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error while fetching agent policies"))
123+
return
124+
}
125+
126+
newEntries := make([]csmThreatsPoliciesListEntryModel, 0)
127+
for _, policyData := range listResponse.GetData() {
128+
policyID := policyData.GetId()
129+
if policyID == "CWS_DD" {
130+
continue
131+
}
132+
attributes := policyData.Attributes
133+
134+
name := attributes.GetName()
135+
priorirty := attributes.GetPriority()
136+
137+
entry := csmThreatsPoliciesListEntryModel{
138+
PolicyID: types.StringValue(policyID),
139+
Name: types.StringValue(name),
140+
Priority: types.Int64Value(int64(priorirty)),
141+
}
142+
newEntries = append(newEntries, entry)
143+
}
144+
145+
state.Entries = newEntries
146+
response.Diagnostics.Append(response.State.Set(ctx, &state)...)
147+
}
148+
149+
func (r *csmThreatsPoliciesListResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) {
150+
var plan csmThreatsPoliciesListModel
151+
response.Diagnostics.Append(request.Plan.Get(ctx, &plan)...)
152+
if response.Diagnostics.HasError() {
153+
return
154+
}
155+
156+
updatedEntries, err := r.applyBatchPolicies(ctx, plan.Entries, &response.Diagnostics)
157+
if err != nil {
158+
return
159+
}
160+
161+
plan.Entries = updatedEntries
162+
response.Diagnostics.Append(response.State.Set(ctx, &plan)...)
163+
}
164+
165+
func (r *csmThreatsPoliciesListResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) {
166+
_, err := r.applyBatchPolicies(ctx, []csmThreatsPoliciesListEntryModel{}, &response.Diagnostics)
167+
if err != nil {
168+
return
169+
}
170+
response.State.RemoveResource(ctx)
171+
}
172+
173+
func (r *csmThreatsPoliciesListResource) applyBatchPolicies(ctx context.Context, entries []csmThreatsPoliciesListEntryModel, diags *diag.Diagnostics) ([]csmThreatsPoliciesListEntryModel, error) {
174+
listResp, httpResp, err := r.api.ListCSMThreatsAgentPolicies(r.auth)
175+
if err != nil {
176+
if httpResp != nil && httpResp.StatusCode == 404 {
177+
diags.Append(utils.FrameworkErrorDiag(err, "error while fetching agent policies"))
178+
return nil, err
179+
}
180+
}
181+
182+
existingPolicies := make(map[string]struct{})
183+
for _, policy := range listResp.GetData() {
184+
if policy.GetId() == "CWS_DD" {
185+
continue
186+
}
187+
existingPolicies[policy.GetId()] = struct{}{}
188+
}
189+
190+
var batchItems []datadogV2.CloudWorkloadSecurityAgentPolicyBatchUpdateAttributesPoliciesItems
191+
192+
for i := range entries {
193+
policyID := entries[i].PolicyID.ValueString()
194+
name := entries[i].Name.ValueString()
195+
196+
if name == "" {
197+
name = policyID
198+
entries[i].Name = types.StringValue(name)
199+
}
200+
priority := entries[i].Priority.ValueInt64()
201+
202+
item := datadogV2.CloudWorkloadSecurityAgentPolicyBatchUpdateAttributesPoliciesItems{
203+
Id: &policyID,
204+
Name: &name,
205+
Priority: &priority,
206+
}
207+
208+
batchItems = append(batchItems, item)
209+
delete(existingPolicies, policyID)
210+
}
211+
212+
for policyID := range existingPolicies {
213+
DeleteTrue := true
214+
item := datadogV2.CloudWorkloadSecurityAgentPolicyBatchUpdateAttributesPoliciesItems{
215+
Id: &policyID,
216+
Delete: &DeleteTrue,
217+
}
218+
batchItems = append(batchItems, item)
219+
}
220+
221+
patchID := "batch_update_req"
222+
typ := datadogV2.CLOUDWORKLOADSECURITYAGENTPOLICYBATCHUPDATEDATATYPE_POLICIES
223+
attributes := datadogV2.NewCloudWorkloadSecurityAgentPolicyBatchUpdateAttributes()
224+
attributes.SetPolicies(batchItems)
225+
data := datadogV2.NewCloudWorkloadSecurityAgentPolicyBatchUpdateData(*attributes, patchID, typ)
226+
batchReq := datadogV2.NewCloudWorkloadSecurityAgentPolicyBatchUpdateRequest(*data)
227+
228+
response, _, err := r.api.BatchUpdateCSMThreatsAgentPolicy(
229+
r.auth,
230+
*batchReq,
231+
)
232+
if err != nil {
233+
diags.Append(utils.FrameworkErrorDiag(err, "error while applying batch policies"))
234+
return nil, err
235+
}
236+
237+
finalEntries := make([]csmThreatsPoliciesListEntryModel, 0)
238+
for _, policy := range response.GetData() {
239+
policyID := policy.GetId()
240+
attributes := policy.Attributes
241+
242+
name := ""
243+
if attributes.GetName() == "" {
244+
name = policyID
245+
}
246+
name = attributes.GetName()
247+
priority := attributes.GetPriority()
248+
249+
entry := csmThreatsPoliciesListEntryModel{
250+
PolicyID: types.StringValue(policyID),
251+
Name: types.StringValue(name),
252+
Priority: types.Int64Value(int64(priority)),
253+
}
254+
finalEntries = append(finalEntries, entry)
255+
}
256+
257+
return finalEntries, nil
258+
}

datadog/fwprovider/resource_datadog_csm_threats_policy.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package fwprovider
33
import (
44
"context"
55
"fmt"
6+
mathrand "math/rand"
67

78
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV2"
89
"github.com/hashicorp/terraform-plugin-framework/path"
@@ -47,7 +48,7 @@ func (r *csmThreatsPolicyResource) Schema(_ context.Context, _ resource.SchemaRe
4748
Attributes: map[string]schema.Attribute{
4849
"id": utils.ResourceIDAttribute(),
4950
"name": schema.StringAttribute{
50-
Required: true,
51+
Computed: true,
5152
Description: "The name of the policy.",
5253
},
5354
"description": schema.StringAttribute{
@@ -182,13 +183,13 @@ func (r *csmThreatsPolicyResource) Delete(ctx context.Context, request resource.
182183
}
183184

184185
func (r *csmThreatsPolicyResource) buildCreateCSMThreatsPolicyPayload(state *csmThreatsPolicyModel) (*datadogV2.CloudWorkloadSecurityAgentPolicyCreateRequest, error) {
185-
_, name, description, enabled, tags, err := r.extractPolicyAttributesFromResource(state)
186+
_, description, enabled, tags, err := r.extractPolicyAttributesFromResource(state)
186187
if err != nil {
187188
return nil, err
188189
}
189190

190191
attributes := datadogV2.CloudWorkloadSecurityAgentPolicyCreateAttributes{}
191-
attributes.Name = name
192+
attributes.Name = fmt.Sprintf("policy-%d", mathrand.Intn(1000))
192193
attributes.Description = description
193194
attributes.Enabled = enabled
194195
attributes.HostTags = tags
@@ -198,12 +199,11 @@ func (r *csmThreatsPolicyResource) buildCreateCSMThreatsPolicyPayload(state *csm
198199
}
199200

200201
func (r *csmThreatsPolicyResource) buildUpdateCSMThreatsPolicyPayload(state *csmThreatsPolicyModel) (*datadogV2.CloudWorkloadSecurityAgentPolicyUpdateRequest, error) {
201-
policyId, name, description, enabled, tags, err := r.extractPolicyAttributesFromResource(state)
202+
policyId, description, enabled, tags, err := r.extractPolicyAttributesFromResource(state)
202203
if err != nil {
203204
return nil, err
204205
}
205206
attributes := datadogV2.CloudWorkloadSecurityAgentPolicyUpdateAttributes{}
206-
attributes.Name = &name
207207
attributes.Description = description
208208
attributes.Enabled = enabled
209209
attributes.HostTags = tags
@@ -213,24 +213,23 @@ func (r *csmThreatsPolicyResource) buildUpdateCSMThreatsPolicyPayload(state *csm
213213
return datadogV2.NewCloudWorkloadSecurityAgentPolicyUpdateRequest(*data), nil
214214
}
215215

216-
func (r *csmThreatsPolicyResource) extractPolicyAttributesFromResource(state *csmThreatsPolicyModel) (string, string, *string, *bool, []string, error) {
216+
func (r *csmThreatsPolicyResource) extractPolicyAttributesFromResource(state *csmThreatsPolicyModel) (string, *string, *bool, []string, error) {
217217
// Mandatory fields
218218
id := state.Id.ValueString()
219-
name := state.Name.ValueString()
220219
enabled := state.Enabled.ValueBoolPointer()
221220
description := state.Description.ValueStringPointer()
222221
var tags []string
223222
if !state.Tags.IsNull() && !state.Tags.IsUnknown() {
224223
for _, tag := range state.Tags.Elements() {
225224
tagStr, ok := tag.(types.String)
226225
if !ok {
227-
return "", "", nil, nil, nil, fmt.Errorf("expected item to be of type types.String, got %T", tag)
226+
return "", nil, nil, nil, fmt.Errorf("expected item to be of type types.String, got %T", tag)
228227
}
229228
tags = append(tags, tagStr.ValueString())
230229
}
231230
}
232231

233-
return id, name, description, enabled, tags, nil
232+
return id, description, enabled, tags, nil
234233
}
235234

236235
func (r *csmThreatsPolicyResource) updateStateFromResponse(ctx context.Context, state *csmThreatsPolicyModel, res *datadogV2.CloudWorkloadSecurityAgentPolicyResponse) {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "datadog_csm_threats_policies_list Resource - terraform-provider-datadog"
4+
subcategory: ""
5+
description: |-
6+
Provides a Datadog CSM Threats policies API resource.
7+
---
8+
9+
# datadog_csm_threats_policies_list (Resource)
10+
11+
Provides a Datadog CSM Threats policies API resource.
12+
13+
14+
15+
<!-- schema generated by tfplugindocs -->
16+
## Schema
17+
18+
### Optional
19+
20+
- `entries` (Block Set) A set of policies that belong to this list/batch. All non-listed policies get deleted. (see [below for nested schema](#nestedblock--entries))
21+
22+
### Read-Only
23+
24+
- `id` (String) The ID of this resource.
25+
26+
<a id="nestedblock--entries"></a>
27+
### Nested Schema for `entries`
28+
29+
Required:
30+
31+
- `policy_id` (String) The ID of the policy to manage (from `csm_threats_policy`).
32+
- `priority` (Number) The priority of the policy in this list.
33+
34+
Optional:
35+
36+
- `name` (String) Optional name. If omitted, fallback to the policy_id as name.

docs/resources/csm_threats_policy.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ Provides a Datadog CSM Threats policy API resource.
1515
<!-- schema generated by tfplugindocs -->
1616
## Schema
1717

18-
### Required
19-
20-
- `name` (String) The name of the policy.
21-
2218
### Optional
2319

2420
- `description` (String) A description for the policy.
@@ -28,3 +24,4 @@ Provides a Datadog CSM Threats policy API resource.
2824
### Read-Only
2925

3026
- `id` (String) The ID of this resource.
27+
- `name` (String) The name of the policy.

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,4 @@ require (
9797
)
9898

9999
go 1.23
100+
replace github.com/DataDog/datadog-api-client-go/v2 v2.34.1-0.20241226155556-e60f30b0e84e => ../datadog-api-spec/generated/datadog-api-client-go

0 commit comments

Comments
 (0)