diff --git a/.changelog/31711.txt b/.changelog/31711.txt new file mode 100644 index 00000000000..a69db22f3a8 --- /dev/null +++ b/.changelog/31711.txt @@ -0,0 +1,3 @@ +```release-note:new-data-source +aws_guardduty_finding_ids +``` diff --git a/internal/service/guardduty/finding_ids_data_source.go b/internal/service/guardduty/finding_ids_data_source.go new file mode 100644 index 00000000000..eda3382c039 --- /dev/null +++ b/internal/service/guardduty/finding_ids_data_source.go @@ -0,0 +1,104 @@ +package guardduty + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/guardduty" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkDataSource(name="Finding Ids") +func newDataSourceFindingIds(context.Context) (datasource.DataSourceWithConfigure, error) { + return &dataSourceFindingIds{}, nil +} + +const ( + DSNameFindingIds = "Finding Ids Data Source" +) + +type dataSourceFindingIds struct { + framework.DataSourceWithConfigure +} + +func (d *dataSourceFindingIds) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { // nosemgrep:ci.meta-in-func-name + resp.TypeName = "aws_guardduty_finding_ids" +} + +func (d *dataSourceFindingIds) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "detector_id": schema.StringAttribute{ + Required: true, + }, + "finding_ids": schema.ListAttribute{ + Computed: true, + ElementType: types.StringType, + }, + "id": framework.IDAttribute(), + }, + } +} + +func (d *dataSourceFindingIds) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + conn := d.Meta().GuardDutyConn() + + var data dataSourceFindingIdsData + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + out, err := findFindingIds(ctx, conn, data.DetectorID.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.GuardDuty, create.ErrActionReading, DSNameFindingIds, data.DetectorID.String(), err), + err.Error(), + ) + return + } + + data.ID = types.StringValue(data.DetectorID.ValueString()) + data.FindingIDs = flex.FlattenFrameworkStringList(ctx, out) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func findFindingIds(ctx context.Context, conn *guardduty.GuardDuty, id string) ([]*string, error) { + in := &guardduty.ListFindingsInput{ + DetectorId: aws.String(id), + } + + var findingIds []*string + err := conn.ListFindingsPagesWithContext(ctx, in, func(page *guardduty.ListFindingsOutput, lastPage bool) bool { + findingIds = append(findingIds, page.FindingIds...) + return !lastPage + }) + + if tfawserr.ErrMessageContains(err, guardduty.ErrCodeBadRequestException, "The request is rejected because the input detectorId is not owned by the current account.") { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, + } + } + + if err != nil { + return nil, err + } + + return findingIds, nil +} + +type dataSourceFindingIdsData struct { + DetectorID types.String `tfsdk:"detector_id"` + FindingIDs types.List `tfsdk:"finding_ids"` + ID types.String `tfsdk:"id"` +} diff --git a/internal/service/guardduty/finding_ids_data_source_test.go b/internal/service/guardduty/finding_ids_data_source_test.go new file mode 100644 index 00000000000..442515177c3 --- /dev/null +++ b/internal/service/guardduty/finding_ids_data_source_test.go @@ -0,0 +1,43 @@ +package guardduty_test + +import ( + "testing" + + "github.com/aws/aws-sdk-go/service/guardduty" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" +) + +func TestAccGuardDutyFindingIdsDataSource_basic(t *testing.T) { + ctx := acctest.Context(t) + dataSourceName := "data.aws_guardduty_finding_ids.test" + detectorDataSourceName := "data.aws_guardduty_detector.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorExists(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccFindingIdsDataSourceConfig_basic(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, "detector_id", detectorDataSourceName, "id"), + resource.TestCheckResourceAttrSet(dataSourceName, "finding_ids.#"), + ), + }, + }, + }) +} + +func testAccFindingIdsDataSourceConfig_basic() string { + return ` +data "aws_guardduty_detector" "test" {} + +data "aws_guardduty_finding_ids" "test" { + detector_id = data.aws_guardduty_detector.test.id +} +` +} diff --git a/internal/service/guardduty/guardduty_test.go b/internal/service/guardduty/guardduty_test.go index d9b8e2c9069..8ff03dd07de 100644 --- a/internal/service/guardduty/guardduty_test.go +++ b/internal/service/guardduty/guardduty_test.go @@ -1,10 +1,13 @@ package guardduty_test import ( + "context" "os" "testing" + "github.com/aws/aws-sdk-go/service/guardduty" "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" ) func TestAccGuardDuty_serial(t *testing.T) { @@ -80,3 +83,21 @@ func testAccMemberFromEnv(t *testing.T) (string, string) { } return accountID, email } + +// testAccPreCheckDetectorExists verifies the current account has a single active +// GuardDuty detector configured. +func testAccPreCheckDetectorExists(ctx context.Context, t *testing.T) { + conn := acctest.Provider.Meta().(*conns.AWSClient).GuardDutyConn() + + out, err := conn.ListDetectorsWithContext(ctx, &guardduty.ListDetectorsInput{}) + if out == nil || len(out.DetectorIds) == 0 { + t.Skip("this AWS account must have an existing GuardDuty detector configured") + } + if len(out.DetectorIds) > 1 { + t.Skipf("this AWS account must have a single existing GuardDuty detector configured. Found %d.", len(out.DetectorIds)) + } + + if err != nil { + t.Fatalf("listing GuardDuty Detectors: %s", err) + } +} diff --git a/internal/service/guardduty/service_package_gen.go b/internal/service/guardduty/service_package_gen.go index 9fb1238c9ef..3d4067d9bcc 100644 --- a/internal/service/guardduty/service_package_gen.go +++ b/internal/service/guardduty/service_package_gen.go @@ -12,7 +12,12 @@ import ( type servicePackage struct{} func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.ServicePackageFrameworkDataSource { - return []*types.ServicePackageFrameworkDataSource{} + return []*types.ServicePackageFrameworkDataSource{ + { + Factory: newDataSourceFindingIds, + Name: "Finding Ids", + }, + } } func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { diff --git a/website/docs/d/guardduty_finding_ids.html.markdown b/website/docs/d/guardduty_finding_ids.html.markdown new file mode 100644 index 00000000000..18384feec54 --- /dev/null +++ b/website/docs/d/guardduty_finding_ids.html.markdown @@ -0,0 +1,33 @@ +--- +subcategory: "GuardDuty" +layout: "aws" +page_title: "AWS: aws_guardduty_finding_ids" +description: |- + Terraform data source for managing an AWS GuardDuty Finding Ids. +--- + +# Data Source: aws_guardduty_finding_ids + +Terraform data source for managing an AWS GuardDuty Finding Ids. + +## Example Usage + +### Basic Usage + +```terraform +data "aws_guardduty_finding_ids" "example" { + detector_id = aws_guardduty_detector.example.id +} +``` + +## Argument Reference + +The following arguments are required: + +* `detector_id` - (Required) ID of the GuardDuty detector. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `finding_ids` - A list of finding IDs for the specified detector.