diff --git a/.changelog/21030.txt b/.changelog/21030.txt new file mode 100644 index 00000000000..8b0aa9a536e --- /dev/null +++ b/.changelog/21030.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_guardduty_filter: Add plan-time validation of `name` +``` \ No newline at end of file diff --git a/.changelog/31463.txt b/.changelog/31463.txt new file mode 100644 index 00000000000..8a5bf815242 --- /dev/null +++ b/.changelog/31463.txt @@ -0,0 +1,7 @@ +```release-note:new-resource +aws_guardduty_detector_feature +``` + +```release-note:enhancement +data-source/aws_guardduty_detector: Add `features` attribute +``` \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b30f4386f7f..4147e6ae687 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,14 @@ ## 5.20.0 (Unreleased) + +FEATURES: + +* **New Resource:** `aws_guardduty_detector_feature` ([#31463](https://github.com/hashicorp/terraform-provider-aws/issues/31463)) + +ENHANCEMENTS: + +* data-source/aws_guardduty_detector: Add `features` attribute ([#31463](https://github.com/hashicorp/terraform-provider-aws/issues/31463)) +* resource/aws_guardduty_filter: Add plan-time validation of `name` ([#21030](https://github.com/hashicorp/terraform-provider-aws/issues/21030)) + ## 5.19.0 (September 29, 2023) BREAKING CHANGES: diff --git a/go.mod b/go.mod index 36cdb062f35..5ed4f945c26 100644 --- a/go.mod +++ b/go.mod @@ -59,6 +59,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/s3control v1.33.0 github.com/aws/aws-sdk-go-v2/service/scheduler v1.3.0 github.com/aws/aws-sdk-go-v2/service/securitylake v1.7.0 + github.com/aws/aws-sdk-go-v2/service/servicequotas v1.16.0 github.com/aws/aws-sdk-go-v2/service/sesv2 v1.20.1 github.com/aws/aws-sdk-go-v2/service/signer v1.16.5 github.com/aws/aws-sdk-go-v2/service/ssm v1.38.0 diff --git a/go.sum b/go.sum index 1ec9feca774..9d4adefa7eb 100644 --- a/go.sum +++ b/go.sum @@ -156,6 +156,8 @@ github.com/aws/aws-sdk-go-v2/service/scheduler v1.3.0 h1:uzCEL2ILopsOcWvbmeMmmy3 github.com/aws/aws-sdk-go-v2/service/scheduler v1.3.0/go.mod h1:cdpHC7Nd4Yvtf/rhRqyqqI0fzoCb0fpo2oOFVZ0HTeQ= github.com/aws/aws-sdk-go-v2/service/securitylake v1.7.0 h1:Ou2rjk3siybv09bzpi5fs+9zOYZACxT1LT0KkcDAtIs= github.com/aws/aws-sdk-go-v2/service/securitylake v1.7.0/go.mod h1:/MCawoN8Xib5q04k2HsIQ+K2cNtC3CHamrfLZXd6KmA= +github.com/aws/aws-sdk-go-v2/service/servicequotas v1.16.0 h1:dhp7Do5oaybdDdYGcdUNyzYFPsM4sNCvuPqph7MG5X0= +github.com/aws/aws-sdk-go-v2/service/servicequotas v1.16.0/go.mod h1:7+H2efEiOCrUl5EEsDFhe5BeI4gHGLUlisCyAJAcSvs= github.com/aws/aws-sdk-go-v2/service/sesv2 v1.20.1 h1:QxzS/Hr5kixvMyPIXTfspnRUiKgFJSTPrhnglAi2YLI= github.com/aws/aws-sdk-go-v2/service/sesv2 v1.20.1/go.mod h1:qpAr/ear7teIUoBd1gaPbvavdICoo1XyAIHPVlyawQc= github.com/aws/aws-sdk-go-v2/service/signer v1.16.5 h1:nqZqDR44/ao9zQXyuCJI8L/C3QQIo4wtZyLtgwJfpEY= diff --git a/internal/conns/awsclient_gen.go b/internal/conns/awsclient_gen.go index a4f259fd8cc..7ca3baa43bb 100644 --- a/internal/conns/awsclient_gen.go +++ b/internal/conns/awsclient_gen.go @@ -53,6 +53,7 @@ import ( s3control_sdkv2 "github.com/aws/aws-sdk-go-v2/service/s3control" scheduler_sdkv2 "github.com/aws/aws-sdk-go-v2/service/scheduler" securitylake_sdkv2 "github.com/aws/aws-sdk-go-v2/service/securitylake" + servicequotas_sdkv2 "github.com/aws/aws-sdk-go-v2/service/servicequotas" sesv2_sdkv2 "github.com/aws/aws-sdk-go-v2/service/sesv2" signer_sdkv2 "github.com/aws/aws-sdk-go-v2/service/signer" ssm_sdkv2 "github.com/aws/aws-sdk-go-v2/service/ssm" @@ -201,7 +202,6 @@ import ( serverlessapplicationrepository_sdkv1 "github.com/aws/aws-sdk-go/service/serverlessapplicationrepository" servicecatalog_sdkv1 "github.com/aws/aws-sdk-go/service/servicecatalog" servicediscovery_sdkv1 "github.com/aws/aws-sdk-go/service/servicediscovery" - servicequotas_sdkv1 "github.com/aws/aws-sdk-go/service/servicequotas" ses_sdkv1 "github.com/aws/aws-sdk-go/service/ses" sfn_sdkv1 "github.com/aws/aws-sdk-go/service/sfn" shield_sdkv1 "github.com/aws/aws-sdk-go/service/shield" @@ -1010,8 +1010,8 @@ func (c *AWSClient) ServiceDiscoveryConn(ctx context.Context) *servicediscovery_ return errs.Must(conn[*servicediscovery_sdkv1.ServiceDiscovery](ctx, c, names.ServiceDiscovery)) } -func (c *AWSClient) ServiceQuotasConn(ctx context.Context) *servicequotas_sdkv1.ServiceQuotas { - return errs.Must(conn[*servicequotas_sdkv1.ServiceQuotas](ctx, c, names.ServiceQuotas)) +func (c *AWSClient) ServiceQuotasClient(ctx context.Context) *servicequotas_sdkv2.Client { + return errs.Must(client[*servicequotas_sdkv2.Client](ctx, c, names.ServiceQuotas)) } func (c *AWSClient) ShieldConn(ctx context.Context) *shield_sdkv1.Shield { diff --git a/internal/service/guardduty/detector.go b/internal/service/guardduty/detector.go index 77b2bc1d591..239a2294c44 100644 --- a/internal/service/guardduty/detector.go +++ b/internal/service/guardduty/detector.go @@ -41,12 +41,10 @@ func ResourceDetector() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "arn": { Type: schema.TypeString, Computed: true, }, - "datasources": { Type: schema.TypeList, MaxItems: 1, @@ -54,20 +52,6 @@ func ResourceDetector() *schema.Resource { Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "s3_logs": { - Type: schema.TypeList, - Optional: true, - Computed: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "enable": { - Type: schema.TypeBool, - Required: true, - }, - }, - }, - }, "kubernetes": { Type: schema.TypeList, Optional: true, @@ -123,16 +107,28 @@ func ResourceDetector() *schema.Resource { }, }, }, + "s3_logs": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enable": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + }, }, }, }, - "enable": { Type: schema.TypeBool, Optional: true, Default: true, }, - // finding_publishing_frequency is marked as Computed:true since // GuardDuty member accounts inherit setting from master account "finding_publishing_frequency": { @@ -140,7 +136,6 @@ func ResourceDetector() *schema.Resource { Optional: true, Computed: true, }, - names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), }, @@ -153,24 +148,25 @@ func resourceDetectorCreate(ctx context.Context, d *schema.ResourceData, meta in var diags diag.Diagnostics conn := meta.(*conns.AWSClient).GuardDutyConn(ctx) - input := guardduty.CreateDetectorInput{ + input := &guardduty.CreateDetectorInput{ Enable: aws.Bool(d.Get("enable").(bool)), Tags: getTagsIn(ctx), } + if v, ok := d.GetOk("datasources"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.DataSources = expandDataSourceConfigurations(v.([]interface{})[0].(map[string]interface{})) + } + if v, ok := d.GetOk("finding_publishing_frequency"); ok { input.FindingPublishingFrequency = aws.String(v.(string)) } - if v, ok := d.GetOk("datasources"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.DataSources = expandDataSourceConfigurations(v.([]interface{})[0].(map[string]interface{})) - } + output, err := conn.CreateDetectorWithContext(ctx, input) - log.Printf("[DEBUG] Creating GuardDuty Detector: %s", input) - output, err := conn.CreateDetectorWithContext(ctx, &input) if err != nil { - return sdkdiag.AppendErrorf(diags, "Creating GuardDuty Detector failed: %s", err) + return sdkdiag.AppendErrorf(diags, "creating GuardDuty Detector: %s", err) } + d.SetId(aws.StringValue(output.DetectorId)) return append(diags, resourceDetectorRead(ctx, d, meta)...) @@ -180,21 +176,19 @@ func resourceDetectorRead(ctx context.Context, d *schema.ResourceData, meta inte var diags diag.Diagnostics conn := meta.(*conns.AWSClient).GuardDutyConn(ctx) - input := guardduty.GetDetectorInput{ - DetectorId: aws.String(d.Id()), + gdo, err := FindDetectorByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] GuardDuty Detector (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - log.Printf("[DEBUG] Reading GuardDuty Detector: %s", input) - gdo, err := conn.GetDetectorWithContext(ctx, &input) if err != nil { - if tfawserr.ErrMessageContains(err, guardduty.ErrCodeBadRequestException, "The request is rejected because the input detectorId is not owned by the current account.") { - log.Printf("[WARN] GuardDuty detector %q not found, removing from state", d.Id()) - d.SetId("") - return diags - } - return sdkdiag.AppendErrorf(diags, "Reading GuardDuty Detector '%s' failed: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading GuardDuty Detector (%s): %s", d.Id(), err) } + d.Set("account_id", meta.(*conns.AWSClient).AccountID) arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, Region: meta.(*conns.AWSClient).Region, @@ -204,8 +198,6 @@ func resourceDetectorRead(ctx context.Context, d *schema.ResourceData, meta inte }.String() d.Set("arn", arn) - d.Set("account_id", meta.(*conns.AWSClient).AccountID) - if gdo.DataSources != nil { if err := d.Set("datasources", []interface{}{flattenDataSourceConfigurationsResult(gdo.DataSources)}); err != nil { return sdkdiag.AppendErrorf(diags, "setting datasources: %s", err) @@ -213,7 +205,6 @@ func resourceDetectorRead(ctx context.Context, d *schema.ResourceData, meta inte } else { d.Set("datasources", nil) } - d.Set("enable", aws.StringValue(gdo.Status) == guardduty.DetectorStatusEnabled) d.Set("finding_publishing_frequency", gdo.FindingPublishingFrequency) @@ -227,7 +218,7 @@ func resourceDetectorUpdate(ctx context.Context, d *schema.ResourceData, meta in conn := meta.(*conns.AWSClient).GuardDutyConn(ctx) if d.HasChangesExcept("tags", "tags_all") { - input := guardduty.UpdateDetectorInput{ + input := &guardduty.UpdateDetectorInput{ DetectorId: aws.String(d.Id()), Enable: aws.Bool(d.Get("enable").(bool)), FindingPublishingFrequency: aws.String(d.Get("finding_publishing_frequency").(string)), @@ -237,8 +228,8 @@ func resourceDetectorUpdate(ctx context.Context, d *schema.ResourceData, meta in input.DataSources = expandDataSourceConfigurations(d.Get("datasources").([]interface{})[0].(map[string]interface{})) } - log.Printf("[DEBUG] Update GuardDuty Detector: %s", input) - _, err := conn.UpdateDetectorWithContext(ctx, &input) + _, err := conn.UpdateDetectorWithContext(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "updating GuardDuty Detector (%s): %s", d.Id(), err) } @@ -251,26 +242,15 @@ func resourceDetectorDelete(ctx context.Context, d *schema.ResourceData, meta in var diags diag.Diagnostics conn := meta.(*conns.AWSClient).GuardDutyConn(ctx) - input := &guardduty.DeleteDetectorInput{ - DetectorId: aws.String(d.Id()), - } - - err := retry.RetryContext(ctx, membershipPropagationTimeout, func() *retry.RetryError { - _, err := conn.DeleteDetectorWithContext(ctx, input) + log.Printf("[DEBUG] Deleting GuardDuty Detector: %s", d.Id()) + _, err := tfresource.RetryWhenAWSErrMessageContains(ctx, membershipPropagationTimeout, func() (interface{}, error) { + return conn.DeleteDetectorWithContext(ctx, &guardduty.DeleteDetectorInput{ + DetectorId: aws.String(d.Id()), + }) + }, guardduty.ErrCodeBadRequestException, "cannot delete detector while it has invited or associated members") - if tfawserr.ErrMessageContains(err, guardduty.ErrCodeBadRequestException, "cannot delete detector while it has invited or associated members") { - return retry.RetryableError(err) - } - - if err != nil { - return retry.NonRetryableError(err) - } - - return nil - }) - - if tfresource.TimedOut(err) { - _, err = conn.DeleteDetectorWithContext(ctx, input) + if tfawserr.ErrMessageContains(err, guardduty.ErrCodeBadRequestException, "The request is rejected because the input detectorId is not owned by the current account.") { + return diags } if err != nil { @@ -287,27 +267,16 @@ func expandDataSourceConfigurations(tfMap map[string]interface{}) *guardduty.Dat apiObject := &guardduty.DataSourceConfigurations{} - if v, ok := tfMap["s3_logs"].([]interface{}); ok && len(v) > 0 { - apiObject.S3Logs = expandS3LogsConfiguration(v[0].(map[string]interface{})) - } if v, ok := tfMap["kubernetes"].([]interface{}); ok && len(v) > 0 { apiObject.Kubernetes = expandKubernetesConfiguration(v[0].(map[string]interface{})) } + if v, ok := tfMap["malware_protection"].([]interface{}); ok && len(v) > 0 { apiObject.MalwareProtection = expandMalwareProtectionConfiguration(v[0].(map[string]interface{})) } - return apiObject -} - -func expandS3LogsConfiguration(tfMap map[string]interface{}) *guardduty.S3LogsConfiguration { - if tfMap == nil { - return nil - } - - apiObject := &guardduty.S3LogsConfiguration{} - if v, ok := tfMap["enable"].(bool); ok { - apiObject.Enable = aws.Bool(v) + if v, ok := tfMap["s3_logs"].([]interface{}); ok && len(v) > 0 { + apiObject.S3Logs = expandS3LogsConfiguration(v[0].(map[string]interface{})) } return apiObject @@ -363,11 +332,11 @@ func expandMalwareProtectionConfiguration(tfMap map[string]interface{}) *guarddu } return &guardduty.MalwareProtectionConfiguration{ - ScanEc2InstanceWithFindings: expandMalwareProtectionScanEC2InstanceWithFindingsConfiguration(m), + ScanEc2InstanceWithFindings: expandScanEc2InstanceWithFindings(m), } } -func expandMalwareProtectionScanEC2InstanceWithFindingsConfiguration(tfMap map[string]interface{}) *guardduty.ScanEc2InstanceWithFindings { +func expandScanEc2InstanceWithFindings(tfMap map[string]interface{}) *guardduty.ScanEc2InstanceWithFindings { // nosemgrep:ci.caps3-in-func-name if tfMap == nil { return nil } @@ -385,6 +354,7 @@ func expandMalwareProtectionScanEC2InstanceWithFindingsConfiguration(tfMap map[s apiObject := &guardduty.ScanEc2InstanceWithFindings{ EbsVolumes: expandMalwareProtectionEBSVolumesConfiguration(m), } + return apiObject } @@ -402,6 +372,20 @@ func expandMalwareProtectionEBSVolumesConfiguration(tfMap map[string]interface{} return apiObject } +func expandS3LogsConfiguration(tfMap map[string]interface{}) *guardduty.S3LogsConfiguration { + if tfMap == nil { + return nil + } + + apiObject := &guardduty.S3LogsConfiguration{} + + if v, ok := tfMap["enable"].(bool); ok { + apiObject.Enable = aws.Bool(v) + } + + return apiObject +} + func flattenDataSourceConfigurationsResult(apiObject *guardduty.DataSourceConfigurationsResult) map[string]interface{} { if apiObject == nil { return nil @@ -409,10 +393,6 @@ func flattenDataSourceConfigurationsResult(apiObject *guardduty.DataSourceConfig tfMap := map[string]interface{}{} - if v := apiObject.S3Logs; v != nil { - tfMap["s3_logs"] = []interface{}{flattenS3LogsConfigurationResult(v)} - } - if v := apiObject.Kubernetes; v != nil { tfMap["kubernetes"] = []interface{}{flattenKubernetesConfiguration(v)} } @@ -421,18 +401,8 @@ func flattenDataSourceConfigurationsResult(apiObject *guardduty.DataSourceConfig tfMap["malware_protection"] = []interface{}{flattenMalwareProtectionConfiguration(v)} } - return tfMap -} - -func flattenS3LogsConfigurationResult(apiObject *guardduty.S3LogsConfigurationResult) map[string]interface{} { - if apiObject == nil { - return nil - } - - tfMap := map[string]interface{}{} - - if v := apiObject.Status; v != nil { - tfMap["enable"] = aws.StringValue(v) == guardduty.DataSourceStatusEnabled + if v := apiObject.S3Logs; v != nil { + tfMap["s3_logs"] = []interface{}{flattenS3LogsConfigurationResult(v)} } return tfMap @@ -474,13 +444,13 @@ func flattenMalwareProtectionConfiguration(apiObject *guardduty.MalwareProtectio tfMap := map[string]interface{}{} if v := apiObject.ScanEc2InstanceWithFindings; v != nil { - tfMap["scan_ec2_instance_with_findings"] = []interface{}{flattenMalwareProtectionScanEC2InstanceWithFindingsConfigurationResult(v)} + tfMap["scan_ec2_instance_with_findings"] = []interface{}{flattenScanEc2InstanceWithFindingsResult(v)} } return tfMap } -func flattenMalwareProtectionScanEC2InstanceWithFindingsConfigurationResult(apiObject *guardduty.ScanEc2InstanceWithFindingsResult) map[string]interface{} { +func flattenScanEc2InstanceWithFindingsResult(apiObject *guardduty.ScanEc2InstanceWithFindingsResult) map[string]interface{} { // nosemgrep:ci.caps3-in-func-name if apiObject == nil { return nil } @@ -488,13 +458,13 @@ func flattenMalwareProtectionScanEC2InstanceWithFindingsConfigurationResult(apiO tfMap := map[string]interface{}{} if v := apiObject.EbsVolumes; v != nil { - tfMap["ebs_volumes"] = []interface{}{flattenMalwareProtectionEBSVolumesConfigurationResult(v)} + tfMap["ebs_volumes"] = []interface{}{flattenEbsVolumesResult(v)} } return tfMap } -func flattenMalwareProtectionEBSVolumesConfigurationResult(apiObject *guardduty.EbsVolumesResult) map[string]interface{} { +func flattenEbsVolumesResult(apiObject *guardduty.EbsVolumesResult) map[string]interface{} { // nosemgrep:ci.caps3-in-func-name if apiObject == nil { return nil } @@ -507,3 +477,70 @@ func flattenMalwareProtectionEBSVolumesConfigurationResult(apiObject *guardduty. return tfMap } + +func flattenS3LogsConfigurationResult(apiObject *guardduty.S3LogsConfigurationResult) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.Status; v != nil { + tfMap["enable"] = aws.StringValue(v) == guardduty.DataSourceStatusEnabled + } + + return tfMap +} + +func FindDetectorByID(ctx context.Context, conn *guardduty.GuardDuty, id string) (*guardduty.GetDetectorOutput, error) { + input := &guardduty.GetDetectorInput{ + DetectorId: aws.String(id), + } + + output, err := conn.GetDetectorWithContext(ctx, input) + + 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: input, + } + } + + if err != nil { + return nil, err + } + + return output, nil +} + +// FindDetector returns the ID of the current account's active GuardDuty detector. +func FindDetector(ctx context.Context, conn *guardduty.GuardDuty) (*string, error) { + output, err := findDetectors(ctx, conn) + + if err != nil { + return nil, err + } + + return tfresource.AssertSinglePtrResult(output) +} + +func findDetectors(ctx context.Context, conn *guardduty.GuardDuty) ([]*string, error) { + input := &guardduty.ListDetectorsInput{} + var output []*string + + err := conn.ListDetectorsPagesWithContext(ctx, input, func(page *guardduty.ListDetectorsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + output = append(output, page.DetectorIds...) + + return !lastPage + }) + + if err != nil { + return nil, err + } + + return output, nil +} diff --git a/internal/service/guardduty/detector_data_source.go b/internal/service/guardduty/detector_data_source.go index ca6eb8b51cb..fb83731ef11 100644 --- a/internal/service/guardduty/detector_data_source.go +++ b/internal/service/guardduty/detector_data_source.go @@ -7,7 +7,6 @@ import ( "context" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/guardduty" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -20,20 +19,52 @@ func DataSourceDetector() *schema.Resource { ReadWithoutTimeout: dataSourceDetectorRead, Schema: map[string]*schema.Schema{ - "id": { + "features": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "additional_configuration": { + Computed: true, + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "finding_publishing_frequency": { Type: schema.TypeString, - Optional: true, Computed: true, }, - "status": { + "id": { Type: schema.TypeString, + Optional: true, Computed: true, }, "service_role_arn": { Type: schema.TypeString, Computed: true, }, - "finding_publishing_frequency": { + "status": { Type: schema.TypeString, Computed: true, }, @@ -45,43 +76,35 @@ func dataSourceDetectorRead(ctx context.Context, d *schema.ResourceData, meta in var diags diag.Diagnostics conn := meta.(*conns.AWSClient).GuardDutyConn(ctx) - detectorId := d.Get("id").(string) + detectorID := d.Get("id").(string) - if detectorId == "" { - input := &guardduty.ListDetectorsInput{} + if detectorID == "" { + output, err := FindDetector(ctx, conn) - resp, err := conn.ListDetectorsWithContext(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "listing GuardDuty Detectors: %s", err) - } - - if resp == nil || len(resp.DetectorIds) == 0 { - return sdkdiag.AppendErrorf(diags, "no GuardDuty Detectors found") - } - if len(resp.DetectorIds) > 1 { - return sdkdiag.AppendErrorf(diags, "multiple GuardDuty Detectors found; please use the `id` argument to look up a single detector") + return sdkdiag.AppendErrorf(diags, "reading this account's single GuardDuty Detector: %s", err) } - detectorId = aws.StringValue(resp.DetectorIds[0]) + detectorID = aws.StringValue(output) } - getInput := &guardduty.GetDetectorInput{ - DetectorId: aws.String(detectorId), - } + gdo, err := FindDetectorByID(ctx, conn, detectorID) - getResp, err := conn.GetDetectorWithContext(ctx, getInput) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading GuardDuty Detector (%s): %s", detectorId, err) + return sdkdiag.AppendErrorf(diags, "reading GuardDuty Detector (%s): %s", detectorID, err) } - if getResp == nil { - return sdkdiag.AppendErrorf(diags, "reading GuardDuty Detector (%s): empty result", detectorId) + d.SetId(detectorID) + if gdo.Features != nil { + if err := d.Set("features", flattenDetectorFeatureConfigurationResults(gdo.Features)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting features: %s", err) + } + } else { + d.Set("features", nil) } - - d.SetId(detectorId) - d.Set("status", getResp.Status) - d.Set("service_role_arn", getResp.ServiceRole) - d.Set("finding_publishing_frequency", getResp.FindingPublishingFrequency) + d.Set("finding_publishing_frequency", gdo.FindingPublishingFrequency) + d.Set("service_role_arn", gdo.ServiceRole) + d.Set("status", gdo.Status) return diags } diff --git a/internal/service/guardduty/detector_data_source_test.go b/internal/service/guardduty/detector_data_source_test.go index 5892b939b9d..0b20056ef19 100644 --- a/internal/service/guardduty/detector_data_source_test.go +++ b/internal/service/guardduty/detector_data_source_test.go @@ -13,23 +13,26 @@ import ( func testAccDetectorDataSource_basic(t *testing.T) { ctx := acctest.Context(t) + datasourceName := "data.aws_guardduty_detector.test" + resourceName := "aws_guardduty_detector.test" + resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, PreventPostDestroyRefresh: true, Steps: []resource.TestStep{ { - Config: testAccDetectorDataSourceConfig_basicResource(), - Check: resource.ComposeTestCheckFunc(), - }, - { - Config: testAccDetectorDataSourceConfig_basicResource2(), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair("data.aws_guardduty_detector.test", "id", "aws_guardduty_detector.test", "id"), - resource.TestCheckResourceAttr("data.aws_guardduty_detector.test", "status", "ENABLED"), - acctest.CheckResourceAttrGlobalARN("data.aws_guardduty_detector.test", "service_role_arn", "iam", "role/aws-service-role/guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDuty"), - resource.TestCheckResourceAttrPair("data.aws_guardduty_detector.test", "finding_publishing_frequency", "aws_guardduty_detector.test", "finding_publishing_frequency"), + Config: testAccDetectorDataSourceConfig_basic, + Check: resource.ComposeAggregateTestCheckFunc( + acctest.CheckResourceAttrGreaterThanValue(datasourceName, "features.#", 0), + resource.TestCheckResourceAttrPair(datasourceName, "finding_publishing_frequency", resourceName, "finding_publishing_frequency"), + resource.TestCheckResourceAttrPair(datasourceName, "id", resourceName, "id"), + acctest.CheckResourceAttrGlobalARN(datasourceName, "service_role_arn", "iam", "role/aws-service-role/guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDuty"), + resource.TestCheckResourceAttr(datasourceName, "status", "ENABLED"), ), }, }, @@ -38,44 +41,43 @@ func testAccDetectorDataSource_basic(t *testing.T) { func testAccDetectorDataSource_ID(t *testing.T) { ctx := acctest.Context(t) + datasourceName := "data.aws_guardduty_detector.test" + resourceName := "aws_guardduty_detector.test" + resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { - Config: testAccDetectorDataSourceConfig_explicit(), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair("data.aws_guardduty_detector.test", "id", "aws_guardduty_detector.test", "id"), - resource.TestCheckResourceAttr("data.aws_guardduty_detector.test", "status", "ENABLED"), - acctest.CheckResourceAttrGlobalARN("data.aws_guardduty_detector.test", "service_role_arn", "iam", "role/aws-service-role/guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDuty"), - resource.TestCheckResourceAttrPair("data.aws_guardduty_detector.test", "finding_publishing_frequency", "aws_guardduty_detector.test", "finding_publishing_frequency"), + Config: testAccDetectorDataSourceConfig_id, + Check: resource.ComposeAggregateTestCheckFunc( + acctest.CheckResourceAttrGreaterThanValue(datasourceName, "features.#", 0), + resource.TestCheckResourceAttrPair(datasourceName, "finding_publishing_frequency", resourceName, "finding_publishing_frequency"), + resource.TestCheckResourceAttrPair(datasourceName, "id", resourceName, "id"), + acctest.CheckResourceAttrGlobalARN(datasourceName, "service_role_arn", "iam", "role/aws-service-role/guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDuty"), + resource.TestCheckResourceAttr(datasourceName, "status", "ENABLED"), ), }, }, }) } -func testAccDetectorDataSourceConfig_basicResource() string { - return ` -resource "aws_guardduty_detector" "test" {} -` -} - -func testAccDetectorDataSourceConfig_basicResource2() string { - return ` +const testAccDetectorDataSourceConfig_basic = ` resource "aws_guardduty_detector" "test" {} -data "aws_guardduty_detector" "test" {} -` +data "aws_guardduty_detector" "test" { + depends_on = [aws_guardduty_detector.test] } +` -func testAccDetectorDataSourceConfig_explicit() string { - return ` +const testAccDetectorDataSourceConfig_id = ` resource "aws_guardduty_detector" "test" {} data "aws_guardduty_detector" "test" { id = aws_guardduty_detector.test.id } ` -} diff --git a/internal/service/guardduty/detector_feature.go b/internal/service/guardduty/detector_feature.go new file mode 100644 index 00000000000..b69ec950975 --- /dev/null +++ b/internal/service/guardduty/detector_feature.go @@ -0,0 +1,284 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package guardduty + +import ( + "context" + "fmt" + "log" + "strings" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/guardduty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" +) + +// @SDKResource("aws_guardduty_detector_feature", name="Detector Feature") +func ResourceDetectorFeature() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceDetectorFeaturePut, + ReadWithoutTimeout: resourceDetectorFeatureRead, + UpdateWithoutTimeout: resourceDetectorFeaturePut, + DeleteWithoutTimeout: schema.NoopContext, + + Schema: map[string]*schema.Schema{ + "additional_configuration": { + Optional: true, + ForceNew: true, + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(guardduty.FeatureAdditionalConfiguration_Values(), false), + }, + "status": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(guardduty.FeatureStatus_Values(), false), + }, + }, + }, + }, + "detector_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(guardduty.DetectorFeature_Values(), false), + }, + "status": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(guardduty.FeatureStatus_Values(), false), + }, + }, + } +} + +func resourceDetectorFeaturePut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).GuardDutyConn(ctx) + + detectorID, name := d.Get("detector_id").(string), d.Get("name").(string) + feature := &guardduty.DetectorFeatureConfiguration{ + Name: aws.String(name), + Status: aws.String(d.Get("status").(string)), + } + + if v, ok := d.GetOk("additional_configuration"); ok && len(v.([]interface{})) > 0 { + feature.AdditionalConfiguration = expandDetectorAdditionalConfigurations(v.([]interface{})) + } + + input := &guardduty.UpdateDetectorInput{ + DetectorId: aws.String(detectorID), + Features: []*guardduty.DetectorFeatureConfiguration{feature}, + } + + _, err := conn.UpdateDetectorWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating GuardDuty Detector (%s) Feature (%s): %s", detectorID, name, err) + } + + if d.IsNewResource() { + d.SetId(detectorFeatureCreateResourceID(detectorID, name)) + } + + return append(diags, resourceDetectorFeatureRead(ctx, d, meta)...) +} + +func resourceDetectorFeatureRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).GuardDutyConn(ctx) + + detectorID, name, err := detectorFeatureParseResourceID(d.Id()) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + + feature, err := FindDetectorFeatureByTwoPartKey(ctx, conn, detectorID, name) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] GuardDuty Detector Feature (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading GuardDuty Detector Feature (%s): %s", d.Id(), err) + } + + if err := d.Set("additional_configuration", flattenDetectorAdditionalConfigurationResults(feature.AdditionalConfiguration)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting additional_configuration: %s", err) + } + d.Set("detector_id", detectorID) + d.Set("name", feature.Name) + d.Set("status", feature.Status) + + return diags +} + +const detectorFeatureResourceIDSeparator = "/" + +func detectorFeatureCreateResourceID(detectorID, name string) string { + parts := []string{detectorID, name} + id := strings.Join(parts, detectorFeatureResourceIDSeparator) + + return id +} + +func detectorFeatureParseResourceID(id string) (string, string, error) { + parts := strings.Split(id, detectorFeatureResourceIDSeparator) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected DETECTORID%[2]sFEATURENAME", id, detectorFeatureResourceIDSeparator) +} + +func FindDetectorFeatureByTwoPartKey(ctx context.Context, conn *guardduty.GuardDuty, detectorID, name string) (*guardduty.DetectorFeatureConfigurationResult, error) { + output, err := FindDetectorByID(ctx, conn, detectorID) + + if err != nil { + return nil, err + } + + return tfresource.AssertSinglePtrResult(tfslices.Filter(output.Features, func(v *guardduty.DetectorFeatureConfigurationResult) bool { + return aws.StringValue(v.Name) == name + })) +} + +func expandDetectorAdditionalConfiguration(tfMap map[string]interface{}) *guardduty.DetectorAdditionalConfiguration { + if tfMap == nil { + return nil + } + + apiObject := &guardduty.DetectorAdditionalConfiguration{} + + if v, ok := tfMap["name"].(string); ok && v != "" { + apiObject.Name = aws.String(v) + } + + if v, ok := tfMap["status"].(string); ok && v != "" { + apiObject.Status = aws.String(v) + } + + return apiObject +} + +func expandDetectorAdditionalConfigurations(tfList []interface{}) []*guardduty.DetectorAdditionalConfiguration { + if len(tfList) == 0 { + return nil + } + + var apiObjects []*guardduty.DetectorAdditionalConfiguration + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue + } + + apiObject := expandDetectorAdditionalConfiguration(tfMap) + + if apiObject == nil { + continue + } + + apiObjects = append(apiObjects, apiObject) + } + + return apiObjects +} + +func flattenDetectorFeatureConfigurationResult(apiObject *guardduty.DetectorFeatureConfigurationResult) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.AdditionalConfiguration; v != nil { + tfMap["additional_configuration"] = flattenDetectorAdditionalConfigurationResults(v) + } + + if v := apiObject.Name; v != nil { + tfMap["name"] = aws.StringValue(v) + } + + if v := apiObject.Status; v != nil { + tfMap["status"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenDetectorFeatureConfigurationResults(apiObjects []*guardduty.DetectorFeatureConfigurationResult) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + tfList = append(tfList, flattenDetectorFeatureConfigurationResult(apiObject)) + } + + return tfList +} + +func flattenDetectorAdditionalConfigurationResult(apiObject *guardduty.DetectorAdditionalConfigurationResult) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.Name; v != nil { + tfMap["name"] = aws.StringValue(v) + } + + if v := apiObject.Status; v != nil { + tfMap["status"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenDetectorAdditionalConfigurationResults(apiObjects []*guardduty.DetectorAdditionalConfigurationResult) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + tfList = append(tfList, flattenDetectorAdditionalConfigurationResult(apiObject)) + } + + return tfList +} diff --git a/internal/service/guardduty/detector_feature_test.go b/internal/service/guardduty/detector_feature_test.go new file mode 100644 index 00000000000..43e5d626489 --- /dev/null +++ b/internal/service/guardduty/detector_feature_test.go @@ -0,0 +1,238 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package guardduty_test + +import ( + "context" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/guardduty" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfguardduty "github.com/hashicorp/terraform-provider-aws/internal/service/guardduty" +) + +func testAccDetectorFeature_basic(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_guardduty_detector_feature.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccDetectorFeatureConfig_basic("RDS_LOGIN_EVENTS", "ENABLED"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDetectorFeatureExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "detector_id"), + resource.TestCheckResourceAttr(resourceName, "name", "RDS_LOGIN_EVENTS"), + resource.TestCheckResourceAttr(resourceName, "status", "ENABLED"), + ), + }, + }, + }) +} + +func testAccDetectorFeature_additionalConfiguration(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_guardduty_detector_feature.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccDetectorFeatureConfig_additionalConfiguration("ENABLED", "ENABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckDetectorFeatureExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.0.name", "EKS_ADDON_MANAGEMENT"), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.0.status", "ENABLED"), + resource.TestCheckResourceAttr(resourceName, "name", "EKS_RUNTIME_MONITORING"), + resource.TestCheckResourceAttr(resourceName, "status", "ENABLED"), + ), + }, + { + Config: testAccDetectorFeatureConfig_additionalConfiguration("DISABLED", "DISABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckDetectorFeatureExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.0.name", "EKS_ADDON_MANAGEMENT"), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.0.status", "DISABLED"), + resource.TestCheckResourceAttr(resourceName, "name", "EKS_RUNTIME_MONITORING"), + resource.TestCheckResourceAttr(resourceName, "status", "DISABLED"), + ), + }, + { + Config: testAccDetectorFeatureConfig_additionalConfiguration("ENABLED", "DISABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckDetectorFeatureExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.0.name", "EKS_ADDON_MANAGEMENT"), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.0.status", "DISABLED"), + resource.TestCheckResourceAttr(resourceName, "name", "EKS_RUNTIME_MONITORING"), + resource.TestCheckResourceAttr(resourceName, "status", "ENABLED"), + ), + }, + }, + }) +} + +func testAccDetectorFeature_multiple(t *testing.T) { + ctx := acctest.Context(t) + resource1Name := "aws_guardduty_detector_feature.test1" + resource2Name := "aws_guardduty_detector_feature.test2" + resource3Name := "aws_guardduty_detector_feature.test3" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccDetectorFeatureConfig_multiple("ENABLED", "DISABLED", "ENABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckDetectorFeatureExists(ctx, resource1Name), + testAccCheckDetectorFeatureExists(ctx, resource2Name), + testAccCheckDetectorFeatureExists(ctx, resource3Name), + resource.TestCheckResourceAttr(resource1Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource1Name, "name", "EBS_MALWARE_PROTECTION"), + resource.TestCheckResourceAttr(resource1Name, "status", "ENABLED"), + resource.TestCheckResourceAttr(resource2Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource2Name, "name", "LAMBDA_NETWORK_LOGS"), + resource.TestCheckResourceAttr(resource2Name, "status", "DISABLED"), + resource.TestCheckResourceAttr(resource3Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource3Name, "name", "S3_DATA_EVENTS"), + resource.TestCheckResourceAttr(resource3Name, "status", "ENABLED"), + ), + }, + { + Config: testAccDetectorFeatureConfig_multiple("DISABLED", "ENABLED", "ENABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckDetectorFeatureExists(ctx, resource1Name), + testAccCheckDetectorFeatureExists(ctx, resource2Name), + testAccCheckDetectorFeatureExists(ctx, resource3Name), + resource.TestCheckResourceAttr(resource1Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource1Name, "name", "EBS_MALWARE_PROTECTION"), + resource.TestCheckResourceAttr(resource1Name, "status", "DISABLED"), + resource.TestCheckResourceAttr(resource2Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource2Name, "name", "LAMBDA_NETWORK_LOGS"), + resource.TestCheckResourceAttr(resource2Name, "status", "ENABLED"), + resource.TestCheckResourceAttr(resource3Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource3Name, "name", "S3_DATA_EVENTS"), + resource.TestCheckResourceAttr(resource3Name, "status", "ENABLED"), + ), + }, + { + Config: testAccDetectorFeatureConfig_multiple("DISABLED", "DISABLED", "DISABLED"), + Check: resource.ComposeTestCheckFunc( + testAccCheckDetectorFeatureExists(ctx, resource1Name), + testAccCheckDetectorFeatureExists(ctx, resource2Name), + testAccCheckDetectorFeatureExists(ctx, resource3Name), + resource.TestCheckResourceAttr(resource1Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource1Name, "name", "EBS_MALWARE_PROTECTION"), + resource.TestCheckResourceAttr(resource1Name, "status", "DISABLED"), + resource.TestCheckResourceAttr(resource2Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource2Name, "name", "LAMBDA_NETWORK_LOGS"), + resource.TestCheckResourceAttr(resource2Name, "status", "DISABLED"), + resource.TestCheckResourceAttr(resource3Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource3Name, "name", "S3_DATA_EVENTS"), + resource.TestCheckResourceAttr(resource3Name, "status", "DISABLED"), + ), + }, + }, + }) +} + +func testAccCheckDetectorFeatureExists(ctx context.Context, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).GuardDutyConn(ctx) + + _, err := tfguardduty.FindDetectorFeatureByTwoPartKey(ctx, conn, rs.Primary.Attributes["detector_id"], rs.Primary.Attributes["name"]) + + return err + } +} + +func testAccDetectorFeatureConfig_basic(name, status string) string { + return fmt.Sprintf(` +resource "aws_guardduty_detector" "test" { + enable = true +} + +resource "aws_guardduty_detector_feature" "test" { + detector_id = aws_guardduty_detector.test.id + name = %[1]q + status = %[2]q +} +`, name, status) +} + +func testAccDetectorFeatureConfig_additionalConfiguration(featureStatus, additionalConfigurationStatus string) string { + return fmt.Sprintf(` +resource "aws_guardduty_detector" "test" { + enable = true +} + +resource "aws_guardduty_detector_feature" "test" { + detector_id = aws_guardduty_detector.test.id + name = "EKS_RUNTIME_MONITORING" + status = %[1]q + + additional_configuration { + name = "EKS_ADDON_MANAGEMENT" + status = %[2]q + } +} +`, featureStatus, additionalConfigurationStatus) +} + +func testAccDetectorFeatureConfig_multiple(status1, status2, status3 string) string { + return fmt.Sprintf(` +resource "aws_guardduty_detector" "test" { + enable = true +} + +resource "aws_guardduty_detector_feature" "test1" { + detector_id = aws_guardduty_detector.test.id + name = "EBS_MALWARE_PROTECTION" + status = %[1]q +} + +resource "aws_guardduty_detector_feature" "test2" { + detector_id = aws_guardduty_detector.test.id + name = "LAMBDA_NETWORK_LOGS" + status = %[2]q +} + +resource "aws_guardduty_detector_feature" "test3" { + detector_id = aws_guardduty_detector.test.id + name = "S3_DATA_EVENTS" + status = %[3]q +} +`, status1, status2, status3) +} diff --git a/internal/service/guardduty/detector_test.go b/internal/service/guardduty/detector_test.go index ee3e39e3176..b2a667e013e 100644 --- a/internal/service/guardduty/detector_test.go +++ b/internal/service/guardduty/detector_test.go @@ -9,13 +9,13 @@ import ( "testing" "github.com/YakDriver/regexache" - "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-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfguardduty "github.com/hashicorp/terraform-provider-aws/internal/service/guardduty" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func testAccDetector_basic(t *testing.T) { @@ -23,7 +23,10 @@ func testAccDetector_basic(t *testing.T) { resourceName := "aws_guardduty_detector.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckDetectorDestroy(ctx), @@ -77,7 +80,10 @@ func testAccDetector_tags(t *testing.T) { resourceName := "aws_guardduty_detector.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckDetectorDestroy(ctx), @@ -121,7 +127,10 @@ func testAccDetector_datasources_s3logs(t *testing.T) { resourceName := "aws_guardduty_detector.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckDetectorDestroy(ctx), @@ -158,7 +167,10 @@ func testAccDetector_datasources_kubernetes_audit_logs(t *testing.T) { resourceName := "aws_guardduty_detector.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckDetectorDestroy(ctx), @@ -195,7 +207,10 @@ func testAccDetector_datasources_malware_protection(t *testing.T) { resourceName := "aws_guardduty_detector.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckDetectorDestroy(ctx), @@ -235,7 +250,10 @@ func testAccDetector_datasources_all(t *testing.T) { resourceName := "aws_guardduty_detector.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckDetectorDestroy(ctx), @@ -310,51 +328,35 @@ func testAccCheckDetectorDestroy(ctx context.Context) resource.TestCheckFunc { continue } - input := &guardduty.GetDetectorInput{ - DetectorId: aws.String(rs.Primary.ID), + _, err := tfguardduty.FindDetectorByID(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue } - _, err := conn.GetDetectorWithContext(ctx, input) if err != nil { - if tfawserr.ErrMessageContains(err, guardduty.ErrCodeBadRequestException, "The request is rejected because the input detectorId is not owned by the current account.") { - return nil - } return err } - return fmt.Errorf("Expected GuardDuty Detector to be destroyed, %s found", rs.Primary.ID) + return fmt.Errorf("GuardDuty Detector %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckDetectorExists(ctx context.Context, name string) resource.TestCheckFunc { +func testAccCheckDetectorExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("Resource (%s) has empty ID", name) + return fmt.Errorf("Not found: %s", n) } conn := acctest.Provider.Meta().(*conns.AWSClient).GuardDutyConn(ctx) - output, err := conn.GetDetectorWithContext(ctx, &guardduty.GetDetectorInput{ - DetectorId: aws.String(rs.Primary.ID), - }) - - if err != nil { - return err - } + _, err := tfguardduty.FindDetectorByID(ctx, conn, rs.Primary.ID) - if output == nil { - return fmt.Errorf("GuardDuty Detector not found: %s", name) - } - - return nil + return err } } diff --git a/internal/service/guardduty/filter.go b/internal/service/guardduty/filter.go index c443cf15c81..e773dc93ae3 100644 --- a/internal/service/guardduty/filter.go +++ b/internal/service/guardduty/filter.go @@ -11,6 +11,7 @@ import ( "strings" "time" + "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/guardduty" @@ -37,29 +38,27 @@ func ResourceFilter() *schema.Resource { Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, + Schema: map[string]*schema.Schema{ + "action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(guardduty.FilterAction_Values(), false), + }, "arn": { Type: schema.TypeString, Computed: true, }, - "detector_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringLenBetween(3, 64), - }, "description": { Type: schema.TypeString, Optional: true, ValidateFunc: validation.StringLenBetween(0, 512), }, - names.AttrTags: tftags.TagsSchema(), - names.AttrTagsAll: tftags.TagsSchemaComputed(), + "detector_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, "finding_criteria": { Type: schema.TypeList, MaxItems: 1, @@ -72,21 +71,15 @@ func ResourceFilter() *schema.Resource { Required: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "field": { - Type: schema.TypeString, - Required: true, - }, "equals": { Type: schema.TypeList, Optional: true, MinItems: 1, Elem: &schema.Schema{Type: schema.TypeString}, }, - "not_equals": { - Type: schema.TypeList, - Optional: true, - MinItems: 1, - Elem: &schema.Schema{Type: schema.TypeString}, + "field": { + Type: schema.TypeString, + Required: true, }, "greater_than": { Type: schema.TypeString, @@ -108,24 +101,34 @@ func ResourceFilter() *schema.Resource { Optional: true, ValidateFunc: verify.ValidStringDateOrPositiveInt, }, + "not_equals": { + Type: schema.TypeList, + Optional: true, + MinItems: 1, + Elem: &schema.Schema{Type: schema.TypeString}, + }, }, }, }, }, }, }, - "action": { + "name": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.StringInSlice([]string{ - guardduty.FilterActionNoop, - guardduty.FilterActionArchive, - }, false), + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(3, 64), + validation.StringMatch(regexache.MustCompile(`^[a-zA-Z0-9_.-]+$`), + "only alphanumeric characters, hyphens, underscores, and periods are allowed"), + ), }, "rank": { Type: schema.TypeInt, Required: true, }, + names.AttrTags: tftags.TagsSchema(), + names.AttrTagsAll: tftags.TagsSchemaComputed(), }, CustomizeDiff: verify.SetTagsDiff, diff --git a/internal/service/guardduty/filter_test.go b/internal/service/guardduty/filter_test.go index d390f5c25c1..6a90cf9c387 100644 --- a/internal/service/guardduty/filter_test.go +++ b/internal/service/guardduty/filter_test.go @@ -30,7 +30,10 @@ func testAccFilter_basic(t *testing.T) { endDate := "2020-02-01T00:00:00Z" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckFilterDestroy(ctx), @@ -97,7 +100,10 @@ func testAccFilter_update(t *testing.T) { endDate := "2020-02-01T00:00:00Z" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckFilterDestroy(ctx), @@ -142,7 +148,10 @@ func testAccFilter_tags(t *testing.T) { endDate := "2020-02-01T00:00:00Z" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckFilterDestroy(ctx), @@ -184,7 +193,10 @@ func testAccFilter_disappears(t *testing.T) { endDate := "2020-02-01T00:00:00Z" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckACMPCACertificateAuthorityDestroy(ctx), diff --git a/internal/service/guardduty/finding_ids_data_source_test.go b/internal/service/guardduty/finding_ids_data_source_test.go index 546f6c86424..2c6c5fb0f2e 100644 --- a/internal/service/guardduty/finding_ids_data_source_test.go +++ b/internal/service/guardduty/finding_ids_data_source_test.go @@ -11,12 +11,12 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" ) -func TestAccGuardDutyFindingIdsDataSource_basic(t *testing.T) { +func testAccFindingIDsDataSource_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{ + resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) testAccPreCheckDetectorExists(ctx, t) @@ -25,7 +25,7 @@ func TestAccGuardDutyFindingIdsDataSource_basic(t *testing.T) { ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { - Config: testAccFindingIdsDataSourceConfig_basic(), + Config: testAccFindingIDsDataSourceConfig_basic(), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrPair(dataSourceName, "detector_id", detectorDataSourceName, "id"), resource.TestCheckResourceAttrSet(dataSourceName, "has_findings"), @@ -36,7 +36,7 @@ func TestAccGuardDutyFindingIdsDataSource_basic(t *testing.T) { }) } -func testAccFindingIdsDataSourceConfig_basic() string { +func testAccFindingIDsDataSourceConfig_basic() string { return ` data "aws_guardduty_detector" "test" {} diff --git a/internal/service/guardduty/guardduty_test.go b/internal/service/guardduty/guardduty_test.go index 4e79096acbe..2eed345735d 100644 --- a/internal/service/guardduty/guardduty_test.go +++ b/internal/service/guardduty/guardduty_test.go @@ -8,9 +8,10 @@ import ( "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" + tfguardduty "github.com/hashicorp/terraform-provider-aws/internal/service/guardduty" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccGuardDuty_serial(t *testing.T) { @@ -27,12 +28,20 @@ func TestAccGuardDuty_serial(t *testing.T) { "datasource_basic": testAccDetectorDataSource_basic, "datasource_id": testAccDetectorDataSource_ID, }, + "DetectorFeature": { + "basic": testAccDetectorFeature_basic, + "additional_configuration": testAccDetectorFeature_additionalConfiguration, + "multiple": testAccDetectorFeature_multiple, + }, "Filter": { "basic": testAccFilter_basic, "update": testAccFilter_update, "tags": testAccFilter_tags, "disappears": testAccFilter_disappears, }, + "FindingIDs": { + "datasource_basic": testAccFindingIDsDataSource_basic, + }, "InviteAccepter": { "basic": testAccInviteAccepter_basic, }, @@ -87,20 +96,34 @@ func testAccMemberFromEnv(t *testing.T) (string, string) { return accountID, email } -// testAccPreCheckDetectorExists verifies the current account has a single active -// GuardDuty detector configured. +// 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(ctx) - 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") + _, err := tfguardduty.FindDetector(ctx, conn) + + if tfresource.NotFound(err) { + t.Skipf("reading this AWS account's single GuardDuty Detector: %s", err) } - 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) + } +} + +// testAccPreCheckDetectorNotExists verifies the current account has no active GuardDuty detector configured. +func testAccPreCheckDetectorNotExists(ctx context.Context, t *testing.T) { + conn := acctest.Provider.Meta().(*conns.AWSClient).GuardDutyConn(ctx) + + _, err := tfguardduty.FindDetector(ctx, conn) + + if tfresource.NotFound(err) { + return } if err != nil { t.Fatalf("listing GuardDuty Detectors: %s", err) } + + t.Skip("this AWS account has a GuardDuty Detector") } diff --git a/internal/service/guardduty/invite_accepter_test.go b/internal/service/guardduty/invite_accepter_test.go index 41b98378bb4..c9bab1c33d5 100644 --- a/internal/service/guardduty/invite_accepter_test.go +++ b/internal/service/guardduty/invite_accepter_test.go @@ -28,6 +28,7 @@ func testAccInviteAccepter_basic(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckAlternateAccount(t) + testAccPreCheckDetectorNotExists(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5FactoriesAlternate(ctx, t), diff --git a/internal/service/guardduty/ipset_test.go b/internal/service/guardduty/ipset_test.go index 0bb529063cd..ac859203d1f 100644 --- a/internal/service/guardduty/ipset_test.go +++ b/internal/service/guardduty/ipset_test.go @@ -30,7 +30,10 @@ func testAccIPSet_basic(t *testing.T) { resourceName := "aws_guardduty_ipset.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIPSetDestroy(ctx), @@ -70,7 +73,10 @@ func testAccIPSet_tags(t *testing.T) { resourceName := "aws_guardduty_ipset.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIPSetDestroy(ctx), diff --git a/internal/service/guardduty/member_test.go b/internal/service/guardduty/member_test.go index 00c59fcf9dd..c5eb68e86ba 100644 --- a/internal/service/guardduty/member_test.go +++ b/internal/service/guardduty/member_test.go @@ -24,7 +24,10 @@ func testAccMember_basic(t *testing.T) { accountID := "111111111111" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMemberDestroy(ctx), @@ -54,7 +57,10 @@ func testAccMember_invite_disassociate(t *testing.T) { accountID, email := testAccMemberFromEnv(t) resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMemberDestroy(ctx), @@ -94,7 +100,10 @@ func testAccMember_invite_onUpdate(t *testing.T) { accountID, email := testAccMemberFromEnv(t) resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMemberDestroy(ctx), @@ -135,7 +144,10 @@ func testAccMember_invitationMessage(t *testing.T) { invitationMessage := "inviting" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckMemberDestroy(ctx), diff --git a/internal/service/guardduty/organization_admin_account_test.go b/internal/service/guardduty/organization_admin_account_test.go index 03c50016612..ff05880d3fd 100644 --- a/internal/service/guardduty/organization_admin_account_test.go +++ b/internal/service/guardduty/organization_admin_account_test.go @@ -25,6 +25,7 @@ func testAccOrganizationAdminAccount_basic(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckOrganizationsAccount(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, diff --git a/internal/service/guardduty/organization_configuration_test.go b/internal/service/guardduty/organization_configuration_test.go index 3d600f553cf..5743d933b15 100644 --- a/internal/service/guardduty/organization_configuration_test.go +++ b/internal/service/guardduty/organization_configuration_test.go @@ -21,6 +21,7 @@ func testAccOrganizationConfiguration_basic(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckOrganizationsAccount(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -62,6 +63,7 @@ func testAccOrganizationConfiguration_autoEnableOrganizationMembers(t *testing.T PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckOrganizationsAccount(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -160,6 +162,7 @@ func testAccOrganizationConfiguration_s3logs(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckOrganizationsAccount(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -203,6 +206,7 @@ func testAccOrganizationConfiguration_kubernetes(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckOrganizationsAccount(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -248,6 +252,7 @@ func testAccOrganizationConfiguration_malwareprotection(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckOrganizationsAccount(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, diff --git a/internal/service/guardduty/publishing_destination_test.go b/internal/service/guardduty/publishing_destination_test.go index 2ce3bfb4830..8967fe67ff1 100644 --- a/internal/service/guardduty/publishing_destination_test.go +++ b/internal/service/guardduty/publishing_destination_test.go @@ -27,7 +27,10 @@ func testAccPublishingDestination_basic(t *testing.T) { kmsKeyResourceName := "aws_kms_key.gd_key" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckPublishingDestinationDestroy(ctx), @@ -56,7 +59,10 @@ func testAccPublishingDestination_disappears(t *testing.T) { bucketName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckPublishingDestinationDestroy(ctx), diff --git a/internal/service/guardduty/service_package_gen.go b/internal/service/guardduty/service_package_gen.go index bcd3713e47f..ed2dbc93187 100644 --- a/internal/service/guardduty/service_package_gen.go +++ b/internal/service/guardduty/service_package_gen.go @@ -47,6 +47,11 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka IdentifierAttribute: "arn", }, }, + { + Factory: ResourceDetectorFeature, + TypeName: "aws_guardduty_detector_feature", + Name: "Detector Feature", + }, { Factory: ResourceFilter, TypeName: "aws_guardduty_filter", diff --git a/internal/service/guardduty/threatintelset_test.go b/internal/service/guardduty/threatintelset_test.go index ca95f676c0a..985c0ea9646 100644 --- a/internal/service/guardduty/threatintelset_test.go +++ b/internal/service/guardduty/threatintelset_test.go @@ -30,7 +30,10 @@ func testAccThreatIntelSet_basic(t *testing.T) { resourceName := "aws_guardduty_threatintelset.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckThreatIntelSetDestroy(ctx), @@ -70,7 +73,10 @@ func testAccThreatIntelSet_tags(t *testing.T) { resourceName := "aws_guardduty_threatintelset.test" resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckThreatIntelSetDestroy(ctx), diff --git a/internal/service/servicequotas/acc_test.go b/internal/service/servicequotas/acc_test.go index 5f9ff82c9d9..c2a7d92b7fc 100644 --- a/internal/service/servicequotas/acc_test.go +++ b/internal/service/servicequotas/acc_test.go @@ -5,11 +5,12 @@ package servicequotas_test import ( "context" + "errors" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/servicequotas" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/servicequotas" + "github.com/aws/aws-sdk-go-v2/service/servicequotas/types" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" ) @@ -29,11 +30,11 @@ const ( ) func testAccPreCheck(ctx context.Context, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).ServiceQuotasConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ServiceQuotasClient(ctx) input := &servicequotas.ListServicesInput{} - _, err := conn.ListServicesWithContext(ctx, input) + _, err := conn.ListServices(ctx, input) if acctest.PreCheckSkipError(err) { t.Skipf("skipping acceptance testing: %s", err) @@ -46,15 +47,16 @@ func testAccPreCheck(ctx context.Context, t *testing.T) { // nosemgrep:ci.servicequotas-in-func-name func preCheckServiceQuotaSet(ctx context.Context, serviceCode, quotaCode string, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).ServiceQuotasConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ServiceQuotasClient(ctx) input := &servicequotas.GetServiceQuotaInput{ QuotaCode: aws.String(quotaCode), ServiceCode: aws.String(serviceCode), } - _, err := conn.GetServiceQuotaWithContext(ctx, input) - if tfawserr.ErrCodeEquals(err, servicequotas.ErrCodeNoSuchResourceException) { + _, err := conn.GetServiceQuota(ctx, input) + var nsr *types.NoSuchResourceException + if errors.As(err, &nsr) { t.Fatalf("The Service Quota (%s/%s) has never been set. This test can only be run with a quota that has previously been set. Please update the test to check a new quota.", serviceCode, quotaCode) } if err != nil { @@ -63,31 +65,32 @@ func preCheckServiceQuotaSet(ctx context.Context, serviceCode, quotaCode string, } func preCheckServiceQuotaUnset(ctx context.Context, serviceCode, quotaCode string, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).ServiceQuotasConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ServiceQuotasClient(ctx) input := &servicequotas.GetServiceQuotaInput{ QuotaCode: aws.String(quotaCode), ServiceCode: aws.String(serviceCode), } - _, err := conn.GetServiceQuotaWithContext(ctx, input) + _, err := conn.GetServiceQuota(ctx, input) if err == nil { t.Fatalf("The Service Quota (%s/%s) has been set. This test can only be run with a quota that has never been set. Please update the test to check a new quota.", serviceCode, quotaCode) } - if !tfawserr.ErrCodeEquals(err, servicequotas.ErrCodeNoSuchResourceException) { + var nsr *types.NoSuchResourceException + if !errors.As(err, &nsr) { t.Fatalf("unexpected PreCheck error getting Service Quota (%s/%s) : %s", serviceCode, quotaCode, err) } } func preCheckServiceQuotaHasUsageMetric(ctx context.Context, serviceCode, quotaCode string, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).ServiceQuotasConn(ctx) + conn := acctest.Provider.Meta().(*conns.AWSClient).ServiceQuotasClient(ctx) input := &servicequotas.GetAWSDefaultServiceQuotaInput{ QuotaCode: aws.String(quotaCode), ServiceCode: aws.String(serviceCode), } - quota, err := conn.GetAWSDefaultServiceQuotaWithContext(ctx, input) + quota, err := conn.GetAWSDefaultServiceQuota(ctx, input) if err != nil { t.Fatalf("unexpected PreCheck error getting Service Quota (%s/%s) : %s", serviceCode, quotaCode, err) } diff --git a/internal/service/servicequotas/find.go b/internal/service/servicequotas/find.go index fd67bf3a796..bf2208d9569 100644 --- a/internal/service/servicequotas/find.go +++ b/internal/service/servicequotas/find.go @@ -5,22 +5,23 @@ package servicequotas import ( "context" + "errors" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/servicequotas" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/servicequotas" + "github.com/aws/aws-sdk-go-v2/service/servicequotas/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -func findServiceQuotaDefaultByID(ctx context.Context, conn *servicequotas.ServiceQuotas, serviceCode, quotaCode string) (*servicequotas.ServiceQuota, error) { +func findServiceQuotaDefaultByID(ctx context.Context, conn *servicequotas.Client, serviceCode, quotaCode string) (*types.ServiceQuota, error) { input := &servicequotas.GetAWSDefaultServiceQuotaInput{ ServiceCode: aws.String(serviceCode), QuotaCode: aws.String(quotaCode), } - output, err := conn.GetAWSDefaultServiceQuotaWithContext(ctx, input) + output, err := conn.GetAWSDefaultServiceQuota(ctx, input) if err != nil { return nil, err @@ -32,45 +33,38 @@ func findServiceQuotaDefaultByID(ctx context.Context, conn *servicequotas.Servic return output.Quota, nil } -func findServiceQuotaDefaultByName(ctx context.Context, conn *servicequotas.ServiceQuotas, serviceCode, quotaName string) (*servicequotas.ServiceQuota, error) { +func findServiceQuotaDefaultByName(ctx context.Context, conn *servicequotas.Client, serviceCode, quotaName string) (*types.ServiceQuota, error) { input := &servicequotas.ListAWSDefaultServiceQuotasInput{ ServiceCode: aws.String(serviceCode), } - var defaultQuota *servicequotas.ServiceQuota - err := conn.ListAWSDefaultServiceQuotasPagesWithContext(ctx, input, func(page *servicequotas.ListAWSDefaultServiceQuotasOutput, lastPage bool) bool { - if page == nil { - return !lastPage + paginator := servicequotas.NewListAWSDefaultServiceQuotasPaginator(conn, input) + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return nil, err } for _, q := range page.Quotas { - if aws.StringValue(q.QuotaName) == quotaName { - defaultQuota = q - return false + if aws.ToString(q.QuotaName) == quotaName { + return &q, nil } } - - return !lastPage - }) - if err != nil { - return nil, err - } - if defaultQuota == nil { - return nil, tfresource.NewEmptyResultError(input) } - return defaultQuota, nil + return nil, tfresource.NewEmptyResultError(input) } -func findServiceQuotaByID(ctx context.Context, conn *servicequotas.ServiceQuotas, serviceCode, quotaCode string) (*servicequotas.ServiceQuota, error) { +func findServiceQuotaByID(ctx context.Context, conn *servicequotas.Client, serviceCode, quotaCode string) (*types.ServiceQuota, error) { input := &servicequotas.GetServiceQuotaInput{ ServiceCode: aws.String(serviceCode), QuotaCode: aws.String(quotaCode), } - output, err := conn.GetServiceQuotaWithContext(ctx, input) + output, err := conn.GetServiceQuota(ctx, input) - if tfawserr.ErrCodeEquals(err, servicequotas.ErrCodeNoSuchResourceException) { + var nsr *types.NoSuchResourceException + if errors.As(err, &nsr) { return nil, &retry.NotFoundError{ LastError: err, LastRequest: input, @@ -86,7 +80,7 @@ func findServiceQuotaByID(ctx context.Context, conn *servicequotas.ServiceQuotas if output.Quota.ErrorReason != nil { return nil, &retry.NotFoundError{ - Message: fmt.Sprintf("%s: %s", aws.StringValue(output.Quota.ErrorReason.ErrorCode), aws.StringValue(output.Quota.ErrorReason.ErrorMessage)), + Message: fmt.Sprintf("%s: %s", output.Quota.ErrorReason.ErrorCode, aws.ToString(output.Quota.ErrorReason.ErrorMessage)), LastRequest: input, } } diff --git a/internal/service/servicequotas/service_data_source.go b/internal/service/servicequotas/service_data_source.go index c9d5b26f344..736e8f4f387 100644 --- a/internal/service/servicequotas/service_data_source.go +++ b/internal/service/servicequotas/service_data_source.go @@ -6,8 +6,9 @@ package servicequotas import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/servicequotas" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/servicequotas" + "github.com/aws/aws-sdk-go-v2/service/servicequotas/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -34,26 +35,31 @@ func DataSourceService() *schema.Resource { func dataSourceServiceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ServiceQuotasConn(ctx) + conn := meta.(*conns.AWSClient).ServiceQuotasClient(ctx) serviceName := d.Get("service_name").(string) input := &servicequotas.ListServicesInput{} - var service *servicequotas.ServiceInfo - err := conn.ListServicesPagesWithContext(ctx, input, func(page *servicequotas.ListServicesOutput, lastPage bool) bool { + var service *types.ServiceInfo + paginator := servicequotas.NewListServicesPaginator(conn, input) + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return sdkdiag.AppendErrorf(diags, "listing Services: %s", err) + } + for _, s := range page.Services { - if aws.StringValue(s.ServiceName) == serviceName { - service = s + s := s + if aws.ToString(s.ServiceName) == serviceName { + service = &s break } } - return !lastPage - }) - - if err != nil { - return sdkdiag.AppendErrorf(diags, "listing Services: %s", err) + if service != nil { + break // stop paging once found + } } if service == nil { @@ -62,7 +68,7 @@ func dataSourceServiceRead(ctx context.Context, d *schema.ResourceData, meta int d.Set("service_code", service.ServiceCode) d.Set("service_name", service.ServiceName) - d.SetId(aws.StringValue(service.ServiceCode)) + d.SetId(aws.ToString(service.ServiceCode)) return diags } diff --git a/internal/service/servicequotas/service_data_source_test.go b/internal/service/servicequotas/service_data_source_test.go index 1bbef1388e6..f7726560664 100644 --- a/internal/service/servicequotas/service_data_source_test.go +++ b/internal/service/servicequotas/service_data_source_test.go @@ -7,9 +7,9 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/service/servicequotas" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" ) func TestAccServiceQuotasServiceDataSource_serviceName(t *testing.T) { @@ -17,8 +17,11 @@ func TestAccServiceQuotasServiceDataSource_serviceName(t *testing.T) { dataSourceName := "data.aws_servicequotas_service.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, servicequotas.EndpointsID) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.ServiceQuotasEndpointID) + }, + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { diff --git a/internal/service/servicequotas/service_package_gen.go b/internal/service/servicequotas/service_package_gen.go index 5f2630a6e04..489fa622f8c 100644 --- a/internal/service/servicequotas/service_package_gen.go +++ b/internal/service/servicequotas/service_package_gen.go @@ -5,9 +5,8 @@ package servicequotas import ( "context" - aws_sdkv1 "github.com/aws/aws-sdk-go/aws" - session_sdkv1 "github.com/aws/aws-sdk-go/aws/session" - servicequotas_sdkv1 "github.com/aws/aws-sdk-go/service/servicequotas" + aws_sdkv2 "github.com/aws/aws-sdk-go-v2/aws" + servicequotas_sdkv2 "github.com/aws/aws-sdk-go-v2/service/servicequotas" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/types" "github.com/hashicorp/terraform-provider-aws/names" @@ -49,11 +48,15 @@ func (p *servicePackage) ServicePackageName() string { return names.ServiceQuotas } -// NewConn returns a new AWS SDK for Go v1 client for this service package's AWS API. -func (p *servicePackage) NewConn(ctx context.Context, config map[string]any) (*servicequotas_sdkv1.ServiceQuotas, error) { - sess := config["session"].(*session_sdkv1.Session) +// NewClient returns a new AWS SDK for Go v2 client for this service package's AWS API. +func (p *servicePackage) NewClient(ctx context.Context, config map[string]any) (*servicequotas_sdkv2.Client, error) { + cfg := *(config["aws_sdkv2_config"].(*aws_sdkv2.Config)) - return servicequotas_sdkv1.New(sess.Copy(&aws_sdkv1.Config{Endpoint: aws_sdkv1.String(config["endpoint"].(string))})), nil + return servicequotas_sdkv2.NewFromConfig(cfg, func(o *servicequotas_sdkv2.Options) { + if endpoint := config["endpoint"].(string); endpoint != "" { + o.BaseEndpoint = aws_sdkv2.String(endpoint) + } + }), nil } func ServicePackage(ctx context.Context) conns.ServicePackage { diff --git a/internal/service/servicequotas/service_quota.go b/internal/service/servicequotas/service_quota.go index f7a016dcde9..daaac872408 100644 --- a/internal/service/servicequotas/service_quota.go +++ b/internal/service/servicequotas/service_quota.go @@ -5,13 +5,14 @@ package servicequotas import ( "context" + "errors" "fmt" "strings" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/servicequotas" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/servicequotas" + "github.com/aws/aws-sdk-go-v2/service/servicequotas/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -134,7 +135,7 @@ func ResourceServiceQuota() *schema.Resource { func resourceServiceQuotaCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ServiceQuotasConn(ctx) + conn := meta.(*conns.AWSClient).ServiceQuotasClient(ctx) quotaCode := d.Get("quota_code").(string) serviceCode := d.Get("service_code").(string) @@ -148,14 +149,14 @@ func resourceServiceQuotaCreate(ctx context.Context, d *schema.ResourceData, met if err != nil { return sdkdiag.AppendErrorf(diags, "getting Default Service Quota for (%s/%s): %s", serviceCode, quotaCode, err) } - quotaValue := aws.Float64Value(defaultQuota.Value) + quotaValue := aws.ToFloat64(defaultQuota.Value) serviceQuota, err := findServiceQuotaByID(ctx, conn, serviceCode, quotaCode) if err != nil && !tfresource.NotFound(err) { return sdkdiag.AppendErrorf(diags, "getting Service Quota for (%s/%s): %s", serviceCode, quotaCode, err) } if serviceQuota != nil { - quotaValue = aws.Float64Value(serviceQuota.Value) + quotaValue = aws.ToFloat64(serviceQuota.Value) } if value > quotaValue { @@ -165,7 +166,7 @@ func resourceServiceQuotaCreate(ctx context.Context, d *schema.ResourceData, met ServiceCode: aws.String(serviceCode), } - output, err := conn.RequestServiceQuotaIncreaseWithContext(ctx, input) + output, err := conn.RequestServiceQuotaIncrease(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "requesting Service Quota (%s) increase: %s", d.Id(), err) @@ -183,7 +184,7 @@ func resourceServiceQuotaCreate(ctx context.Context, d *schema.ResourceData, met func resourceServiceQuotaRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ServiceQuotasConn(ctx) + conn := meta.(*conns.AWSClient).ServiceQuotasClient(ctx) serviceCode, quotaCode, err := resourceServiceQuotaParseID(d.Id()) @@ -228,9 +229,10 @@ func resourceServiceQuotaRead(ctx context.Context, d *schema.ResourceData, meta RequestId: aws.String(requestID), } - output, err := conn.GetRequestedServiceQuotaChangeWithContext(ctx, input) + output, err := conn.GetRequestedServiceQuotaChange(ctx, input) - if tfawserr.ErrCodeEquals(err, servicequotas.ErrCodeNoSuchResourceException) { + var nsr *types.NoSuchResourceException + if errors.As(err, &nsr) { d.Set("request_id", "") d.Set("request_status", "") return diags @@ -244,13 +246,12 @@ func resourceServiceQuotaRead(ctx context.Context, d *schema.ResourceData, meta return sdkdiag.AppendErrorf(diags, "getting Service Quotas Requested Service Quota Change (%s): empty result", requestID) } - requestStatus := aws.StringValue(output.RequestedQuota.Status) - d.Set("request_status", requestStatus) + d.Set("request_status", output.RequestedQuota.Status) - switch requestStatus { - case servicequotas.RequestStatusApproved, servicequotas.RequestStatusCaseClosed, servicequotas.RequestStatusDenied: + switch output.RequestedQuota.Status { + case types.RequestStatusApproved, types.RequestStatusCaseClosed, types.RequestStatusDenied: d.Set("request_id", "") - case servicequotas.RequestStatusCaseOpened, servicequotas.RequestStatusPending: + case types.RequestStatusCaseOpened, types.RequestStatusPending: d.Set("value", output.RequestedQuota.DesiredValue) } } @@ -260,7 +261,7 @@ func resourceServiceQuotaRead(ctx context.Context, d *schema.ResourceData, meta func resourceServiceQuotaUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ServiceQuotasConn(ctx) + conn := meta.(*conns.AWSClient).ServiceQuotasClient(ctx) value := d.Get("value").(float64) serviceCode, quotaCode, err := resourceServiceQuotaParseID(d.Id()) @@ -275,7 +276,7 @@ func resourceServiceQuotaUpdate(ctx context.Context, d *schema.ResourceData, met ServiceCode: aws.String(serviceCode), } - output, err := conn.RequestServiceQuotaIncreaseWithContext(ctx, input) + output, err := conn.RequestServiceQuotaIncrease(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "requesting Service Quota (%s) increase: %s", d.Id(), err) diff --git a/internal/service/servicequotas/service_quota_data_source.go b/internal/service/servicequotas/service_quota_data_source.go index 6bd3c03328e..6979abe91bf 100644 --- a/internal/service/servicequotas/service_quota_data_source.go +++ b/internal/service/servicequotas/service_quota_data_source.go @@ -6,8 +6,8 @@ package servicequotas import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/servicequotas" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/servicequotas/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -109,7 +109,7 @@ func DataSourceServiceQuota() *schema.Resource { } } -func flattenUsageMetric(usageMetric *servicequotas.MetricInfo) []interface{} { +func flattenUsageMetric(usageMetric *types.MetricInfo) []interface{} { if usageMetric == nil { return []interface{}{} } @@ -117,7 +117,7 @@ func flattenUsageMetric(usageMetric *servicequotas.MetricInfo) []interface{} { var usageMetrics []interface{} var metricDimensions []interface{} - if usageMetric.MetricDimensions != nil && usageMetric.MetricDimensions["Service"] != nil { + if usageMetric.MetricDimensions != nil && usageMetric.MetricDimensions["Service"] != "" { metricDimensions = append(metricDimensions, map[string]interface{}{ "service": usageMetric.MetricDimensions["Service"], "class": usageMetric.MetricDimensions["Class"], @@ -140,14 +140,14 @@ func flattenUsageMetric(usageMetric *servicequotas.MetricInfo) []interface{} { func dataSourceServiceQuotaRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ServiceQuotasConn(ctx) + conn := meta.(*conns.AWSClient).ServiceQuotasClient(ctx) quotaCode := d.Get("quota_code").(string) quotaName := d.Get("quota_name").(string) serviceCode := d.Get("service_code").(string) var err error - var defaultQuota *servicequotas.ServiceQuota + var defaultQuota *types.ServiceQuota // A Service Quota will always have a default value, but will only have a current value if it has been set. // If it is not set, `GetServiceQuota` will return "NoSuchResourceException" @@ -157,7 +157,7 @@ func dataSourceServiceQuotaRead(ctx context.Context, d *schema.ResourceData, met return sdkdiag.AppendErrorf(diags, "getting Default Service Quota for (%s/%s): %s", serviceCode, quotaName, err) } - quotaCode = aws.StringValue(defaultQuota.QuotaCode) + quotaCode = aws.ToString(defaultQuota.QuotaCode) } else { defaultQuota, err = findServiceQuotaDefaultByID(ctx, conn, serviceCode, quotaCode) if err != nil { @@ -165,7 +165,7 @@ func dataSourceServiceQuotaRead(ctx context.Context, d *schema.ResourceData, met } } - d.SetId(aws.StringValue(defaultQuota.QuotaArn)) + d.SetId(aws.ToString(defaultQuota.QuotaArn)) d.Set("adjustable", defaultQuota.Adjustable) d.Set("arn", defaultQuota.QuotaArn) d.Set("default_value", defaultQuota.Value) diff --git a/internal/service/servicequotas/service_quota_data_source_test.go b/internal/service/servicequotas/service_quota_data_source_test.go index f558d7470d1..2fb65c7c358 100644 --- a/internal/service/servicequotas/service_quota_data_source_test.go +++ b/internal/service/servicequotas/service_quota_data_source_test.go @@ -8,9 +8,9 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/service/servicequotas" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" ) func TestAccServiceQuotasServiceQuotaDataSource_quotaCode(t *testing.T) { @@ -20,10 +20,10 @@ func TestAccServiceQuotasServiceQuotaDataSource_quotaCode(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, servicequotas.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.ServiceQuotasEndpointID) preCheckServiceQuotaSet(ctx, setQuotaServiceCode, setQuotaQuotaCode, t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { @@ -52,10 +52,10 @@ func TestAccServiceQuotasServiceQuotaDataSource_quotaCode_Unset(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, servicequotas.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.ServiceQuotasEndpointID) preCheckServiceQuotaUnset(ctx, unsetQuotaServiceCode, unsetQuotaQuotaCode, t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { @@ -85,10 +85,10 @@ func TestAccServiceQuotasServiceQuotaDataSource_quotaCode_hasUsageMetric(t *test resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, servicequotas.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.ServiceQuotasEndpointID) preCheckServiceQuotaHasUsageMetric(ctx, hasUsageMetricServiceCode, hasUsageMetricQuotaCode, t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { @@ -126,7 +126,7 @@ func TestAccServiceQuotasServiceQuotaDataSource_PermissionError_quotaCode(t *tes testAccPreCheck(ctx, t) acctest.PreCheckAssumeRoleARN(t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: nil, Steps: []resource.TestStep{ @@ -145,10 +145,10 @@ func TestAccServiceQuotasServiceQuotaDataSource_quotaName(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, servicequotas.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.ServiceQuotasEndpointID) preCheckServiceQuotaSet(ctx, setQuotaServiceCode, setQuotaQuotaCode, t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { @@ -177,10 +177,10 @@ func TestAccServiceQuotasServiceQuotaDataSource_quotaName_Unset(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, servicequotas.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.ServiceQuotasEndpointID) preCheckServiceQuotaUnset(ctx, unsetQuotaServiceCode, unsetQuotaQuotaCode, t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { @@ -210,10 +210,10 @@ func TestAccServiceQuotasServiceQuotaDataSource_quotaName_hasUsageMetric(t *test resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) - acctest.PreCheckPartitionHasService(t, servicequotas.EndpointsID) + acctest.PreCheckPartitionHasService(t, names.ServiceQuotasEndpointID) preCheckServiceQuotaHasUsageMetric(ctx, hasUsageMetricServiceCode, hasUsageMetricQuotaCode, t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, Steps: []resource.TestStep{ { @@ -251,7 +251,7 @@ func TestAccServiceQuotasServiceQuotaDataSource_PermissionError_quotaName(t *tes testAccPreCheck(ctx, t) acctest.PreCheckAssumeRoleARN(t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: nil, Steps: []resource.TestStep{ diff --git a/internal/service/servicequotas/service_quota_test.go b/internal/service/servicequotas/service_quota_test.go index f083f1211e2..1a62f9a1186 100644 --- a/internal/service/servicequotas/service_quota_test.go +++ b/internal/service/servicequotas/service_quota_test.go @@ -9,9 +9,9 @@ import ( "testing" "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go/service/servicequotas" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" ) // This resource is different than many since quotas are pre-existing @@ -29,7 +29,7 @@ func TestAccServiceQuotasServiceQuota_basic(t *testing.T) { testAccPreCheck(ctx, t) preCheckServiceQuotaSet(ctx, setQuotaServiceCode, setQuotaQuotaCode, t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: nil, Steps: []resource.TestStep{ @@ -69,7 +69,7 @@ func TestAccServiceQuotasServiceQuota_basic_Unset(t *testing.T) { testAccPreCheck(ctx, t) preCheckServiceQuotaUnset(ctx, unsetQuotaServiceCode, unsetQuotaQuotaCode, t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: nil, Steps: []resource.TestStep{ @@ -109,7 +109,7 @@ func TestAccServiceQuotasServiceQuota_basic_hasUsageMetric(t *testing.T) { testAccPreCheck(ctx, t) preCheckServiceQuotaHasUsageMetric(ctx, hasUsageMetricServiceCode, hasUsageMetricQuotaCode, t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: nil, Steps: []resource.TestStep{ @@ -166,7 +166,7 @@ func TestAccServiceQuotasServiceQuota_Value_increaseOnCreate(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: nil, Steps: []resource.TestStep{ @@ -211,7 +211,7 @@ func TestAccServiceQuotasServiceQuota_Value_increaseOnUpdate(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: nil, Steps: []resource.TestStep{ @@ -241,7 +241,7 @@ func TestAccServiceQuotasServiceQuota_permissionError(t *testing.T) { ctx := acctest.Context(t) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t); acctest.PreCheckAssumeRoleARN(t) }, - ErrorCheck: acctest.ErrorCheck(t, servicequotas.EndpointsID), + ErrorCheck: acctest.ErrorCheck(t, names.ServiceQuotasEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: nil, Steps: []resource.TestStep{ diff --git a/names/names.go b/names/names.go index 3ae55c1531f..c49b717ee59 100644 --- a/names/names.go +++ b/names/names.go @@ -57,6 +57,7 @@ const ( RolesAnywhereEndpointID = "rolesanywhere" Route53DomainsEndpointID = "route53domains" SchedulerEndpointID = "scheduler" + ServiceQuotasEndpointID = "servicequotas" S3EndpointID = "s3" S3ControlEndpointID = "s3-control" SESV2EndpointID = "sesv2" diff --git a/names/names_data.csv b/names/names_data.csv index 79e804a2190..ce8ec5fe0d0 100644 --- a/names/names_data.csv +++ b/names/names_data.csv @@ -322,7 +322,7 @@ securitylake,securitylake,securitylake,securitylake,,securitylake,,,SecurityLake serverlessrepo,serverlessrepo,serverlessapplicationrepository,serverlessapplicationrepository,,serverlessrepo,,serverlessapprepo;serverlessapplicationrepository,ServerlessRepo,ServerlessApplicationRepository,,1,,aws_serverlessapplicationrepository_,aws_serverlessrepo_,,serverlessapplicationrepository_,Serverless Application Repository,AWS,,,,,, servicecatalog,servicecatalog,servicecatalog,servicecatalog,,servicecatalog,,,ServiceCatalog,ServiceCatalog,,1,,,aws_servicecatalog_,,servicecatalog_,Service Catalog,AWS,,,,,, servicecatalog-appregistry,servicecatalogappregistry,appregistry,servicecatalogappregistry,,servicecatalogappregistry,,appregistry,ServiceCatalogAppRegistry,AppRegistry,,1,,,aws_servicecatalogappregistry_,,servicecatalogappregistry_,Service Catalog AppRegistry,AWS,,x,,,, -service-quotas,servicequotas,servicequotas,servicequotas,,servicequotas,,,ServiceQuotas,ServiceQuotas,,1,,,aws_servicequotas_,,servicequotas_,Service Quotas,,,,,,, +service-quotas,servicequotas,servicequotas,servicequotas,,servicequotas,,,ServiceQuotas,ServiceQuotas,,,2,,aws_servicequotas_,,servicequotas_,Service Quotas,,,,,,, ses,ses,ses,ses,,ses,,,SES,SES,,1,,,aws_ses_,,ses_,SES (Simple Email),Amazon,,,,,, sesv2,sesv2,sesv2,sesv2,,sesv2,,,SESV2,SESV2,,,2,,aws_sesv2_,,sesv2_,SESv2 (Simple Email V2),Amazon,,,,,, stepfunctions,stepfunctions,sfn,sfn,,sfn,,stepfunctions,SFN,SFN,,1,,,aws_sfn_,,sfn_,SFN (Step Functions),AWS,,,,,, diff --git a/website/docs/d/guardduty_detector.html.markdown b/website/docs/d/guardduty_detector.html.markdown index 99f3598446d..bce278dcdbf 100644 --- a/website/docs/d/guardduty_detector.html.markdown +++ b/website/docs/d/guardduty_detector.html.markdown @@ -24,6 +24,12 @@ data "aws_guardduty_detector" "example" {} This data source exports the following attributes in addition to the arguments above: +* `features` - Current configuration of the detector features. + * `additional_configuration` - Additional feature configuration. + * `name` - The name of the additional configuration. + * `status` - The status of the additional configuration. + * `name` - The name of the detector feature. + * `status` - The status of the detector feature. * `finding_publishing_frequency` - The frequency of notifications sent about subsequent finding occurrences. * `service_role_arn` - Service-linked role that grants GuardDuty access to the resources in the AWS account. * `status` - Current status of the detector. diff --git a/website/docs/r/guardduty_detector.html.markdown b/website/docs/r/guardduty_detector.html.markdown index 2f0b6c13b2c..26f20120faf 100644 --- a/website/docs/r/guardduty_detector.html.markdown +++ b/website/docs/r/guardduty_detector.html.markdown @@ -3,12 +3,12 @@ subcategory: "GuardDuty" layout: "aws" page_title: "AWS: aws_guardduty_detector" description: |- - Provides a resource to manage a GuardDuty detector + Provides a resource to manage an Amazon GuardDuty detector --- # Resource: aws_guardduty_detector -Provides a resource to manage a GuardDuty detector. +Provides a resource to manage an Amazon GuardDuty detector. ~> **NOTE:** Deleting this resource is equivalent to "disabling" GuardDuty for an AWS region, which removes all existing findings. You can set the `enable` attribute to `false` to instead "suspend" monitoring and feedback reporting while keeping existing data. See the [Suspending or Disabling Amazon GuardDuty documentation](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_suspend-disable.html) for more information. @@ -44,7 +44,7 @@ This resource supports the following arguments: * `enable` - (Optional) Enable monitoring and feedback reporting. Setting to `false` is equivalent to "suspending" GuardDuty. Defaults to `true`. * `finding_publishing_frequency` - (Optional) Specifies the frequency of notifications sent for subsequent finding occurrences. If the detector is a GuardDuty member account, the value is determined by the GuardDuty primary account and cannot be modified, otherwise defaults to `SIX_HOURS`. For standalone and GuardDuty primary accounts, it must be configured in Terraform to enable drift detection. Valid values for standalone and primary accounts: `FIFTEEN_MINUTES`, `ONE_HOUR`, `SIX_HOURS`. See [AWS Documentation](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings_cloudwatch.html#guardduty_findings_cloudwatch_notification_frequency) for more information. -* `datasources` - (Optional) Describes which data sources will be enabled for the detector. See [Data Sources](#data-sources) below for more details. +* `datasources` - (Optional) Describes which data sources will be enabled for the detector. See [Data Sources](#data-sources) below for more details. [Deprecated](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty-feature-object-api-changes-march2023.html) in favor of [`aws_guardduty_detector_feature` resources](guardduty_detector_feature.html). * `tags` - (Optional) Key-value map of resource tags. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. ### Data Sources @@ -58,6 +58,8 @@ The `datasources` block supports the following: * `malware_protection` - (Optional) Configures [Malware Protection](https://docs.aws.amazon.com/guardduty/latest/ug/malware-protection.html). See [Malware Protection](#malware-protection), [Scan EC2 instance with findings](#scan-ec2-instance-with-findings) and [EBS volumes](#ebs-volumes) below for more details. +The `datasources` block is deprecated since March 2023. Use the `features` block instead and [map each `datasources` block to the corresponding `features` block](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty-feature-object-api-changes-march2023.html#guardduty-feature-enablement-datasource-relation). + ### S3 Logs The `s3_logs` block supports the following: diff --git a/website/docs/r/guardduty_detector_feature.html.markdown b/website/docs/r/guardduty_detector_feature.html.markdown new file mode 100644 index 00000000000..66ee1b5a648 --- /dev/null +++ b/website/docs/r/guardduty_detector_feature.html.markdown @@ -0,0 +1,52 @@ +--- +subcategory: "GuardDuty" +layout: "aws" +page_title: "AWS: aws_guardduty_detector_feature" +description: |- + Provides a resource to manage an Amazon GuardDuty detector feature +--- + +# Resource: aws_guardduty_detector_feature + +Provides a resource to manage a single Amazon GuardDuty [detector feature](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty-features-activation-model.html#guardduty-features). + +~> **NOTE:** Deleting this resource does not disable the detector feature, the resource in simply removed from state instead. + +## Example Usage + +```terraform +resource "aws_guardduty_detector" "example" { + enable = true +} + +resource "aws_guardduty_detector_feature" "eks_runtime_monitoring" { + detector_id = aws_guardduty_detector.example.id + name = "EKS_RUNTIME_MONITORING" + status = "ENABLED" + + additional_configuration { + name = "EKS_ADDON_MANAGEMENT" + status = "ENABLED" + } +} +``` + +## Argument Reference + +This resource supports the following arguments: + +* `detector_id` - (Required) Amazon GuardDuty detector ID. +* `name` - (Required) The name of the detector feature. Valid values: `S3_DATA_EVENTS`, `EKS_AUDIT_LOGS`, `EBS_MALWARE_PROTECTION`, `RDS_LOGIN_EVENTS`, `EKS_RUNTIME_MONITORING`, `LAMBDA_NETWORK_LOGS`. +* `status` - (Required) The status of the detector feature. Valid values: `ENABLED`, `DISABLED`. +* `additional_configuration` - (Optional) Additional feature configuration block. See [below](#additional-configuration). + +### Additional Configuration + +The `additional_configuration` block supports the following: + +* `name` - (Required) The name of the additional configuration. Valid values: `EKS_ADDON_MANAGEMENT`. +* `status` - (Required) The status of the additional configuration. Valid values: `ENABLED`, `DISABLED`. + +## Attribute Reference + +This resource exports no additional attributes.