From 2610f357d32c94a03a52f2f453398a95effc110e Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Wed, 18 Jan 2023 15:04:40 -0500 Subject: [PATCH 1/4] d/aws_auditmanager_control: data source implementation --- .../auditmanager/control_data_source.go | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 internal/service/auditmanager/control_data_source.go diff --git a/internal/service/auditmanager/control_data_source.go b/internal/service/auditmanager/control_data_source.go new file mode 100644 index 00000000000..f9f80771591 --- /dev/null +++ b/internal/service/auditmanager/control_data_source.go @@ -0,0 +1,208 @@ +package auditmanager + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/auditmanager" + awstypes "github.com/aws/aws-sdk-go-v2/service/auditmanager/types" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + sdkv2resource "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" +) + +func init() { + _sp.registerFrameworkDataSourceFactory(newDataSourceControl) +} + +func newDataSourceControl(context.Context) (datasource.DataSourceWithConfigure, error) { + return &dataSourceControl{}, nil +} + +type dataSourceControl struct { + framework.DataSourceWithConfigure +} + +func (d *dataSourceControl) Metadata(_ context.Context, request datasource.MetadataRequest, response *datasource.MetadataResponse) { // nosemgrep:ci.meta-in-func-name + response.TypeName = "aws_auditmanager_control" +} + +func (d *dataSourceControl) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "action_plan_instructions": schema.StringAttribute{ + Computed: true, + }, + "action_plan_title": schema.StringAttribute{ + Computed: true, + }, + "arn": framework.ARNAttributeComputedOnly(), + "description": schema.StringAttribute{ + Computed: true, + }, + "id": framework.IDAttribute(), + "name": schema.StringAttribute{ + Required: true, + }, + "tags": tftags.TagsAttributeComputedOnly(), + "testing_information": schema.StringAttribute{ + Computed: true, + }, + "type": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + enum.FrameworkValidate[awstypes.ControlType](), + }, + }, + }, + Blocks: map[string]schema.Block{ + "control_mapping_sources": schema.SetNestedBlock{ + Validators: []validator.Set{ + setvalidator.SizeAtLeast(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "source_description": schema.StringAttribute{ + Computed: true, + }, + "source_frequency": schema.StringAttribute{ + Computed: true, + }, + "source_id": framework.IDAttribute(), + "source_name": schema.StringAttribute{ + Computed: true, + }, + "source_set_up_option": schema.StringAttribute{ + Computed: true, + }, + "source_type": schema.StringAttribute{ + Computed: true, + }, + "troubleshooting_text": schema.StringAttribute{ + Computed: true, + }, + }, + Blocks: map[string]schema.Block{ + "source_keyword": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "keyword_input_type": schema.StringAttribute{ + Computed: true, + }, + "keyword_value": schema.StringAttribute{ + Computed: true, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func (d *dataSourceControl) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + conn := d.Meta().AuditManagerClient() + + var data dataSourceControlData + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + controlMetadata, err := FindControlByName(ctx, conn, data.Name.ValueString(), data.Type.ValueString()) + if err != nil { + resp.Diagnostics.AddError("finding control by name", err.Error()) + return + } + + // Control metadata from the ListControls API does not contain all information available + // about a control. Use control ID to get complete information. + control, err := FindControlByID(ctx, conn, aws.ToString(controlMetadata.Id)) + if err != nil { + resp.Diagnostics.AddError("finding control by ID", err.Error()) + return + } + + resp.Diagnostics.Append(data.refreshFromOutput(ctx, d.Meta(), control)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func FindControlByName(ctx context.Context, conn *auditmanager.Client, name, controlType string) (*awstypes.ControlMetadata, error) { + in := &auditmanager.ListControlsInput{ + ControlType: awstypes.ControlType(controlType), + } + pages := auditmanager.NewListControlsPaginator(conn, in) + + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + if err != nil { + return nil, err + } + + for _, control := range page.ControlMetadataList { + if name == aws.ToString(control.Name) { + return &control, nil + } + } + } + + return nil, &sdkv2resource.NotFoundError{ + LastRequest: in, + } +} + +type dataSourceControlData struct { + ActionPlanInstructions types.String `tfsdk:"action_plan_instructions"` + ActionPlanTitle types.String `tfsdk:"action_plan_title"` + ARN types.String `tfsdk:"arn"` + ControlMappingSources types.Set `tfsdk:"control_mapping_sources"` + Description types.String `tfsdk:"description"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + Tags types.Map `tfsdk:"tags"` + TestingInformation types.String `tfsdk:"testing_information"` + Type types.String `tfsdk:"type"` +} + +// refreshFromOutput writes state data from an AWS response object +func (rd *dataSourceControlData) refreshFromOutput(ctx context.Context, meta *conns.AWSClient, out *awstypes.Control) diag.Diagnostics { + var diags diag.Diagnostics + + if out == nil { + return diags + } + + rd.ID = types.StringValue(aws.ToString(out.Id)) + rd.Name = types.StringValue(aws.ToString(out.Name)) + cms, d := flattenControlMappingSources(ctx, out.ControlMappingSources) + diags.Append(d...) + rd.ControlMappingSources = cms + + rd.ActionPlanInstructions = flex.StringToFramework(ctx, out.ActionPlanInstructions) + rd.ActionPlanTitle = flex.StringToFramework(ctx, out.ActionPlanTitle) + rd.Description = flex.StringToFramework(ctx, out.Description) + rd.TestingInformation = flex.StringToFramework(ctx, out.TestingInformation) + rd.ARN = flex.StringToFramework(ctx, out.Arn) + rd.Type = types.StringValue(string(out.Type)) + + ignoreTagsConfig := meta.IgnoreTagsConfig + tags := KeyValueTags(out.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) + rd.Tags = flex.FlattenFrameworkStringValueMapLegacy(ctx, tags.Map()) + + return diags +} From 1b76926cc65cf528b7d9e00b4908539c4efe3730 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Wed, 18 Jan 2023 15:04:57 -0500 Subject: [PATCH 2/4] d/aws_auditmanager_control: tests --- .../auditmanager/control_data_source_test.go | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 internal/service/auditmanager/control_data_source_test.go diff --git a/internal/service/auditmanager/control_data_source_test.go b/internal/service/auditmanager/control_data_source_test.go new file mode 100644 index 00000000000..d9ec9814a96 --- /dev/null +++ b/internal/service/auditmanager/control_data_source_test.go @@ -0,0 +1,91 @@ +package auditmanager_test + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/auditmanager/types" + sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccAuditManagerControlDataSource_standard(t *testing.T) { + // Standard controls are managed by AWS and will exist in the account automatically + // once AuditManager is enabled. + name := "1. Risk Management" + dataSourceName := "data.aws_auditmanager_control.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(t) + acctest.PreCheckPartitionHasService(names.AuditManagerEndpointID, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.AuditManagerEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccControlDataSourceConfig_standard(name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "name", name), + resource.TestCheckResourceAttr(dataSourceName, "control_mapping_sources.#", "3"), + ), + }, + }, + }) +} + +func TestAccAuditManagerControlDataSource_custom(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceName := "data.aws_auditmanager_control.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(t) + acctest.PreCheckPartitionHasService(names.AuditManagerEndpointID, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.AuditManagerEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccControlDataSourceConfig_custom(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(dataSourceName, "name", rName), + resource.TestCheckResourceAttr(dataSourceName, "control_mapping_sources.#", "1"), + resource.TestCheckResourceAttr(dataSourceName, "control_mapping_sources.0.source_name", rName), + resource.TestCheckResourceAttr(dataSourceName, "control_mapping_sources.0.source_set_up_option", string(types.SourceSetUpOptionProceduralControlsMapping)), + resource.TestCheckResourceAttr(dataSourceName, "control_mapping_sources.0.source_type", string(types.SourceTypeManual)), + ), + }, + }, + }) +} + +func testAccControlDataSourceConfig_standard(rName string) string { + return fmt.Sprintf(` +data "aws_auditmanager_control" "test" { + name = %[1]q + type = "Standard" +} +`, rName) +} + +func testAccControlDataSourceConfig_custom(rName string) string { + return fmt.Sprintf(` +resource "aws_auditmanager_control" "test" { + name = %[1]q + + control_mapping_sources { + source_name = %[1]q + source_set_up_option = "Procedural_Controls_Mapping" + source_type = "MANUAL" + } +} + +data "aws_auditmanager_control" "test" { + name = aws_auditmanager_control.test.name + type = "Custom" +} +`, rName) +} From 69f67793e7b5de6c01aa9f3f462536a130bbe1e2 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Wed, 18 Jan 2023 15:38:29 -0500 Subject: [PATCH 3/4] d/aws_auditmanager_control: docs --- .../docs/d/auditmanager_control.html.markdown | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 website/docs/d/auditmanager_control.html.markdown diff --git a/website/docs/d/auditmanager_control.html.markdown b/website/docs/d/auditmanager_control.html.markdown new file mode 100644 index 00000000000..86c96114c9c --- /dev/null +++ b/website/docs/d/auditmanager_control.html.markdown @@ -0,0 +1,64 @@ +--- +subcategory: "Audit Manager" +layout: "aws" +page_title: "AWS: aws_auditmanager_control" +description: |- + Terraform data source for managing an AWS Audit Manager Control. +--- + +# Data Source: aws_auditmanager_control + +Terraform data source for managing an AWS Audit Manager Control. + +## Example Usage + +### Basic Usage + +```terraform +data "aws_auditmanager_control" "example" { + name = "1. Risk Management" + type = "Standard" +} +``` + +### With Framework Resource + +```terraform +data "aws_auditmanager_control" "example" { + name = "1. Risk Management" + type = "Standard" +} + +data "aws_auditmanager_control" "example2" { + name = "2. Personnel" + type = "Standard" +} + +resource "aws_auditmanager_framework" "example" { + name = "example" + + control_sets { + name = "example" + controls { + id = data.aws_auditmanager_control.example.id + } + } + control_sets { + name = "example2" + controls { + id = data.aws_auditmanager_control.example2.id + } + } +} +``` + +## Argument Reference + +The following arguments are required: + +* `name` - (Required) Name of the control. +* `type` - (Required) Type of control. Valid values are `Custom` and `Standard`. + +## Attributes Reference + +See the [`aws_auditmanager_control` resource](/docs/providers/aws/r/auditmanager_control.html) for details on the returned attributes - they are identical. From d2432350a8fcfb568766a7548c60621ddd76116f Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Wed, 18 Jan 2023 15:43:50 -0500 Subject: [PATCH 4/4] chore: changelog --- .changelog/28967.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/28967.txt diff --git a/.changelog/28967.txt b/.changelog/28967.txt new file mode 100644 index 00000000000..fd020df1a67 --- /dev/null +++ b/.changelog/28967.txt @@ -0,0 +1,3 @@ +```release-note:new-data-source +aws_auditmanager_control +```