diff --git a/.changelog/33913.txt b/.changelog/33913.txt new file mode 100644 index 000000000000..b81487af89c0 --- /dev/null +++ b/.changelog/33913.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_guardduty_organization_configuration_feature +``` \ No newline at end of file diff --git a/internal/service/guardduty/detector.go b/internal/service/guardduty/detector.go index 239a2294c444..1e15ae1bd6ee 100644 --- a/internal/service/guardduty/detector.go +++ b/internal/service/guardduty/detector.go @@ -510,6 +510,10 @@ func FindDetectorByID(ctx context.Context, conn *guardduty.GuardDuty, id string) return nil, err } + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + return output, nil } diff --git a/internal/service/guardduty/guardduty_test.go b/internal/service/guardduty/guardduty_test.go index 2eed345735de..fa0ccf8b32bd 100644 --- a/internal/service/guardduty/guardduty_test.go +++ b/internal/service/guardduty/guardduty_test.go @@ -59,6 +59,11 @@ func TestAccGuardDuty_serial(t *testing.T) { "kubernetes": testAccOrganizationConfiguration_kubernetes, "malwareProtection": testAccOrganizationConfiguration_malwareprotection, }, + "OrganizationConfigurationFeature": { + "basic": testAccOrganizationConfigurationFeature_basic, + "additional_configuration": testAccOrganizationConfigurationFeature_additionalConfiguration, + "multiple": testAccOrganizationConfigurationFeature_multiple, + }, "ThreatIntelSet": { "basic": testAccThreatIntelSet_basic, "tags": testAccThreatIntelSet_tags, diff --git a/internal/service/guardduty/organization_configuration.go b/internal/service/guardduty/organization_configuration.go index ae09eea6d546..676d497b1ab4 100644 --- a/internal/service/guardduty/organization_configuration.go +++ b/internal/service/guardduty/organization_configuration.go @@ -12,18 +12,20 @@ import ( "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "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" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -// @SDKResource("aws_guardduty_organization_configuration") +// @SDKResource("aws_guardduty_organization_configuration", name="Organization Configuration") func ResourceOrganizationConfiguration() *schema.Resource { return &schema.Resource{ - CreateWithoutTimeout: resourceOrganizationConfigurationUpdate, + CreateWithoutTimeout: resourceOrganizationConfigurationPut, ReadWithoutTimeout: resourceOrganizationConfigurationRead, - UpdateWithoutTimeout: resourceOrganizationConfigurationUpdate, + UpdateWithoutTimeout: resourceOrganizationConfigurationPut, DeleteWithoutTimeout: schema.NoopContext, Importer: &schema.ResourceImporter{ @@ -38,7 +40,6 @@ func ResourceOrganizationConfiguration() *schema.Resource { ExactlyOneOf: []string{"auto_enable", "auto_enable_organization_members"}, Deprecated: "Use auto_enable_organization_members instead", }, - "auto_enable_organization_members": { Type: schema.TypeString, Optional: true, @@ -46,7 +47,6 @@ func ResourceOrganizationConfiguration() *schema.Resource { ExactlyOneOf: []string{"auto_enable", "auto_enable_organization_members"}, ValidateFunc: validation.StringInSlice(guardduty.AutoEnableMembers_Values(), false), }, - "datasources": { Type: schema.TypeList, Optional: true, @@ -54,20 +54,6 @@ func ResourceOrganizationConfiguration() *schema.Resource { MaxItems: 1, 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{ - "auto_enable": { - Type: schema.TypeBool, - Required: true, - }, - }, - }, - }, "kubernetes": { Type: schema.TypeList, Optional: true, @@ -123,10 +109,23 @@ func ResourceOrganizationConfiguration() *schema.Resource { }, }, }, + "s3_logs": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "auto_enable": { + Type: schema.TypeBool, + Required: true, + }, + }, + }, + }, }, }, }, - "detector_id": { Type: schema.TypeString, Required: true, @@ -166,12 +165,11 @@ func ResourceOrganizationConfiguration() *schema.Resource { } } -func resourceOrganizationConfigurationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceOrganizationConfigurationPut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).GuardDutyConn(ctx) detectorID := d.Get("detector_id").(string) - input := &guardduty.UpdateOrganizationConfigurationInput{ AutoEnableOrganizationMembers: aws.String(d.Get("auto_enable_organization_members").(string)), DetectorId: aws.String(detectorID), @@ -181,13 +179,20 @@ func resourceOrganizationConfigurationUpdate(ctx context.Context, d *schema.Reso input.DataSources = expandOrganizationDataSourceConfigurations(v.([]interface{})[0].(map[string]interface{})) } + // We have seen occasional acceptance test failures when updating multiple features on the same detector concurrently, + // so use a mutex to ensure that multiple features being updated concurrently don't trample on each other. + conns.GlobalMutexKV.Lock(detectorID) + defer conns.GlobalMutexKV.Unlock(detectorID) + _, err := conn.UpdateOrganizationConfigurationWithContext(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating GuardDuty Organization Configuration (%s): %s", detectorID, err) } - d.SetId(detectorID) + if d.IsNewResource() { + d.SetId(detectorID) + } return append(diags, resourceOrganizationConfigurationRead(ctx, d, meta)...) } @@ -196,13 +201,9 @@ func resourceOrganizationConfigurationRead(ctx context.Context, d *schema.Resour var diags diag.Diagnostics conn := meta.(*conns.AWSClient).GuardDutyConn(ctx) - input := &guardduty.DescribeOrganizationConfigurationInput{ - DetectorId: aws.String(d.Id()), - } - - output, err := conn.DescribeOrganizationConfigurationWithContext(ctx, input) + output, err := FindOrganizationConfigurationByID(ctx, conn, d.Id()) - if tfawserr.ErrMessageContains(err, guardduty.ErrCodeBadRequestException, "The request is rejected because the input detectorId is not owned by the current account.") { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] GuardDuty Organization Configuration (%s) not found, removing from state", d.Id()) d.SetId("") return diags @@ -218,7 +219,6 @@ func resourceOrganizationConfigurationRead(ctx context.Context, d *schema.Resour d.Set("auto_enable", output.AutoEnable) d.Set("auto_enable_organization_members", output.AutoEnableOrganizationMembers) - if output.DataSources != nil { if err := d.Set("datasources", []interface{}{flattenOrganizationDataSourceConfigurationsResult(output.DataSources)}); err != nil { return sdkdiag.AppendErrorf(diags, "setting datasources: %s", err) @@ -226,7 +226,6 @@ func resourceOrganizationConfigurationRead(ctx context.Context, d *schema.Resour } else { d.Set("datasources", nil) } - d.Set("detector_id", d.Id()) return diags @@ -239,10 +238,6 @@ func expandOrganizationDataSourceConfigurations(tfMap map[string]interface{}) *g apiObject := &guardduty.OrganizationDataSourceConfigurations{} - if v, ok := tfMap["s3_logs"].([]interface{}); ok && len(v) > 0 { - apiObject.S3Logs = expandOrganizationS3LogsConfiguration(v[0].(map[string]interface{})) - } - if v, ok := tfMap["kubernetes"].([]interface{}); ok && len(v) > 0 { apiObject.Kubernetes = expandOrganizationKubernetesConfiguration(v[0].(map[string]interface{})) } @@ -251,6 +246,10 @@ func expandOrganizationDataSourceConfigurations(tfMap map[string]interface{}) *g apiObject.MalwareProtection = expandOrganizationMalwareProtectionConfiguration(v[0].(map[string]interface{})) } + if v, ok := tfMap["s3_logs"].([]interface{}); ok && len(v) > 0 { + apiObject.S3Logs = expandOrganizationS3LogsConfiguration(v[0].(map[string]interface{})) + } + return apiObject } @@ -304,11 +303,11 @@ func expandOrganizationMalwareProtectionConfiguration(tfMap map[string]interface } return &guardduty.OrganizationMalwareProtectionConfiguration{ - ScanEc2InstanceWithFindings: expandOrganizationScanEC2InstanceWithFindingsConfiguration(m), + ScanEc2InstanceWithFindings: expandOrganizationScanEc2InstanceWithFindings(m), } } -func expandOrganizationScanEC2InstanceWithFindingsConfiguration(tfMap map[string]interface{}) *guardduty.OrganizationScanEc2InstanceWithFindings { +func expandOrganizationScanEc2InstanceWithFindings(tfMap map[string]interface{}) *guardduty.OrganizationScanEc2InstanceWithFindings { // nosemgrep:ci.caps3-in-func-name if tfMap == nil { return nil } @@ -324,11 +323,11 @@ func expandOrganizationScanEC2InstanceWithFindingsConfiguration(tfMap map[string } return &guardduty.OrganizationScanEc2InstanceWithFindings{ - EbsVolumes: expandOrganizationEBSVolumesConfiguration(m), + EbsVolumes: expandOrganizationEbsVolumes(m), } } -func expandOrganizationEBSVolumesConfiguration(tfMap map[string]interface{}) *guardduty.OrganizationEbsVolumes { +func expandOrganizationEbsVolumes(tfMap map[string]interface{}) *guardduty.OrganizationEbsVolumes { // nosemgrep:ci.caps3-in-func-name if tfMap == nil { return nil } @@ -397,13 +396,13 @@ func flattenOrganizationKubernetesConfigurationResult(apiObject *guardduty.Organ tfMap := map[string]interface{}{} if v := apiObject.AuditLogs; v != nil { - tfMap["audit_logs"] = []interface{}{flattenOrganizationKubernetesAuditLogsConfiguration(v)} + tfMap["audit_logs"] = []interface{}{flattenOrganizationKubernetesAuditLogsConfigurationResult(v)} } return tfMap } -func flattenOrganizationKubernetesAuditLogsConfiguration(apiObject *guardduty.OrganizationKubernetesAuditLogsConfigurationResult) map[string]interface{} { +func flattenOrganizationKubernetesAuditLogsConfigurationResult(apiObject *guardduty.OrganizationKubernetesAuditLogsConfigurationResult) map[string]interface{} { if apiObject == nil { return nil } @@ -425,13 +424,13 @@ func flattenOrganizationMalwareProtectionConfigurationResult(apiObject *guarddut tfMap := map[string]interface{}{} if v := apiObject.ScanEc2InstanceWithFindings; v != nil { - tfMap["scan_ec2_instance_with_findings"] = []interface{}{flattenOrganizationMalwareProtectionScanEC2InstanceWithFindingsResult(v)} + tfMap["scan_ec2_instance_with_findings"] = []interface{}{flattenOrganizationScanEc2InstanceWithFindingsResult(v)} } return tfMap } -func flattenOrganizationMalwareProtectionScanEC2InstanceWithFindingsResult(apiObject *guardduty.OrganizationScanEc2InstanceWithFindingsResult) map[string]interface{} { +func flattenOrganizationScanEc2InstanceWithFindingsResult(apiObject *guardduty.OrganizationScanEc2InstanceWithFindingsResult) map[string]interface{} { // nosemgrep:ci.caps3-in-func-name if apiObject == nil { return nil } @@ -439,13 +438,13 @@ func flattenOrganizationMalwareProtectionScanEC2InstanceWithFindingsResult(apiOb tfMap := map[string]interface{}{} if v := apiObject.EbsVolumes; v != nil { - tfMap["ebs_volumes"] = []interface{}{flattenOrganizationMalwareProtectionEBSVolumesResult(v)} + tfMap["ebs_volumes"] = []interface{}{flattenOrganizationEbsVolumesResult(v)} } return tfMap } -func flattenOrganizationMalwareProtectionEBSVolumesResult(apiObject *guardduty.OrganizationEbsVolumesResult) map[string]interface{} { +func flattenOrganizationEbsVolumesResult(apiObject *guardduty.OrganizationEbsVolumesResult) map[string]interface{} { // nosemgrep:ci.caps3-in-func-name if apiObject == nil { return nil } @@ -458,3 +457,28 @@ func flattenOrganizationMalwareProtectionEBSVolumesResult(apiObject *guardduty.O return tfMap } + +func FindOrganizationConfigurationByID(ctx context.Context, conn *guardduty.GuardDuty, id string) (*guardduty.DescribeOrganizationConfigurationOutput, error) { + input := &guardduty.DescribeOrganizationConfigurationInput{ + DetectorId: aws.String(id), + } + + output, err := conn.DescribeOrganizationConfigurationWithContext(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 + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/guardduty/organization_configuration_feature.go b/internal/service/guardduty/organization_configuration_feature.go new file mode 100644 index 000000000000..c3ec6809440c --- /dev/null +++ b/internal/service/guardduty/organization_configuration_feature.go @@ -0,0 +1,258 @@ +// 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_organization_configuration_feature", name="Organization Configuration Feature") +func ResourceOrganizationConfigurationFeature() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceOrganizationConfigurationFeaturePut, + ReadWithoutTimeout: resourceOrganizationConfigurationFeatureRead, + UpdateWithoutTimeout: resourceOrganizationConfigurationFeaturePut, + 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{ + "auto_enable": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(guardduty.OrgFeatureStatus_Values(), false), + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(guardduty.OrgFeatureAdditionalConfiguration_Values(), false), + }, + }, + }, + }, + "auto_enable": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(guardduty.OrgFeatureStatus_Values(), false), + }, + "detector_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(guardduty.OrgFeature_Values(), false), + }, + }, + } +} + +func resourceOrganizationConfigurationFeaturePut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).GuardDutyConn(ctx) + + detectorID := d.Get("detector_id").(string) + + // We have seen occasional acceptance test failures when updating multiple features on the same detector concurrently, + // so use a mutex to ensure that multiple features being updated concurrently don't trample on each other. + conns.GlobalMutexKV.Lock(detectorID) + defer conns.GlobalMutexKV.Unlock(detectorID) + + output, err := FindOrganizationConfigurationByID(ctx, conn, detectorID) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading GuardDuty Organization Configuration (%s): %s", detectorID, err) + } + + name := d.Get("name").(string) + feature := &guardduty.OrganizationFeatureConfiguration{ + AutoEnable: aws.String(d.Get("auto_enable").(string)), + Name: aws.String(name), + } + + if v, ok := d.GetOk("additional_configuration"); ok && len(v.([]interface{})) > 0 { + feature.AdditionalConfiguration = expandOrganizationAdditionalConfigurations(v.([]interface{})) + } + + input := &guardduty.UpdateOrganizationConfigurationInput{ + AutoEnableOrganizationMembers: output.AutoEnableOrganizationMembers, + DetectorId: aws.String(detectorID), + Features: []*guardduty.OrganizationFeatureConfiguration{feature}, + } + + _, err = conn.UpdateOrganizationConfigurationWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating GuardDuty Organization Configuration (%s) Feature (%s): %s", detectorID, name, err) + } + + if d.IsNewResource() { + d.SetId(organizationConfigurationFeatureCreateResourceID(detectorID, name)) + } + + return append(diags, resourceOrganizationConfigurationFeatureRead(ctx, d, meta)...) +} + +func resourceOrganizationConfigurationFeatureRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).GuardDutyConn(ctx) + + detectorID, name, err := organizationConfigurationFeatureParseResourceID(d.Id()) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + + feature, err := FindOrganizationConfigurationFeatureByTwoPartKey(ctx, conn, detectorID, name) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] GuardDuty Organization Configuration Feature (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading GuardDuty Organization Configuration Feature (%s): %s", d.Id(), err) + } + + if err := d.Set("additional_configuration", flattenOrganizationAdditionalConfigurationResults(feature.AdditionalConfiguration)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting additional_configuration: %s", err) + } + d.Set("auto_enable", feature.AutoEnable) + d.Set("detector_id", detectorID) + d.Set("name", feature.Name) + + return diags +} + +const organizationConfigurationFeatureResourceIDSeparator = "/" + +func organizationConfigurationFeatureCreateResourceID(detectorID, name string) string { + parts := []string{detectorID, name} + id := strings.Join(parts, organizationConfigurationFeatureResourceIDSeparator) + + return id +} + +func organizationConfigurationFeatureParseResourceID(id string) (string, string, error) { + parts := strings.Split(id, organizationConfigurationFeatureResourceIDSeparator) + + 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, organizationConfigurationFeatureResourceIDSeparator) +} + +func FindOrganizationConfigurationFeatureByTwoPartKey(ctx context.Context, conn *guardduty.GuardDuty, detectorID, name string) (*guardduty.OrganizationFeatureConfigurationResult, error) { + output, err := FindOrganizationConfigurationByID(ctx, conn, detectorID) + + if err != nil { + return nil, err + } + + return tfresource.AssertSinglePtrResult(tfslices.Filter(output.Features, func(v *guardduty.OrganizationFeatureConfigurationResult) bool { + return aws.StringValue(v.Name) == name + })) +} + +func expandOrganizationAdditionalConfiguration(tfMap map[string]interface{}) *guardduty.OrganizationAdditionalConfiguration { + if tfMap == nil { + return nil + } + + apiObject := &guardduty.OrganizationAdditionalConfiguration{} + + if v, ok := tfMap["auto_enable"].(string); ok && v != "" { + apiObject.AutoEnable = aws.String(v) + } + + if v, ok := tfMap["name"].(string); ok && v != "" { + apiObject.Name = aws.String(v) + } + + return apiObject +} + +func expandOrganizationAdditionalConfigurations(tfList []interface{}) []*guardduty.OrganizationAdditionalConfiguration { + if len(tfList) == 0 { + return nil + } + + var apiObjects []*guardduty.OrganizationAdditionalConfiguration + + for _, tfMapRaw := range tfList { + tfMap, ok := tfMapRaw.(map[string]interface{}) + + if !ok { + continue + } + + apiObject := expandOrganizationAdditionalConfiguration(tfMap) + + if apiObject == nil { + continue + } + + apiObjects = append(apiObjects, apiObject) + } + + return apiObjects +} + +func flattenOrganizationAdditionalConfigurationResult(apiObject *guardduty.OrganizationAdditionalConfigurationResult) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.AutoEnable; v != nil { + tfMap["auto_enable"] = aws.StringValue(v) + } + + if v := apiObject.Name; v != nil { + tfMap["name"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenOrganizationAdditionalConfigurationResults(apiObjects []*guardduty.OrganizationAdditionalConfigurationResult) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + tfList = append(tfList, flattenOrganizationAdditionalConfigurationResult(apiObject)) + } + + return tfList +} diff --git a/internal/service/guardduty/organization_configuration_feature_test.go b/internal/service/guardduty/organization_configuration_feature_test.go new file mode 100644 index 000000000000..282b57c78530 --- /dev/null +++ b/internal/service/guardduty/organization_configuration_feature_test.go @@ -0,0 +1,237 @@ +// 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 testAccOrganizationConfigurationFeature_basic(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_guardduty_organization_configuration_feature.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckOrganizationsAccount(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccOrganizationConfigurationFeatureConfig_basic("RDS_LOGIN_EVENTS", "ALL"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccOrganizationConfigurationFeatureExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resourceName, "auto_enable", "ALL"), + resource.TestCheckResourceAttrSet(resourceName, "detector_id"), + resource.TestCheckResourceAttr(resourceName, "name", "RDS_LOGIN_EVENTS"), + ), + }, + }, + }) +} + +func testAccOrganizationConfigurationFeature_additionalConfiguration(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_guardduty_organization_configuration_feature.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckOrganizationsAccount(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccOrganizationConfigurationFeatureConfig_additionalConfiguration("NEW", "NONE"), + Check: resource.ComposeTestCheckFunc( + testAccOrganizationConfigurationFeatureExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "auto_enable", "NEW"), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.0.auto_enable", "NONE"), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.0.name", "EKS_ADDON_MANAGEMENT"), + resource.TestCheckResourceAttr(resourceName, "name", "EKS_RUNTIME_MONITORING"), + ), + }, + { + Config: testAccOrganizationConfigurationFeatureConfig_additionalConfiguration("ALL", "ALL"), + Check: resource.ComposeTestCheckFunc( + testAccOrganizationConfigurationFeatureExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "auto_enable", "ALL"), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.0.auto_enable", "ALL"), + resource.TestCheckResourceAttr(resourceName, "additional_configuration.0.name", "EKS_ADDON_MANAGEMENT"), + resource.TestCheckResourceAttr(resourceName, "name", "EKS_RUNTIME_MONITORING"), + ), + }, + }, + }) +} + +func testAccOrganizationConfigurationFeature_multiple(t *testing.T) { + ctx := acctest.Context(t) + resource1Name := "aws_guardduty_organization_configuration_feature.test1" + resource2Name := "aws_guardduty_organization_configuration_feature.test2" + resource3Name := "aws_guardduty_organization_configuration_feature.test3" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckOrganizationsAccount(ctx, t) + testAccPreCheckDetectorNotExists(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccOrganizationConfigurationFeatureConfig_multiple("ALL", "NEW", "NONE"), + Check: resource.ComposeTestCheckFunc( + testAccOrganizationConfigurationFeatureExists(ctx, resource1Name), + testAccOrganizationConfigurationFeatureExists(ctx, resource2Name), + testAccOrganizationConfigurationFeatureExists(ctx, resource3Name), + resource.TestCheckResourceAttr(resource1Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource1Name, "auto_enable", "ALL"), + resource.TestCheckResourceAttr(resource1Name, "name", "EBS_MALWARE_PROTECTION"), + resource.TestCheckResourceAttr(resource2Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource2Name, "auto_enable", "NEW"), + resource.TestCheckResourceAttr(resource2Name, "name", "LAMBDA_NETWORK_LOGS"), + resource.TestCheckResourceAttr(resource3Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource3Name, "auto_enable", "NONE"), + resource.TestCheckResourceAttr(resource3Name, "name", "S3_DATA_EVENTS"), + ), + }, + { + Config: testAccOrganizationConfigurationFeatureConfig_multiple("NEW", "ALL", "ALL"), + Check: resource.ComposeTestCheckFunc( + testAccOrganizationConfigurationFeatureExists(ctx, resource1Name), + testAccOrganizationConfigurationFeatureExists(ctx, resource2Name), + testAccOrganizationConfigurationFeatureExists(ctx, resource3Name), + resource.TestCheckResourceAttr(resource1Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource1Name, "auto_enable", "NEW"), + resource.TestCheckResourceAttr(resource1Name, "name", "EBS_MALWARE_PROTECTION"), + resource.TestCheckResourceAttr(resource2Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource2Name, "auto_enable", "ALL"), + resource.TestCheckResourceAttr(resource2Name, "name", "LAMBDA_NETWORK_LOGS"), + resource.TestCheckResourceAttr(resource3Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource3Name, "auto_enable", "ALL"), + resource.TestCheckResourceAttr(resource3Name, "name", "S3_DATA_EVENTS"), + ), + }, + { + Config: testAccOrganizationConfigurationFeatureConfig_multiple("NONE", "NONE", "NONE"), + Check: resource.ComposeTestCheckFunc( + testAccOrganizationConfigurationFeatureExists(ctx, resource1Name), + testAccOrganizationConfigurationFeatureExists(ctx, resource2Name), + testAccOrganizationConfigurationFeatureExists(ctx, resource3Name), + resource.TestCheckResourceAttr(resource1Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource1Name, "auto_enable", "NONE"), + resource.TestCheckResourceAttr(resource1Name, "name", "EBS_MALWARE_PROTECTION"), + resource.TestCheckResourceAttr(resource2Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource2Name, "auto_enable", "NONE"), + resource.TestCheckResourceAttr(resource2Name, "name", "LAMBDA_NETWORK_LOGS"), + resource.TestCheckResourceAttr(resource3Name, "additional_configuration.#", "0"), + resource.TestCheckResourceAttr(resource3Name, "auto_enable", "NONE"), + resource.TestCheckResourceAttr(resource3Name, "name", "S3_DATA_EVENTS"), + ), + }, + }, + }) +} + +func testAccOrganizationConfigurationFeatureExists(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.FindOrganizationConfigurationFeatureByTwoPartKey(ctx, conn, rs.Primary.Attributes["detector_id"], rs.Primary.Attributes["name"]) + + return err + } +} + +var testAccOrganizationConfigurationFeatureConfig_base = acctest.ConfigCompose(testAccOrganizationConfigurationConfig_base, ` +resource "aws_guardduty_organization_configuration" "test" { + depends_on = [aws_guardduty_organization_admin_account.test] + + auto_enable_organization_members = "ALL" + detector_id = aws_guardduty_detector.test.id +} +`) + +func testAccOrganizationConfigurationFeatureConfig_basic(name, autoEnable string) string { + return acctest.ConfigCompose(testAccOrganizationConfigurationFeatureConfig_base, fmt.Sprintf(` +resource "aws_guardduty_organization_configuration_feature" "test" { + depends_on = [aws_guardduty_organization_configuration.test] + + detector_id = aws_guardduty_detector.test.id + name = %[1]q + auto_enable = %[2]q +} +`, name, autoEnable)) +} + +func testAccOrganizationConfigurationFeatureConfig_additionalConfiguration(featureAutoEnable, additionalConfigurationAutoEnable string) string { + return acctest.ConfigCompose(testAccOrganizationConfigurationFeatureConfig_base, fmt.Sprintf(` +resource "aws_guardduty_organization_configuration_feature" "test" { + depends_on = [aws_guardduty_organization_configuration.test] + + detector_id = aws_guardduty_detector.test.id + name = "EKS_RUNTIME_MONITORING" + auto_enable = %[1]q + + additional_configuration { + name = "EKS_ADDON_MANAGEMENT" + auto_enable = %[2]q + } +} +`, featureAutoEnable, additionalConfigurationAutoEnable)) +} + +func testAccOrganizationConfigurationFeatureConfig_multiple(autoEnable1, autoEnable2, autoEnable3 string) string { + return acctest.ConfigCompose(testAccOrganizationConfigurationFeatureConfig_base, fmt.Sprintf(` +resource "aws_guardduty_organization_configuration_feature" "test1" { + depends_on = [aws_guardduty_organization_configuration.test] + + detector_id = aws_guardduty_detector.test.id + name = "EBS_MALWARE_PROTECTION" + auto_enable = %[1]q +} + +resource "aws_guardduty_organization_configuration_feature" "test2" { + depends_on = [aws_guardduty_organization_configuration.test] + + detector_id = aws_guardduty_detector.test.id + name = "LAMBDA_NETWORK_LOGS" + auto_enable = %[2]q +} + +resource "aws_guardduty_organization_configuration_feature" "test3" { + depends_on = [aws_guardduty_organization_configuration.test] + + detector_id = aws_guardduty_detector.test.id + name = "S3_DATA_EVENTS" + auto_enable = %[3]q +} +`, autoEnable1, autoEnable2, autoEnable3)) +} diff --git a/internal/service/guardduty/organization_configuration_test.go b/internal/service/guardduty/organization_configuration_test.go index 5743d933b150..83e6c21f9055 100644 --- a/internal/service/guardduty/organization_configuration_test.go +++ b/internal/service/guardduty/organization_configuration_test.go @@ -4,12 +4,16 @@ 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 testAccOrganizationConfiguration_basic(t *testing.T) { @@ -25,13 +29,12 @@ func testAccOrganizationConfiguration_basic(t *testing.T) { }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - // GuardDuty Organization Configuration cannot be deleted separately. - // Ensure parent resource is destroyed instead. - CheckDestroy: testAccCheckDetectorDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccOrganizationConfigurationConfig_autoEnable(true), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable", "true"), resource.TestCheckResourceAttr(resourceName, "auto_enable_organization_members", "NEW"), resource.TestCheckResourceAttrPair(resourceName, "detector_id", detectorResourceName, "id"), @@ -45,6 +48,7 @@ func testAccOrganizationConfiguration_basic(t *testing.T) { { Config: testAccOrganizationConfigurationConfig_autoEnable(false), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable", "false"), resource.TestCheckResourceAttr(resourceName, "auto_enable_organization_members", "NONE"), resource.TestCheckResourceAttrPair(resourceName, "detector_id", detectorResourceName, "id"), @@ -67,13 +71,12 @@ func testAccOrganizationConfiguration_autoEnableOrganizationMembers(t *testing.T }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - // GuardDuty Organization Configuration cannot be deleted separately. - // Ensure parent resource is destroyed instead. - CheckDestroy: testAccCheckDetectorDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccOrganizationConfigurationConfig_autoEnableOrganizationMembers("ALL"), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable_organization_members", "ALL"), resource.TestCheckResourceAttr(resourceName, "auto_enable", "true"), resource.TestCheckResourceAttrPair(resourceName, "detector_id", detectorResourceName, "id"), @@ -87,68 +90,48 @@ func testAccOrganizationConfiguration_autoEnableOrganizationMembers(t *testing.T { Config: testAccOrganizationConfigurationConfig_autoEnableOrganizationMembers("NONE"), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable_organization_members", "NONE"), resource.TestCheckResourceAttr(resourceName, "auto_enable", "false"), resource.TestCheckResourceAttrPair(resourceName, "detector_id", detectorResourceName, "id"), ), }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, { Config: testAccOrganizationConfigurationConfig_autoEnableOrganizationMembers("ALL"), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable_organization_members", "ALL"), resource.TestCheckResourceAttr(resourceName, "auto_enable", "true"), resource.TestCheckResourceAttrPair(resourceName, "detector_id", detectorResourceName, "id"), ), }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, { Config: testAccOrganizationConfigurationConfig_autoEnable(true), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable_organization_members", "NEW"), resource.TestCheckResourceAttr(resourceName, "auto_enable", "true"), resource.TestCheckResourceAttrPair(resourceName, "detector_id", detectorResourceName, "id"), ), }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, { Config: testAccOrganizationConfigurationConfig_autoEnableOrganizationMembers("NONE"), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable_organization_members", "NONE"), resource.TestCheckResourceAttr(resourceName, "auto_enable", "false"), resource.TestCheckResourceAttrPair(resourceName, "detector_id", detectorResourceName, "id"), ), }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, { Config: testAccOrganizationConfigurationConfig_autoEnable(false), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable_organization_members", "NONE"), resource.TestCheckResourceAttr(resourceName, "auto_enable", "false"), resource.TestCheckResourceAttrPair(resourceName, "detector_id", detectorResourceName, "id"), ), }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, }, }) } @@ -166,11 +149,12 @@ func testAccOrganizationConfiguration_s3logs(t *testing.T) { }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckDetectorDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccOrganizationConfigurationConfig_s3Logs(true), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable", "true"), resource.TestCheckResourceAttr(resourceName, "datasources.#", "1"), resource.TestCheckResourceAttr(resourceName, "datasources.0.s3_logs.#", "1"), @@ -186,6 +170,7 @@ func testAccOrganizationConfiguration_s3logs(t *testing.T) { { Config: testAccOrganizationConfigurationConfig_s3Logs(false), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable", "true"), resource.TestCheckResourceAttr(resourceName, "datasources.#", "1"), resource.TestCheckResourceAttr(resourceName, "datasources.0.s3_logs.#", "1"), @@ -210,11 +195,12 @@ func testAccOrganizationConfiguration_kubernetes(t *testing.T) { }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckDetectorDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccOrganizationConfigurationConfig_kubernetes(true), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable", "true"), resource.TestCheckResourceAttr(resourceName, "datasources.#", "1"), resource.TestCheckResourceAttr(resourceName, "datasources.0.kubernetes.#", "1"), @@ -231,6 +217,7 @@ func testAccOrganizationConfiguration_kubernetes(t *testing.T) { { Config: testAccOrganizationConfigurationConfig_kubernetes(false), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable", "true"), resource.TestCheckResourceAttr(resourceName, "datasources.#", "1"), resource.TestCheckResourceAttr(resourceName, "datasources.0.kubernetes.#", "1"), @@ -256,11 +243,12 @@ func testAccOrganizationConfiguration_malwareprotection(t *testing.T) { }, ErrorCheck: acctest.ErrorCheck(t, guardduty.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckDetectorDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccOrganizationConfigurationConfig_malwareprotection(true), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable", "true"), resource.TestCheckResourceAttr(resourceName, "datasources.#", "1"), resource.TestCheckResourceAttr(resourceName, "datasources.0.malware_protection.#", "1"), @@ -278,6 +266,7 @@ func testAccOrganizationConfiguration_malwareprotection(t *testing.T) { { Config: testAccOrganizationConfigurationConfig_malwareprotection(false), Check: resource.ComposeTestCheckFunc( + testAccCheckOrganizationConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "auto_enable", "true"), resource.TestCheckResourceAttr(resourceName, "datasources.#", "1"), resource.TestCheckResourceAttr(resourceName, "datasources.0.malware_protection.#", "1"), @@ -291,7 +280,22 @@ func testAccOrganizationConfiguration_malwareprotection(t *testing.T) { }) } -const testAccOrganizationConfigurationConfigBase = ` +func testAccCheckOrganizationConfigurationExists(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.FindOrganizationConfigurationByID(ctx, conn, rs.Primary.ID) + + return err + } +} + +const testAccOrganizationConfigurationConfig_base = ` data "aws_caller_identity" "current" {} data "aws_partition" "current" {} @@ -315,9 +319,7 @@ resource "aws_guardduty_organization_admin_account" "test" { ` func testAccOrganizationConfigurationConfig_autoEnable(autoEnable bool) string { - return acctest.ConfigCompose( - testAccOrganizationConfigurationConfigBase, - fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationConfigurationConfig_base, fmt.Sprintf(` resource "aws_guardduty_organization_configuration" "test" { depends_on = [aws_guardduty_organization_admin_account.test] @@ -328,9 +330,7 @@ resource "aws_guardduty_organization_configuration" "test" { } func testAccOrganizationConfigurationConfig_autoEnableOrganizationMembers(value string) string { - return acctest.ConfigCompose( - testAccOrganizationConfigurationConfigBase, - fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationConfigurationConfig_base, fmt.Sprintf(` resource "aws_guardduty_organization_configuration" "test" { depends_on = [aws_guardduty_organization_admin_account.test] @@ -341,9 +341,7 @@ resource "aws_guardduty_organization_configuration" "test" { } func testAccOrganizationConfigurationConfig_s3Logs(autoEnable bool) string { - return acctest.ConfigCompose( - testAccOrganizationConfigurationConfigBase, - fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationConfigurationConfig_base, fmt.Sprintf(` resource "aws_guardduty_organization_configuration" "test" { depends_on = [aws_guardduty_organization_admin_account.test] @@ -360,9 +358,7 @@ resource "aws_guardduty_organization_configuration" "test" { } func testAccOrganizationConfigurationConfig_kubernetes(autoEnable bool) string { - return acctest.ConfigCompose( - testAccOrganizationConfigurationConfigBase, - fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationConfigurationConfig_base, fmt.Sprintf(` resource "aws_guardduty_organization_configuration" "test" { depends_on = [aws_guardduty_organization_admin_account.test] @@ -381,9 +377,7 @@ resource "aws_guardduty_organization_configuration" "test" { } func testAccOrganizationConfigurationConfig_malwareprotection(autoEnable bool) string { - return acctest.ConfigCompose( - testAccOrganizationConfigurationConfigBase, - fmt.Sprintf(` + return acctest.ConfigCompose(testAccOrganizationConfigurationConfig_base, fmt.Sprintf(` resource "aws_guardduty_organization_configuration" "test" { depends_on = [aws_guardduty_organization_admin_account.test] diff --git a/internal/service/guardduty/service_package_gen.go b/internal/service/guardduty/service_package_gen.go index ed2dbc93187a..034bfff562b1 100644 --- a/internal/service/guardduty/service_package_gen.go +++ b/internal/service/guardduty/service_package_gen.go @@ -83,6 +83,12 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka { Factory: ResourceOrganizationConfiguration, TypeName: "aws_guardduty_organization_configuration", + Name: "Organization Configuration", + }, + { + Factory: ResourceOrganizationConfigurationFeature, + TypeName: "aws_guardduty_organization_configuration_feature", + Name: "Organization Configuration Feature", }, { Factory: ResourcePublishingDestination, diff --git a/website/docs/r/guardduty_organization_configuration_feature.html.markdown b/website/docs/r/guardduty_organization_configuration_feature.html.markdown new file mode 100644 index 000000000000..c57da5cc3298 --- /dev/null +++ b/website/docs/r/guardduty_organization_configuration_feature.html.markdown @@ -0,0 +1,52 @@ +--- +subcategory: "GuardDuty" +layout: "aws" +page_title: "AWS: aws_guardduty_organization_configuration_feature" +description: |- + Provides a resource to manage an Amazon GuardDuty organization configuration feature +--- + +# Resource: aws_guardduty_organization_configuration_feature + +Provides a resource to manage a single Amazon GuardDuty [organization configuration feature](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty-features-activation-model.html#guardduty-features). + +~> **NOTE:** Deleting this resource does not disable the organization configuration feature, the resource in simply removed from state instead. + +## Example Usage + +```terraform +resource "aws_guardduty_detector" "example" { + enable = true +} + +resource "aws_guardduty_organization_configuration_feature" "eks_runtime_monitoring" { + detector_id = aws_guardduty_detector.example.id + name = "EKS_RUNTIME_MONITORING" + auto_enable = "ALL" + + additional_configuration { + name = "EKS_ADDON_MANAGEMENT" + auto_enable = "NEW" + } +} +``` + +## Argument Reference + +This resource supports the following arguments: + +* `auto_enable` - (Required) The status of the feature that is configured for the member accounts within the organization. Valid values: `NEW`, `ALL`, `NONE`. +* `detector_id` - (Required) The ID of the detector that configures the delegated administrator. +* `name` - (Required) The name of the feature that will be configured for the organization. Valid values: `S3_DATA_EVENTS`, `EKS_AUDIT_LOGS`, `EBS_MALWARE_PROTECTION`, `RDS_LOGIN_EVENTS`, `EKS_RUNTIME_MONITORING`, `LAMBDA_NETWORK_LOGS`. +* `additional_configuration` - (Optional) The additional information that will be configured for the organization See [below](#additional-configuration). + +### Additional Configuration + +The `additional_configuration` block supports the following: + +* `auto_enable` - (Required) The status of the additional configuration that will be configured for the organization. Valid values: `NEW`, `ALL`, `NONE`. +* `name` - (Required) The name of the additional configuration that will be configured for the organization. Valid values: `EKS_ADDON_MANAGEMENT`. + +## Attribute Reference + +This resource exports no additional attributes.