Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[datadog_csm_threats_agent_rules] Add ressource and datasource for CSM Threats agent rule #2316

Merged
merged 24 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
42825c9
[CWS-1047] - basic structure
Malo10LeGoff Mar 13, 2024
9ab785f
[CWS-1047] - change name
Malo10LeGoff Mar 13, 2024
f04c7e4
[CWS-1047] - resource type name
Malo10LeGoff Mar 13, 2024
eeb3423
[CWS-1047] - fix tests
Malo10LeGoff Mar 13, 2024
ee3d2e8
[CWS-1047] - regenerate docs
Malo10LeGoff Mar 13, 2024
70b6ca0
[CWS-1047] - record cassettes
Malo10LeGoff Mar 13, 2024
50ced68
[CWS-1047] - record create and update cassette
Malo10LeGoff Mar 13, 2024
50fdd6c
[CWS-1047] - nits and renaminbg
Malo10LeGoff Mar 13, 2024
6b5b494
[CWS-1047] - re-run resource tesgt
Malo10LeGoff Mar 13, 2024
bbbfb87
[CWS-1047] - re-generate doc
Malo10LeGoff Mar 13, 2024
ca789da
[CWS-1047] - nits
Malo10LeGoff Mar 13, 2024
f33ef1b
[CWS-1047] - run against prod api
Malo10LeGoff Mar 13, 2024
a80b386
[CWS-1047] - again run it against prod api
Malo10LeGoff Mar 13, 2024
449c6f9
fix random agent rule function
nkzou Mar 14, 2024
69a9dfd
fix function description
nkzou Mar 14, 2024
6c7827c
[CWS-1047] - Docs comments
Malo10LeGoff Mar 14, 2024
5b86cce
Merge branch 'malo.legoff/cws-1047-final' of github.com:DataDog/terra…
Malo10LeGoff Mar 14, 2024
305fbdc
[CWS-1047] - adress comments
Malo10LeGoff Mar 19, 2024
3cadb17
Merge branch 'master' into malo.legoff/cws-1047-final
Malo10LeGoff Mar 19, 2024
2ad99de
[CWS-1047] - rmv default
Malo10LeGoff Mar 19, 2024
fb62672
[CWS-1049] - Add a default value for descriptionm
Malo10LeGoff Mar 19, 2024
a05b4a7
[CWS-1047] - hash the datasource vid
Malo10LeGoff Mar 19, 2024
566e0aa
[CWS-1047] - run gofmt
Malo10LeGoff Mar 19, 2024
74b4ad4
remove println
nkzou Mar 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions datadog/fwprovider/data_source_datadog_csm_threats_agent_rule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package fwprovider

import (
"context"
"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
}

state.Id = types.StringValue(strings.Join(agentRuleIds, "--"))
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 (*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,
},
},
},
},
}
}
2 changes: 2 additions & 0 deletions datadog/fwprovider/framework_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ var Resources = []func() resource.Resource{
NewTeamPermissionSettingResource,
NewTeamResource,
NewSecurityMonitoringSuppressionResource,
NewCSMThreatsAgentRuleResource,
NewServiceAccountResource,
}

Expand All @@ -78,6 +79,7 @@ var Datasources = []func() datasource.DataSource{
NewSensitiveDataScannerGroupOrderDatasource,
NewDatadogUsersDataSource,
NewSecurityMonitoringSuppressionDataSource,
NewCSMThreatsAgentRulesDataSource,
}

// FrameworkProvider struct
Expand Down
218 changes: 218 additions & 0 deletions datadog/fwprovider/resource_datadog_csm_threats_agent_rule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
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/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.",
},
"description": schema.StringAttribute{
Optional: true,
Description: "A description for the Agent rule.",
},
Malo10LeGoff marked this conversation as resolved.
Show resolved Hide resolved
"enabled": schema.BoolAttribute{
Required: true,
Description: "Indicates Whether the Agent rule is enabled.",
},
"expression": schema.StringAttribute{
Optional: true,
Description: "The SECL expression of the Agent rule",
},
Malo10LeGoff marked this conversation as resolved.
Show resolved Hide resolved
},
}
}

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)
Malo10LeGoff marked this conversation as resolved.
Show resolved Hide resolved

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())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2024-03-14T12:54:12.185366-04:00
Loading
Loading