-
Notifications
You must be signed in to change notification settings - Fork 370
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[datadog_csm_threats_agent_rules] Add ressource and datasource for CS…
…M Threats agent rule (#2316) * [CWS-1047] - basic structure * [CWS-1047] - change name * [CWS-1047] - resource type name * [CWS-1047] - fix tests * [CWS-1047] - regenerate docs * [CWS-1047] - record cassettes * [CWS-1047] - record create and update cassette * [CWS-1047] - nits and renaminbg * [CWS-1047] - re-run resource tesgt * [CWS-1047] - re-generate doc * [CWS-1047] - nits * [CWS-1047] - run against prod api * [CWS-1047] - again run it against prod api * fix random agent rule function * fix function description * [CWS-1047] - Docs comments * [CWS-1047] - adress comments * [CWS-1047] - rmv default * [CWS-1049] - Add a default value for descriptionm * [CWS-1047] - hash the datasource vid * [CWS-1047] - run gofmt * remove println --------- Co-authored-by: Kevin Zou <kevin.zou@datadoghq.com>
- Loading branch information
1 parent
260e448
commit 1387446
Showing
14 changed files
with
1,442 additions
and
0 deletions.
There are no files selected for viewing
125 changes: 125 additions & 0 deletions
125
datadog/fwprovider/data_source_datadog_csm_threats_agent_rule.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
package fwprovider | ||
|
||
import ( | ||
"context" | ||
"crypto/sha256" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" | ||
"github.com/hashicorp/terraform-plugin-framework/attr" | ||
"github.com/hashicorp/terraform-plugin-framework/datasource" | ||
"github.com/hashicorp/terraform-plugin-framework/datasource/schema" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
|
||
"github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" | ||
) | ||
|
||
var ( | ||
_ datasource.DataSourceWithConfigure = &csmThreatsAgentRulesDataSource{} | ||
) | ||
|
||
type csmThreatsAgentRulesDataSource struct { | ||
api *datadogV2.CloudWorkloadSecurityApi | ||
auth context.Context | ||
} | ||
|
||
type csmThreatsAgentRulesDataSourceModel struct { | ||
Id types.String `tfsdk:"id"` | ||
AgentRulesIds types.List `tfsdk:"agent_rules_ids"` | ||
AgentRules []csmThreatsAgentRuleModel `tfsdk:"agent_rules"` | ||
} | ||
|
||
func NewCSMThreatsAgentRulesDataSource() datasource.DataSource { | ||
return &csmThreatsAgentRulesDataSource{} | ||
} | ||
|
||
func (r *csmThreatsAgentRulesDataSource) Configure(_ context.Context, request datasource.ConfigureRequest, _ *datasource.ConfigureResponse) { | ||
providerData := request.ProviderData.(*FrameworkProvider) | ||
r.api = providerData.DatadogApiInstances.GetCloudWorkloadSecurityApiV2() | ||
r.auth = providerData.Auth | ||
} | ||
|
||
func (*csmThreatsAgentRulesDataSource) Metadata(_ context.Context, _ datasource.MetadataRequest, response *datasource.MetadataResponse) { | ||
response.TypeName = "csm_threats_agent_rules" | ||
} | ||
|
||
func (r *csmThreatsAgentRulesDataSource) Read(ctx context.Context, request datasource.ReadRequest, response *datasource.ReadResponse) { | ||
var state csmThreatsAgentRulesDataSourceModel | ||
response.Diagnostics.Append(request.Config.Get(ctx, &state)...) | ||
if response.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
res, _, err := r.api.ListCSMThreatsAgentRules(r.auth) | ||
if err != nil { | ||
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error while fetching agent rules")) | ||
return | ||
} | ||
|
||
data := res.GetData() | ||
agentRuleIds := make([]string, len(data)) | ||
agentRules := make([]csmThreatsAgentRuleModel, len(data)) | ||
|
||
for idx, agentRule := range res.GetData() { | ||
var agentRuleModel csmThreatsAgentRuleModel | ||
agentRuleModel.Id = types.StringValue(agentRule.GetId()) | ||
attributes := agentRule.Attributes | ||
agentRuleModel.Name = types.StringValue(attributes.GetName()) | ||
agentRuleModel.Description = types.StringValue(attributes.GetDescription()) | ||
agentRuleModel.Enabled = types.BoolValue(attributes.GetEnabled()) | ||
agentRuleModel.Expression = types.StringValue(*attributes.Expression) | ||
|
||
agentRuleIds[idx] = agentRule.GetId() | ||
agentRules[idx] = agentRuleModel | ||
} | ||
|
||
stateId := strings.Join(agentRuleIds, "--") | ||
state.Id = types.StringValue(computeAgentRulesDataSourceID(&stateId)) | ||
tfAgentRuleIds, diags := types.ListValueFrom(ctx, types.StringType, agentRuleIds) | ||
response.Diagnostics.Append(diags...) | ||
state.AgentRulesIds = tfAgentRuleIds | ||
state.AgentRules = agentRules | ||
|
||
response.Diagnostics.Append(response.State.Set(ctx, &state)...) | ||
} | ||
|
||
func computeAgentRulesDataSourceID(agentruleIds *string) string { | ||
// Key for hashing | ||
var b strings.Builder | ||
if agentruleIds != nil { | ||
b.WriteString(*agentruleIds) | ||
} | ||
keyStr := b.String() | ||
h := sha256.New() | ||
h.Write([]byte(keyStr)) | ||
|
||
return fmt.Sprintf("%x", h.Sum(nil)) | ||
} | ||
|
||
func (*csmThreatsAgentRulesDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, response *datasource.SchemaResponse) { | ||
response.Schema = schema.Schema{ | ||
Description: "Use this data source to retrieve information about existing Agent rules.", | ||
Attributes: map[string]schema.Attribute{ | ||
"id": utils.ResourceIDAttribute(), | ||
"agent_rules_ids": schema.ListAttribute{ | ||
Computed: true, | ||
Description: "List of IDs for the Agent rules.", | ||
ElementType: types.StringType, | ||
}, | ||
"agent_rules": schema.ListAttribute{ | ||
Computed: true, | ||
Description: "List of Agent rules", | ||
ElementType: types.ObjectType{ | ||
AttrTypes: map[string]attr.Type{ | ||
"id": types.StringType, | ||
"name": types.StringType, | ||
"description": types.StringType, | ||
"enabled": types.BoolType, | ||
"expression": types.StringType, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
229 changes: 229 additions & 0 deletions
229
datadog/fwprovider/resource_datadog_csm_threats_agent_rule.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
package fwprovider | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" | ||
"github.com/hashicorp/terraform-plugin-framework/path" | ||
"github.com/hashicorp/terraform-plugin-framework/resource" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" | ||
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" | ||
"github.com/hashicorp/terraform-plugin-framework/types" | ||
|
||
"github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" | ||
) | ||
|
||
var ( | ||
_ resource.ResourceWithConfigure = &csmThreatsAgentRuleResource{} | ||
_ resource.ResourceWithImportState = &csmThreatsAgentRuleResource{} | ||
) | ||
|
||
type csmThreatsAgentRuleModel struct { | ||
Id types.String `tfsdk:"id"` | ||
Name types.String `tfsdk:"name"` | ||
Description types.String `tfsdk:"description"` | ||
Enabled types.Bool `tfsdk:"enabled"` | ||
Expression types.String `tfsdk:"expression"` | ||
} | ||
|
||
type csmThreatsAgentRuleResource struct { | ||
api *datadogV2.CloudWorkloadSecurityApi | ||
auth context.Context | ||
} | ||
|
||
func NewCSMThreatsAgentRuleResource() resource.Resource { | ||
return &csmThreatsAgentRuleResource{} | ||
} | ||
|
||
func (r *csmThreatsAgentRuleResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { | ||
response.TypeName = "csm_threats_agent_rule" | ||
} | ||
|
||
func (r *csmThreatsAgentRuleResource) Configure(_ context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) { | ||
providerData := request.ProviderData.(*FrameworkProvider) | ||
r.api = providerData.DatadogApiInstances.GetCloudWorkloadSecurityApiV2() | ||
r.auth = providerData.Auth | ||
} | ||
|
||
func (r *csmThreatsAgentRuleResource) Schema(_ context.Context, _ resource.SchemaRequest, response *resource.SchemaResponse) { | ||
response.Schema = schema.Schema{ | ||
Description: "Provides a Datadog CSM Threats Agent Rule API resource.", | ||
Attributes: map[string]schema.Attribute{ | ||
"id": utils.ResourceIDAttribute(), | ||
"name": schema.StringAttribute{ | ||
Required: true, | ||
Description: "The name of the Agent rule.", | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
"description": schema.StringAttribute{ | ||
Optional: true, | ||
Description: "A description for the Agent rule.", | ||
Default: stringdefault.StaticString(""), | ||
Computed: true, | ||
}, | ||
"enabled": schema.BoolAttribute{ | ||
Required: true, | ||
Description: "Indicates Whether the Agent rule is enabled.", | ||
}, | ||
"expression": schema.StringAttribute{ | ||
Required: true, | ||
Description: "The SECL expression of the Agent rule", | ||
PlanModifiers: []planmodifier.String{ | ||
stringplanmodifier.RequiresReplace(), | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func (r *csmThreatsAgentRuleResource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { | ||
resource.ImportStatePassthroughID(ctx, path.Root("id"), request, response) | ||
} | ||
|
||
func (r *csmThreatsAgentRuleResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { | ||
var state csmThreatsAgentRuleModel | ||
response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) | ||
if response.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
agentRulePayload, err := r.buildCreateCSMThreatsAgentRulePayload(&state) | ||
if err != nil { | ||
response.Diagnostics.AddError("error while parsing resource", err.Error()) | ||
} | ||
|
||
res, _, err := r.api.CreateCSMThreatsAgentRule(r.auth, *agentRulePayload) | ||
if err != nil { | ||
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error creating agent rule")) | ||
return | ||
} | ||
if err := utils.CheckForUnparsed(response); err != nil { | ||
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "response contains unparsed object")) | ||
return | ||
} | ||
|
||
r.updateStateFromResponse(ctx, &state, &res) | ||
response.Diagnostics.Append(response.State.Set(ctx, &state)...) | ||
} | ||
|
||
func (r *csmThreatsAgentRuleResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { | ||
var state csmThreatsAgentRuleModel | ||
response.Diagnostics.Append(request.State.Get(ctx, &state)...) | ||
if response.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
agentRuleId := state.Id.ValueString() | ||
res, httpResponse, err := r.api.GetCSMThreatsAgentRule(r.auth, agentRuleId) | ||
if err != nil { | ||
if httpResponse != nil && httpResponse.StatusCode == 404 { | ||
response.State.RemoveResource(ctx) | ||
return | ||
} | ||
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error fetching agent rule")) | ||
return | ||
} | ||
if err := utils.CheckForUnparsed(response); err != nil { | ||
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "response contains unparsed object")) | ||
return | ||
} | ||
|
||
r.updateStateFromResponse(ctx, &state, &res) | ||
response.Diagnostics.Append(response.State.Set(ctx, &state)...) | ||
} | ||
|
||
func (r *csmThreatsAgentRuleResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { | ||
var state csmThreatsAgentRuleModel | ||
response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) | ||
if response.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
agentRulePayload, err := r.buildUpdateCSMThreatsAgentRulePayload(&state) | ||
if err != nil { | ||
response.Diagnostics.AddError("error while parsing resource", err.Error()) | ||
} | ||
|
||
res, _, err := r.api.UpdateCSMThreatsAgentRule(r.auth, state.Id.ValueString(), *agentRulePayload) | ||
if err != nil { | ||
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error updating agent rule")) | ||
return | ||
} | ||
if err := utils.CheckForUnparsed(response); err != nil { | ||
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "response contains unparsed object")) | ||
return | ||
} | ||
|
||
r.updateStateFromResponse(ctx, &state, &res) | ||
response.Diagnostics.Append(response.State.Set(ctx, &state)...) | ||
} | ||
|
||
func (r *csmThreatsAgentRuleResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { | ||
var state csmThreatsAgentRuleModel | ||
response.Diagnostics.Append(request.State.Get(ctx, &state)...) | ||
if response.Diagnostics.HasError() { | ||
return | ||
} | ||
|
||
id := state.Id.ValueString() | ||
|
||
httpResp, err := r.api.DeleteCSMThreatsAgentRule(r.auth, id) | ||
if err != nil { | ||
if httpResp != nil && httpResp.StatusCode == 404 { | ||
return | ||
} | ||
response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error deleting agent rule")) | ||
return | ||
} | ||
} | ||
|
||
func (r *csmThreatsAgentRuleResource) buildCreateCSMThreatsAgentRulePayload(state *csmThreatsAgentRuleModel) (*datadogV2.CloudWorkloadSecurityAgentRuleCreateRequest, error) { | ||
_, name, description, enabled, expression := r.extractAgentRuleAttributesFromResource(state) | ||
|
||
attributes := datadogV2.CloudWorkloadSecurityAgentRuleCreateAttributes{} | ||
attributes.Expression = expression | ||
attributes.Name = name | ||
attributes.Description = description | ||
attributes.Enabled = &enabled | ||
|
||
data := datadogV2.NewCloudWorkloadSecurityAgentRuleCreateData(attributes, datadogV2.CLOUDWORKLOADSECURITYAGENTRULETYPE_AGENT_RULE) | ||
return datadogV2.NewCloudWorkloadSecurityAgentRuleCreateRequest(*data), nil | ||
} | ||
|
||
func (r *csmThreatsAgentRuleResource) buildUpdateCSMThreatsAgentRulePayload(state *csmThreatsAgentRuleModel) (*datadogV2.CloudWorkloadSecurityAgentRuleUpdateRequest, error) { | ||
agentRuleId, _, description, enabled, _ := r.extractAgentRuleAttributesFromResource(state) | ||
|
||
attributes := datadogV2.CloudWorkloadSecurityAgentRuleUpdateAttributes{} | ||
attributes.Description = description | ||
attributes.Enabled = &enabled | ||
|
||
data := datadogV2.NewCloudWorkloadSecurityAgentRuleUpdateData(attributes, datadogV2.CLOUDWORKLOADSECURITYAGENTRULETYPE_AGENT_RULE) | ||
data.Id = &agentRuleId | ||
return datadogV2.NewCloudWorkloadSecurityAgentRuleUpdateRequest(*data), nil | ||
} | ||
|
||
func (r *csmThreatsAgentRuleResource) extractAgentRuleAttributesFromResource(state *csmThreatsAgentRuleModel) (string, string, *string, bool, string) { | ||
// Mandatory fields | ||
id := state.Id.ValueString() | ||
name := state.Name.ValueString() | ||
enabled := state.Enabled.ValueBool() | ||
expression := state.Expression.ValueString() | ||
description := state.Description.ValueStringPointer() | ||
|
||
return id, name, description, enabled, expression | ||
} | ||
|
||
func (r *csmThreatsAgentRuleResource) updateStateFromResponse(ctx context.Context, state *csmThreatsAgentRuleModel, res *datadogV2.CloudWorkloadSecurityAgentRuleResponse) { | ||
state.Id = types.StringValue(res.Data.GetId()) | ||
|
||
attributes := res.Data.Attributes | ||
|
||
state.Name = types.StringValue(attributes.GetName()) | ||
state.Description = types.StringValue(attributes.GetDescription()) | ||
state.Enabled = types.BoolValue(attributes.GetEnabled()) | ||
state.Expression = types.StringValue(attributes.GetExpression()) | ||
} |
1 change: 1 addition & 0 deletions
1
datadog/tests/cassettes/TestAccCSMThreatsAgentRuleDataSource.freeze
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
2024-03-14T12:54:12.185366-04:00 |
Oops, something went wrong.