From 1056b0ebb35785513bdc0164f0c3865f6bf7b807 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Thu, 8 May 2025 17:13:58 +0100 Subject: [PATCH 01/21] Initial resource evaluation filter implementation --- datadog/fwprovider/framework_provider.go | 3 +- ..._datadog_csm_resource_evaluation_filter.go | 278 ++++++++++++++++++ .../TestAccResourceEvaluationFilter.freeze | 1 + .../TestAccResourceEvaluationFilter.yaml | 207 +++++++++++++ ...tAccResourceEvaluationFilterInvalid.freeze | 1 + ...estAccResourceEvaluationFilterInvalid.yaml | 39 +++ datadog/tests/provider_test.go | 1 + ...dog_csm_resource_evaluation_filter_test.go | 216 ++++++++++++++ 8 files changed, 745 insertions(+), 1 deletion(-) create mode 100644 datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go create mode 100644 datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze create mode 100644 datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml create mode 100644 datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze create mode 100644 datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml create mode 100644 datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go diff --git a/datadog/fwprovider/framework_provider.go b/datadog/fwprovider/framework_provider.go index 54b6818fa..613385548 100644 --- a/datadog/fwprovider/framework_provider.go +++ b/datadog/fwprovider/framework_provider.go @@ -84,6 +84,7 @@ var Resources = []func() resource.Resource{ NewWorkflowAutomationResource, NewAppBuilderAppResource, NewObservabilitPipelineResource, + NewResourceEvaluationFilter, } var Datasources = []func() datasource.DataSource{ @@ -514,7 +515,7 @@ func defaultConfigureFunc(p *FrameworkProvider, request *provider.ConfigureReque } valid, ok := resp.GetValidOk() if (ok && !*valid) || !ok { - err := errors.New(`Invalid or missing credentials provided to the Datadog Provider. Please confirm your API and APP keys are valid and are for the correct region, see https://www.terraform.io/docs/providers/datadog/ for more information on providing credentials for the Datadog Provider`) + err := errors.New(`Invalid or missing credentials provided to the Datadog CloudProvider. Please confirm your API and APP keys are valid and are for the correct region, see https://www.terraform.io/docs/providers/datadog/ for more information on providing credentials for the Datadog CloudProvider`) diags.AddError("[ERROR] Datadog Client validation error", err.Error()) return diags } diff --git a/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go b/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go new file mode 100644 index 000000000..afc482e01 --- /dev/null +++ b/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go @@ -0,0 +1,278 @@ +package fwprovider + +import ( + "context" + "encoding/json" + "fmt" + "regexp" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + + "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" + + "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" + "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +type ResourceEvaluationFilter struct { + API *datadogV2.SecurityMonitoringApi + Auth context.Context +} + +type ResourceEvaluationFilterModel struct { + CloudProvider types.String `tfsdk:"cloud_provider"` + ID types.String `tfsdk:"id"` + Tags types.Set `tfsdk:"tags"` +} + +func NewResourceEvaluationFilter() resource.Resource { + return &ResourceEvaluationFilter{} +} + +func (r *ResourceEvaluationFilter) Configure(_ context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) { + providerData, _ := request.ProviderData.(*FrameworkProvider) + r.API = providerData.DatadogApiInstances.GetSecurityMonitoringApiV2() + r.Auth = providerData.Auth +} + +func (r *ResourceEvaluationFilter) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "resource_evaluation_filter" +} + +var tagFormatValidator = stringvalidator.RegexMatches( + regexp.MustCompile(`^[^:]+:[^:]+$`), + "each tag must be in the format 'key:value' (colon-separated)", +) + +func toSliceString(set types.Set) ([]string, diag.Diagnostics) { + var diags diag.Diagnostics + result := make([]string, 0) + + if set.IsNull() || set.IsUnknown() { + return result, nil + } + + for _, elem := range set.Elements() { + strVal, ok := elem.(types.String) + if !ok { + diags.AddError("Invalid element type", "Expected string in set but found a different type") + continue + } + result = append(result, strVal.ValueString()) + } + + return result, diags +} + +func (r *ResourceEvaluationFilter) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: "Manage a single resource evaluation filter.", + Attributes: map[string]schema.Attribute{ + "cloud_provider": schema.StringAttribute{ + Required: true, + }, + "id": schema.StringAttribute{ + Required: true, + }, + "tags": schema.SetAttribute{ + Required: true, + ElementType: types.StringType, + Validators: []validator.Set{ + setvalidator.ValueStringsAre(tagFormatValidator), + }, + }, + }, + } +} + +func (r *ResourceEvaluationFilter) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var state ResourceEvaluationFilterModel + + response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + body, diags := r.buildUpdateResourceEvaluationFilterRequest(ctx, &state) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + resp, _, err := r.API.UpdateResourceEvaluationFilters(r.Auth, *body) + + if err != nil { + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error updating resource evaluation filter")) + return + } + if err := utils.CheckForUnparsed(resp); err != nil { + response.Diagnostics.AddError("response contains unparsedObject", err.Error()) + return + } + + attributes := resp.Data.GetAttributes() + r.UpdateState(ctx, &state, &attributes) + + // Save data into Terraform state + response.Diagnostics.Append(response.State.Set(ctx, &state)...) +} + +func convertStringSliceToAttrValues(s []string) []attr.Value { + out := make([]attr.Value, len(s)) + for i, v := range s { + out[i] = types.StringValue(v) + } + return out +} + +func (r *ResourceEvaluationFilter) UpdateState(_ context.Context, state *ResourceEvaluationFilterModel, attributes *datadogV2.ResourceFilterAttributes) { + // since we are handling a response after an update/read request, the cloud provider map will have at most one key + // and the map of each cloud provider will also have at most one key + for p, accounts := range attributes.CloudProvider { + for id, tagList := range accounts { + tags := types.SetValueMust(types.StringType, convertStringSliceToAttrValues(tagList)) + state.CloudProvider = types.StringValue(p) + state.ID = types.StringValue(id) + state.Tags = tags + break + } + break + } +} + +func (r *ResourceEvaluationFilter) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var state ResourceEvaluationFilterModel + response.Diagnostics.Append(request.State.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + if state.CloudProvider.IsNull() || state.CloudProvider.IsUnknown() { + response.Diagnostics.AddError("Missing cloud_provider", "cloud_provider is required for lookup") + return + } + provider, err := datadogV2.NewResourceFilterProviderEnumFromValue(state.CloudProvider.ValueString()) + + params := datadogV2.GetResourceEvaluationFiltersOptionalParameters{ + CloudProvider: provider, + AccountId: state.ID.ValueStringPointer(), + } + + resp, _, err := r.API.GetResourceEvaluationFilters(r.Auth, params) + if err != nil { + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving ResourceEvaluationFilter")) + return + } + + attributes := resp.Data.GetAttributes() + r.UpdateState(ctx, &state, &attributes) + + // Save data into Terraform state + response.Diagnostics.Append(response.State.Set(ctx, &state)...) +} + +func (r *ResourceEvaluationFilter) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + var state ResourceEvaluationFilterModel + response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + body, diags := r.buildUpdateResourceEvaluationFilterRequest(ctx, &state) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + resp, _, err := r.API.UpdateResourceEvaluationFilters(r.Auth, *body) + if err != nil { + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error updating ResourceEvaluationFilter")) + return + } + if err := utils.CheckForUnparsed(resp); err != nil { + response.Diagnostics.AddError("response contains unparsedObject", err.Error()) + return + } + + attributes := resp.Data.GetAttributes() + r.UpdateState(ctx, &state, &attributes) + + // Save data into Terraform state + response.Diagnostics.Append(response.State.Set(ctx, &state)...) +} + +func (r *ResourceEvaluationFilter) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + var state ResourceEvaluationFilterModel + response.Diagnostics.Append(request.State.Get(ctx, &state)...) + if response.Diagnostics.HasError() { + return + } + + // empty tags + state.Tags = types.SetValueMust(types.StringType, []attr.Value{}) + + // create body as normal with empty tags + body, diags := r.buildUpdateResourceEvaluationFilterRequest(ctx, &state) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + resp, _, err := r.API.UpdateResourceEvaluationFilters(r.Auth, *body) + if err != nil { + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error deleting ResourceEvaluationFilter")) + return + } + if err := utils.CheckForUnparsed(resp); err != nil { + response.Diagnostics.AddError("response contains unparsedObject", err.Error()) + return + } + attributes := resp.Data.GetAttributes() + r.UpdateState(ctx, &state, &attributes) +} + +func (r *ResourceEvaluationFilter) buildUpdateResourceEvaluationFilterRequest(ctx context.Context, state *ResourceEvaluationFilterModel) (*datadogV2.UpdateResourceEvaluationFiltersRequest, diag.Diagnostics) { + diags := diag.Diagnostics{} + data := datadogV2.NewUpdateResourceEvaluationFiltersRequestDataWithDefaults() + + tagsList, tagDiags := toSliceString(state.Tags) + diags.Append(tagDiags...) + if tagDiags.HasError() { + return nil, diags + } + + if state.CloudProvider.IsNull() || state.CloudProvider.IsUnknown() { + diags.AddError("Missing cloud_provider", "cloud_provider is required but was null or unknown") + return nil, diags + } + if state.ID.IsNull() || state.ID.IsUnknown() { + diags.AddError("Missing id", "id is required but was null or unknown") + return nil, diags + } + + attributes := datadogV2.ResourceFilterAttributes{ + CloudProvider: map[string]map[string][]string{ + state.CloudProvider.ValueString(): { + state.ID.ValueString(): tagsList, + }, + }, + } + + data.SetId(string(datadogV2.RESOURCEFILTERREQUESTTYPE_CSM_RESOURCE_FILTER)) + data.SetType(datadogV2.RESOURCEFILTERREQUESTTYPE_CSM_RESOURCE_FILTER) + data.SetAttributes(attributes) + + bytes, _ := json.MarshalIndent(attributes, "", " ") + fmt.Println(string(bytes)) + + req := datadogV2.NewUpdateResourceEvaluationFiltersRequestWithDefaults() + req.SetData(*data) + + return req, diags +} diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze new file mode 100644 index 000000000..34db1b6b7 --- /dev/null +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze @@ -0,0 +1 @@ +2025-05-08T15:59:57.892282+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml new file mode 100644 index 000000000..ea12e2e42 --- /dev/null +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml @@ -0,0 +1,207 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 159 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: | + {"data":{"attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}},"id":"csm_resource_filter","type":"csm_resource_filter"}} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters + method: PUT + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 204 + uncompressed: false + body: '{"data":{"id":"csm_resource_filter","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}},"uuid":"3ad549bf-eba0-11e9-a77a-0705486660d0"}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 201 Created + code: 201 + duration: 509.108875ms + - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Accept: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 118 + uncompressed: false + body: '{"data":{"id":"e225cbaa-c002-43a0-ac0d-c10f6465d7e3","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 200 OK + code: 200 + duration: 147.701792ms + - id: 2 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Accept: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 118 + uncompressed: false + body: '{"data":{"id":"f88a2794-2c3e-4530-9471-886363842069","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 200 OK + code: 200 + duration: 146.4985ms + - id: 3 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Accept: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 118 + uncompressed: false + body: '{"data":{"id":"c67e5093-0e63-4446-bf2a-e062db14e3f5","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 200 OK + code: 200 + duration: 145.176958ms + - id: 4 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 124 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: | + {"data":{"attributes":{"cloud_provider":{"aws":{"123456789":[]}}},"id":"csm_resource_filter","type":"csm_resource_filter"}} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters + method: PUT + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 169 + uncompressed: false + body: '{"data":{"id":"csm_resource_filter","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":[]}},"uuid":"3ad549bf-eba0-11e9-a77a-0705486660d0"}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 201 Created + code: 201 + duration: 160.609458ms + - id: 5 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Accept: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 118 + uncompressed: false + body: '{"data":{"id":"6281038e-3448-4a29-83eb-6ac64ce5606b","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 200 OK + code: 200 + duration: 150.959458ms diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze new file mode 100644 index 000000000..b205caf00 --- /dev/null +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze @@ -0,0 +1 @@ +2025-05-08T16:00:01.764778+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml new file mode 100644 index 000000000..1ea21bb67 --- /dev/null +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml @@ -0,0 +1,39 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 151 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: | + {"data":{"attributes":{"cloud_provider":{"invalid":{"223456789":["tag1:val1","tag3:val3"]}}},"id":"csm_resource_filter","type":"csm_resource_filter"}} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters + method: PUT + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 93 + uncompressed: false + body: '{"errors":[{"status":"400","title":"Bad Request","detail":"Invalid cloud provider invalid"}]}' + headers: + Content-Type: + - application/vnd.api+json + status: 400 Bad Request + code: 400 + duration: 141.509917ms diff --git a/datadog/tests/provider_test.go b/datadog/tests/provider_test.go index 1999e40e5..1d9aefd33 100644 --- a/datadog/tests/provider_test.go +++ b/datadog/tests/provider_test.go @@ -266,6 +266,7 @@ var testFiles2EndpointTags = map[string]string{ "tests/resource_datadog_webhook_custom_variable_test": "webhook_custom_variable", "tests/resource_datadog_webhook_test": "webhook", "tests/resource_datadog_workflow_automation_test": "workflow_automation", + "tests/resource_datadog_csm_resource_evaluation_filter_test": "resource_filters", } // getEndpointTagValue traverses callstack frames to find the test function that invoked this call; diff --git a/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go b/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go new file mode 100644 index 000000000..68379f334 --- /dev/null +++ b/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go @@ -0,0 +1,216 @@ +package test + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "regexp" + "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 TestAccResourceEvaluationFilter(t *testing.T) { + _, providers, accProviders := testAccFrameworkMuxProviders(context.Background(), t) + + accountId := "123456789" + resourceName := "datadog_resource_evaluation_filter.filter_test" + simpleTags := []string{"tag1:val1", "tag2:val2", "tag3:val3"} + provider := "aws" + //invalidProvider := "invalid" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: accProviders, + CheckDestroy: testAccCheckResourceEvaluationFilterDestroy(providers.frameworkProvider), + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + resource "datadog_resource_evaluation_filter" "filter_test" { + tags = ["tag1:val1", "tag2:val2", "tag3:val3"] + cloud_provider = "%s" + id = "%s" + } + `, provider, accountId), + Check: resource.ComposeTestCheckFunc( + testAccCheckResourceEvaluationFilterExists(providers.frameworkProvider, resourceName), + checkResourceEvaluationFilterContent( + resourceName, + accountId, + provider, + simpleTags, + ), + ), + }, + // Check if reordering tags caused update + { + // Same tags as step 1 but reordered + Config: fmt.Sprintf(` + resource "datadog_resource_evaluation_filter" "filter_test" { + tags = ["tag3:val3", "tag1:val1", "tag2:val2"] + cloud_provider = "%s" + id = "%s" + } + `, provider, accountId), + // This should not trigger a diff or update because tags are now a set + ExpectNonEmptyPlan: false, + PlanOnly: true, + }, + }, + }) +} + +func TestAccResourceEvaluationFilterInvalid(t *testing.T) { + _, _, accProviders := testAccFrameworkMuxProviders(context.Background(), t) + + accountId := "223456789" + provider := "aws" + invalidProvider := "invalid" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: accProviders, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + resource "datadog_resource_evaluation_filter" "filter_test" { + tags = ["tag1:val1", "invalidTag:asdasf:InvalidTag", "tag3:val3"] + cloud_provider = "%s" + id = "%s" + } + `, provider, accountId), + ExpectError: regexp.MustCompile(`Invalid Attribute Value Match`), + PlanOnly: true, + }, + { + Config: fmt.Sprintf(` + resource "datadog_resource_evaluation_filter" "filter_test" { + tags = ["tag1:val1", "tag3:val3"] + cloud_provider = "%s" + id = "%s" + } + `, invalidProvider, accountId), + ExpectError: regexp.MustCompile(`Invalid cloud provider invalid`), + }, + }, + }) +} + +func checkResourceEvaluationFilterContent(resourceName string, id string, provider string, expectedTags []string) resource.TestCheckFunc { + checks := []resource.TestCheckFunc{ + resource.TestCheckResourceAttr(resourceName, "cloud_provider", provider), + resource.TestCheckResourceAttr(resourceName, "id", id), + } + + checks = append(checks, resource.TestCheckFunc(func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("resource %s not found", resourceName) + } + + actualCountStr := rs.Primary.Attributes["tags.#"] + actualCount, err := strconv.Atoi(actualCountStr) + if err != nil { + return fmt.Errorf("failed to parse tag count: %v", err) + } + + if actualCount != len(expectedTags) { + return fmt.Errorf("expected %d tags, but found %d", len(expectedTags), actualCount) + } + + // Build actual tag set + actualTagSet := make(map[string]struct{}, actualCount) + for i := 0; i < actualCount; i++ { + tag := rs.Primary.Attributes[fmt.Sprintf("tags.%d", i)] + actualTagSet[tag] = struct{}{} + } + + // Check all expected tags are present + for _, expectedTag := range expectedTags { + if _, ok := actualTagSet[expectedTag]; !ok { + return fmt.Errorf("expected tag %q not found in actual set", expectedTag) + } + } + + // Optional: fail if extra tags are present (strict match) + if len(actualTagSet) != len(expectedTags) { + return fmt.Errorf("tag set contains unexpected tags") + } + + return nil + })) + + return resource.ComposeTestCheckFunc(checks...) +} + +func testAccCheckResourceEvaluationFilterExists(accProvider *fwprovider.FrameworkProvider, resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + r, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("resource '%s' not found in the state %s", resourceName, s.RootModule().Resources) + } + + if r.Type != "datadog_resource_evaluation_filter" { + return fmt.Errorf("resource %s is not of type datadog_resource_evaluation_filter, found %s instead", resourceName, r.Type) + } + + auth := accProvider.Auth + apiInstances := accProvider.DatadogApiInstances + provider, err := datadogV2.NewResourceFilterProviderEnumFromValue(r.Primary.Attributes["cloud_provider"]) + id := r.Primary.Attributes["id"] + + params := datadogV2.GetResourceEvaluationFiltersOptionalParameters{ + CloudProvider: provider, + AccountId: &id, + } + _, _, err = apiInstances.GetSecurityMonitoringApiV2().GetResourceEvaluationFilters(auth, params) + if err != nil { + return fmt.Errorf("received an error retrieving agent rule: %s", err) + } + + return nil + } +} + +func testAccCheckResourceEvaluationFilterDestroy(accProvider *fwprovider.FrameworkProvider) resource.TestCheckFunc { + return func(s *terraform.State) error { + auth := accProvider.Auth + apiInstances := accProvider.DatadogApiInstances + + for _, r := range s.RootModule().Resources { + if r.Type == "datadog_resource_evaluation_filter" { + provider, err := datadogV2.NewResourceFilterProviderEnumFromValue(r.Primary.Attributes["cloud_provider"]) + id := r.Primary.Attributes["id"] + + params := datadogV2.GetResourceEvaluationFiltersOptionalParameters{ + CloudProvider: provider, + AccountId: &id, + } + response, httpResponse, err := apiInstances.GetSecurityMonitoringApiV2().GetResourceEvaluationFilters(auth, params) + + if err != nil { + return errors.New("Error retrieving resource evaluation filter") + } + + if len(response.Data.Attributes.CloudProvider[r.Primary.Attributes["cloud_provider"]][id]) != 0 { + bytes, _ := json.MarshalIndent(response.Data.Attributes, "", " ") + fmt.Println(string(bytes)) + return errors.New("filters were not destroyed") + } + + if httpResponse == nil { + return fmt.Errorf("received an error while getting the resource evaluation filter: %s", err) + } + } + } + + return nil + } +} From b07e39f5ead61c477e8520d0d51f191a082246b3 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Fri, 9 May 2025 09:16:57 +0100 Subject: [PATCH 02/21] revert CloudProvider string refactor --- datadog/fwprovider/framework_provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datadog/fwprovider/framework_provider.go b/datadog/fwprovider/framework_provider.go index 613385548..27430c07f 100644 --- a/datadog/fwprovider/framework_provider.go +++ b/datadog/fwprovider/framework_provider.go @@ -515,7 +515,7 @@ func defaultConfigureFunc(p *FrameworkProvider, request *provider.ConfigureReque } valid, ok := resp.GetValidOk() if (ok && !*valid) || !ok { - err := errors.New(`Invalid or missing credentials provided to the Datadog CloudProvider. Please confirm your API and APP keys are valid and are for the correct region, see https://www.terraform.io/docs/providers/datadog/ for more information on providing credentials for the Datadog CloudProvider`) + err := errors.New(`Invalid or missing credentials provided to the Datadog Provider. Please confirm your API and APP keys are valid and are for the correct region, see https://www.terraform.io/docs/providers/datadog/ for more information on providing credentials for the Datadog CloudProvider`) diags.AddError("[ERROR] Datadog Client validation error", err.Error()) return diags } From ac8063fba5e692b5a925591bfb57e2383420b71e Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Fri, 16 May 2025 18:45:24 +0100 Subject: [PATCH 03/21] updated terraform provider to use skipCache --- ..._datadog_csm_resource_evaluation_filter.go | 59 +++++- .../TestAccResourceEvaluationFilter.freeze | 2 +- .../TestAccResourceEvaluationFilter.yaml | 24 +-- ...stAccResourceEvaluationFilterImport.freeze | 1 + ...TestAccResourceEvaluationFilterImport.yaml | 174 ++++++++++++++++++ ...tAccResourceEvaluationFilterInvalid.freeze | 2 +- ...estAccResourceEvaluationFilterInvalid.yaml | 4 +- ...dog_csm_resource_evaluation_filter_test.go | 59 +++++- 8 files changed, 295 insertions(+), 30 deletions(-) create mode 100644 datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze create mode 100644 datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml diff --git a/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go b/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go index afc482e01..423e47321 100644 --- a/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go +++ b/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go @@ -5,6 +5,10 @@ import ( "encoding/json" "fmt" "regexp" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -35,6 +39,11 @@ func NewResourceEvaluationFilter() resource.Resource { return &ResourceEvaluationFilter{} } +var ( + _ resource.ResourceWithConfigure = &ResourceEvaluationFilter{} + _ resource.ResourceWithImportState = &ResourceEvaluationFilter{} +) + func (r *ResourceEvaluationFilter) Configure(_ context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) { providerData, _ := request.ProviderData.(*FrameworkProvider) r.API = providerData.DatadogApiInstances.GetSecurityMonitoringApiV2() @@ -118,7 +127,7 @@ func (r *ResourceEvaluationFilter) Create(ctx context.Context, request resource. attributes := resp.Data.GetAttributes() r.UpdateState(ctx, &state, &attributes) - + time.Sleep(1000 * time.Millisecond) // Save data into Terraform state response.Diagnostics.Append(response.State.Set(ctx, &state)...) } @@ -157,13 +166,16 @@ func (r *ResourceEvaluationFilter) Read(ctx context.Context, request resource.Re response.Diagnostics.AddError("Missing cloud_provider", "cloud_provider is required for lookup") return } - provider, err := datadogV2.NewResourceFilterProviderEnumFromValue(state.CloudProvider.ValueString()) + provider := state.CloudProvider.ValueString() + skipCache := true params := datadogV2.GetResourceEvaluationFiltersOptionalParameters{ - CloudProvider: provider, + CloudProvider: &provider, AccountId: state.ID.ValueStringPointer(), + SkipCache: &skipCache, } - + bytes, _ := json.MarshalIndent(params, "", " ") + fmt.Println("DEBUG params for read:", string(bytes)) resp, _, err := r.API.GetResourceEvaluationFilters(r.Auth, params) if err != nil { response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving ResourceEvaluationFilter")) @@ -171,7 +183,10 @@ func (r *ResourceEvaluationFilter) Read(ctx context.Context, request resource.Re } attributes := resp.Data.GetAttributes() + bytes, _ = json.MarshalIndent(attributes, "", " ") + fmt.Println("DEBUG attributes from API:", string(bytes)) r.UpdateState(ctx, &state, &attributes) + time.Sleep(1000 * time.Millisecond) // Save data into Terraform state response.Diagnostics.Append(response.State.Set(ctx, &state)...) @@ -184,7 +199,9 @@ func (r *ResourceEvaluationFilter) Update(ctx context.Context, request resource. return } + fmt.Println("DEBUG UPDATE - creating payload") body, diags := r.buildUpdateResourceEvaluationFilterRequest(ctx, &state) + response.Diagnostics.Append(diags...) if response.Diagnostics.HasError() { return @@ -201,8 +218,10 @@ func (r *ResourceEvaluationFilter) Update(ctx context.Context, request resource. } attributes := resp.Data.GetAttributes() + bytes, _ := json.MarshalIndent(resp.Data.GetAttributes(), "", " ") + fmt.Println("UPDATEEEEEEEEE response: ", string(bytes)) r.UpdateState(ctx, &state, &attributes) - + time.Sleep(1000 * time.Millisecond) // Save data into Terraform state response.Diagnostics.Append(response.State.Set(ctx, &state)...) } @@ -233,8 +252,32 @@ func (r *ResourceEvaluationFilter) Delete(ctx context.Context, request resource. response.Diagnostics.AddError("response contains unparsedObject", err.Error()) return } - attributes := resp.Data.GetAttributes() - r.UpdateState(ctx, &state, &attributes) + time.Sleep(1000 * time.Millisecond) + bytes, _ := json.MarshalIndent(resp.Data.GetAttributes(), "", " ") + fmt.Println("delete response: ", string(bytes)) +} + +func (r *ResourceEvaluationFilter) ImportState( + ctx context.Context, + req resource.ImportStateRequest, + resp *resource.ImportStateResponse, +) { + // Expected import format: "cloud_provider:id" (e.g., "aws:123456789") + parts := strings.Split(req.ID, ":") + if len(parts) != 2 { + resp.Diagnostics.AddError( + "Invalid import format", + `Expected format: "cloud_provider:id" (e.g., "aws:123456789")`, + ) + return + } + + cloudProvider := parts[0] + id := parts[1] + + // Set attributes into Terraform state so Read() can hydrate the full resource + resp.State.SetAttribute(ctx, path.Root("cloud_provider"), cloudProvider) + resp.State.SetAttribute(ctx, path.Root("id"), id) } func (r *ResourceEvaluationFilter) buildUpdateResourceEvaluationFilterRequest(ctx context.Context, state *ResourceEvaluationFilterModel) (*datadogV2.UpdateResourceEvaluationFiltersRequest, diag.Diagnostics) { @@ -269,7 +312,7 @@ func (r *ResourceEvaluationFilter) buildUpdateResourceEvaluationFilterRequest(ct data.SetAttributes(attributes) bytes, _ := json.MarshalIndent(attributes, "", " ") - fmt.Println(string(bytes)) + fmt.Println("building update payload: ", string(bytes)) req := datadogV2.NewUpdateResourceEvaluationFiltersRequestWithDefaults() req.SetData(*data) diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze index 34db1b6b7..debacb945 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze @@ -1 +1 @@ -2025-05-08T15:59:57.892282+01:00 \ No newline at end of file +2025-05-09T09:50:05.738305+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml index ea12e2e42..087c062e7 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml @@ -36,7 +36,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 509.108875ms + duration: 541.942292ms - id: 1 request: proto: HTTP/1.1 @@ -61,15 +61,15 @@ interactions: proto_minor: 1 transfer_encoding: [] trailer: {} - content_length: 118 + content_length: 175 uncompressed: false - body: '{"data":{"id":"e225cbaa-c002-43a0-ac0d-c10f6465d7e3","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' + body: '{"data":{"id":"58803539-0869-496a-b6f4-6770f9238b1b","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 147.701792ms + duration: 158.787208ms - id: 2 request: proto: HTTP/1.1 @@ -96,13 +96,13 @@ interactions: trailer: {} content_length: 118 uncompressed: false - body: '{"data":{"id":"f88a2794-2c3e-4530-9471-886363842069","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' + body: '{"data":{"id":"3898bc60-1e96-4a1e-99f9-d2156a49bafd","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 146.4985ms + duration: 159.110583ms - id: 3 request: proto: HTTP/1.1 @@ -127,15 +127,15 @@ interactions: proto_minor: 1 transfer_encoding: [] trailer: {} - content_length: 118 + content_length: 175 uncompressed: false - body: '{"data":{"id":"c67e5093-0e63-4446-bf2a-e062db14e3f5","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' + body: '{"data":{"id":"6f317b4f-ee80-4292-afef-9e73108ff04e","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 145.176958ms + duration: 159.659125ms - id: 4 request: proto: HTTP/1.1 @@ -171,7 +171,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 160.609458ms + duration: 179.546166ms - id: 5 request: proto: HTTP/1.1 @@ -198,10 +198,10 @@ interactions: trailer: {} content_length: 118 uncompressed: false - body: '{"data":{"id":"6281038e-3448-4a29-83eb-6ac64ce5606b","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' + body: '{"data":{"id":"f31f3495-67d8-423a-8d1b-33b820dc56a7","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 150.959458ms + duration: 152.485417ms diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze new file mode 100644 index 000000000..3c86a5f62 --- /dev/null +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze @@ -0,0 +1 @@ +2025-05-09T09:50:14.86783+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml new file mode 100644 index 000000000..3ffff9ba8 --- /dev/null +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml @@ -0,0 +1,174 @@ +--- +version: 2 +interactions: + - id: 0 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 159 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: | + {"data":{"attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}},"id":"csm_resource_filter","type":"csm_resource_filter"}} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters + method: PUT + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 204 + uncompressed: false + body: '{"data":{"id":"csm_resource_filter","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}},"uuid":"3ad549bf-eba0-11e9-a77a-0705486660d0"}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 201 Created + code: 201 + duration: 177.469292ms + - id: 1 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Accept: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=223456789&cloud_provider=aws + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 118 + uncompressed: false + body: '{"data":{"id":"458856d4-de73-4984-aa57-e1e96211579b","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 200 OK + code: 200 + duration: 146.258125ms + - id: 2 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Accept: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=223456789&cloud_provider=aws + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 175 + uncompressed: false + body: '{"data":{"id":"edc41ca0-7e95-4749-ab4b-961c17f4ce49","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 200 OK + code: 200 + duration: 142.834292ms + - id: 3 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Accept: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=223456789&cloud_provider=aws + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 175 + uncompressed: false + body: '{"data":{"id":"fd3dfb9d-2c37-4c9a-a1ce-036809c1c4bc","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 200 OK + code: 200 + duration: 145.890792ms + - id: 4 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 124 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: | + {"data":{"attributes":{"cloud_provider":{"aws":{"223456789":[]}}},"id":"csm_resource_filter","type":"csm_resource_filter"}} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters + method: PUT + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 169 + uncompressed: false + body: '{"data":{"id":"csm_resource_filter","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":[]}},"uuid":"3ad549bf-eba0-11e9-a77a-0705486660d0"}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 201 Created + code: 201 + duration: 172.632ms diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze index b205caf00..20c9a9408 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze @@ -1 +1 @@ -2025-05-08T16:00:01.764778+01:00 \ No newline at end of file +2025-05-09T09:50:21.227124+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml index 1ea21bb67..f45b51532 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml @@ -13,7 +13,7 @@ interactions: remote_addr: "" request_uri: "" body: | - {"data":{"attributes":{"cloud_provider":{"invalid":{"223456789":["tag1:val1","tag3:val3"]}}},"id":"csm_resource_filter","type":"csm_resource_filter"}} + {"data":{"attributes":{"cloud_provider":{"invalid":{"323456789":["tag1:val1","tag3:val3"]}}},"id":"csm_resource_filter","type":"csm_resource_filter"}} form: {} headers: Accept: @@ -36,4 +36,4 @@ interactions: - application/vnd.api+json status: 400 Bad Request code: 400 - duration: 141.509917ms + duration: 150.158083ms diff --git a/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go b/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go index 68379f334..994c0e929 100644 --- a/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go +++ b/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go @@ -8,6 +8,7 @@ import ( "regexp" "strconv" "testing" + "time" "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" @@ -67,10 +68,51 @@ func TestAccResourceEvaluationFilter(t *testing.T) { }) } +func TestAccResourceEvaluationFilterImport(t *testing.T) { + _, providers, accProviders := testAccFrameworkMuxProviders(context.Background(), t) + + accountId := "223456789" + resourceName := "datadog_resource_evaluation_filter.filter_test" + simpleTags := []string{"tag1:val1", "tag2:val2", "tag3:val3"} + provider := "aws" + //invalidProvider := "invalid" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV5ProviderFactories: accProviders, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + resource "datadog_resource_evaluation_filter" "filter_test" { + tags = ["tag1:val1", "tag2:val2", "tag3:val3"] + cloud_provider = "%s" + id = "%s" + } + `, provider, accountId), + Check: resource.ComposeTestCheckFunc( + testAccCheckResourceEvaluationFilterExists(providers.frameworkProvider, resourceName), + checkResourceEvaluationFilterContent( + resourceName, + accountId, + provider, + simpleTags, + ), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateId: fmt.Sprintf("%s:%s", provider, accountId), + ImportStateVerify: true, + }, + }, + }) +} + func TestAccResourceEvaluationFilterInvalid(t *testing.T) { _, _, accProviders := testAccFrameworkMuxProviders(context.Background(), t) - accountId := "223456789" + accountId := "323456789" provider := "aws" invalidProvider := "invalid" @@ -163,14 +205,16 @@ func testAccCheckResourceEvaluationFilterExists(accProvider *fwprovider.Framewor auth := accProvider.Auth apiInstances := accProvider.DatadogApiInstances - provider, err := datadogV2.NewResourceFilterProviderEnumFromValue(r.Primary.Attributes["cloud_provider"]) + provider := r.Primary.Attributes["cloud_provider"] id := r.Primary.Attributes["id"] + skipCache := true params := datadogV2.GetResourceEvaluationFiltersOptionalParameters{ - CloudProvider: provider, + CloudProvider: &provider, AccountId: &id, + SkipCache: &skipCache, } - _, _, err = apiInstances.GetSecurityMonitoringApiV2().GetResourceEvaluationFilters(auth, params) + _, _, err := apiInstances.GetSecurityMonitoringApiV2().GetResourceEvaluationFilters(auth, params) if err != nil { return fmt.Errorf("received an error retrieving agent rule: %s", err) } @@ -186,13 +230,16 @@ func testAccCheckResourceEvaluationFilterDestroy(accProvider *fwprovider.Framewo for _, r := range s.RootModule().Resources { if r.Type == "datadog_resource_evaluation_filter" { - provider, err := datadogV2.NewResourceFilterProviderEnumFromValue(r.Primary.Attributes["cloud_provider"]) + provider := r.Primary.Attributes["cloud_provider"] id := r.Primary.Attributes["id"] + skipCache := true params := datadogV2.GetResourceEvaluationFiltersOptionalParameters{ - CloudProvider: provider, + CloudProvider: &provider, AccountId: &id, + SkipCache: &skipCache, } + time.Sleep(1 * time.Second) response, httpResponse, err := apiInstances.GetSecurityMonitoringApiV2().GetResourceEvaluationFilters(auth, params) if err != nil { From 353a825ec29233941aae07f5254b3d9f4df44fd7 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Mon, 19 May 2025 18:32:34 +0100 Subject: [PATCH 04/21] Adding descriptions to schema. Removing debug specific code --- ..._datadog_csm_resource_evaluation_filter.go | 24 ++++--------------- ...dog_csm_resource_evaluation_filter_test.go | 5 +--- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go b/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go index 423e47321..dd8fe5f96 100644 --- a/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go +++ b/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go @@ -2,11 +2,9 @@ package fwprovider import ( "context" - "encoding/json" "fmt" "regexp" "strings" - "time" "github.com/hashicorp/terraform-plugin-framework/path" @@ -84,10 +82,12 @@ func (r *ResourceEvaluationFilter) Schema(_ context.Context, _ resource.SchemaRe Description: "Manage a single resource evaluation filter.", Attributes: map[string]schema.Attribute{ "cloud_provider": schema.StringAttribute{ - Required: true, + Required: true, + Description: "The cloud provider of the resource that will be target of the filters.", }, "id": schema.StringAttribute{ - Required: true, + Required: true, + Description: "The ID of the resource that will be the target of the filters. Different cloud providers target different resource ids:\n - `aws`: account id \n - `gcp`: project id\n - `azure`: subscription id", }, "tags": schema.SetAttribute{ Required: true, @@ -95,6 +95,7 @@ func (r *ResourceEvaluationFilter) Schema(_ context.Context, _ resource.SchemaRe Validators: []validator.Set{ setvalidator.ValueStringsAre(tagFormatValidator), }, + Description: "Set of tags to filter the misconfiguration detections on. Each entry should follow the format: \"key:\":\"value\".", }, }, } @@ -127,7 +128,6 @@ func (r *ResourceEvaluationFilter) Create(ctx context.Context, request resource. attributes := resp.Data.GetAttributes() r.UpdateState(ctx, &state, &attributes) - time.Sleep(1000 * time.Millisecond) // Save data into Terraform state response.Diagnostics.Append(response.State.Set(ctx, &state)...) } @@ -174,8 +174,6 @@ func (r *ResourceEvaluationFilter) Read(ctx context.Context, request resource.Re AccountId: state.ID.ValueStringPointer(), SkipCache: &skipCache, } - bytes, _ := json.MarshalIndent(params, "", " ") - fmt.Println("DEBUG params for read:", string(bytes)) resp, _, err := r.API.GetResourceEvaluationFilters(r.Auth, params) if err != nil { response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving ResourceEvaluationFilter")) @@ -183,10 +181,7 @@ func (r *ResourceEvaluationFilter) Read(ctx context.Context, request resource.Re } attributes := resp.Data.GetAttributes() - bytes, _ = json.MarshalIndent(attributes, "", " ") - fmt.Println("DEBUG attributes from API:", string(bytes)) r.UpdateState(ctx, &state, &attributes) - time.Sleep(1000 * time.Millisecond) // Save data into Terraform state response.Diagnostics.Append(response.State.Set(ctx, &state)...) @@ -218,10 +213,7 @@ func (r *ResourceEvaluationFilter) Update(ctx context.Context, request resource. } attributes := resp.Data.GetAttributes() - bytes, _ := json.MarshalIndent(resp.Data.GetAttributes(), "", " ") - fmt.Println("UPDATEEEEEEEEE response: ", string(bytes)) r.UpdateState(ctx, &state, &attributes) - time.Sleep(1000 * time.Millisecond) // Save data into Terraform state response.Diagnostics.Append(response.State.Set(ctx, &state)...) } @@ -252,9 +244,6 @@ func (r *ResourceEvaluationFilter) Delete(ctx context.Context, request resource. response.Diagnostics.AddError("response contains unparsedObject", err.Error()) return } - time.Sleep(1000 * time.Millisecond) - bytes, _ := json.MarshalIndent(resp.Data.GetAttributes(), "", " ") - fmt.Println("delete response: ", string(bytes)) } func (r *ResourceEvaluationFilter) ImportState( @@ -311,9 +300,6 @@ func (r *ResourceEvaluationFilter) buildUpdateResourceEvaluationFilterRequest(ct data.SetType(datadogV2.RESOURCEFILTERREQUESTTYPE_CSM_RESOURCE_FILTER) data.SetAttributes(attributes) - bytes, _ := json.MarshalIndent(attributes, "", " ") - fmt.Println("building update payload: ", string(bytes)) - req := datadogV2.NewUpdateResourceEvaluationFiltersRequestWithDefaults() req.SetData(*data) diff --git a/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go b/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go index 994c0e929..a96d22608 100644 --- a/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go +++ b/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go @@ -5,12 +5,10 @@ import ( "encoding/json" "errors" "fmt" + "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" "regexp" "strconv" "testing" - "time" - - "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" @@ -239,7 +237,6 @@ func testAccCheckResourceEvaluationFilterDestroy(accProvider *fwprovider.Framewo AccountId: &id, SkipCache: &skipCache, } - time.Sleep(1 * time.Second) response, httpResponse, err := apiInstances.GetSecurityMonitoringApiV2().GetResourceEvaluationFilters(auth, params) if err != nil { From ae91d243458110757fd8952d29cdc565289189f4 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Wed, 21 May 2025 09:31:46 +0100 Subject: [PATCH 05/21] adding documentation examples --- .../resources/datadog_resource_evaluation_filter/import.sh | 1 + .../datadog_resource_evaluation_filter/resource.tf | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 examples/resources/datadog_resource_evaluation_filter/import.sh create mode 100644 examples/resources/datadog_resource_evaluation_filter/resource.tf diff --git a/examples/resources/datadog_resource_evaluation_filter/import.sh b/examples/resources/datadog_resource_evaluation_filter/import.sh new file mode 100644 index 000000000..29fc8d948 --- /dev/null +++ b/examples/resources/datadog_resource_evaluation_filter/import.sh @@ -0,0 +1 @@ +terraform import datadog_resource_evaluation_filer.test_filter aws:00000000000000 diff --git a/examples/resources/datadog_resource_evaluation_filter/resource.tf b/examples/resources/datadog_resource_evaluation_filter/resource.tf new file mode 100644 index 000000000..92eb35217 --- /dev/null +++ b/examples/resources/datadog_resource_evaluation_filter/resource.tf @@ -0,0 +1,6 @@ +# Manage Datadog resource evaluation filters +resource "datadog_resource_evaluation_filter" "basic_filter" { + tags = ["tag1:val1"] + cloud_provider = "aws" + id = "000000000000" +} From bac3ce102e289b4744a24a1323c91c3bd8c3855c Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Wed, 21 May 2025 09:53:03 +0100 Subject: [PATCH 06/21] updating go client version --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8c7514cc4..cb2cde3ca 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,7 @@ module github.com/terraform-providers/terraform-provider-datadog require ( - github.com/DataDog/datadog-api-client-go/v2 v2.37.2-0.20250429195731-d6c3507540d3 + github.com/DataDog/datadog-api-client-go/v2 v2.37.2-0.20250520155755-ebb9ae45f17a github.com/DataDog/dd-sdk-go-testing v0.0.0-20211116174033-1cd082e322ad github.com/Masterminds/semver/v3 v3.2.0 github.com/google/go-cmp v0.7.0 diff --git a/go.sum b/go.sum index 353987f2e..b7c425112 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/datadog-api-client-go/v2 v2.37.2-0.20250429195731-d6c3507540d3 h1:rrnGH7u4UvqSSfjOEVVXgryezNX8KGgBFixpYrjf9n8= -github.com/DataDog/datadog-api-client-go/v2 v2.37.2-0.20250429195731-d6c3507540d3/go.mod h1:d3tOEgUd2kfsr9uuHQdY+nXrWp4uikgTgVCPdKNK30U= +github.com/DataDog/datadog-api-client-go/v2 v2.37.2-0.20250520155755-ebb9ae45f17a h1:fZ0C3ldliI+N7+/9M0NdsGuLYlmh5IrIhfh/VeiMGkM= +github.com/DataDog/datadog-api-client-go/v2 v2.37.2-0.20250520155755-ebb9ae45f17a/go.mod h1:d3tOEgUd2kfsr9uuHQdY+nXrWp4uikgTgVCPdKNK30U= github.com/DataDog/datadog-go v4.4.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v4.8.3+incompatible h1:fNGaYSuObuQb5nzeTQqowRAd9bpDIRRV4/gUtIBjh8Q= github.com/DataDog/datadog-go v4.8.3+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= From a0bd6f9e903effc5862dc8d82322b1534c2cc164 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Wed, 21 May 2025 10:00:56 +0100 Subject: [PATCH 07/21] pushing the docs changes --- docs/resources/resource_evaluation_filter.md | 42 ++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 docs/resources/resource_evaluation_filter.md diff --git a/docs/resources/resource_evaluation_filter.md b/docs/resources/resource_evaluation_filter.md new file mode 100644 index 000000000..0da7645ad --- /dev/null +++ b/docs/resources/resource_evaluation_filter.md @@ -0,0 +1,42 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "datadog_resource_evaluation_filter Resource - terraform-provider-datadog" +subcategory: "" +description: |- + Manage a single resource evaluation filter. +--- + +# datadog_resource_evaluation_filter (Resource) + +Manage a single resource evaluation filter. + +## Example Usage + +```terraform +# Manage Datadog resource evaluation filters +resource "datadog_resource_evaluation_filter" "basic_filter" { + tags = ["tag1:val1"] + cloud_provider = "aws" + id = "000000000000" +} +``` + + +## Schema + +### Required + +- `cloud_provider` (String) The cloud provider of the resource that will be target of the filters. +- `id` (String) The ID of the resource that will be the target of the filters. Different cloud providers target different resource ids: + - `aws`: account id + - `gcp`: project id + - `azure`: subscription id +- `tags` (Set of String) Set of tags to filter the misconfiguration detections on. Each entry should follow the format: "key:":"value". + +## Import + +Import is supported using the following syntax: + +```shell +terraform import datadog_resource_evaluation_filer.test_filter aws:00000000000000 +``` From 8adc19691fd52a3bdbc4472bef5973c9f938ca1c Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Wed, 21 May 2025 10:06:54 +0100 Subject: [PATCH 08/21] go fmt --- .../resource_datadog_csm_resource_evaluation_filter_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go b/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go index a96d22608..7b70e4754 100644 --- a/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go +++ b/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go @@ -5,11 +5,12 @@ import ( "encoding/json" "errors" "fmt" - "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" "regexp" "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" From a06ec10df1dd0fbb6997cdc41f720c3d7cd03f3b Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Wed, 21 May 2025 11:11:17 +0100 Subject: [PATCH 09/21] adding generated cassettes --- .../TestAccResourceEvaluationFilter.freeze | 2 +- .../TestAccResourceEvaluationFilter.yaml | 32 +++++++++---------- ...stAccResourceEvaluationFilterImport.freeze | 2 +- ...TestAccResourceEvaluationFilterImport.yaml | 24 +++++++------- ...tAccResourceEvaluationFilterInvalid.freeze | 2 +- ...estAccResourceEvaluationFilterInvalid.yaml | 2 +- 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze index debacb945..e8f6ba132 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze @@ -1 +1 @@ -2025-05-09T09:50:05.738305+01:00 \ No newline at end of file +2025-05-21T11:09:27.646789+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml index 087c062e7..8ddbeabce 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml @@ -36,7 +36,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 541.942292ms + duration: 526.824125ms - id: 1 request: proto: HTTP/1.1 @@ -53,7 +53,7 @@ interactions: headers: Accept: - application/json - url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws&skip_cache=true method: GET response: proto: HTTP/1.1 @@ -63,13 +63,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"58803539-0869-496a-b6f4-6770f9238b1b","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"9035e680-ab17-4064-a574-816aa83a4492","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 158.787208ms + duration: 157.36075ms - id: 2 request: proto: HTTP/1.1 @@ -86,7 +86,7 @@ interactions: headers: Accept: - application/json - url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws&skip_cache=true method: GET response: proto: HTTP/1.1 @@ -94,15 +94,15 @@ interactions: proto_minor: 1 transfer_encoding: [] trailer: {} - content_length: 118 + content_length: 175 uncompressed: false - body: '{"data":{"id":"3898bc60-1e96-4a1e-99f9-d2156a49bafd","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' + body: '{"data":{"id":"b6634a2f-8442-4ec3-8124-b910dc84ca1b","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 159.110583ms + duration: 152.993ms - id: 3 request: proto: HTTP/1.1 @@ -119,7 +119,7 @@ interactions: headers: Accept: - application/json - url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws&skip_cache=true method: GET response: proto: HTTP/1.1 @@ -129,13 +129,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"6f317b4f-ee80-4292-afef-9e73108ff04e","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"a5fe11a0-55fc-4924-96a6-1b63aa54f35b","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 159.659125ms + duration: 147.532875ms - id: 4 request: proto: HTTP/1.1 @@ -171,7 +171,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 179.546166ms + duration: 160.320375ms - id: 5 request: proto: HTTP/1.1 @@ -188,7 +188,7 @@ interactions: headers: Accept: - application/json - url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws&skip_cache=true method: GET response: proto: HTTP/1.1 @@ -196,12 +196,12 @@ interactions: proto_minor: 1 transfer_encoding: [] trailer: {} - content_length: 118 + content_length: 140 uncompressed: false - body: '{"data":{"id":"f31f3495-67d8-423a-8d1b-33b820dc56a7","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' + body: '{"data":{"id":"50d57174-7cd0-498d-a061-93b1e6f3a3fc","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":[]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 152.485417ms + duration: 155.017917ms diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze index 3c86a5f62..3288e1b4c 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze @@ -1 +1 @@ -2025-05-09T09:50:14.86783+01:00 \ No newline at end of file +2025-05-21T11:09:31.18075+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml index 3ffff9ba8..0b0d81ce3 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml @@ -36,7 +36,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 177.469292ms + duration: 183.254417ms - id: 1 request: proto: HTTP/1.1 @@ -53,7 +53,7 @@ interactions: headers: Accept: - application/json - url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=223456789&cloud_provider=aws + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=223456789&cloud_provider=aws&skip_cache=true method: GET response: proto: HTTP/1.1 @@ -61,15 +61,15 @@ interactions: proto_minor: 1 transfer_encoding: [] trailer: {} - content_length: 118 + content_length: 175 uncompressed: false - body: '{"data":{"id":"458856d4-de73-4984-aa57-e1e96211579b","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' + body: '{"data":{"id":"2f2579e4-cdcc-4fde-820b-899736a36606","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 146.258125ms + duration: 135.790208ms - id: 2 request: proto: HTTP/1.1 @@ -86,7 +86,7 @@ interactions: headers: Accept: - application/json - url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=223456789&cloud_provider=aws + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=223456789&cloud_provider=aws&skip_cache=true method: GET response: proto: HTTP/1.1 @@ -96,13 +96,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"edc41ca0-7e95-4749-ab4b-961c17f4ce49","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"5a612be1-2d77-4303-aae3-02ec17166dcb","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 142.834292ms + duration: 165.943333ms - id: 3 request: proto: HTTP/1.1 @@ -119,7 +119,7 @@ interactions: headers: Accept: - application/json - url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=223456789&cloud_provider=aws + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=223456789&cloud_provider=aws&skip_cache=true method: GET response: proto: HTTP/1.1 @@ -129,13 +129,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"fd3dfb9d-2c37-4c9a-a1ce-036809c1c4bc","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"b6ea2cd5-67b1-465c-ad09-5a93ce5c43f4","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 145.890792ms + duration: 134.249ms - id: 4 request: proto: HTTP/1.1 @@ -171,4 +171,4 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 172.632ms + duration: 173.737041ms diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze index 20c9a9408..63a3ef354 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze @@ -1 +1 @@ -2025-05-09T09:50:21.227124+01:00 \ No newline at end of file +2025-05-21T11:09:33.382955+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml index f45b51532..c38bd6d36 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml @@ -36,4 +36,4 @@ interactions: - application/vnd.api+json status: 400 Bad Request code: 400 - duration: 150.158083ms + duration: 126.587083ms From 9f1767e0f779a91b4318a7b0f7d228547131bbf7 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Thu, 22 May 2025 09:34:35 +0100 Subject: [PATCH 10/21] swapping from set to list to stay consistent with backend implementation of filters and prevent performance issues --- ..._datadog_csm_resource_evaluation_filter.go | 85 +++++------- .../TestAccResourceEvaluationFilter.freeze | 2 +- .../TestAccResourceEvaluationFilter.yaml | 124 ++++++++++++++++-- ...stAccResourceEvaluationFilterImport.freeze | 2 +- ...TestAccResourceEvaluationFilterImport.yaml | 16 +-- ...tAccResourceEvaluationFilterInvalid.freeze | 2 +- ...estAccResourceEvaluationFilterInvalid.yaml | 2 +- ...dog_csm_resource_evaluation_filter_test.go | 14 +- 8 files changed, 171 insertions(+), 76 deletions(-) diff --git a/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go b/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go index dd8fe5f96..5f8780229 100644 --- a/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go +++ b/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go @@ -6,23 +6,21 @@ import ( "regexp" "strings" - "github.com/hashicorp/terraform-plugin-framework/path" - + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" - - "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" - - "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" - "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" "github.com/hashicorp/terraform-plugin-framework/diag" + "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/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" + + "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" + "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" ) -type ResourceEvaluationFilter struct { +type CsmResourceEvaluationFilter struct { API *datadogV2.SecurityMonitoringApi Auth context.Context } @@ -30,25 +28,25 @@ type ResourceEvaluationFilter struct { type ResourceEvaluationFilterModel struct { CloudProvider types.String `tfsdk:"cloud_provider"` ID types.String `tfsdk:"id"` - Tags types.Set `tfsdk:"tags"` + Tags types.List `tfsdk:"tags"` } func NewResourceEvaluationFilter() resource.Resource { - return &ResourceEvaluationFilter{} + return &CsmResourceEvaluationFilter{} } var ( - _ resource.ResourceWithConfigure = &ResourceEvaluationFilter{} - _ resource.ResourceWithImportState = &ResourceEvaluationFilter{} + _ resource.ResourceWithConfigure = &CsmResourceEvaluationFilter{} + _ resource.ResourceWithImportState = &CsmResourceEvaluationFilter{} ) -func (r *ResourceEvaluationFilter) Configure(_ context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) { +func (r *CsmResourceEvaluationFilter) Configure(_ context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) { providerData, _ := request.ProviderData.(*FrameworkProvider) r.API = providerData.DatadogApiInstances.GetSecurityMonitoringApiV2() r.Auth = providerData.Auth } -func (r *ResourceEvaluationFilter) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { +func (r *CsmResourceEvaluationFilter) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = "resource_evaluation_filter" } @@ -57,18 +55,18 @@ var tagFormatValidator = stringvalidator.RegexMatches( "each tag must be in the format 'key:value' (colon-separated)", ) -func toSliceString(set types.Set) ([]string, diag.Diagnostics) { +func toSliceString(list types.List) ([]string, diag.Diagnostics) { var diags diag.Diagnostics result := make([]string, 0) - if set.IsNull() || set.IsUnknown() { + if list.IsNull() || list.IsUnknown() { return result, nil } - for _, elem := range set.Elements() { + for _, elem := range list.Elements() { strVal, ok := elem.(types.String) if !ok { - diags.AddError("Invalid element type", "Expected string in set but found a different type") + diags.AddError("Invalid element type", "Expected string in list but found a different type") continue } result = append(result, strVal.ValueString()) @@ -77,7 +75,7 @@ func toSliceString(set types.Set) ([]string, diag.Diagnostics) { return result, diags } -func (r *ResourceEvaluationFilter) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *CsmResourceEvaluationFilter) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Description: "Manage a single resource evaluation filter.", Attributes: map[string]schema.Attribute{ @@ -89,21 +87,20 @@ func (r *ResourceEvaluationFilter) Schema(_ context.Context, _ resource.SchemaRe Required: true, Description: "The ID of the resource that will be the target of the filters. Different cloud providers target different resource ids:\n - `aws`: account id \n - `gcp`: project id\n - `azure`: subscription id", }, - "tags": schema.SetAttribute{ + "tags": schema.ListAttribute{ Required: true, ElementType: types.StringType, - Validators: []validator.Set{ - setvalidator.ValueStringsAre(tagFormatValidator), + Validators: []validator.List{ + listvalidator.ValueStringsAre(tagFormatValidator), }, - Description: "Set of tags to filter the misconfiguration detections on. Each entry should follow the format: \"key:\":\"value\".", + Description: "List of tags to filter the misconfiguration detections on. Each entry should follow the format: 'key:value'.", }, }, } } -func (r *ResourceEvaluationFilter) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { +func (r *CsmResourceEvaluationFilter) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { var state ResourceEvaluationFilterModel - response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) if response.Diagnostics.HasError() { return @@ -116,7 +113,6 @@ func (r *ResourceEvaluationFilter) Create(ctx context.Context, request resource. } resp, _, err := r.API.UpdateResourceEvaluationFilters(r.Auth, *body) - if err != nil { response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error updating resource evaluation filter")) return @@ -128,7 +124,6 @@ func (r *ResourceEvaluationFilter) Create(ctx context.Context, request resource. attributes := resp.Data.GetAttributes() r.UpdateState(ctx, &state, &attributes) - // Save data into Terraform state response.Diagnostics.Append(response.State.Set(ctx, &state)...) } @@ -140,12 +135,10 @@ func convertStringSliceToAttrValues(s []string) []attr.Value { return out } -func (r *ResourceEvaluationFilter) UpdateState(_ context.Context, state *ResourceEvaluationFilterModel, attributes *datadogV2.ResourceFilterAttributes) { - // since we are handling a response after an update/read request, the cloud provider map will have at most one key - // and the map of each cloud provider will also have at most one key +func (r *CsmResourceEvaluationFilter) UpdateState(_ context.Context, state *ResourceEvaluationFilterModel, attributes *datadogV2.ResourceFilterAttributes) { for p, accounts := range attributes.CloudProvider { for id, tagList := range accounts { - tags := types.SetValueMust(types.StringType, convertStringSliceToAttrValues(tagList)) + tags := types.ListValueMust(types.StringType, convertStringSliceToAttrValues(tagList)) state.CloudProvider = types.StringValue(p) state.ID = types.StringValue(id) state.Tags = tags @@ -155,7 +148,7 @@ func (r *ResourceEvaluationFilter) UpdateState(_ context.Context, state *Resourc } } -func (r *ResourceEvaluationFilter) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { +func (r *CsmResourceEvaluationFilter) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { var state ResourceEvaluationFilterModel response.Diagnostics.Append(request.State.Get(ctx, &state)...) if response.Diagnostics.HasError() { @@ -166,6 +159,7 @@ func (r *ResourceEvaluationFilter) Read(ctx context.Context, request resource.Re response.Diagnostics.AddError("Missing cloud_provider", "cloud_provider is required for lookup") return } + provider := state.CloudProvider.ValueString() skipCache := true @@ -176,18 +170,16 @@ func (r *ResourceEvaluationFilter) Read(ctx context.Context, request resource.Re } resp, _, err := r.API.GetResourceEvaluationFilters(r.Auth, params) if err != nil { - response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving ResourceEvaluationFilter")) + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving CsmResourceEvaluationFilter")) return } attributes := resp.Data.GetAttributes() r.UpdateState(ctx, &state, &attributes) - - // Save data into Terraform state response.Diagnostics.Append(response.State.Set(ctx, &state)...) } -func (r *ResourceEvaluationFilter) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { +func (r *CsmResourceEvaluationFilter) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { var state ResourceEvaluationFilterModel response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) if response.Diagnostics.HasError() { @@ -196,7 +188,6 @@ func (r *ResourceEvaluationFilter) Update(ctx context.Context, request resource. fmt.Println("DEBUG UPDATE - creating payload") body, diags := r.buildUpdateResourceEvaluationFilterRequest(ctx, &state) - response.Diagnostics.Append(diags...) if response.Diagnostics.HasError() { return @@ -204,7 +195,7 @@ func (r *ResourceEvaluationFilter) Update(ctx context.Context, request resource. resp, _, err := r.API.UpdateResourceEvaluationFilters(r.Auth, *body) if err != nil { - response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error updating ResourceEvaluationFilter")) + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error updating CsmResourceEvaluationFilter")) return } if err := utils.CheckForUnparsed(resp); err != nil { @@ -214,21 +205,17 @@ func (r *ResourceEvaluationFilter) Update(ctx context.Context, request resource. attributes := resp.Data.GetAttributes() r.UpdateState(ctx, &state, &attributes) - // Save data into Terraform state response.Diagnostics.Append(response.State.Set(ctx, &state)...) } -func (r *ResourceEvaluationFilter) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { +func (r *CsmResourceEvaluationFilter) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { var state ResourceEvaluationFilterModel response.Diagnostics.Append(request.State.Get(ctx, &state)...) if response.Diagnostics.HasError() { return } - // empty tags - state.Tags = types.SetValueMust(types.StringType, []attr.Value{}) - - // create body as normal with empty tags + state.Tags = types.ListValueMust(types.StringType, []attr.Value{}) body, diags := r.buildUpdateResourceEvaluationFilterRequest(ctx, &state) response.Diagnostics.Append(diags...) if response.Diagnostics.HasError() { @@ -237,7 +224,7 @@ func (r *ResourceEvaluationFilter) Delete(ctx context.Context, request resource. resp, _, err := r.API.UpdateResourceEvaluationFilters(r.Auth, *body) if err != nil { - response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error deleting ResourceEvaluationFilter")) + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error deleting CsmResourceEvaluationFilter")) return } if err := utils.CheckForUnparsed(resp); err != nil { @@ -246,12 +233,11 @@ func (r *ResourceEvaluationFilter) Delete(ctx context.Context, request resource. } } -func (r *ResourceEvaluationFilter) ImportState( +func (r *CsmResourceEvaluationFilter) ImportState( ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, ) { - // Expected import format: "cloud_provider:id" (e.g., "aws:123456789") parts := strings.Split(req.ID, ":") if len(parts) != 2 { resp.Diagnostics.AddError( @@ -264,12 +250,11 @@ func (r *ResourceEvaluationFilter) ImportState( cloudProvider := parts[0] id := parts[1] - // Set attributes into Terraform state so Read() can hydrate the full resource resp.State.SetAttribute(ctx, path.Root("cloud_provider"), cloudProvider) resp.State.SetAttribute(ctx, path.Root("id"), id) } -func (r *ResourceEvaluationFilter) buildUpdateResourceEvaluationFilterRequest(ctx context.Context, state *ResourceEvaluationFilterModel) (*datadogV2.UpdateResourceEvaluationFiltersRequest, diag.Diagnostics) { +func (r *CsmResourceEvaluationFilter) buildUpdateResourceEvaluationFilterRequest(ctx context.Context, state *ResourceEvaluationFilterModel) (*datadogV2.UpdateResourceEvaluationFiltersRequest, diag.Diagnostics) { diags := diag.Diagnostics{} data := datadogV2.NewUpdateResourceEvaluationFiltersRequestDataWithDefaults() diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze index e8f6ba132..8787c45dd 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze @@ -1 +1 @@ -2025-05-21T11:09:27.646789+01:00 \ No newline at end of file +2025-05-22T09:33:15.988267+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml index 8ddbeabce..96f07e6ca 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml @@ -36,7 +36,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 526.824125ms + duration: 572.485416ms - id: 1 request: proto: HTTP/1.1 @@ -63,13 +63,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"9035e680-ab17-4064-a574-816aa83a4492","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"93188503-4438-4ca4-a1bb-a13c202fc9a7","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 157.36075ms + duration: 164.233875ms - id: 2 request: proto: HTTP/1.1 @@ -96,13 +96,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"b6634a2f-8442-4ec3-8124-b910dc84ca1b","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"fb682925-c67c-4dfc-b194-5ab4ef2ae18a","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 152.993ms + duration: 155.971792ms - id: 3 request: proto: HTTP/1.1 @@ -129,14 +129,116 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"a5fe11a0-55fc-4924-96a6-1b63aa54f35b","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"0fb48cea-0d82-4232-ad95-4f2c2ac96ca5","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 147.532875ms + duration: 167.042708ms - id: 4 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 159 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: | + {"data":{"attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}},"id":"csm_resource_filter","type":"csm_resource_filter"}} + form: {} + headers: + Accept: + - application/json + Content-Type: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters + method: PUT + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 204 + uncompressed: false + body: '{"data":{"id":"csm_resource_filter","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}},"uuid":"3ad549bf-eba0-11e9-a77a-0705486660d0"}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 201 Created + code: 201 + duration: 196.586041ms + - id: 5 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Accept: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws&skip_cache=true + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 175 + uncompressed: false + body: '{"data":{"id":"8d765924-52f9-427c-a4a4-9e437bb5f3fd","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 200 OK + code: 200 + duration: 165.923166ms + - id: 6 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Accept: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws&skip_cache=true + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 175 + uncompressed: false + body: '{"data":{"id":"8c41ffaf-c39f-4240-8af5-a160057ea39a","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 200 OK + code: 200 + duration: 144.249166ms + - id: 7 request: proto: HTTP/1.1 proto_major: 1 @@ -171,8 +273,8 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 160.320375ms - - id: 5 + duration: 181.923208ms + - id: 8 request: proto: HTTP/1.1 proto_major: 1 @@ -198,10 +300,10 @@ interactions: trailer: {} content_length: 140 uncompressed: false - body: '{"data":{"id":"50d57174-7cd0-498d-a061-93b1e6f3a3fc","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":[]}}}}}' + body: '{"data":{"id":"a2150b11-a52f-4584-8a9e-bdbe959d8b82","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":[]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 155.017917ms + duration: 161.934583ms diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze index 3288e1b4c..90b7eb6ee 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze @@ -1 +1 @@ -2025-05-21T11:09:31.18075+01:00 \ No newline at end of file +2025-05-22T09:33:20.762262+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml index 0b0d81ce3..1399a0c88 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml @@ -36,7 +36,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 183.254417ms + duration: 168.291708ms - id: 1 request: proto: HTTP/1.1 @@ -63,13 +63,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"2f2579e4-cdcc-4fde-820b-899736a36606","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"55d6615a-7b8b-453f-ba78-27d2ad05ef25","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 135.790208ms + duration: 171.787833ms - id: 2 request: proto: HTTP/1.1 @@ -96,13 +96,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"5a612be1-2d77-4303-aae3-02ec17166dcb","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"278ca348-e6aa-482c-bea8-09f551de96a7","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 165.943333ms + duration: 157.403042ms - id: 3 request: proto: HTTP/1.1 @@ -129,13 +129,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"b6ea2cd5-67b1-465c-ad09-5a93ce5c43f4","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"0fdcb999-f497-4ed6-a578-4cbdb88664e4","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 134.249ms + duration: 154.992625ms - id: 4 request: proto: HTTP/1.1 @@ -171,4 +171,4 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 173.737041ms + duration: 169.999958ms diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze index 63a3ef354..fc7214de4 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze @@ -1 +1 @@ -2025-05-21T11:09:33.382955+01:00 \ No newline at end of file +2025-05-22T09:33:22.99662+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml index c38bd6d36..0377e1e72 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml @@ -36,4 +36,4 @@ interactions: - application/vnd.api+json status: 400 Bad Request code: 400 - duration: 126.587083ms + duration: 140.015667ms diff --git a/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go b/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go index 7b70e4754..5424880d1 100644 --- a/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go +++ b/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go @@ -23,6 +23,7 @@ func TestAccResourceEvaluationFilter(t *testing.T) { accountId := "123456789" resourceName := "datadog_resource_evaluation_filter.filter_test" simpleTags := []string{"tag1:val1", "tag2:val2", "tag3:val3"} + reorderedTags := []string{"tag3:val3", "tag2:val2", "tag1:val1"} provider := "aws" //invalidProvider := "invalid" @@ -59,9 +60,16 @@ func TestAccResourceEvaluationFilter(t *testing.T) { id = "%s" } `, provider, accountId), - // This should not trigger a diff or update because tags are now a set - ExpectNonEmptyPlan: false, - PlanOnly: true, + // This should trigger a diff or update because tags are now a list + Check: resource.ComposeTestCheckFunc( + testAccCheckResourceEvaluationFilterExists(providers.frameworkProvider, resourceName), + checkResourceEvaluationFilterContent( + resourceName, + accountId, + provider, + reorderedTags, + ), + ), }, }, }) From 25460bf93dc355c8532e284220387699cbc00c9f Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Thu, 22 May 2025 10:13:33 +0100 Subject: [PATCH 11/21] applying suggested documentation changes. --- .../resource_datadog_csm_resource_evaluation_filter.go | 7 ++++--- docs/resources/resource_evaluation_filter.md | 8 ++++---- .../datadog_resource_evaluation_filter/import.sh | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go b/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go index 5f8780229..116d1e471 100644 --- a/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go +++ b/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/DataDog/datadog-api-client-go/v2/api/datadogV2" + "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" ) @@ -81,11 +82,11 @@ func (r *CsmResourceEvaluationFilter) Schema(_ context.Context, _ resource.Schem Attributes: map[string]schema.Attribute{ "cloud_provider": schema.StringAttribute{ Required: true, - Description: "The cloud provider of the resource that will be target of the filters.", + Description: "The cloud provider of the filter's targeted resource", }, "id": schema.StringAttribute{ Required: true, - Description: "The ID of the resource that will be the target of the filters. Different cloud providers target different resource ids:\n - `aws`: account id \n - `gcp`: project id\n - `azure`: subscription id", + Description: "The ID of the of the filter's targeted resource. Different cloud providers target different resource IDs\n - `aws`: account id \n - `gcp`: project id\n - `azure`: subscription id", }, "tags": schema.ListAttribute{ Required: true, @@ -93,7 +94,7 @@ func (r *CsmResourceEvaluationFilter) Schema(_ context.Context, _ resource.Schem Validators: []validator.List{ listvalidator.ValueStringsAre(tagFormatValidator), }, - Description: "List of tags to filter the misconfiguration detections on. Each entry should follow the format: 'key:value'.", + Description: "List of tags to filter misconfiguration detections. Each entry should follow the format: \"key:\":\"value\".", }, }, } diff --git a/docs/resources/resource_evaluation_filter.md b/docs/resources/resource_evaluation_filter.md index 0da7645ad..b3701537b 100644 --- a/docs/resources/resource_evaluation_filter.md +++ b/docs/resources/resource_evaluation_filter.md @@ -26,17 +26,17 @@ resource "datadog_resource_evaluation_filter" "basic_filter" { ### Required -- `cloud_provider` (String) The cloud provider of the resource that will be target of the filters. -- `id` (String) The ID of the resource that will be the target of the filters. Different cloud providers target different resource ids: +- `cloud_provider` (String) The cloud provider of the filter's targeted resource +- `id` (String) The ID of the of the filter's targeted resource. Different cloud providers target different resource IDs - `aws`: account id - `gcp`: project id - `azure`: subscription id -- `tags` (Set of String) Set of tags to filter the misconfiguration detections on. Each entry should follow the format: "key:":"value". +- `tags` (List of String) List of tags to filter misconfiguration detections. Each entry should follow the format: "key:":"value". ## Import Import is supported using the following syntax: ```shell -terraform import datadog_resource_evaluation_filer.test_filter aws:00000000000000 +terraform import datadog_resource_evaluation_filter.test_filter aws:00000000000000 ``` diff --git a/examples/resources/datadog_resource_evaluation_filter/import.sh b/examples/resources/datadog_resource_evaluation_filter/import.sh index 29fc8d948..4ac172717 100644 --- a/examples/resources/datadog_resource_evaluation_filter/import.sh +++ b/examples/resources/datadog_resource_evaluation_filter/import.sh @@ -1 +1 @@ -terraform import datadog_resource_evaluation_filer.test_filter aws:00000000000000 +terraform import datadog_resource_evaluation_filter.test_filter aws:00000000000000 From 9e97616a870b8ec36d431d427e1a0e2d062fa0ff Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Thu, 22 May 2025 10:34:16 +0100 Subject: [PATCH 12/21] adding missing ponctuation --- .../resource_datadog_csm_resource_evaluation_filter.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go b/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go index 116d1e471..d06220185 100644 --- a/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go +++ b/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go @@ -82,11 +82,11 @@ func (r *CsmResourceEvaluationFilter) Schema(_ context.Context, _ resource.Schem Attributes: map[string]schema.Attribute{ "cloud_provider": schema.StringAttribute{ Required: true, - Description: "The cloud provider of the filter's targeted resource", + Description: "The cloud provider of the filter's targeted resource.", }, "id": schema.StringAttribute{ Required: true, - Description: "The ID of the of the filter's targeted resource. Different cloud providers target different resource IDs\n - `aws`: account id \n - `gcp`: project id\n - `azure`: subscription id", + Description: "The ID of the of the filter's targeted resource. Different cloud providers target different resource IDs:\n - `aws`: account id \n - `gcp`: project id\n - `azure`: subscription id", }, "tags": schema.ListAttribute{ Required: true, @@ -94,7 +94,7 @@ func (r *CsmResourceEvaluationFilter) Schema(_ context.Context, _ resource.Schem Validators: []validator.List{ listvalidator.ValueStringsAre(tagFormatValidator), }, - Description: "List of tags to filter misconfiguration detections. Each entry should follow the format: \"key:\":\"value\".", + Description: "List of tags to filter misconfiguration detections. Each entry should follow the format: \"key\":\"value\".", }, }, } From f121dc2ac192cc79059a6c872c73aed219f2b663 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Thu, 22 May 2025 10:39:29 +0100 Subject: [PATCH 13/21] pushing missing doc --- docs/resources/resource_evaluation_filter.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/resources/resource_evaluation_filter.md b/docs/resources/resource_evaluation_filter.md index b3701537b..e991521bf 100644 --- a/docs/resources/resource_evaluation_filter.md +++ b/docs/resources/resource_evaluation_filter.md @@ -26,12 +26,12 @@ resource "datadog_resource_evaluation_filter" "basic_filter" { ### Required -- `cloud_provider` (String) The cloud provider of the filter's targeted resource -- `id` (String) The ID of the of the filter's targeted resource. Different cloud providers target different resource IDs +- `cloud_provider` (String) The cloud provider of the filter's targeted resource. +- `id` (String) The ID of the of the filter's targeted resource. Different cloud providers target different resource IDs: - `aws`: account id - `gcp`: project id - `azure`: subscription id -- `tags` (List of String) List of tags to filter misconfiguration detections. Each entry should follow the format: "key:":"value". +- `tags` (List of String) List of tags to filter misconfiguration detections. Each entry should follow the format: "key":"value". ## Import From 8913363fb2b3ff6b2c7aea2051a3b170969f1e00 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Thu, 22 May 2025 15:52:20 +0100 Subject: [PATCH 14/21] renaming resource --- ..._compliance_resource_evaluation_filter.go} | 36 +++++++++---------- .../TestAccResourceEvaluationFilter.freeze | 2 +- .../TestAccResourceEvaluationFilter.yaml | 30 ++++++++-------- ...stAccResourceEvaluationFilterImport.freeze | 2 +- ...TestAccResourceEvaluationFilterImport.yaml | 16 ++++----- ...tAccResourceEvaluationFilterInvalid.freeze | 2 +- ...estAccResourceEvaluationFilterInvalid.yaml | 2 +- datadog/tests/provider_test.go | 2 +- ...liance_resource_evaluation_filter_test.go} | 20 +++++------ ... compliance_resource_evaluation_filter.md} | 21 ++--------- .../import.sh | 2 +- .../resource.tf | 2 +- 12 files changed, 60 insertions(+), 77 deletions(-) rename datadog/fwprovider/{resource_datadog_csm_resource_evaluation_filter.go => resource_datadog_compliance_resource_evaluation_filter.go} (81%) rename datadog/tests/{resource_datadog_csm_resource_evaluation_filter_test.go => resource_datadog_compliance_resource_evaluation_filter_test.go} (90%) rename docs/resources/{resource_evaluation_filter.md => compliance_resource_evaluation_filter.md} (56%) diff --git a/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go b/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go similarity index 81% rename from datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go rename to datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go index d06220185..a3aace4a3 100644 --- a/datadog/fwprovider/resource_datadog_csm_resource_evaluation_filter.go +++ b/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go @@ -21,7 +21,7 @@ import ( "github.com/terraform-providers/terraform-provider-datadog/datadog/internal/utils" ) -type CsmResourceEvaluationFilter struct { +type ComplianceResourceEvaluationFilter struct { API *datadogV2.SecurityMonitoringApi Auth context.Context } @@ -33,22 +33,22 @@ type ResourceEvaluationFilterModel struct { } func NewResourceEvaluationFilter() resource.Resource { - return &CsmResourceEvaluationFilter{} + return &ComplianceResourceEvaluationFilter{} } var ( - _ resource.ResourceWithConfigure = &CsmResourceEvaluationFilter{} - _ resource.ResourceWithImportState = &CsmResourceEvaluationFilter{} + _ resource.ResourceWithConfigure = &ComplianceResourceEvaluationFilter{} + _ resource.ResourceWithImportState = &ComplianceResourceEvaluationFilter{} ) -func (r *CsmResourceEvaluationFilter) Configure(_ context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) { +func (r *ComplianceResourceEvaluationFilter) Configure(_ context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) { providerData, _ := request.ProviderData.(*FrameworkProvider) r.API = providerData.DatadogApiInstances.GetSecurityMonitoringApiV2() r.Auth = providerData.Auth } -func (r *CsmResourceEvaluationFilter) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = "resource_evaluation_filter" +func (r *ComplianceResourceEvaluationFilter) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "compliance_resource_evaluation_filter" } var tagFormatValidator = stringvalidator.RegexMatches( @@ -76,7 +76,7 @@ func toSliceString(list types.List) ([]string, diag.Diagnostics) { return result, diags } -func (r *CsmResourceEvaluationFilter) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *ComplianceResourceEvaluationFilter) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Description: "Manage a single resource evaluation filter.", Attributes: map[string]schema.Attribute{ @@ -100,7 +100,7 @@ func (r *CsmResourceEvaluationFilter) Schema(_ context.Context, _ resource.Schem } } -func (r *CsmResourceEvaluationFilter) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { +func (r *ComplianceResourceEvaluationFilter) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { var state ResourceEvaluationFilterModel response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) if response.Diagnostics.HasError() { @@ -136,7 +136,7 @@ func convertStringSliceToAttrValues(s []string) []attr.Value { return out } -func (r *CsmResourceEvaluationFilter) UpdateState(_ context.Context, state *ResourceEvaluationFilterModel, attributes *datadogV2.ResourceFilterAttributes) { +func (r *ComplianceResourceEvaluationFilter) UpdateState(_ context.Context, state *ResourceEvaluationFilterModel, attributes *datadogV2.ResourceFilterAttributes) { for p, accounts := range attributes.CloudProvider { for id, tagList := range accounts { tags := types.ListValueMust(types.StringType, convertStringSliceToAttrValues(tagList)) @@ -149,7 +149,7 @@ func (r *CsmResourceEvaluationFilter) UpdateState(_ context.Context, state *Reso } } -func (r *CsmResourceEvaluationFilter) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { +func (r *ComplianceResourceEvaluationFilter) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { var state ResourceEvaluationFilterModel response.Diagnostics.Append(request.State.Get(ctx, &state)...) if response.Diagnostics.HasError() { @@ -171,7 +171,7 @@ func (r *CsmResourceEvaluationFilter) Read(ctx context.Context, request resource } resp, _, err := r.API.GetResourceEvaluationFilters(r.Auth, params) if err != nil { - response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving CsmResourceEvaluationFilter")) + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error retrieving ComplianceResourceEvaluationFilter")) return } @@ -180,7 +180,7 @@ func (r *CsmResourceEvaluationFilter) Read(ctx context.Context, request resource response.Diagnostics.Append(response.State.Set(ctx, &state)...) } -func (r *CsmResourceEvaluationFilter) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { +func (r *ComplianceResourceEvaluationFilter) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { var state ResourceEvaluationFilterModel response.Diagnostics.Append(request.Plan.Get(ctx, &state)...) if response.Diagnostics.HasError() { @@ -196,7 +196,7 @@ func (r *CsmResourceEvaluationFilter) Update(ctx context.Context, request resour resp, _, err := r.API.UpdateResourceEvaluationFilters(r.Auth, *body) if err != nil { - response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error updating CsmResourceEvaluationFilter")) + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error updating ComplianceResourceEvaluationFilter")) return } if err := utils.CheckForUnparsed(resp); err != nil { @@ -209,7 +209,7 @@ func (r *CsmResourceEvaluationFilter) Update(ctx context.Context, request resour response.Diagnostics.Append(response.State.Set(ctx, &state)...) } -func (r *CsmResourceEvaluationFilter) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { +func (r *ComplianceResourceEvaluationFilter) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { var state ResourceEvaluationFilterModel response.Diagnostics.Append(request.State.Get(ctx, &state)...) if response.Diagnostics.HasError() { @@ -225,7 +225,7 @@ func (r *CsmResourceEvaluationFilter) Delete(ctx context.Context, request resour resp, _, err := r.API.UpdateResourceEvaluationFilters(r.Auth, *body) if err != nil { - response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error deleting CsmResourceEvaluationFilter")) + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error deleting ComplianceResourceEvaluationFilter")) return } if err := utils.CheckForUnparsed(resp); err != nil { @@ -234,7 +234,7 @@ func (r *CsmResourceEvaluationFilter) Delete(ctx context.Context, request resour } } -func (r *CsmResourceEvaluationFilter) ImportState( +func (r *ComplianceResourceEvaluationFilter) ImportState( ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse, @@ -255,7 +255,7 @@ func (r *CsmResourceEvaluationFilter) ImportState( resp.State.SetAttribute(ctx, path.Root("id"), id) } -func (r *CsmResourceEvaluationFilter) buildUpdateResourceEvaluationFilterRequest(ctx context.Context, state *ResourceEvaluationFilterModel) (*datadogV2.UpdateResourceEvaluationFiltersRequest, diag.Diagnostics) { +func (r *ComplianceResourceEvaluationFilter) buildUpdateResourceEvaluationFilterRequest(ctx context.Context, state *ResourceEvaluationFilterModel) (*datadogV2.UpdateResourceEvaluationFiltersRequest, diag.Diagnostics) { diags := diag.Diagnostics{} data := datadogV2.NewUpdateResourceEvaluationFiltersRequestDataWithDefaults() diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze index 8787c45dd..476c02a2f 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze @@ -1 +1 @@ -2025-05-22T09:33:15.988267+01:00 \ No newline at end of file +2025-05-22T15:29:52.616608+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml index 96f07e6ca..d35e6c4c7 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml @@ -36,7 +36,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 572.485416ms + duration: 513.520667ms - id: 1 request: proto: HTTP/1.1 @@ -63,13 +63,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"93188503-4438-4ca4-a1bb-a13c202fc9a7","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"a9cf4842-ed7a-4ed0-bad1-7dc7af2ea5da","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 164.233875ms + duration: 153.7085ms - id: 2 request: proto: HTTP/1.1 @@ -96,13 +96,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"fb682925-c67c-4dfc-b194-5ab4ef2ae18a","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"4d4063eb-e529-41ff-b39a-3994c6700295","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 155.971792ms + duration: 149.562875ms - id: 3 request: proto: HTTP/1.1 @@ -129,13 +129,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"0fb48cea-0d82-4232-ad95-4f2c2ac96ca5","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"9ee02489-0894-4c16-a714-bba19277152a","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 167.042708ms + duration: 147.901583ms - id: 4 request: proto: HTTP/1.1 @@ -171,7 +171,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 196.586041ms + duration: 164.101333ms - id: 5 request: proto: HTTP/1.1 @@ -198,13 +198,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"8d765924-52f9-427c-a4a4-9e437bb5f3fd","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' + body: '{"data":{"id":"5d5f19d5-3a69-47ca-9b96-2e8e8cc31159","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 165.923166ms + duration: 133.716125ms - id: 6 request: proto: HTTP/1.1 @@ -231,13 +231,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"8c41ffaf-c39f-4240-8af5-a160057ea39a","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' + body: '{"data":{"id":"e8190d78-8ada-448c-a6a7-31fa1ef75d29","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 144.249166ms + duration: 152.102166ms - id: 7 request: proto: HTTP/1.1 @@ -273,7 +273,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 181.923208ms + duration: 166.117416ms - id: 8 request: proto: HTTP/1.1 @@ -300,10 +300,10 @@ interactions: trailer: {} content_length: 140 uncompressed: false - body: '{"data":{"id":"a2150b11-a52f-4584-8a9e-bdbe959d8b82","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":[]}}}}}' + body: '{"data":{"id":"612696d3-b877-40b7-9b58-699cd5fcba43","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":[]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 161.934583ms + duration: 144.21425ms diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze index 90b7eb6ee..1f8a7e352 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze @@ -1 +1 @@ -2025-05-22T09:33:20.762262+01:00 \ No newline at end of file +2025-05-22T15:29:56.944065+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml index 1399a0c88..358a2da28 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml @@ -36,7 +36,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 168.291708ms + duration: 158.263ms - id: 1 request: proto: HTTP/1.1 @@ -63,13 +63,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"55d6615a-7b8b-453f-ba78-27d2ad05ef25","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"feae3ec8-99c4-4a7a-97a9-51b8a9b1b0bf","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 171.787833ms + duration: 147.323625ms - id: 2 request: proto: HTTP/1.1 @@ -96,13 +96,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"278ca348-e6aa-482c-bea8-09f551de96a7","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"c1f73230-6f37-4713-9b13-179049db6f9a","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 157.403042ms + duration: 145.40975ms - id: 3 request: proto: HTTP/1.1 @@ -129,13 +129,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"0fdcb999-f497-4ed6-a578-4cbdb88664e4","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"5879db92-6496-4167-ad9e-ac338277964b","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 154.992625ms + duration: 143.792208ms - id: 4 request: proto: HTTP/1.1 @@ -171,4 +171,4 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 169.999958ms + duration: 172.2335ms diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze index fc7214de4..550e0865e 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze @@ -1 +1 @@ -2025-05-22T09:33:22.99662+01:00 \ No newline at end of file +2025-05-22T15:29:59.12083+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml index 0377e1e72..7227979e6 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml @@ -36,4 +36,4 @@ interactions: - application/vnd.api+json status: 400 Bad Request code: 400 - duration: 140.015667ms + duration: 126.966458ms diff --git a/datadog/tests/provider_test.go b/datadog/tests/provider_test.go index 1324b7db3..3cd98eab7 100644 --- a/datadog/tests/provider_test.go +++ b/datadog/tests/provider_test.go @@ -267,7 +267,7 @@ var testFiles2EndpointTags = map[string]string{ "tests/resource_datadog_webhook_custom_variable_test": "webhook_custom_variable", "tests/resource_datadog_webhook_test": "webhook", "tests/resource_datadog_workflow_automation_test": "workflow_automation", - "tests/resource_datadog_csm_resource_evaluation_filter_test": "resource_filters", + "tests/resource_datadog_compliance_resource_evaluation_filter_test": "resource_filters", } // getEndpointTagValue traverses callstack frames to find the test function that invoked this call; diff --git a/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go b/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go similarity index 90% rename from datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go rename to datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go index 5424880d1..96dfcc85e 100644 --- a/datadog/tests/resource_datadog_csm_resource_evaluation_filter_test.go +++ b/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go @@ -21,7 +21,7 @@ func TestAccResourceEvaluationFilter(t *testing.T) { _, providers, accProviders := testAccFrameworkMuxProviders(context.Background(), t) accountId := "123456789" - resourceName := "datadog_resource_evaluation_filter.filter_test" + resourceName := "datadog_compliance_resource_evaluation_filter.filter_test" simpleTags := []string{"tag1:val1", "tag2:val2", "tag3:val3"} reorderedTags := []string{"tag3:val3", "tag2:val2", "tag1:val1"} provider := "aws" @@ -34,7 +34,7 @@ func TestAccResourceEvaluationFilter(t *testing.T) { Steps: []resource.TestStep{ { Config: fmt.Sprintf(` - resource "datadog_resource_evaluation_filter" "filter_test" { + resource "datadog_compliance_resource_evaluation_filter" "filter_test" { tags = ["tag1:val1", "tag2:val2", "tag3:val3"] cloud_provider = "%s" id = "%s" @@ -54,7 +54,7 @@ func TestAccResourceEvaluationFilter(t *testing.T) { { // Same tags as step 1 but reordered Config: fmt.Sprintf(` - resource "datadog_resource_evaluation_filter" "filter_test" { + resource "datadog_compliance_resource_evaluation_filter" "filter_test" { tags = ["tag3:val3", "tag1:val1", "tag2:val2"] cloud_provider = "%s" id = "%s" @@ -79,7 +79,7 @@ func TestAccResourceEvaluationFilterImport(t *testing.T) { _, providers, accProviders := testAccFrameworkMuxProviders(context.Background(), t) accountId := "223456789" - resourceName := "datadog_resource_evaluation_filter.filter_test" + resourceName := "datadog_compliance_resource_evaluation_filter.filter_test" simpleTags := []string{"tag1:val1", "tag2:val2", "tag3:val3"} provider := "aws" //invalidProvider := "invalid" @@ -90,7 +90,7 @@ func TestAccResourceEvaluationFilterImport(t *testing.T) { Steps: []resource.TestStep{ { Config: fmt.Sprintf(` - resource "datadog_resource_evaluation_filter" "filter_test" { + resource "datadog_compliance_resource_evaluation_filter" "filter_test" { tags = ["tag1:val1", "tag2:val2", "tag3:val3"] cloud_provider = "%s" id = "%s" @@ -129,7 +129,7 @@ func TestAccResourceEvaluationFilterInvalid(t *testing.T) { Steps: []resource.TestStep{ { Config: fmt.Sprintf(` - resource "datadog_resource_evaluation_filter" "filter_test" { + resource "datadog_compliance_resource_evaluation_filter" "filter_test" { tags = ["tag1:val1", "invalidTag:asdasf:InvalidTag", "tag3:val3"] cloud_provider = "%s" id = "%s" @@ -140,7 +140,7 @@ func TestAccResourceEvaluationFilterInvalid(t *testing.T) { }, { Config: fmt.Sprintf(` - resource "datadog_resource_evaluation_filter" "filter_test" { + resource "datadog_compliance_resource_evaluation_filter" "filter_test" { tags = ["tag1:val1", "tag3:val3"] cloud_provider = "%s" id = "%s" @@ -206,8 +206,8 @@ func testAccCheckResourceEvaluationFilterExists(accProvider *fwprovider.Framewor return fmt.Errorf("resource '%s' not found in the state %s", resourceName, s.RootModule().Resources) } - if r.Type != "datadog_resource_evaluation_filter" { - return fmt.Errorf("resource %s is not of type datadog_resource_evaluation_filter, found %s instead", resourceName, r.Type) + if r.Type != "datadog_compliance_resource_evaluation_filter" { + return fmt.Errorf("resource %s is not of type datadog_compliance_resource_evaluation_filter, found %s instead", resourceName, r.Type) } auth := accProvider.Auth @@ -236,7 +236,7 @@ func testAccCheckResourceEvaluationFilterDestroy(accProvider *fwprovider.Framewo apiInstances := accProvider.DatadogApiInstances for _, r := range s.RootModule().Resources { - if r.Type == "datadog_resource_evaluation_filter" { + if r.Type == "datadog_compliance_resource_evaluation_filter" { provider := r.Primary.Attributes["cloud_provider"] id := r.Primary.Attributes["id"] skipCache := true diff --git a/docs/resources/resource_evaluation_filter.md b/docs/resources/compliance_resource_evaluation_filter.md similarity index 56% rename from docs/resources/resource_evaluation_filter.md rename to docs/resources/compliance_resource_evaluation_filter.md index e991521bf..cac6e78b5 100644 --- a/docs/resources/resource_evaluation_filter.md +++ b/docs/resources/compliance_resource_evaluation_filter.md @@ -1,25 +1,16 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "datadog_resource_evaluation_filter Resource - terraform-provider-datadog" +page_title: "datadog_compliance_resource_evaluation_filter Resource - terraform-provider-datadog" subcategory: "" description: |- Manage a single resource evaluation filter. --- -# datadog_resource_evaluation_filter (Resource) +# datadog_compliance_resource_evaluation_filter (Resource) Manage a single resource evaluation filter. -## Example Usage -```terraform -# Manage Datadog resource evaluation filters -resource "datadog_resource_evaluation_filter" "basic_filter" { - tags = ["tag1:val1"] - cloud_provider = "aws" - id = "000000000000" -} -``` ## Schema @@ -32,11 +23,3 @@ resource "datadog_resource_evaluation_filter" "basic_filter" { - `gcp`: project id - `azure`: subscription id - `tags` (List of String) List of tags to filter misconfiguration detections. Each entry should follow the format: "key":"value". - -## Import - -Import is supported using the following syntax: - -```shell -terraform import datadog_resource_evaluation_filter.test_filter aws:00000000000000 -``` diff --git a/examples/resources/datadog_resource_evaluation_filter/import.sh b/examples/resources/datadog_resource_evaluation_filter/import.sh index 4ac172717..aa733b214 100644 --- a/examples/resources/datadog_resource_evaluation_filter/import.sh +++ b/examples/resources/datadog_resource_evaluation_filter/import.sh @@ -1 +1 @@ -terraform import datadog_resource_evaluation_filter.test_filter aws:00000000000000 +terraform import datadog_compliance_resource_evaluation_filter.test_filter aws:00000000000000 diff --git a/examples/resources/datadog_resource_evaluation_filter/resource.tf b/examples/resources/datadog_resource_evaluation_filter/resource.tf index 92eb35217..5e638c9ef 100644 --- a/examples/resources/datadog_resource_evaluation_filter/resource.tf +++ b/examples/resources/datadog_resource_evaluation_filter/resource.tf @@ -1,5 +1,5 @@ # Manage Datadog resource evaluation filters -resource "datadog_resource_evaluation_filter" "basic_filter" { +resource "datadog_compliance_resource_evaluation_filter" "basic_filter" { tags = ["tag1:val1"] cloud_provider = "aws" id = "000000000000" From 8123dc6cbaf275d5d6939d9d246ee8fe0a7ea4af Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Thu, 22 May 2025 16:02:20 +0100 Subject: [PATCH 15/21] removed debug line. Addressed various comments. --- ...g_compliance_resource_evaluation_filter.go | 6 ++--- .../compliance_resource_evaluation_filter.md | 23 ++++++++++++++++--- .../import.sh | 0 .../resource.tf | 0 4 files changed, 22 insertions(+), 7 deletions(-) rename examples/resources/{datadog_resource_evaluation_filter => datadog_compliance_resource_evaluation_filter}/import.sh (100%) rename examples/resources/{datadog_resource_evaluation_filter => datadog_compliance_resource_evaluation_filter}/resource.tf (100%) diff --git a/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go b/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go index a3aace4a3..cf8bba479 100644 --- a/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go +++ b/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go @@ -2,7 +2,6 @@ package fwprovider import ( "context" - "fmt" "regexp" "strings" @@ -78,11 +77,11 @@ func toSliceString(list types.List) ([]string, diag.Diagnostics) { func (r *ComplianceResourceEvaluationFilter) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - Description: "Manage a single resource evaluation filter.", + Description: "Provides a Datadog ResourceEvaluationFilter resource. This can be used to create and manage a resource evaluation filter.", Attributes: map[string]schema.Attribute{ "cloud_provider": schema.StringAttribute{ Required: true, - Description: "The cloud provider of the filter's targeted resource.", + Description: "The cloud provider of the filter's targeted resource. Only `aws`, `gcp` or `azure` are considered valid cloud providers.", }, "id": schema.StringAttribute{ Required: true, @@ -187,7 +186,6 @@ func (r *ComplianceResourceEvaluationFilter) Update(ctx context.Context, request return } - fmt.Println("DEBUG UPDATE - creating payload") body, diags := r.buildUpdateResourceEvaluationFilterRequest(ctx, &state) response.Diagnostics.Append(diags...) if response.Diagnostics.HasError() { diff --git a/docs/resources/compliance_resource_evaluation_filter.md b/docs/resources/compliance_resource_evaluation_filter.md index cac6e78b5..56b35c0ed 100644 --- a/docs/resources/compliance_resource_evaluation_filter.md +++ b/docs/resources/compliance_resource_evaluation_filter.md @@ -3,23 +3,40 @@ page_title: "datadog_compliance_resource_evaluation_filter Resource - terraform-provider-datadog" subcategory: "" description: |- - Manage a single resource evaluation filter. + Provides a Datadog ResourceEvaluationFilter resource. This can be used to create and manage a resource evaluation filter. --- # datadog_compliance_resource_evaluation_filter (Resource) -Manage a single resource evaluation filter. +Provides a Datadog ResourceEvaluationFilter resource. This can be used to create and manage a resource evaluation filter. +## Example Usage +```terraform +# Manage Datadog resource evaluation filters +resource "datadog_compliance_resource_evaluation_filter" "basic_filter" { + tags = ["tag1:val1"] + cloud_provider = "aws" + id = "000000000000" +} +``` ## Schema ### Required -- `cloud_provider` (String) The cloud provider of the filter's targeted resource. +- `cloud_provider` (String) The cloud provider of the filter's targeted resource. Only `aws`, `gcp` or `azure` are considered valid cloud providers. - `id` (String) The ID of the of the filter's targeted resource. Different cloud providers target different resource IDs: - `aws`: account id - `gcp`: project id - `azure`: subscription id - `tags` (List of String) List of tags to filter misconfiguration detections. Each entry should follow the format: "key":"value". + +## Import + +Import is supported using the following syntax: + +```shell +terraform import datadog_compliance_resource_evaluation_filter.test_filter aws:00000000000000 +``` diff --git a/examples/resources/datadog_resource_evaluation_filter/import.sh b/examples/resources/datadog_compliance_resource_evaluation_filter/import.sh similarity index 100% rename from examples/resources/datadog_resource_evaluation_filter/import.sh rename to examples/resources/datadog_compliance_resource_evaluation_filter/import.sh diff --git a/examples/resources/datadog_resource_evaluation_filter/resource.tf b/examples/resources/datadog_compliance_resource_evaluation_filter/resource.tf similarity index 100% rename from examples/resources/datadog_resource_evaluation_filter/resource.tf rename to examples/resources/datadog_compliance_resource_evaluation_filter/resource.tf From d7bade03987c405cb4b59313d2ac144dab114022 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Thu, 22 May 2025 16:25:39 +0100 Subject: [PATCH 16/21] updated error messages --- .../resource_datadog_compliance_resource_evaluation_filter.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go b/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go index cf8bba479..9b2aab4f3 100644 --- a/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go +++ b/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go @@ -2,6 +2,7 @@ package fwprovider import ( "context" + "fmt" "regexp" "strings" @@ -66,7 +67,7 @@ func toSliceString(list types.List) ([]string, diag.Diagnostics) { for _, elem := range list.Elements() { strVal, ok := elem.(types.String) if !ok { - diags.AddError("Invalid element type", "Expected string in list but found a different type") + diags.AddError("Invalid element type creating tags list", fmt.Sprintf("Expected string in list but found %T", elem)) continue } result = append(result, strVal.ValueString()) From 595ea5402886d2bd093aceda0db34511e88de1be Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Thu, 22 May 2025 18:54:45 +0100 Subject: [PATCH 17/21] fix resources not being deleted when id or cloud provider is changed --- ...g_compliance_resource_evaluation_filter.go | 11 +++++++- ...pliance_resource_evaluation_filter_test.go | 26 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go b/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go index 9b2aab4f3..7ca59c1c7 100644 --- a/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go +++ b/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go @@ -6,6 +6,10 @@ import ( "regexp" "strings" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" @@ -83,11 +87,16 @@ func (r *ComplianceResourceEvaluationFilter) Schema(_ context.Context, _ resourc "cloud_provider": schema.StringAttribute{ Required: true, Description: "The cloud provider of the filter's targeted resource. Only `aws`, `gcp` or `azure` are considered valid cloud providers.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "id": schema.StringAttribute{ Required: true, Description: "The ID of the of the filter's targeted resource. Different cloud providers target different resource IDs:\n - `aws`: account id \n - `gcp`: project id\n - `azure`: subscription id", - }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }}, "tags": schema.ListAttribute{ Required: true, ElementType: types.StringType, diff --git a/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go b/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go index 96dfcc85e..27d5d2f52 100644 --- a/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go +++ b/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go @@ -71,6 +71,32 @@ func TestAccResourceEvaluationFilter(t *testing.T) { ), ), }, + { + // Changing the cloud provider, but keeping the rest should force deletion + Config: fmt.Sprintf(` + resource "datadog_compliance_resource_evaluation_filter" "filter_test" { + tags = ["tag3:val3", "tag1:val1", "tag2:val2"] + cloud_provider = "azure" + id = "%s" + } + `, accountId), + // This should trigger a diff or update because tags are now a list + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, + { + // Changing the id, but keeping the rest should force deletion + Config: fmt.Sprintf(` + resource "datadog_compliance_resource_evaluation_filter" "filter_test" { + tags = ["tag3:val3", "tag1:val1", "tag2:val2"] + cloud_provider = "%s" + id = "123" + } + `, provider), + // This should trigger a diff or update because tags are now a list + PlanOnly: true, + ExpectNonEmptyPlan: true, + }, }, }) } From fbde81660db524829009eb40b344b4446837a489 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Fri, 23 May 2025 09:00:28 +0100 Subject: [PATCH 18/21] remove commented out code --- ...source_datadog_compliance_resource_evaluation_filter_test.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go b/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go index 27d5d2f52..1abf9baf0 100644 --- a/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go +++ b/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go @@ -25,7 +25,6 @@ func TestAccResourceEvaluationFilter(t *testing.T) { simpleTags := []string{"tag1:val1", "tag2:val2", "tag3:val3"} reorderedTags := []string{"tag3:val3", "tag2:val2", "tag1:val1"} provider := "aws" - //invalidProvider := "invalid" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -108,7 +107,6 @@ func TestAccResourceEvaluationFilterImport(t *testing.T) { resourceName := "datadog_compliance_resource_evaluation_filter.filter_test" simpleTags := []string{"tag1:val1", "tag2:val2", "tag3:val3"} provider := "aws" - //invalidProvider := "invalid" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, From 5779d0dc43a22bec2fb326a0176d055cd31aab73 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Fri, 23 May 2025 11:10:28 +0100 Subject: [PATCH 19/21] adding updated cassettes --- .../TestAccResourceEvaluationFilter.freeze | 2 +- .../TestAccResourceEvaluationFilter.yaml | 98 ++++++++++++++++--- ...stAccResourceEvaluationFilterImport.freeze | 2 +- ...TestAccResourceEvaluationFilterImport.yaml | 16 +-- ...tAccResourceEvaluationFilterInvalid.freeze | 2 +- ...estAccResourceEvaluationFilterInvalid.yaml | 2 +- 6 files changed, 94 insertions(+), 28 deletions(-) diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze index 476c02a2f..98e44e4e8 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze @@ -1 +1 @@ -2025-05-22T15:29:52.616608+01:00 \ No newline at end of file +2025-05-23T11:09:53.591728+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml index d35e6c4c7..8d56b1f4d 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml @@ -36,7 +36,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 513.520667ms + duration: 616.67725ms - id: 1 request: proto: HTTP/1.1 @@ -63,13 +63,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"a9cf4842-ed7a-4ed0-bad1-7dc7af2ea5da","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"06e78735-4460-425e-826b-e488d1781ef5","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 153.7085ms + duration: 173.7325ms - id: 2 request: proto: HTTP/1.1 @@ -96,13 +96,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"4d4063eb-e529-41ff-b39a-3994c6700295","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"aef3fea6-877d-4a6a-bf87-c86ee0f72eb3","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 149.562875ms + duration: 174.539083ms - id: 3 request: proto: HTTP/1.1 @@ -129,13 +129,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"9ee02489-0894-4c16-a714-bba19277152a","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"d4e0a8f2-23c4-47e3-8ac1-f5e193876f01","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 147.901583ms + duration: 169.084292ms - id: 4 request: proto: HTTP/1.1 @@ -171,7 +171,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 164.101333ms + duration: 213.974917ms - id: 5 request: proto: HTTP/1.1 @@ -198,13 +198,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"5d5f19d5-3a69-47ca-9b96-2e8e8cc31159","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' + body: '{"data":{"id":"2f04c11e-f524-4773-ba8f-e7d7c3cc4fb3","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 133.716125ms + duration: 181.406833ms - id: 6 request: proto: HTTP/1.1 @@ -231,14 +231,80 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"e8190d78-8ada-448c-a6a7-31fa1ef75d29","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' + body: '{"data":{"id":"30b9ba51-0895-4bf4-9d73-55a3f0c2d2a8","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 152.102166ms + duration: 173.62275ms - id: 7 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Accept: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws&skip_cache=true + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 175 + uncompressed: false + body: '{"data":{"id":"910b11c4-c3bb-4afa-923c-2e327bbfa0b7","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 200 OK + code: 200 + duration: 179.395417ms + - id: 8 + request: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + content_length: 0 + transfer_encoding: [] + trailer: {} + host: api.datadoghq.com + remote_addr: "" + request_uri: "" + body: "" + form: {} + headers: + Accept: + - application/json + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws&skip_cache=true + method: GET + response: + proto: HTTP/1.1 + proto_major: 1 + proto_minor: 1 + transfer_encoding: [] + trailer: {} + content_length: 175 + uncompressed: false + body: '{"data":{"id":"f4535126-d6c3-481e-9965-691a4ce9ca37","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' + headers: + Content-Type: + - application/vnd.api+json + status: 200 OK + code: 200 + duration: 173.712125ms + - id: 9 request: proto: HTTP/1.1 proto_major: 1 @@ -273,8 +339,8 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 166.117416ms - - id: 8 + duration: 238.154125ms + - id: 10 request: proto: HTTP/1.1 proto_major: 1 @@ -300,10 +366,10 @@ interactions: trailer: {} content_length: 140 uncompressed: false - body: '{"data":{"id":"612696d3-b877-40b7-9b58-699cd5fcba43","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":[]}}}}}' + body: '{"data":{"id":"bd49022d-83c3-4b59-8601-4be9d41d1573","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":[]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 144.21425ms + duration: 163.604333ms diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze index 1f8a7e352..70a5e5901 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze @@ -1 +1 @@ -2025-05-22T15:29:56.944065+01:00 \ No newline at end of file +2025-05-23T11:09:59.911276+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml index 358a2da28..e2b90b251 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml @@ -36,7 +36,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 158.263ms + duration: 191.619167ms - id: 1 request: proto: HTTP/1.1 @@ -63,13 +63,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"feae3ec8-99c4-4a7a-97a9-51b8a9b1b0bf","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"3855cc7c-f696-4e61-8945-c06235eb2980","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 147.323625ms + duration: 161.211625ms - id: 2 request: proto: HTTP/1.1 @@ -96,13 +96,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"c1f73230-6f37-4713-9b13-179049db6f9a","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"fa913f0c-cb6b-4630-9299-2fdd88235d2c","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 145.40975ms + duration: 166.24225ms - id: 3 request: proto: HTTP/1.1 @@ -129,13 +129,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"5879db92-6496-4167-ad9e-ac338277964b","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"6ea14283-4fdc-497d-9987-913547e4e13a","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 143.792208ms + duration: 175.374917ms - id: 4 request: proto: HTTP/1.1 @@ -171,4 +171,4 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 172.2335ms + duration: 181.88725ms diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze index 550e0865e..58de6cef2 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze @@ -1 +1 @@ -2025-05-22T15:29:59.12083+01:00 \ No newline at end of file +2025-05-23T11:10:02.209774+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml index 7227979e6..4c22714ae 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml @@ -36,4 +36,4 @@ interactions: - application/vnd.api+json status: 400 Bad Request code: 400 - duration: 126.966458ms + duration: 181.952959ms From 3ab35b2b169000c7a8ae2787036265d8c0fef39d Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Fri, 23 May 2025 14:47:58 +0100 Subject: [PATCH 20/21] Updating remaining instances of resource evaluation filters to compliance resource evaluation filters --- datadog/fwprovider/framework_provider.go | 2 +- ...esource_datadog_compliance_resource_evaluation_filter.go | 6 +++--- docs/resources/compliance_resource_evaluation_filter.md | 6 +++--- .../resource.tf | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/datadog/fwprovider/framework_provider.go b/datadog/fwprovider/framework_provider.go index e9c68cf59..c12cbc46b 100644 --- a/datadog/fwprovider/framework_provider.go +++ b/datadog/fwprovider/framework_provider.go @@ -84,7 +84,7 @@ var Resources = []func() resource.Resource{ NewWorkflowAutomationResource, NewAppBuilderAppResource, NewObservabilitPipelineResource, - NewResourceEvaluationFilter, + NewComplianceResourceEvaluationFilter, NewSecurityMonitoringRuleJSONResource, } diff --git a/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go b/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go index 7ca59c1c7..971bdba8f 100644 --- a/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go +++ b/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go @@ -36,7 +36,7 @@ type ResourceEvaluationFilterModel struct { Tags types.List `tfsdk:"tags"` } -func NewResourceEvaluationFilter() resource.Resource { +func NewComplianceResourceEvaluationFilter() resource.Resource { return &ComplianceResourceEvaluationFilter{} } @@ -82,7 +82,7 @@ func toSliceString(list types.List) ([]string, diag.Diagnostics) { func (r *ComplianceResourceEvaluationFilter) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ - Description: "Provides a Datadog ResourceEvaluationFilter resource. This can be used to create and manage a resource evaluation filter.", + Description: "Provides a Datadog ComplianceResourceEvaluationFilter resource. This can be used to create and manage a compliance resource evaluation filter.", Attributes: map[string]schema.Attribute{ "cloud_provider": schema.StringAttribute{ Required: true, @@ -124,7 +124,7 @@ func (r *ComplianceResourceEvaluationFilter) Create(ctx context.Context, request resp, _, err := r.API.UpdateResourceEvaluationFilters(r.Auth, *body) if err != nil { - response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error updating resource evaluation filter")) + response.Diagnostics.Append(utils.FrameworkErrorDiag(err, "error updating compliance resource evaluation filter")) return } if err := utils.CheckForUnparsed(resp); err != nil { diff --git a/docs/resources/compliance_resource_evaluation_filter.md b/docs/resources/compliance_resource_evaluation_filter.md index 56b35c0ed..ee2ff4e4d 100644 --- a/docs/resources/compliance_resource_evaluation_filter.md +++ b/docs/resources/compliance_resource_evaluation_filter.md @@ -3,17 +3,17 @@ page_title: "datadog_compliance_resource_evaluation_filter Resource - terraform-provider-datadog" subcategory: "" description: |- - Provides a Datadog ResourceEvaluationFilter resource. This can be used to create and manage a resource evaluation filter. + Provides a Datadog ComplianceResourceEvaluationFilter resource. This can be used to create and manage a compliance resource evaluation filter. --- # datadog_compliance_resource_evaluation_filter (Resource) -Provides a Datadog ResourceEvaluationFilter resource. This can be used to create and manage a resource evaluation filter. +Provides a Datadog ComplianceResourceEvaluationFilter resource. This can be used to create and manage a compliance resource evaluation filter. ## Example Usage ```terraform -# Manage Datadog resource evaluation filters +# Manage Datadog compliance resource evaluation filters resource "datadog_compliance_resource_evaluation_filter" "basic_filter" { tags = ["tag1:val1"] cloud_provider = "aws" diff --git a/examples/resources/datadog_compliance_resource_evaluation_filter/resource.tf b/examples/resources/datadog_compliance_resource_evaluation_filter/resource.tf index 5e638c9ef..264122912 100644 --- a/examples/resources/datadog_compliance_resource_evaluation_filter/resource.tf +++ b/examples/resources/datadog_compliance_resource_evaluation_filter/resource.tf @@ -1,4 +1,4 @@ -# Manage Datadog resource evaluation filters +# Manage Datadog compliance resource evaluation filters resource "datadog_compliance_resource_evaluation_filter" "basic_filter" { tags = ["tag1:val1"] cloud_provider = "aws" From 838d17e32761eb92bd985dca3648654fb86ddf41 Mon Sep 17 00:00:00 2001 From: Manuel Costa Date: Fri, 23 May 2025 17:46:15 +0100 Subject: [PATCH 21/21] fixing incorrect state management (overlap between id field and id internal property of terraform) --- ...g_compliance_resource_evaluation_filter.go | 20 +++++-- .../TestAccResourceEvaluationFilter.freeze | 2 +- .../TestAccResourceEvaluationFilter.yaml | 58 +++++++++---------- ...stAccResourceEvaluationFilterImport.freeze | 2 +- ...TestAccResourceEvaluationFilterImport.yaml | 20 +++---- ...tAccResourceEvaluationFilterInvalid.freeze | 2 +- ...estAccResourceEvaluationFilterInvalid.yaml | 2 +- ...pliance_resource_evaluation_filter_test.go | 30 +++++----- .../compliance_resource_evaluation_filter.md | 8 ++- .../resource.tf | 2 +- 10 files changed, 79 insertions(+), 67 deletions(-) diff --git a/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go b/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go index 971bdba8f..06c852329 100644 --- a/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go +++ b/datadog/fwprovider/resource_datadog_compliance_resource_evaluation_filter.go @@ -32,8 +32,10 @@ type ComplianceResourceEvaluationFilter struct { type ResourceEvaluationFilterModel struct { CloudProvider types.String `tfsdk:"cloud_provider"` - ID types.String `tfsdk:"id"` + ResourceID types.String `tfsdk:"resource_id"` Tags types.List `tfsdk:"tags"` + + ID types.String `tfsdk:"id"` } func NewComplianceResourceEvaluationFilter() resource.Resource { @@ -91,12 +93,17 @@ func (r *ComplianceResourceEvaluationFilter) Schema(_ context.Context, _ resourc stringplanmodifier.RequiresReplace(), }, }, - "id": schema.StringAttribute{ + "resource_id": schema.StringAttribute{ Required: true, Description: "The ID of the of the filter's targeted resource. Different cloud providers target different resource IDs:\n - `aws`: account id \n - `gcp`: project id\n - `azure`: subscription id", PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }}, + + "id": schema.StringAttribute{ + Description: "The ID of the resource evaluation filter resource.", + Computed: true, + }, "tags": schema.ListAttribute{ Required: true, ElementType: types.StringType, @@ -147,10 +154,11 @@ func convertStringSliceToAttrValues(s []string) []attr.Value { func (r *ComplianceResourceEvaluationFilter) UpdateState(_ context.Context, state *ResourceEvaluationFilterModel, attributes *datadogV2.ResourceFilterAttributes) { for p, accounts := range attributes.CloudProvider { - for id, tagList := range accounts { + for resource_id, tagList := range accounts { tags := types.ListValueMust(types.StringType, convertStringSliceToAttrValues(tagList)) state.CloudProvider = types.StringValue(p) - state.ID = types.StringValue(id) + state.ID = types.StringValue(p + ":" + resource_id) + state.ResourceID = types.StringValue(resource_id) state.Tags = tags break } @@ -277,7 +285,7 @@ func (r *ComplianceResourceEvaluationFilter) buildUpdateResourceEvaluationFilter diags.AddError("Missing cloud_provider", "cloud_provider is required but was null or unknown") return nil, diags } - if state.ID.IsNull() || state.ID.IsUnknown() { + if state.ResourceID.IsNull() || state.ResourceID.IsUnknown() { diags.AddError("Missing id", "id is required but was null or unknown") return nil, diags } @@ -285,7 +293,7 @@ func (r *ComplianceResourceEvaluationFilter) buildUpdateResourceEvaluationFilter attributes := datadogV2.ResourceFilterAttributes{ CloudProvider: map[string]map[string][]string{ state.CloudProvider.ValueString(): { - state.ID.ValueString(): tagsList, + state.ResourceID.ValueString(): tagsList, }, }, } diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze index 98e44e4e8..edb3618a0 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.freeze @@ -1 +1 @@ -2025-05-23T11:09:53.591728+01:00 \ No newline at end of file +2025-05-23T17:45:25.774964+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml index 8d56b1f4d..44cfa048e 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilter.yaml @@ -36,7 +36,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 616.67725ms + duration: 543.328667ms - id: 1 request: proto: HTTP/1.1 @@ -63,13 +63,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"06e78735-4460-425e-826b-e488d1781ef5","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"ba924b8e-47a9-4298-9442-c135f2458001","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 173.7325ms + duration: 148.422125ms - id: 2 request: proto: HTTP/1.1 @@ -86,7 +86,7 @@ interactions: headers: Accept: - application/json - url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws&skip_cache=true + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=aws%3A123456789&cloud_provider=aws&skip_cache=true method: GET response: proto: HTTP/1.1 @@ -94,15 +94,15 @@ interactions: proto_minor: 1 transfer_encoding: [] trailer: {} - content_length: 175 + content_length: 118 uncompressed: false - body: '{"data":{"id":"aef3fea6-877d-4a6a-bf87-c86ee0f72eb3","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"4dec75dc-a185-41cb-9c63-961acc0b9a17","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 174.539083ms + duration: 152.391ms - id: 3 request: proto: HTTP/1.1 @@ -119,7 +119,7 @@ interactions: headers: Accept: - application/json - url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws&skip_cache=true + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=aws%3A123456789&cloud_provider=aws&skip_cache=true method: GET response: proto: HTTP/1.1 @@ -127,15 +127,15 @@ interactions: proto_minor: 1 transfer_encoding: [] trailer: {} - content_length: 175 + content_length: 118 uncompressed: false - body: '{"data":{"id":"d4e0a8f2-23c4-47e3-8ac1-f5e193876f01","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"d8a6efc3-2f82-4f85-a472-a5d486d4ab24","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 169.084292ms + duration: 156.569792ms - id: 4 request: proto: HTTP/1.1 @@ -171,7 +171,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 213.974917ms + duration: 181.320667ms - id: 5 request: proto: HTTP/1.1 @@ -198,13 +198,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"2f04c11e-f524-4773-ba8f-e7d7c3cc4fb3","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' + body: '{"data":{"id":"e145e073-af7a-4801-b6d7-6322a1e6cfcc","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 181.406833ms + duration: 154.499042ms - id: 6 request: proto: HTTP/1.1 @@ -221,7 +221,7 @@ interactions: headers: Accept: - application/json - url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws&skip_cache=true + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=aws%3A123456789&cloud_provider=aws&skip_cache=true method: GET response: proto: HTTP/1.1 @@ -229,15 +229,15 @@ interactions: proto_minor: 1 transfer_encoding: [] trailer: {} - content_length: 175 + content_length: 118 uncompressed: false - body: '{"data":{"id":"30b9ba51-0895-4bf4-9d73-55a3f0c2d2a8","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' + body: '{"data":{"id":"45c24892-da45-450d-b6a1-25bd182d3252","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 173.62275ms + duration: 170.1615ms - id: 7 request: proto: HTTP/1.1 @@ -254,7 +254,7 @@ interactions: headers: Accept: - application/json - url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws&skip_cache=true + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=aws%3A123456789&cloud_provider=aws&skip_cache=true method: GET response: proto: HTTP/1.1 @@ -262,15 +262,15 @@ interactions: proto_minor: 1 transfer_encoding: [] trailer: {} - content_length: 175 + content_length: 118 uncompressed: false - body: '{"data":{"id":"910b11c4-c3bb-4afa-923c-2e327bbfa0b7","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' + body: '{"data":{"id":"47933969-f06c-4277-9750-b9fe11f7c96e","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 179.395417ms + duration: 159.799416ms - id: 8 request: proto: HTTP/1.1 @@ -287,7 +287,7 @@ interactions: headers: Accept: - application/json - url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=123456789&cloud_provider=aws&skip_cache=true + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=aws%3A123456789&cloud_provider=aws&skip_cache=true method: GET response: proto: HTTP/1.1 @@ -295,15 +295,15 @@ interactions: proto_minor: 1 transfer_encoding: [] trailer: {} - content_length: 175 + content_length: 118 uncompressed: false - body: '{"data":{"id":"f4535126-d6c3-481e-9965-691a4ce9ca37","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":["tag3:val3","tag1:val1","tag2:val2"]}}}}}' + body: '{"data":{"id":"c0ece007-0c13-41fb-b40c-2819f132f2c0","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 173.712125ms + duration: 162.793375ms - id: 9 request: proto: HTTP/1.1 @@ -339,7 +339,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 238.154125ms + duration: 164.185625ms - id: 10 request: proto: HTTP/1.1 @@ -366,10 +366,10 @@ interactions: trailer: {} content_length: 140 uncompressed: false - body: '{"data":{"id":"bd49022d-83c3-4b59-8601-4be9d41d1573","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":[]}}}}}' + body: '{"data":{"id":"fb9c5b25-45bb-44c8-8048-cd80f8956af8","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"123456789":[]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 163.604333ms + duration: 159.222792ms diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze index 70a5e5901..91c1d40a3 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.freeze @@ -1 +1 @@ -2025-05-23T11:09:59.911276+01:00 \ No newline at end of file +2025-05-23T17:45:31.811625+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml index e2b90b251..5b2df440e 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterImport.yaml @@ -36,7 +36,7 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 191.619167ms + duration: 171.682583ms - id: 1 request: proto: HTTP/1.1 @@ -63,13 +63,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"3855cc7c-f696-4e61-8945-c06235eb2980","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"d72ba91c-4cd9-43fc-b355-cf87ada10dcf","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 161.211625ms + duration: 156.120416ms - id: 2 request: proto: HTTP/1.1 @@ -86,7 +86,7 @@ interactions: headers: Accept: - application/json - url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=223456789&cloud_provider=aws&skip_cache=true + url: https://api.datadoghq.com/api/v2/cloud_security_management/resource_filters?account_id=aws%3A223456789&cloud_provider=aws&skip_cache=true method: GET response: proto: HTTP/1.1 @@ -94,15 +94,15 @@ interactions: proto_minor: 1 transfer_encoding: [] trailer: {} - content_length: 175 + content_length: 118 uncompressed: false - body: '{"data":{"id":"fa913f0c-cb6b-4630-9299-2fdd88235d2c","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"5abf133b-fcad-41d5-9083-a12fd52f3aad","type":"csm_resource_filter","attributes":{"cloud_provider":{}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 166.24225ms + duration: 171.840125ms - id: 3 request: proto: HTTP/1.1 @@ -129,13 +129,13 @@ interactions: trailer: {} content_length: 175 uncompressed: false - body: '{"data":{"id":"6ea14283-4fdc-497d-9987-913547e4e13a","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' + body: '{"data":{"id":"e29a9ce4-0895-4c7a-b278-ef815f0b9f19","type":"csm_resource_filter","attributes":{"cloud_provider":{"aws":{"223456789":["tag1:val1","tag2:val2","tag3:val3"]}}}}}' headers: Content-Type: - application/vnd.api+json status: 200 OK code: 200 - duration: 175.374917ms + duration: 147.27675ms - id: 4 request: proto: HTTP/1.1 @@ -171,4 +171,4 @@ interactions: - application/vnd.api+json status: 201 Created code: 201 - duration: 181.88725ms + duration: 165.622917ms diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze index 58de6cef2..fd98160f4 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.freeze @@ -1 +1 @@ -2025-05-23T11:10:02.209774+01:00 \ No newline at end of file +2025-05-23T17:45:34.022651+01:00 \ No newline at end of file diff --git a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml index 4c22714ae..f111dde1b 100644 --- a/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml +++ b/datadog/tests/cassettes/TestAccResourceEvaluationFilterInvalid.yaml @@ -36,4 +36,4 @@ interactions: - application/vnd.api+json status: 400 Bad Request code: 400 - duration: 181.952959ms + duration: 133.088709ms diff --git a/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go b/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go index 1abf9baf0..68aef9bc9 100644 --- a/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go +++ b/datadog/tests/resource_datadog_compliance_resource_evaluation_filter_test.go @@ -36,7 +36,7 @@ func TestAccResourceEvaluationFilter(t *testing.T) { resource "datadog_compliance_resource_evaluation_filter" "filter_test" { tags = ["tag1:val1", "tag2:val2", "tag3:val3"] cloud_provider = "%s" - id = "%s" + resource_id = "%s" } `, provider, accountId), Check: resource.ComposeTestCheckFunc( @@ -56,7 +56,7 @@ func TestAccResourceEvaluationFilter(t *testing.T) { resource "datadog_compliance_resource_evaluation_filter" "filter_test" { tags = ["tag3:val3", "tag1:val1", "tag2:val2"] cloud_provider = "%s" - id = "%s" + resource_id = "%s" } `, provider, accountId), // This should trigger a diff or update because tags are now a list @@ -76,7 +76,7 @@ func TestAccResourceEvaluationFilter(t *testing.T) { resource "datadog_compliance_resource_evaluation_filter" "filter_test" { tags = ["tag3:val3", "tag1:val1", "tag2:val2"] cloud_provider = "azure" - id = "%s" + resource_id = "%s" } `, accountId), // This should trigger a diff or update because tags are now a list @@ -84,12 +84,12 @@ func TestAccResourceEvaluationFilter(t *testing.T) { ExpectNonEmptyPlan: true, }, { - // Changing the id, but keeping the rest should force deletion + // Changing the resource_id, but keeping the rest should force deletion Config: fmt.Sprintf(` resource "datadog_compliance_resource_evaluation_filter" "filter_test" { tags = ["tag3:val3", "tag1:val1", "tag2:val2"] cloud_provider = "%s" - id = "123" + resource_id = "123" } `, provider), // This should trigger a diff or update because tags are now a list @@ -117,7 +117,7 @@ func TestAccResourceEvaluationFilterImport(t *testing.T) { resource "datadog_compliance_resource_evaluation_filter" "filter_test" { tags = ["tag1:val1", "tag2:val2", "tag3:val3"] cloud_provider = "%s" - id = "%s" + resource_id = "%s" } `, provider, accountId), Check: resource.ComposeTestCheckFunc( @@ -156,7 +156,7 @@ func TestAccResourceEvaluationFilterInvalid(t *testing.T) { resource "datadog_compliance_resource_evaluation_filter" "filter_test" { tags = ["tag1:val1", "invalidTag:asdasf:InvalidTag", "tag3:val3"] cloud_provider = "%s" - id = "%s" + resource_id = "%s" } `, provider, accountId), ExpectError: regexp.MustCompile(`Invalid Attribute Value Match`), @@ -167,7 +167,7 @@ func TestAccResourceEvaluationFilterInvalid(t *testing.T) { resource "datadog_compliance_resource_evaluation_filter" "filter_test" { tags = ["tag1:val1", "tag3:val3"] cloud_provider = "%s" - id = "%s" + resource_id = "%s" } `, invalidProvider, accountId), ExpectError: regexp.MustCompile(`Invalid cloud provider invalid`), @@ -176,10 +176,10 @@ func TestAccResourceEvaluationFilterInvalid(t *testing.T) { }) } -func checkResourceEvaluationFilterContent(resourceName string, id string, provider string, expectedTags []string) resource.TestCheckFunc { +func checkResourceEvaluationFilterContent(resourceName string, resource_id string, provider string, expectedTags []string) resource.TestCheckFunc { checks := []resource.TestCheckFunc{ resource.TestCheckResourceAttr(resourceName, "cloud_provider", provider), - resource.TestCheckResourceAttr(resourceName, "id", id), + resource.TestCheckResourceAttr(resourceName, "resource_id", resource_id), } checks = append(checks, resource.TestCheckFunc(func(s *terraform.State) error { @@ -237,12 +237,12 @@ func testAccCheckResourceEvaluationFilterExists(accProvider *fwprovider.Framewor auth := accProvider.Auth apiInstances := accProvider.DatadogApiInstances provider := r.Primary.Attributes["cloud_provider"] - id := r.Primary.Attributes["id"] + resource_id := r.Primary.Attributes["resource_id"] skipCache := true params := datadogV2.GetResourceEvaluationFiltersOptionalParameters{ CloudProvider: &provider, - AccountId: &id, + AccountId: &resource_id, SkipCache: &skipCache, } _, _, err := apiInstances.GetSecurityMonitoringApiV2().GetResourceEvaluationFilters(auth, params) @@ -262,12 +262,12 @@ func testAccCheckResourceEvaluationFilterDestroy(accProvider *fwprovider.Framewo for _, r := range s.RootModule().Resources { if r.Type == "datadog_compliance_resource_evaluation_filter" { provider := r.Primary.Attributes["cloud_provider"] - id := r.Primary.Attributes["id"] + resource_id := r.Primary.Attributes["resource_id"] skipCache := true params := datadogV2.GetResourceEvaluationFiltersOptionalParameters{ CloudProvider: &provider, - AccountId: &id, + AccountId: &resource_id, SkipCache: &skipCache, } response, httpResponse, err := apiInstances.GetSecurityMonitoringApiV2().GetResourceEvaluationFilters(auth, params) @@ -276,7 +276,7 @@ func testAccCheckResourceEvaluationFilterDestroy(accProvider *fwprovider.Framewo return errors.New("Error retrieving resource evaluation filter") } - if len(response.Data.Attributes.CloudProvider[r.Primary.Attributes["cloud_provider"]][id]) != 0 { + if len(response.Data.Attributes.CloudProvider[r.Primary.Attributes["cloud_provider"]][resource_id]) != 0 { bytes, _ := json.MarshalIndent(response.Data.Attributes, "", " ") fmt.Println(string(bytes)) return errors.New("filters were not destroyed") diff --git a/docs/resources/compliance_resource_evaluation_filter.md b/docs/resources/compliance_resource_evaluation_filter.md index ee2ff4e4d..23394bf65 100644 --- a/docs/resources/compliance_resource_evaluation_filter.md +++ b/docs/resources/compliance_resource_evaluation_filter.md @@ -17,7 +17,7 @@ Provides a Datadog ComplianceResourceEvaluationFilter resource. This can be used resource "datadog_compliance_resource_evaluation_filter" "basic_filter" { tags = ["tag1:val1"] cloud_provider = "aws" - id = "000000000000" + resource_id = "000000000000" } ``` @@ -27,12 +27,16 @@ resource "datadog_compliance_resource_evaluation_filter" "basic_filter" { ### Required - `cloud_provider` (String) The cloud provider of the filter's targeted resource. Only `aws`, `gcp` or `azure` are considered valid cloud providers. -- `id` (String) The ID of the of the filter's targeted resource. Different cloud providers target different resource IDs: +- `resource_id` (String) The ID of the of the filter's targeted resource. Different cloud providers target different resource IDs: - `aws`: account id - `gcp`: project id - `azure`: subscription id - `tags` (List of String) List of tags to filter misconfiguration detections. Each entry should follow the format: "key":"value". +### Read-Only + +- `id` (String) The ID of the resource evaluation filter resource. + ## Import Import is supported using the following syntax: diff --git a/examples/resources/datadog_compliance_resource_evaluation_filter/resource.tf b/examples/resources/datadog_compliance_resource_evaluation_filter/resource.tf index 264122912..16b2fa252 100644 --- a/examples/resources/datadog_compliance_resource_evaluation_filter/resource.tf +++ b/examples/resources/datadog_compliance_resource_evaluation_filter/resource.tf @@ -2,5 +2,5 @@ resource "datadog_compliance_resource_evaluation_filter" "basic_filter" { tags = ["tag1:val1"] cloud_provider = "aws" - id = "000000000000" + resource_id = "000000000000" }