diff --git a/datadog/data_source_datadog_cloud_workload_security_agent_rules.go b/datadog/data_source_datadog_cloud_workload_security_agent_rules.go deleted file mode 100644 index 45f942b0f3..0000000000 --- a/datadog/data_source_datadog_cloud_workload_security_agent_rules.go +++ /dev/null @@ -1,100 +0,0 @@ -package datadog - -import ( - "context" - "fmt" - - "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" - - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSourceDatadogCloudWorkloadSecurityAgentRules() *schema.Resource { - return &schema.Resource{ - Description: "Use this data source to retrieve information about existing Cloud Workload Security Agent Rules for use in other resources. Deprecated, use datadog_csm_threats_agent_rules data source instead: https://registry.terraform.io/providers/DataDog/datadog/latest/docs/data-sources/csm_threats_agent_rules", - DeprecationMessage: "This data source is deprecated — use the datadog_csm_threats_agent_rules data source instead: https://registry.terraform.io/providers/DataDog/datadog/latest/docs/data-sources/csm_threats_agent_rules", - ReadContext: dataSourceDatadogCloudWorkloadSecurityAgentRulesRead, - - SchemaFunc: func() map[string]*schema.Schema { - return map[string]*schema.Schema{ - // Computed - "agent_rules": { - Description: "List of Agent rules.", - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: true, - Description: "The id of the Agent rule.", - }, - "description": { - Type: schema.TypeString, - Computed: true, - Description: "The description of the Agent rule.", - }, - "enabled": { - Type: schema.TypeBool, - Computed: true, - Description: "Whether the Agent rule is enabled.", - }, - "expression": { - Type: schema.TypeString, - Computed: true, - Description: "The SECL expression of the Agent rule.", - }, - "name": { - Type: schema.TypeString, - Computed: true, - Description: "The name of the Agent rule.", - }, - }, - }, - }, - } - }, - } -} - -func dataSourceDatadogCloudWorkloadSecurityAgentRulesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - providerConf := meta.(*ProviderConfiguration) - apiInstances := providerConf.DatadogApiInstances - auth := providerConf.Auth - - agentRules := make([]map[string]interface{}, 0) - response, httpresp, err := apiInstances.GetCSMThreatsApiV2().ListCloudWorkloadSecurityAgentRules(auth) - if err != nil { - return utils.TranslateClientErrorDiag(err, httpresp, "error listing agent rules") - } - - diags := diag.Diagnostics{} - for _, agentRule := range response.GetData() { - if err := utils.CheckForUnparsed(agentRule); err != nil { - diags = append(diags, diag.Diagnostic{ - Severity: diag.Warning, - Summary: fmt.Sprintf("skipping agent rule with id: %s", agentRule.GetId()), - Detail: fmt.Sprintf("rule contains unparsed object: %v", err), - }) - continue - } - - // extract agent rule - agentRuleTF := make(map[string]interface{}) - attributes := agentRule.GetAttributes() - - agentRuleTF["id"] = agentRule.GetId() - agentRuleTF["name"] = attributes.GetName() - agentRuleTF["description"] = attributes.GetDescription() - agentRuleTF["expression"] = attributes.GetExpression() - agentRuleTF["enabled"] = attributes.GetEnabled() - - agentRules = append(agentRules, agentRuleTF) - } - - d.SetId("cloud-workload-security-agent-rules") - d.Set("agent_rules", agentRules) - - return diags -} diff --git a/datadog/fwprovider/data_source_datadog_csm_threats_multi_policy_agent_rules.go b/datadog/fwprovider/data_source_datadog_csm_threats_multi_policy_agent_rules.go new file mode 100644 index 0000000000..1f8d11b101 --- /dev/null +++ b/datadog/fwprovider/data_source_datadog_csm_threats_multi_policy_agent_rules.go @@ -0,0 +1,178 @@ +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 = &csmThreatsMultiPolicyAgentRulesDataSource{} +) + +type csmThreatsMultiPolicyAgentRulesDataSource struct { + api *datadogV2.CSMThreatsApi + auth context.Context +} + +type csmThreatsMultiPolicyAgentRulesDataSourceModel struct { + PolicyId types.String `tfsdk:"policy_id"` + Id types.String `tfsdk:"id"` + AgentRulesIds types.List `tfsdk:"agent_rules_ids"` + AgentRules []csmThreatsMultiPolicyAgentRuleDataSourceModel `tfsdk:"agent_rules"` +} + +type csmThreatsMultiPolicyAgentRuleDataSourceModel 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"` + ProductTags types.Set `tfsdk:"product_tags"` +} + +func NewCSMThreatsMultiPolicyAgentRulesDataSource() datasource.DataSource { + return &csmThreatsMultiPolicyAgentRulesDataSource{} +} + +func (r *csmThreatsMultiPolicyAgentRulesDataSource) Configure(_ context.Context, request datasource.ConfigureRequest, response *datasource.ConfigureResponse) { + if request.ProviderData == nil { + return + } + + providerData, ok := request.ProviderData.(*FrameworkProvider) + if !ok { + response.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *FrameworkProvider, got: %T. Please report this issue to the provider developers.", request.ProviderData), + ) + return + } + + r.api = providerData.DatadogApiInstances.GetCSMThreatsApiV2() + r.auth = providerData.Auth +} + +func (r *csmThreatsMultiPolicyAgentRulesDataSource) Metadata(_ context.Context, request datasource.MetadataRequest, response *datasource.MetadataResponse) { + response.TypeName = "csm_threats_multi_policy_agent_rules" +} + +func (r *csmThreatsMultiPolicyAgentRulesDataSource) Read(ctx context.Context, request datasource.ReadRequest, response *datasource.ReadResponse) { + var state csmThreatsMultiPolicyAgentRulesDataSourceModel + response.Diagnostics.Append(request.Config.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + params := datadogV2.NewListCSMThreatsAgentRulesOptionalParameters() + if !state.PolicyId.IsNull() && !state.PolicyId.IsUnknown() { + policyId := state.PolicyId.ValueString() + params.WithPolicyId(policyId) + } + + res, _, err := r.api.ListCSMThreatsAgentRules(r.auth, *params) + 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([]csmThreatsMultiPolicyAgentRuleDataSourceModel, len(data)) + + for idx, agentRule := range res.GetData() { + var agentRuleModel csmThreatsMultiPolicyAgentRuleDataSourceModel + 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) + tags := attributes.GetProductTags() + tagSet := make(map[string]struct{}) + for _, tag := range tags { + tagSet[tag] = struct{}{} + } + uniqueTags := make([]string, 0, len(tagSet)) + for tag := range tagSet { + uniqueTags = append(uniqueTags, tag) + } + + productTags, diags := types.SetValueFrom(ctx, types.StringType, uniqueTags) + if diags.HasError() { + response.Diagnostics.Append(diags...) + continue + } + agentRuleModel.ProductTags = productTags + agentRuleIds[idx] = agentRule.GetId() + agentRules[idx] = agentRuleModel + } + + stateId := strings.Join(agentRuleIds, "--") + state.Id = types.StringValue(computeDataSourceID(&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 (*csmThreatsMultiPolicyAgentRulesDataSource) 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{ + // Input + "policy_id": schema.StringAttribute{ + Description: "Listing only the rules in the policy with this field as the ID", + Optional: true, + }, + // Output + "id": schema.StringAttribute{ + Description: "The ID of the data source", + Computed: true, + }, + "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, + "product_tags": types.SetType{ElemType: types.StringType}, + }, + }, + }, + }, + } +} + +func computeDataSourceID(ids *string) string { + // Key for hashing + var b strings.Builder + if ids != nil { + b.WriteString(*ids) + } + keyStr := b.String() + h := sha256.New() + h.Write([]byte(keyStr)) + + return fmt.Sprintf("%x", h.Sum(nil)) +} diff --git a/datadog/fwprovider/data_source_datadog_csm_threats_policy.go b/datadog/fwprovider/data_source_datadog_csm_threats_policy.go new file mode 100644 index 0000000000..c8a3455b06 --- /dev/null +++ b/datadog/fwprovider/data_source_datadog_csm_threats_policy.go @@ -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 = &csmThreatsPoliciesDataSource{} +) + +type csmThreatsPoliciesDataSource struct { + api *datadogV2.CSMThreatsApi + auth context.Context +} + +type csmThreatsPoliciesDataSourceModel struct { + Id types.String `tfsdk:"id"` + PolicyIds types.List `tfsdk:"policy_ids"` + Policies []csmThreatsPolicyModel `tfsdk:"policies"` +} + +func NewCSMThreatsPoliciesDataSource() datasource.DataSource { + return &csmThreatsPoliciesDataSource{} +} + +func (r *csmThreatsPoliciesDataSource) Configure(_ context.Context, request datasource.ConfigureRequest, _ *datasource.ConfigureResponse) { + providerData := request.ProviderData.(*FrameworkProvider) + r.api = providerData.DatadogApiInstances.GetCSMThreatsApiV2() + r.auth = providerData.Auth +} + +func (*csmThreatsPoliciesDataSource) Metadata(_ context.Context, _ datasource.MetadataRequest, response *datasource.MetadataResponse) { + response.TypeName = "csm_threats_policies" +} + +func (r *csmThreatsPoliciesDataSource) Read(ctx context.Context, request datasource.ReadRequest, response *datasource.ReadResponse) { + var state csmThreatsPoliciesDataSourceModel + response.Diagnostics.Append(request.Config.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + res, _, err := r.api.ListCSMThreatsAgentPolicies(r.auth) + if err != nil { + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error while fetching agent rules")) + return + } + + data := res.GetData() + policyIds := make([]string, len(data)) + policies := make([]csmThreatsPolicyModel, len(data)) + + for idx, policy := range res.GetData() { + var policyModel csmThreatsPolicyModel + policyModel.Id = types.StringValue(policy.GetId()) + attributes := policy.Attributes + policyModel.Name = types.StringValue(attributes.GetName()) + policyModel.Description = types.StringValue(attributes.GetDescription()) + policyModel.Enabled = types.BoolValue(attributes.GetEnabled()) + policyModel.Tags, _ = types.SetValueFrom(ctx, types.StringType, attributes.GetHostTags()) + policyIds[idx] = policy.GetId() + policies[idx] = policyModel + } + + stateId := strings.Join(policyIds, "--") + state.Id = types.StringValue(computeDataSourceID(&stateId)) + tfAgentRuleIds, diags := types.ListValueFrom(ctx, types.StringType, policyIds) + response.Diagnostics.Append(diags...) + state.PolicyIds = tfAgentRuleIds + state.Policies = policies + + response.Diagnostics.Append(response.State.Set(ctx, &state)...) +} + +func (*csmThreatsPoliciesDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, response *datasource.SchemaResponse) { + response.Schema = schema.Schema{ + Description: "Use this data source to retrieve information about existing policies.", + Attributes: map[string]schema.Attribute{ + "id": utils.ResourceIDAttribute(), + "policy_ids": schema.ListAttribute{ + Computed: true, + Description: "List of IDs for the policies.", + ElementType: types.StringType, + }, + "policies": schema.ListAttribute{ + Computed: true, + Description: "List of policies", + ElementType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "id": types.StringType, + "tags": types.SetType{ElemType: types.StringType}, + "name": types.StringType, + "description": types.StringType, + "enabled": types.BoolType, + }, + }, + }, + }, + } +} diff --git a/datadog/fwprovider/framework_provider.go b/datadog/fwprovider/framework_provider.go index 15da535fb1..9fd49eaf68 100644 --- a/datadog/fwprovider/framework_provider.go +++ b/datadog/fwprovider/framework_provider.go @@ -68,7 +68,6 @@ var Resources = []func() resource.Resource{ NewTeamResource, NewUserRoleResource, NewSecurityMonitoringSuppressionResource, - NewCSMThreatsAgentRuleResource, NewServiceAccountResource, NewWebhookResource, NewWebhookCustomVariableResource, @@ -80,6 +79,9 @@ var Resources = []func() resource.Resource{ NewActionConnectionResource, NewWorkflowAutomationResource, NewAppBuilderAppResource, + NewCSMThreatsAgentRuleResource, + NewCSMThreatsPolicyResource, + NewCSMThreatsMultiPolicyAgentRuleResource, } var Datasources = []func() datasource.DataSource{ @@ -101,13 +103,15 @@ var Datasources = []func() datasource.DataSource{ NewDatadogUsersDataSource, NewDatadogRoleUsersDataSource, NewSecurityMonitoringSuppressionDataSource, - NewCSMThreatsAgentRulesDataSource, + NewCSMThreatsMultiPolicyAgentRulesDataSource, NewLogsPipelinesOrderDataSource, NewDatadogTeamsDataSource, NewDatadogActionConnectionDataSource, NewDatadogSyntheticsGlobalVariableDataSource, NewWorkflowAutomationDataSource, NewDatadogAppBuilderAppDataSource, + NewCSMThreatsAgentRulesDataSource, + NewCSMThreatsPoliciesDataSource, } // FrameworkProvider struct diff --git a/datadog/fwprovider/resource_datadog_csm_threats_agent_rule.go b/datadog/fwprovider/resource_datadog_csm_threats_agent_rule.go index f04ada2d7f..73274922a0 100644 --- a/datadog/fwprovider/resource_datadog_csm_threats_agent_rule.go +++ b/datadog/fwprovider/resource_datadog_csm_threats_agent_rule.go @@ -2,7 +2,6 @@ package fwprovider import ( "context" - "sync" "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" "github.com/hashicorp/terraform-plugin-framework/path" @@ -17,9 +16,8 @@ import ( ) var ( - csmThreatsMutex sync.Mutex - _ resource.ResourceWithConfigure = &csmThreatsAgentRuleResource{} - _ resource.ResourceWithImportState = &csmThreatsAgentRuleResource{} + _ resource.ResourceWithConfigure = &csmThreatsAgentRuleResource{} + _ resource.ResourceWithImportState = &csmThreatsAgentRuleResource{} ) type csmThreatsAgentRuleModel struct { diff --git a/datadog/fwprovider/resource_datadog_csm_threats_multi_policy_agent_rule.go b/datadog/fwprovider/resource_datadog_csm_threats_multi_policy_agent_rule.go new file mode 100644 index 0000000000..4e724823d9 --- /dev/null +++ b/datadog/fwprovider/resource_datadog_csm_threats_multi_policy_agent_rule.go @@ -0,0 +1,272 @@ +package fwprovider + +import ( + "context" + "strings" + "sync" + + "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/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" +) + +var ( + csmThreatsMutex sync.Mutex + _ resource.ResourceWithConfigure = &csmThreatsMultiPolicyAgentRuleResource{} + _ resource.ResourceWithImportState = &csmThreatsMultiPolicyAgentRuleResource{} +) + +type csmThreatsMultiPolicyAgentRuleResource struct { + api *datadogV2.CSMThreatsApi + auth context.Context +} + +type csmThreatsMultiPolicyAgentRuleModel struct { + Id types.String `tfsdk:"id"` + PolicyId types.String `tfsdk:"policy_id"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + Enabled types.Bool `tfsdk:"enabled"` + Expression types.String `tfsdk:"expression"` + ProductTags types.Set `tfsdk:"product_tags"` +} + +func NewCSMThreatsMultiPolicyAgentRuleResource() resource.Resource { + return &csmThreatsMultiPolicyAgentRuleResource{} +} + +func (r *csmThreatsMultiPolicyAgentRuleResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "csm_threats_multi_policy_agent_rule" +} + +func (r *csmThreatsMultiPolicyAgentRuleResource) Configure(_ context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) { + providerData := request.ProviderData.(*FrameworkProvider) + r.api = providerData.DatadogApiInstances.GetCSMThreatsApiV2() + r.auth = providerData.Auth +} + +func (r *csmThreatsMultiPolicyAgentRuleResource) 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(), + "policy_id": schema.StringAttribute{ + Required: true, + Description: "The ID of the agent policy in which the rule is saved", + }, + "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.", + 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(), + }, + }, + "product_tags": schema.SetAttribute{ + Optional: true, + ElementType: types.StringType, + Description: "The list of product tags associated with the rule", + Computed: true, + }, + }, + } +} + +func (r *csmThreatsMultiPolicyAgentRuleResource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { + result := strings.SplitN(request.ID, ":", 2) + if len(result) != 2 { + response.Diagnostics.AddError("error retrieving policy_id or rule_id from given ID", "") + return + } + + response.Diagnostics.Append(response.State.SetAttribute(ctx, path.Root("policy_id"), result[0])...) + response.Diagnostics.Append(response.State.SetAttribute(ctx, path.Root("id"), result[1])...) +} + +func (r *csmThreatsMultiPolicyAgentRuleResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var state csmThreatsMultiPolicyAgentRuleModel + 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 *csmThreatsMultiPolicyAgentRuleResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var state csmThreatsMultiPolicyAgentRuleModel + response.Diagnostics.Append(request.State.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + agentRuleId := state.Id.ValueString() + policyId := state.PolicyId.ValueString() + res, httpResponse, err := r.api.GetCSMThreatsAgentRule(r.auth, agentRuleId, *datadogV2.NewGetCSMThreatsAgentRuleOptionalParameters().WithPolicyId(policyId)) + 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 *csmThreatsMultiPolicyAgentRuleResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + var state csmThreatsMultiPolicyAgentRuleModel + 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 *csmThreatsMultiPolicyAgentRuleResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + var state csmThreatsMultiPolicyAgentRuleModel + response.Diagnostics.Append(request.State.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + id := state.Id.ValueString() + policyId := state.PolicyId.ValueString() + httpResp, err := r.api.DeleteCSMThreatsAgentRule(r.auth, id, *datadogV2.NewDeleteCSMThreatsAgentRuleOptionalParameters().WithPolicyId(policyId)) + if err != nil { + if httpResp != nil && httpResp.StatusCode == 404 { + return + } + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error deleting agent rule")) + return + } +} + +func (r *csmThreatsMultiPolicyAgentRuleResource) buildCreateCSMThreatsAgentRulePayload(state *csmThreatsMultiPolicyAgentRuleModel) (*datadogV2.CloudWorkloadSecurityAgentRuleCreateRequest, error) { + _, policyId, name, description, enabled, expression, productTags := r.extractAgentRuleAttributesFromResource(state) + + attributes := datadogV2.CloudWorkloadSecurityAgentRuleCreateAttributes{} + attributes.Expression = expression + attributes.Name = name + attributes.Description = description + attributes.Enabled = &enabled + attributes.PolicyId = &policyId + attributes.ProductTags = productTags + + data := datadogV2.NewCloudWorkloadSecurityAgentRuleCreateData(attributes, datadogV2.CLOUDWORKLOADSECURITYAGENTRULETYPE_AGENT_RULE) + return datadogV2.NewCloudWorkloadSecurityAgentRuleCreateRequest(*data), nil +} + +func (r *csmThreatsMultiPolicyAgentRuleResource) buildUpdateCSMThreatsAgentRulePayload(state *csmThreatsMultiPolicyAgentRuleModel) (*datadogV2.CloudWorkloadSecurityAgentRuleUpdateRequest, error) { + agentRuleId, policyId, _, description, enabled, _, productTags := r.extractAgentRuleAttributesFromResource(state) + + attributes := datadogV2.CloudWorkloadSecurityAgentRuleUpdateAttributes{} + attributes.Description = description + attributes.Enabled = &enabled + attributes.PolicyId = &policyId + attributes.ProductTags = productTags + + data := datadogV2.NewCloudWorkloadSecurityAgentRuleUpdateData(attributes, datadogV2.CLOUDWORKLOADSECURITYAGENTRULETYPE_AGENT_RULE) + data.Id = &agentRuleId + return datadogV2.NewCloudWorkloadSecurityAgentRuleUpdateRequest(*data), nil +} + +func (r *csmThreatsMultiPolicyAgentRuleResource) extractAgentRuleAttributesFromResource(state *csmThreatsMultiPolicyAgentRuleModel) (string, string, string, *string, bool, string, []string) { + // Mandatory fields + id := state.Id.ValueString() + policyId := state.PolicyId.ValueString() + name := state.Name.ValueString() + enabled := state.Enabled.ValueBool() + expression := state.Expression.ValueString() + description := state.Description.ValueStringPointer() + var productTags []string + if !state.ProductTags.IsNull() && !state.ProductTags.IsUnknown() { + for _, tag := range state.ProductTags.Elements() { + tagStr, ok := tag.(types.String) + if !ok { + return "", "", "", nil, false, "", nil + } + productTags = append(productTags, tagStr.ValueString()) + } + } + + return id, policyId, name, description, enabled, expression, productTags +} + +func (r *csmThreatsMultiPolicyAgentRuleResource) updateStateFromResponse(ctx context.Context, state *csmThreatsMultiPolicyAgentRuleModel, 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()) + tags := attributes.GetProductTags() + if len(tags) == 0 && state.ProductTags.IsNull() { + state.ProductTags = types.SetNull(types.StringType) + } else { + state.ProductTags, _ = types.SetValueFrom(ctx, types.StringType, tags) + } + +} diff --git a/datadog/fwprovider/resource_datadog_csm_threats_policy.go b/datadog/fwprovider/resource_datadog_csm_threats_policy.go new file mode 100644 index 0000000000..8ca301d792 --- /dev/null +++ b/datadog/fwprovider/resource_datadog_csm_threats_policy.go @@ -0,0 +1,277 @@ +package fwprovider + +import ( + "context" + "fmt" + + "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/booldefault" + "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" +) + +type csmThreatsPolicyModel struct { + Id types.String `tfsdk:"id"` + Tags types.Set `tfsdk:"tags"` + HostTagsLists types.Set `tfsdk:"host_tags_lists"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + Enabled types.Bool `tfsdk:"enabled"` +} + +type csmThreatsPolicyResource struct { + api *datadogV2.CSMThreatsApi + auth context.Context +} + +func NewCSMThreatsPolicyResource() resource.Resource { + return &csmThreatsPolicyResource{} +} + +func (r *csmThreatsPolicyResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "csm_threats_policy" +} + +func (r *csmThreatsPolicyResource) Configure(_ context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) { + providerData := request.ProviderData.(*FrameworkProvider) + r.api = providerData.DatadogApiInstances.GetCSMThreatsApiV2() + r.auth = providerData.Auth +} + +func (r *csmThreatsPolicyResource) Schema(_ context.Context, _ resource.SchemaRequest, response *resource.SchemaResponse) { + response.Schema = schema.Schema{ + Description: "Provides a Datadog CSM Threats policy API resource.", + Attributes: map[string]schema.Attribute{ + "id": utils.ResourceIDAttribute(), + "name": schema.StringAttribute{ + Required: true, + Description: "The name of the policy.", + }, + "description": schema.StringAttribute{ + Optional: true, + Description: "A description for the policy.", + Computed: true, + }, + "enabled": schema.BoolAttribute{ + Optional: true, + Default: booldefault.StaticBool(false), + Description: "Indicates whether the policy is enabled.", + Computed: true, + }, + "tags": schema.SetAttribute{ + Optional: true, + Description: "Host tags that define where the policy is deployed. Deprecated, use host_tags_lists instead.", + ElementType: types.StringType, + Computed: true, + }, + "host_tags_lists": schema.SetAttribute{ + Optional: true, + Description: "Host tags that define where the policy is deployed. Inner values are ANDed, outer arrays are ORed.", + ElementType: types.ListType{ + ElemType: types.StringType, + }, + }, + }, + } +} + +func (r *csmThreatsPolicyResource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), request, response) +} + +func (r *csmThreatsPolicyResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var state csmThreatsPolicyModel + response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + csmThreatsMutex.Lock() + defer csmThreatsMutex.Unlock() + + policyPayload, err := r.buildCreateCSMThreatsPolicyPayload(&state) + if err != nil { + response.Diagnostics.AddError("error while parsing resource", err.Error()) + } + + res, _, err := r.api.CreateCSMThreatsAgentPolicy(r.auth, *policyPayload) + if err != nil { + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error creating policy")) + 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 *csmThreatsPolicyResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var state csmThreatsPolicyModel + response.Diagnostics.Append(request.State.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + policyId := state.Id.ValueString() + res, httpResponse, err := r.api.GetCSMThreatsAgentPolicy(r.auth, policyId) + if err != nil { + if httpResponse != nil && httpResponse.StatusCode == 404 { + response.State.RemoveResource(ctx) + return + } + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error fetching agent policy")) + 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 *csmThreatsPolicyResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + var state csmThreatsPolicyModel + response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + csmThreatsMutex.Lock() + defer csmThreatsMutex.Unlock() + + policyPayload, err := r.buildUpdateCSMThreatsPolicyPayload(&state) + if err != nil { + response.Diagnostics.AddError("error while parsing resource", err.Error()) + } + + res, _, err := r.api.UpdateCSMThreatsAgentPolicy(r.auth, state.Id.ValueString(), *policyPayload) + 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 *csmThreatsPolicyResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + var state csmThreatsPolicyModel + response.Diagnostics.Append(request.State.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + csmThreatsMutex.Lock() + defer csmThreatsMutex.Unlock() + + id := state.Id.ValueString() + + httpResp, err := r.api.DeleteCSMThreatsAgentPolicy(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 *csmThreatsPolicyResource) buildCreateCSMThreatsPolicyPayload(state *csmThreatsPolicyModel) (*datadogV2.CloudWorkloadSecurityAgentPolicyCreateRequest, error) { + _, name, description, enabled, tags, hostTagsLists, err := r.extractPolicyAttributesFromResource(state) + if err != nil { + return nil, err + } + + attributes := datadogV2.CloudWorkloadSecurityAgentPolicyCreateAttributes{} + attributes.Name = name + attributes.Description = description + attributes.Enabled = enabled + attributes.HostTags = tags + attributes.HostTagsLists = hostTagsLists + + data := datadogV2.NewCloudWorkloadSecurityAgentPolicyCreateData(attributes, datadogV2.CLOUDWORKLOADSECURITYAGENTPOLICYTYPE_POLICY) + return datadogV2.NewCloudWorkloadSecurityAgentPolicyCreateRequest(*data), nil +} + +func (r *csmThreatsPolicyResource) buildUpdateCSMThreatsPolicyPayload(state *csmThreatsPolicyModel) (*datadogV2.CloudWorkloadSecurityAgentPolicyUpdateRequest, error) { + policyId, name, description, enabled, tags, hostTagsLists, err := r.extractPolicyAttributesFromResource(state) + if err != nil { + return nil, err + } + attributes := datadogV2.CloudWorkloadSecurityAgentPolicyUpdateAttributes{} + attributes.Name = &name + attributes.Description = description + attributes.Enabled = enabled + attributes.HostTags = tags + attributes.HostTagsLists = hostTagsLists + + data := datadogV2.NewCloudWorkloadSecurityAgentPolicyUpdateData(attributes, datadogV2.CLOUDWORKLOADSECURITYAGENTPOLICYTYPE_POLICY) + data.Id = &policyId + return datadogV2.NewCloudWorkloadSecurityAgentPolicyUpdateRequest(*data), nil +} + +func (r *csmThreatsPolicyResource) extractPolicyAttributesFromResource(state *csmThreatsPolicyModel) (string, string, *string, *bool, []string, [][]string, error) { + // Mandatory fields + id := state.Id.ValueString() + name := state.Name.ValueString() + enabled := state.Enabled.ValueBoolPointer() + description := state.Description.ValueStringPointer() + var tags []string + if !state.Tags.IsNull() && !state.Tags.IsUnknown() { + for _, tag := range state.Tags.Elements() { + tagStr, ok := tag.(types.String) + if !ok { + return "", "", nil, nil, nil, nil, fmt.Errorf("expected item to be of type types.String, got %T", tag) + } + tags = append(tags, tagStr.ValueString()) + } + } + var hostTagsLists [][]string + if !state.HostTagsLists.IsNull() && !state.HostTagsLists.IsUnknown() { + for _, hostTagList := range state.HostTagsLists.Elements() { + hostTagListStr, ok := hostTagList.(types.List) + if !ok { + return "", "", nil, nil, nil, nil, fmt.Errorf("expected item to be of type types.List, got %T", hostTagList) + } + var tags []string + for _, hostTag := range hostTagListStr.Elements() { + hostTagStr, ok := hostTag.(types.String) + if !ok { + return "", "", nil, nil, nil, nil, fmt.Errorf("expected item to be of type types.String, got %T", hostTag) + } + tags = append(tags, hostTagStr.ValueString()) + } + hostTagsLists = append(hostTagsLists, tags) + } + } + return id, name, description, enabled, tags, hostTagsLists, nil +} + +func (r *csmThreatsPolicyResource) updateStateFromResponse(ctx context.Context, state *csmThreatsPolicyModel, res *datadogV2.CloudWorkloadSecurityAgentPolicyResponse) { + 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.Tags, _ = types.SetValueFrom(ctx, types.StringType, attributes.GetHostTags()) + state.HostTagsLists, _ = types.SetValueFrom(ctx, types.ListType{ + ElemType: types.ListType{ + ElemType: types.StringType, + }, + }, attributes.GetHostTagsLists()) +} diff --git a/datadog/provider.go b/datadog/provider.go index 37839ef108..e0d30cfa02 100644 --- a/datadog/provider.go +++ b/datadog/provider.go @@ -222,7 +222,6 @@ func Provider() *schema.Provider { // NEW DATA SOURCES ARE NOT ALLOWED TO BE ADDED HERE // New data sources must be implemented using the `terraform-plugin-framework`, and added to the `datadog/fwprovider` directory. DataSourcesMap: map[string]*schema.Resource{ - "datadog_cloud_workload_security_agent_rules": dataSourceDatadogCloudWorkloadSecurityAgentRules(), "datadog_dashboard": dataSourceDatadogDashboard(), "datadog_integration_aws_logs_services": dataSourceDatadogIntegrationAWSLogsServices(), "datadog_logs_archives_order": dataSourceDatadogLogsArchivesOrder(), diff --git a/datadog/tests/data_source_datadog_cloud_workload_security_agent_rules_test.go b/datadog/tests/data_source_datadog_cloud_workload_security_agent_rules_test.go deleted file mode 100644 index 1e0a6a1fe0..0000000000 --- a/datadog/tests/data_source_datadog_cloud_workload_security_agent_rules_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package test - -import ( - "context" - "fmt" - "strconv" - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-plugin-testing/terraform" - - "github.com/terraform-providers/terraform-provider-datadog/datadog" -) - -const tfAgentRulesSource = "data.datadog_cloud_workload_security_agent_rules.acceptance_test" - -func TestAccDatadogCloudWorkloadSecurityAgentRulesDatasource(t *testing.T) { - t.Parallel() - _, accProviders := testAccProviders(context.Background(), t) - accProvider := testAccProvider(t, accProviders) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProviderFactories: accProviders, - Steps: []resource.TestStep{ - { - Config: testAccDataSourceCloudWorkloadSecurityAgentRules(), - Check: resource.ComposeTestCheckFunc( - cloudWorkloadSecurityCheckAgentRulesCount(accProvider), - ), - }, - }, - }) -} - -func cloudWorkloadSecurityCheckAgentRulesCount(accProvider func() (*schema.Provider, error)) func(state *terraform.State) error { - return func(state *terraform.State) error { - provider, _ := accProvider() - providerConf := provider.Meta().(*datadog.ProviderConfiguration) - auth := providerConf.Auth - apiInstances := providerConf.DatadogApiInstances - - agentRulesResponse, _, err := apiInstances.GetCSMThreatsApiV2().ListCloudWorkloadSecurityAgentRules(auth) - if err != nil { - return err - } - return cloudWorkloadSecurityAgentRulesCount(state, len(agentRulesResponse.Data)) - } -} - -func cloudWorkloadSecurityAgentRulesCount(state *terraform.State, responseCount int) error { - resourceAttributes := state.RootModule().Resources[tfAgentRulesSource].Primary.Attributes - agentRulesCount, _ := strconv.Atoi(resourceAttributes["agent_rules.#"]) - - if agentRulesCount != responseCount { - return fmt.Errorf("expected %d agent rules got %d agent rules", - responseCount, agentRulesCount) - } - return nil -} - -func testAccDataSourceCloudWorkloadSecurityAgentRules() string { - return ` -data "datadog_cloud_workload_security_agent_rules" "acceptance_test" { -} -` -} diff --git a/datadog/tests/data_source_datadog_csm_threats_agent_rules_test.go b/datadog/tests/data_source_datadog_csm_threats_agent_rule_test.go similarity index 100% rename from datadog/tests/data_source_datadog_csm_threats_agent_rules_test.go rename to datadog/tests/data_source_datadog_csm_threats_agent_rule_test.go diff --git a/datadog/tests/data_source_datadog_csm_threats_multi_policy_agent_rules_test.go b/datadog/tests/data_source_datadog_csm_threats_multi_policy_agent_rules_test.go new file mode 100644 index 0000000000..16cc6d08f3 --- /dev/null +++ b/datadog/tests/data_source_datadog_csm_threats_multi_policy_agent_rules_test.go @@ -0,0 +1,153 @@ +package test + +import ( + "context" + "fmt" + "strconv" + "testing" + + "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + + "github.com/terraform-providers/terraform-provider-datadog/datadog/fwprovider" +) + +func TestAccCSMThreatsMultiPolicyAgentRulesDataSource(t *testing.T) { + t.Parallel() + ctx, providers, accProviders := testAccFrameworkMuxProviders(context.Background(), t) + + policyName := uniqueAgentRuleName(ctx) + policyConfig := fmt.Sprintf(` + resource "datadog_csm_threats_policy" "policy_for_test" { + name = "%s" + enabled = true + description = "im a policy" + tags = ["host_name:test_host"] + } + `, policyName) + + agentRuleName := uniqueAgentRuleName(ctx) + agentRuleConfig := fmt.Sprintf(` + %s + resource "datadog_csm_threats_multi_policy_agent_rule" "agent_rule_for_data_source_test" { + name = "%s" + policy_id = datadog_csm_threats_policy.policy_for_test.id + enabled = true + description = "im a rule" + expression = "open.file.name == \"etc/shadow/password\"" + product_tags = ["compliance_framework:PCI-DSS"] + } + `, policyConfig, agentRuleName) + dataSourceName := "data.datadog_csm_threats_multi_policy_agent_rules.my_data_source" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: accProviders, + CheckDestroy: testAccCheckCSMThreatsMultiPolicyAgentRuleDestroy(providers.frameworkProvider), + Steps: []resource.TestStep{ + { + // Create an agent rule to have at least one + Config: agentRuleConfig, + Check: testAccCheckCSMThreatsMultiPolicyAgentRuleExists(providers.frameworkProvider, "datadog_csm_threats_multi_policy_agent_rule.agent_rule_for_data_source_test"), + }, + { + Config: fmt.Sprintf(` + %s + data "datadog_csm_threats_multi_policy_agent_rules" "my_data_source" { + policy_id = datadog_csm_threats_policy.policy_for_test.id + } + `, agentRuleConfig), + Check: checkCSMThreatsMultiPolicyAgentRulesDataSourceContent(providers.frameworkProvider, dataSourceName, agentRuleName), + }, + }, + }) +} + +func testAccCheckDatadogCSMThreatsMultiPolicyAgentRulesDataSourceConfig(policyName, agentRuleName string) string { + return fmt.Sprintf(` +data "datadog_csm_threats_multi_policy_agent_rules" "my_data_source" { + policy_id = datadog_csm_threats_policy.policy.id +} + +resource "datadog_csm_threats_policy" "policy" { + name = "%s" + enabled = true + description = "Test description" + tags = ["host_name:test_host"] +} + +resource "datadog_csm_threats_multi_policy_agent_rule" "agent_rule" { + name = "%s" + description = "Test description" + enabled = true + expression = "open.file.name == \"etc/shadow/password\"" + policy_id = datadog_csm_threats_policy.policy.id + product_tags = ["compliance_framework:PCI-DSS"] +} +`, policyName, agentRuleName) +} + +func checkCSMThreatsMultiPolicyAgentRulesDataSourceContent(accProvider *fwprovider.FrameworkProvider, dataSourceName string, agentRuleName string) resource.TestCheckFunc { + return func(state *terraform.State) error { + res, ok := state.RootModule().Resources[dataSourceName] + if !ok { + return fmt.Errorf("resource missing from state: %s", dataSourceName) + } + + auth := accProvider.Auth + apiInstances := accProvider.DatadogApiInstances + + policyId := res.Primary.Attributes["policy_id"] + allAgentRulesResponse, _, err := apiInstances.GetCSMThreatsApiV2().ListCSMThreatsAgentRules(auth, *datadogV2.NewListCSMThreatsAgentRulesOptionalParameters().WithPolicyId(policyId)) + if err != nil { + return err + } + + // Check the agentRule we created is in the API response + agentRuleId := "" + ruleName := "" + for _, rule := range allAgentRulesResponse.GetData() { + if rule.Attributes.GetName() == agentRuleName { + agentRuleId = rule.GetId() + ruleName = rule.Attributes.GetName() + break + } + } + if agentRuleId == "" { + return fmt.Errorf("agent rule with name '%s' not found in API responses", agentRuleName) + } + + // Check that the data_source fetched is correct + resourceAttributes := res.Primary.Attributes + agentRulesIdsCount, err := strconv.Atoi(resourceAttributes["agent_rules_ids.#"]) + if err != nil { + return err + } + agentRulesCount, err := strconv.Atoi(resourceAttributes["agent_rules.#"]) + if err != nil { + return err + } + if agentRulesCount != agentRulesIdsCount { + return fmt.Errorf("the data source contains %d agent rules IDs but %d agent rules", agentRulesIdsCount, agentRulesCount) + } + + // Find in which position is the agent rule we created, and check its values + idx := 0 + for idx < agentRulesIdsCount && resourceAttributes[fmt.Sprintf("agent_rules_ids.%d", idx)] != agentRuleId { + idx++ + } + if idx == len(resourceAttributes) { + return fmt.Errorf("agent rule with ID '%s' not found in data source", agentRuleId) + } + + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, fmt.Sprintf("agent_rules.%d.name", idx), ruleName), + resource.TestCheckResourceAttr(dataSourceName, fmt.Sprintf("agent_rules.%d.enabled", idx), "true"), + resource.TestCheckResourceAttr(dataSourceName, fmt.Sprintf("agent_rules.%d.description", idx), "im a rule"), + resource.TestCheckResourceAttr(dataSourceName, fmt.Sprintf("agent_rules.%d.expression", idx), "open.file.name == \"etc/shadow/password\""), + resource.TestCheckResourceAttr(dataSourceName, fmt.Sprintf("agent_rules.%d.product_tags.#", idx), "1"), + resource.TestCheckTypeSetElemAttr(dataSourceName, fmt.Sprintf("agent_rules.%d.product_tags.*", idx), "compliance_framework:PCI-DSS"), + )(state) + } +} diff --git a/datadog/tests/data_source_datadog_csm_threats_policies_test.go b/datadog/tests/data_source_datadog_csm_threats_policies_test.go new file mode 100644 index 0000000000..f9bed4ab41 --- /dev/null +++ b/datadog/tests/data_source_datadog_csm_threats_policies_test.go @@ -0,0 +1,112 @@ +package test + +import ( + "context" + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + + "github.com/terraform-providers/terraform-provider-datadog/datadog/fwprovider" +) + +func TestAccCSMThreatsPoliciesDataSource(t *testing.T) { + ctx, providers, accProviders := testAccFrameworkMuxProviders(context.Background(), t) + + policyName := uniqueAgentRuleName(ctx) + dataSourceName := "data.datadog_csm_threats_policies.my_data_source" + policyConfig := fmt.Sprintf(` + resource "datadog_csm_threats_policy" "policy_for_data_source_test" { + name = "%s" + enabled = true + description = "im a policy" + tags = ["host_name:test_host"] + host_tags_lists = [["host_name:test_host", "env:prod"], ["host_name:test_host2", "env:staging"]] + } + `, policyName) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: accProviders, + CheckDestroy: testAccCheckCSMThreatsPolicyDestroy(providers.frameworkProvider), + Steps: []resource.TestStep{ + { + // Create a policy to have at least one + Config: policyConfig, + Check: testAccCheckCSMThreatsPolicyExists(providers.frameworkProvider, "datadog_csm_threats_policy.policy_for_data_source_test"), + }, + { + Config: fmt.Sprintf(` + %s + data "datadog_csm_threats_policies" "my_data_source" {} + `, policyConfig), + Check: checkCSMThreatsPoliciesDataSourceContent(providers.frameworkProvider, dataSourceName, policyName), + }, + }, + }) +} + +func checkCSMThreatsPoliciesDataSourceContent(accProvider *fwprovider.FrameworkProvider, dataSourceName string, policyName string) resource.TestCheckFunc { + return func(state *terraform.State) error { + res, ok := state.RootModule().Resources[dataSourceName] + if !ok { + return fmt.Errorf("resource missing from state: %s", dataSourceName) + } + + auth := accProvider.Auth + apiInstances := accProvider.DatadogApiInstances + + allPoliciesResponse, _, err := apiInstances.GetCSMThreatsApiV2().ListCSMThreatsAgentPolicies(auth) + if err != nil { + return err + } + + // Check the policy we created is in the API response + resPolicyId := "" + for _, policy := range allPoliciesResponse.GetData() { + if policy.Attributes.GetName() == policyName { + resPolicyId = policy.GetId() + break + } + } + if resPolicyId == "" { + return fmt.Errorf("policy with name '%s' not found in API responses", policyName) + } + + // Check that the data_source fetched is correct + resourceAttributes := res.Primary.Attributes + policyIdsCount, err := strconv.Atoi(resourceAttributes["policy_ids.#"]) + if err != nil { + return err + } + policiesCount, err := strconv.Atoi(resourceAttributes["policies.#"]) + if err != nil { + return err + } + if policiesCount != policyIdsCount { + return fmt.Errorf("the data source contains %d policy IDs but %d policies", policyIdsCount, policiesCount) + } + + // Find in which position is the policy we created, and check its values + idx := 0 + for idx < policyIdsCount && resourceAttributes[fmt.Sprintf("policy_ids.%d", idx)] != resPolicyId { + idx++ + } + if idx == len(resourceAttributes) { + return fmt.Errorf("policy with ID '%s' not found in data source", resPolicyId) + } + + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, fmt.Sprintf("policies.%d.name", idx), policyName), + resource.TestCheckResourceAttr(dataSourceName, fmt.Sprintf("policies.%d.enabled", idx), "true"), + resource.TestCheckResourceAttr(dataSourceName, fmt.Sprintf("policies.%d.tags.0", idx), "host_name:test_host"), + resource.TestCheckResourceAttr(dataSourceName, fmt.Sprintf("policies.%d.host_tags_lists.0.0", idx), "host_name:test_host"), + resource.TestCheckResourceAttr(dataSourceName, fmt.Sprintf("policies.%d.host_tags_lists.0.1", idx), "env:prod"), + resource.TestCheckResourceAttr(dataSourceName, fmt.Sprintf("policies.%d.host_tags_lists.1.0", idx), "host_name:test_host2"), + resource.TestCheckResourceAttr(dataSourceName, fmt.Sprintf("policies.%d.host_tags_lists.1.1", idx), "env:staging"), + resource.TestCheckResourceAttr(dataSourceName, fmt.Sprintf("policies.%d.description", idx), "im a policy"), + )(state) + } +} diff --git a/datadog/tests/provider_test.go b/datadog/tests/provider_test.go index b334d9a20a..1abbec9977 100644 --- a/datadog/tests/provider_test.go +++ b/datadog/tests/provider_test.go @@ -58,7 +58,10 @@ var testFiles2EndpointTags = map[string]string{ "tests/data_source_datadog_application_key_test": "application_keys", "tests/data_source_datadog_cloud_workload_security_agent_rules_test": "cloud-workload-security", "tests/data_source_datadog_action_connection_test": "action_connection", + "tests/data_source_datadog_csm_threats_agent_rule_test": "cloud-workload-security", "tests/data_source_datadog_csm_threats_agent_rules_test": "cloud-workload-security", + "tests/data_source_datadog_csm_threats_multi_policy_agent_rules_test": "cloud-workload-security", + "tests/data_source_datadog_csm_threats_policies_test": "cloud-workload-security", "tests/data_source_datadog_dashboard_list_test": "dashboard-lists", "tests/data_source_datadog_dashboard_test": "dashboard", "tests/data_source_datadog_hosts_test": "hosts", @@ -119,6 +122,9 @@ var testFiles2EndpointTags = map[string]string{ "tests/resource_datadog_cloud_workload_security_agent_rule_test": "cloud_workload_security", "tests/resource_datadog_action_connection_test": "action_connection", "tests/resource_datadog_csm_threats_agent_rule_test": "cloud-workload-security", + "tests/resource_datadog_csm_threats_multi_policy_agent_rule_test": "cloud-workload-security", + "tests/resource_datadog_csm_threats_policy_test": "cloud-workload-security", + "tests/resource_datadog_csm_threats_policies_list_test": "cloud-workload-security", "tests/resource_datadog_dashboard_alert_graph_test": "dashboards", "tests/resource_datadog_dashboard_alert_value_test": "dashboards", "tests/resource_datadog_dashboard_change_test": "dashboards", diff --git a/datadog/tests/resource_datadog_cloud_workload_security_agent_rule_test.go b/datadog/tests/resource_datadog_cloud_workload_security_agent_rule_test.go deleted file mode 100644 index 9ac3ce0b7c..0000000000 --- a/datadog/tests/resource_datadog_cloud_workload_security_agent_rule_test.go +++ /dev/null @@ -1,130 +0,0 @@ -package test - -import ( - "context" - "fmt" - "strings" - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/hashicorp/terraform-plugin-testing/terraform" - - "github.com/terraform-providers/terraform-provider-datadog/datadog" -) - -const tfAgentRuleName = "datadog_cloud_workload_security_agent_rule.acceptance_test" - -func TestAccDatadogCloudWorkloadSecurityAgentRule(t *testing.T) { - t.Parallel() - ctx, accProviders := testAccProviders(context.Background(), t) - agentRuleName := strings.Replace(uniqueEntityName(ctx, t), "-", "_", -1) - accProvider := testAccProvider(t, accProviders) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProviderFactories: accProviders, - CheckDestroy: testAccCheckDatadogCloudWorkloadSecurityAgentRuleDestroy(accProvider), - Steps: []resource.TestStep{ - { - Config: testAccCheckDatadogCloudWorkloadSecurityAgentRuleCreated(agentRuleName), - Check: testAccCheckDatadogCloudWorkloadSecurityAgentRuleCreatedCheck(accProvider, agentRuleName), - }, - { - Config: testAccCheckDatadogCloudWorkloadSecurityAgentRuleUpdated(agentRuleName), - Check: testAccCheckDatadogCloudWorkloadSecurityAgentRuleUpdatedCheck(accProvider, agentRuleName), - }, - }, - }) -} - -func testAccCheckDatadogCloudWorkloadSecurityAgentRuleCreated(name string) string { - return fmt.Sprintf(` -resource "datadog_cloud_workload_security_agent_rule" "acceptance_test" { - name = "%s" - description = "an agent rule" - enabled = "true" - expression = "exec.file.name == \"java\"" -} -`, name) -} - -func testAccCheckDatadogCloudWorkloadSecurityAgentRuleCreatedCheck(accProvider func() (*schema.Provider, error), agentRuleName string) resource.TestCheckFunc { - return resource.ComposeTestCheckFunc( - testAccCheckDatadogCloudWorkloadSecurityAgentRuleExists(accProvider), - resource.TestCheckResourceAttr( - tfAgentRuleName, "name", agentRuleName), - resource.TestCheckResourceAttr( - tfAgentRuleName, "description", "an agent rule"), - resource.TestCheckResourceAttr( - tfAgentRuleName, "enabled", "true"), - resource.TestCheckResourceAttr( - tfAgentRuleName, "expression", "exec.file.name == \"java\""), - ) -} - -func testAccCheckDatadogCloudWorkloadSecurityAgentRuleUpdated(name string) string { - return fmt.Sprintf(` -resource "datadog_cloud_workload_security_agent_rule" "acceptance_test" { - name = "%s" - description = "a new agent rule" - enabled = "false" - expression = "exec.file.name == \"go\"" -} -`, name) -} - -func testAccCheckDatadogCloudWorkloadSecurityAgentRuleUpdatedCheck(accProvider func() (*schema.Provider, error), agentRuleName string) resource.TestCheckFunc { - return resource.ComposeTestCheckFunc( - testAccCheckDatadogCloudWorkloadSecurityAgentRuleExists(accProvider), - resource.TestCheckResourceAttr( - tfAgentRuleName, "name", agentRuleName), - resource.TestCheckResourceAttr( - tfAgentRuleName, "description", "a new agent rule"), - resource.TestCheckResourceAttr( - tfAgentRuleName, "enabled", "false"), - resource.TestCheckResourceAttr( - tfAgentRuleName, "expression", "exec.file.name == \"go\""), - ) -} - -func testAccCheckDatadogCloudWorkloadSecurityAgentRuleExists(accProvider func() (*schema.Provider, error)) resource.TestCheckFunc { - return func(s *terraform.State) error { - provider, _ := accProvider() - providerConf := provider.Meta().(*datadog.ProviderConfiguration) - auth := providerConf.Auth - apiInstances := providerConf.DatadogApiInstances - - for _, agentRule := range s.RootModule().Resources { - _, _, err := apiInstances.GetCSMThreatsApiV2().GetCloudWorkloadSecurityAgentRule(auth, agentRule.Primary.ID) - if err != nil { - return fmt.Errorf("received an error retrieving cloud workload security agent rule: %s", err) - } - } - return nil - } -} - -func testAccCheckDatadogCloudWorkloadSecurityAgentRuleDestroy(accProvider func() (*schema.Provider, error)) resource.TestCheckFunc { - return func(s *terraform.State) error { - provider, _ := accProvider() - providerConf := provider.Meta().(*datadog.ProviderConfiguration) - auth := providerConf.Auth - apiInstances := providerConf.DatadogApiInstances - - for _, resource := range s.RootModule().Resources { - if resource.Type == "datadog_cloud_workload_security_agent_rule" { - _, httpResponse, err := apiInstances.GetCSMThreatsApiV2().GetCloudWorkloadSecurityAgentRule(auth, resource.Primary.ID) - if err != nil { - if httpResponse != nil && httpResponse.StatusCode == 404 { - continue - } - return fmt.Errorf("received an error deleting cloud workload security agent rule: %s", err) - } - return fmt.Errorf("cloud workload security agent rule still exists") - } - } - return nil - } - -} diff --git a/datadog/tests/resource_datadog_csm_threats_multi_policy_agent_rule_test.go b/datadog/tests/resource_datadog_csm_threats_multi_policy_agent_rule_test.go new file mode 100644 index 0000000000..120c2fc667 --- /dev/null +++ b/datadog/tests/resource_datadog_csm_threats_multi_policy_agent_rule_test.go @@ -0,0 +1,149 @@ +package test + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + + "github.com/terraform-providers/terraform-provider-datadog/datadog/fwprovider" +) + +// Create an agent rule and update its description +func TestAccCSMThreatsMultiPolicyAgentRule_CreateAndUpdate(t *testing.T) { + ctx, providers, accProviders := testAccFrameworkMuxProviders(context.Background(), t) + + agentRuleName := uniqueAgentRuleName(ctx) + resourceName := "datadog_csm_threats_multi_policy_agent_rule.agent_rule_test" + + policyName := uniqueAgentRuleName(ctx) + policyConfig := fmt.Sprintf(` + resource "datadog_csm_threats_policy" "policy_for_test" { + name = "%s" + enabled = true + description = "im a policy" + tags = ["host_name:test_host"] + } + `, policyName) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: accProviders, + CheckDestroy: testAccCheckCSMThreatsMultiPolicyAgentRuleDestroy(providers.frameworkProvider), + Steps: []resource.TestStep{ + { + // Create a policy to have at least one + Config: policyConfig, + Check: testAccCheckCSMThreatsPolicyExists(providers.frameworkProvider, "datadog_csm_threats_policy.policy_for_test"), + }, + { + Config: fmt.Sprintf(` + %s + resource "datadog_csm_threats_multi_policy_agent_rule" "agent_rule_test" { + name = "%s" + policy_id = datadog_csm_threats_policy.policy_for_test.id + enabled = true + description = "im a rule" + expression = "open.file.name == \"etc/shadow/password\"" + product_tags = ["compliance_framework:PCI-DSS"] + } + `, policyConfig, agentRuleName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCSMThreatsMultiPolicyAgentRuleExists(providers.frameworkProvider, resourceName), + checkCSMThreatsAgentRuleContentMultiPolicy( + resourceName, + agentRuleName, + "im a rule", + "open.file.name == \"etc/shadow/password\"", + "compliance_framework:PCI-DSS", + ), + ), + }, + // Update description + { + Config: fmt.Sprintf(` + %s + resource "datadog_csm_threats_multi_policy_agent_rule" "agent_rule_test" { + name = "%s" + policy_id = datadog_csm_threats_policy.policy_for_test.id + enabled = true + description = "updated agent rule for terraform provider test" + expression = "open.file.name == \"etc/shadow/password\"" + product_tags = ["compliance_framework:ISO-27799"] + } + `, policyConfig, agentRuleName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCSMThreatsMultiPolicyAgentRuleExists(providers.frameworkProvider, resourceName), + checkCSMThreatsAgentRuleContentMultiPolicy( + resourceName, + agentRuleName, + "updated agent rule for terraform provider test", + "open.file.name == \"etc/shadow/password\"", + "compliance_framework:ISO-27799", + ), + ), + }, + }, + }) +} + +func testAccCheckCSMThreatsMultiPolicyAgentRuleExists(accProvider *fwprovider.FrameworkProvider, resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + resource, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("resource '%s' not found in the state %s", resourceName, s.RootModule().Resources) + } + + if resource.Type != "datadog_csm_threats_multi_policy_agent_rule" { + return fmt.Errorf("resource %s is not of type datadog_csm_threats_multi_policy_agent_rule, found %s instead", resourceName, resource.Type) + } + + auth := accProvider.Auth + apiInstances := accProvider.DatadogApiInstances + + policyId := resource.Primary.Attributes["policy_id"] + _, _, err := apiInstances.GetCSMThreatsApiV2().GetCSMThreatsAgentRule(auth, resource.Primary.ID, *datadogV2.NewGetCSMThreatsAgentRuleOptionalParameters().WithPolicyId(policyId)) + if err != nil { + return fmt.Errorf("received an error retrieving agent rule: %s", err) + } + + return nil + } +} + +func testAccCheckCSMThreatsMultiPolicyAgentRuleDestroy(accProvider *fwprovider.FrameworkProvider) resource.TestCheckFunc { + return func(s *terraform.State) error { + auth := accProvider.Auth + apiInstances := accProvider.DatadogApiInstances + + for _, resource := range s.RootModule().Resources { + if resource.Type == "datadog_csm_threats_multi_policy_agent_rule" { + policyId := resource.Primary.Attributes["policy_id"] + _, httpResponse, err := apiInstances.GetCSMThreatsApiV2().GetCSMThreatsAgentRule(auth, resource.Primary.ID, *datadogV2.NewGetCSMThreatsAgentRuleOptionalParameters().WithPolicyId(policyId)) + if err == nil { + return errors.New("agent rule still exists") + } + if httpResponse == nil || httpResponse.StatusCode != 404 { + return fmt.Errorf("received an error while getting the agent rule: %s", err) + } + } + } + + return nil + } +} + +func checkCSMThreatsAgentRuleContentMultiPolicy(resourceName string, name string, description string, expression string, product_tags string) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "description", description), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "expression", expression), + resource.TestCheckResourceAttr(resourceName, "product_tags.#", "1"), + resource.TestCheckTypeSetElemAttr(resourceName, "product_tags.*", product_tags), + ) +} diff --git a/datadog/tests/resource_datadog_csm_threats_policy_test.go b/datadog/tests/resource_datadog_csm_threats_policy_test.go new file mode 100644 index 0000000000..4eca2fdd0f --- /dev/null +++ b/datadog/tests/resource_datadog_csm_threats_policy_test.go @@ -0,0 +1,204 @@ +package test + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + + "github.com/terraform-providers/terraform-provider-datadog/datadog/fwprovider" +) + +// Create an agent policy and update its description +func TestAccCSMThreatsPolicy_CreateAndUpdate(t *testing.T) { + ctx, providers, accProviders := testAccFrameworkMuxProviders(context.Background(), t) + + policyName := uniqueAgentRuleName(ctx) + resourceName := "datadog_csm_threats_policy.policy_test" + tags := []string{"host_name:test_host"} + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: accProviders, + CheckDestroy: testAccCheckCSMThreatsPolicyDestroy(providers.frameworkProvider), + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + resource "datadog_csm_threats_policy" "policy_test" { + name = "%s" + enabled = true + description = "im a policy" + tags = ["host_name:test_host"] + } + `, policyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCSMThreatsPolicyExists(providers.frameworkProvider, "datadog_csm_threats_policy.policy_test"), + checkCSMThreatsPolicyContent( + resourceName, + policyName, + "im a policy", + tags, + ), + ), + }, + // Update description + { + Config: fmt.Sprintf(` + resource "datadog_csm_threats_policy" "policy_test" { + name = "%s" + enabled = true + description = "updated policy for terraform provider test" + tags = ["host_name:test_host"] + } + `, policyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCSMThreatsPolicyExists(providers.frameworkProvider, resourceName), + checkCSMThreatsPolicyContent( + resourceName, + policyName, + "updated policy for terraform provider test", + tags, + ), + ), + }, + }, + }) +} + +// Create an agent policy with host_tags_lists and update its description +func TestAccCSMThreatsPolicy_CreateAndUpdateWithHostTagsLists(t *testing.T) { + ctx, providers, accProviders := testAccFrameworkMuxProviders(context.Background(), t) + + policyName := uniqueAgentRuleName(ctx) + resourceName := "datadog_csm_threats_policy.policy_test" + hostTagsLists := [][]string{ + {"host_name:test_host", "env:prod"}, + {"host_name:test_host2", "env:staging"}, + } + updatedHostTagsLists := [][]string{ + {"host_name:test", "env:prod"}, + {"host_name:test_host2", "env:test"}, + } + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: accProviders, + CheckDestroy: testAccCheckCSMThreatsPolicyDestroy(providers.frameworkProvider), + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + resource "datadog_csm_threats_policy" "policy_test" { + name = "%s" + enabled = true + description = "im a policy" + host_tags_lists = [["host_name:test_host", "env:prod"], ["host_name:test_host2", "env:staging"]] + } + `, policyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCSMThreatsPolicyExists(providers.frameworkProvider, "datadog_csm_threats_policy.policy_test"), + checkCSMThreatsPolicyContentWithHostTagsLists( + resourceName, + policyName, + "im a policy", + hostTagsLists, + ), + ), + }, + // Update description and tags + { + Config: fmt.Sprintf(` + resource "datadog_csm_threats_policy" "policy_test" { + name = "%s" + enabled = true + description = "updated policy for terraform provider test" + host_tags_lists = [["host_name:test", "env:prod"], ["host_name:test_host2", "env:test"]] + } + `, policyName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCSMThreatsPolicyExists(providers.frameworkProvider, resourceName), + checkCSMThreatsPolicyContentWithHostTagsLists( + resourceName, + policyName, + "updated policy for terraform provider test", + updatedHostTagsLists, + ), + ), + }, + }, + }) +} + +func checkCSMThreatsPolicyContent(resourceName string, name string, description string, tags []string) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "description", description), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "tags.0", tags[0]), + ) +} + +func checkCSMThreatsPolicyContentWithHostTagsLists(resourceName string, name string, description string, hostTagsLists [][]string) resource.TestCheckFunc { + checks := []resource.TestCheckFunc{ + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "description", description), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + } + + // Add checks for each host tags list + for i, tagList := range hostTagsLists { + for j, tag := range tagList { + checks = append(checks, resource.TestCheckResourceAttr( + resourceName, + fmt.Sprintf("host_tags_lists.%d.%d", i, j), + tag, + )) + } + } + + return resource.ComposeTestCheckFunc(checks...) +} + +func testAccCheckCSMThreatsPolicyExists(accProvider *fwprovider.FrameworkProvider, resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + resource, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("resource '%s' not found in the state %s", resourceName, s.RootModule().Resources) + } + + if resource.Type != "datadog_csm_threats_policy" { + return fmt.Errorf("resource %s is not of type datadog_csm_threats_policy, found %s instead", resourceName, resource.Type) + } + + auth := accProvider.Auth + apiInstances := accProvider.DatadogApiInstances + + _, _, err := apiInstances.GetCSMThreatsApiV2().GetCSMThreatsAgentPolicy(auth, resource.Primary.ID) + if err != nil { + return fmt.Errorf("received an error retrieving policy: %s", err) + } + + return nil + } +} + +func testAccCheckCSMThreatsPolicyDestroy(accProvider *fwprovider.FrameworkProvider) resource.TestCheckFunc { + return func(s *terraform.State) error { + auth := accProvider.Auth + apiInstances := accProvider.DatadogApiInstances + + for _, resource := range s.RootModule().Resources { + if resource.Type == "datadog_csm_threats_policy" { + _, httpResponse, err := apiInstances.GetCSMThreatsApiV2().GetCSMThreatsAgentPolicy(auth, resource.Primary.ID) + if err == nil { + return errors.New("policy still exists") + } + if httpResponse == nil || httpResponse.StatusCode != 404 { + return fmt.Errorf("received an error while getting the policy: %s", err) + } + } + } + + return nil + } +} diff --git a/docs/data-sources/cloud_workload_security_agent_rules.md b/docs/data-sources/cloud_workload_security_agent_rules.md deleted file mode 100644 index 6bfafbe24b..0000000000 --- a/docs/data-sources/cloud_workload_security_agent_rules.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -# generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "datadog_cloud_workload_security_agent_rules Data Source - terraform-provider-datadog" -subcategory: "" -description: |- - Use this data source to retrieve information about existing Cloud Workload Security Agent Rules for use in other resources. Deprecated, use datadog_csm_threats_agent_rules data source instead: https://registry.terraform.io/providers/DataDog/datadog/latest/docs/data-sources/csm_threats_agent_rules ---- - -# datadog_cloud_workload_security_agent_rules (Data Source) - -Use this data source to retrieve information about existing Cloud Workload Security Agent Rules for use in other resources. Deprecated, use datadog_csm_threats_agent_rules data source instead: https://registry.terraform.io/providers/DataDog/datadog/latest/docs/data-sources/csm_threats_agent_rules - -## Example Usage - -```terraform -data "datadog_cloud_workload_security_agent_rules" "test" { -} -``` - - -## Schema - -### Read-Only - -- `agent_rules` (List of Object) List of Agent rules. (see [below for nested schema](#nestedatt--agent_rules)) -- `id` (String) The ID of this resource. - - -### Nested Schema for `agent_rules` - -Read-Only: - -- `description` (String) -- `enabled` (Boolean) -- `expression` (String) -- `id` (String) -- `name` (String) diff --git a/docs/data-sources/csm_threats_multi_policy_agent_rules.md b/docs/data-sources/csm_threats_multi_policy_agent_rules.md new file mode 100644 index 0000000000..db71337c99 --- /dev/null +++ b/docs/data-sources/csm_threats_multi_policy_agent_rules.md @@ -0,0 +1,38 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "datadog_csm_threats_multi_policy_agent_rules Data Source - terraform-provider-datadog" +subcategory: "" +description: |- + Use this data source to retrieve information about existing Agent rules. +--- + +# datadog_csm_threats_multi_policy_agent_rules (Data Source) + +Use this data source to retrieve information about existing Agent rules. + + + + +## Schema + +### Optional + +- `policy_id` (String) Listing only the rules in the policy with this field as the ID + +### Read-Only + +- `agent_rules` (List of Object) List of Agent rules (see [below for nested schema](#nestedatt--agent_rules)) +- `agent_rules_ids` (List of String) List of IDs for the Agent rules. +- `id` (String) The ID of the data source + + +### Nested Schema for `agent_rules` + +Read-Only: + +- `description` (String) +- `enabled` (Boolean) +- `expression` (String) +- `id` (String) +- `name` (String) +- `product_tags` (Set of String) diff --git a/docs/data-sources/csm_threats_policies.md b/docs/data-sources/csm_threats_policies.md new file mode 100644 index 0000000000..46ae797871 --- /dev/null +++ b/docs/data-sources/csm_threats_policies.md @@ -0,0 +1,33 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "datadog_csm_threats_policies Data Source - terraform-provider-datadog" +subcategory: "" +description: |- + Use this data source to retrieve information about existing policies. +--- + +# datadog_csm_threats_policies (Data Source) + +Use this data source to retrieve information about existing policies. + + + + +## Schema + +### Read-Only + +- `id` (String) The ID of this resource. +- `policies` (List of Object) List of policies (see [below for nested schema](#nestedatt--policies)) +- `policy_ids` (List of String) List of IDs for the policies. + + +### Nested Schema for `policies` + +Read-Only: + +- `description` (String) +- `enabled` (Boolean) +- `id` (String) +- `name` (String) +- `tags` (Set of String) diff --git a/docs/resources/csm_threats_multi_policy_agent_rule.md b/docs/resources/csm_threats_multi_policy_agent_rule.md new file mode 100644 index 0000000000..67f4e6c52b --- /dev/null +++ b/docs/resources/csm_threats_multi_policy_agent_rule.md @@ -0,0 +1,32 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "datadog_csm_threats_multi_policy_agent_rule Resource - terraform-provider-datadog" +subcategory: "" +description: |- + Provides a Datadog CSM Threats Agent Rule API resource. +--- + +# datadog_csm_threats_multi_policy_agent_rule (Resource) + +Provides a Datadog CSM Threats Agent Rule API resource. + + + + +## Schema + +### Required + +- `enabled` (Boolean) Indicates Whether the Agent rule is enabled. +- `expression` (String) The SECL expression of the Agent rule +- `name` (String) The name of the Agent rule. +- `policy_id` (String) The ID of the agent policy in which the rule is saved + +### Optional + +- `description` (String) A description for the Agent rule. +- `product_tags` (Set of String) The list of product tags associated with the rule + +### Read-Only + +- `id` (String) The ID of this resource. diff --git a/docs/resources/csm_threats_policy.md b/docs/resources/csm_threats_policy.md new file mode 100644 index 0000000000..61039b47f7 --- /dev/null +++ b/docs/resources/csm_threats_policy.md @@ -0,0 +1,31 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "datadog_csm_threats_policy Resource - terraform-provider-datadog" +subcategory: "" +description: |- + Provides a Datadog CSM Threats policy API resource. +--- + +# datadog_csm_threats_policy (Resource) + +Provides a Datadog CSM Threats policy API resource. + + + + +## Schema + +### Required + +- `name` (String) The name of the policy. + +### Optional + +- `description` (String) A description for the policy. +- `enabled` (Boolean) Indicates whether the policy is enabled. Defaults to `false`. +- `host_tags_lists` (Set of List of String) Host tags that define where the policy is deployed. Inner values are ANDed, outer arrays are ORed. +- `tags` (Set of String) Host tags that define where the policy is deployed. Deprecated, use host_tags_lists instead. + +### Read-Only + +- `id` (String) The ID of this resource. diff --git a/go.mod b/go.mod index a02074fa79..053540190b 100644 --- a/go.mod +++ b/go.mod @@ -101,4 +101,9 @@ require ( google.golang.org/protobuf v1.36.3 // indirect ) +<<<<<<< Updated upstream go 1.23.0 +======= +go 1.23.0 +replace github.com/DataDog/datadog-api-client-go/v2 v2.31.0 => ../datadog-api-client-go +>>>>>>> Stashed changes