From e45b7ba7405fc778410feebac949dca5ea2a4477 Mon Sep 17 00:00:00 2001 From: Weston Reed Date: Mon, 7 Oct 2024 19:22:11 -0700 Subject: [PATCH 1/8] feat: add new resource `aws_pinpointsmsvoicev2_configuration_set` --- .../pinpointsmsvoicev2/configuration_set.go | 313 ++++++++++++++++++ .../configuration_set_test.go | 241 ++++++++++++++ .../pinpointsmsvoicev2/exports_test.go | 10 +- .../pinpointsmsvoicev2/service_package_gen.go | 7 + ...smsvoicev2_configuration_set.html.markdown | 54 +++ 5 files changed, 621 insertions(+), 4 deletions(-) create mode 100644 internal/service/pinpointsmsvoicev2/configuration_set.go create mode 100644 internal/service/pinpointsmsvoicev2/configuration_set_test.go create mode 100644 website/docs/r/pinpointsmsvoicev2_configuration_set.html.markdown diff --git a/internal/service/pinpointsmsvoicev2/configuration_set.go b/internal/service/pinpointsmsvoicev2/configuration_set.go new file mode 100644 index 00000000000..58e81ce99a9 --- /dev/null +++ b/internal/service/pinpointsmsvoicev2/configuration_set.go @@ -0,0 +1,313 @@ +package pinpointsmsvoicev2 + +import ( + "context" + "fmt" + + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/pinpointsmsvoicev2" + awstypes "github.com/aws/aws-sdk-go-v2/service/pinpointsmsvoicev2/types" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + sdkid "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkResource("aws_pinpointsmsvoicev2_configuration_set", name="Configuration Set") +// @Tags(identifierAttribute="arn") +func newConfigurationSetResource(context.Context) (resource.ResourceWithConfigure, error) { + r := &configurationSetResource{} + + return r, nil +} + +type configurationSetResource struct { + framework.ResourceWithConfigure + framework.WithNoOpUpdate[configurationSetResourceModel] + framework.WithImportByID +} + +func (*configurationSetResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "aws_pinpointsmsvoicev2_configuration_set" +} + +func (r *configurationSetResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { + response.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + names.AttrARN: framework.ARNAttributeComputedOnly(), + names.AttrID: framework.IDAttribute(), + names.AttrName: schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexache.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_\-]{0,63}$`), ""), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "default_sender_id": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexache.MustCompile(`^[A-Za-z0-9_-]{1,11}$`), "must be between 1 and 11 characters long and contain only letters, numbers, underscores, and dashes"), + }, + }, + "default_message_type": schema.StringAttribute{ + Optional: true, + CustomType: fwtypes.StringEnumType[awstypes.MessageType](), + Validators: []validator.String{ + stringvalidator.RegexMatches(regexache.MustCompile(`^(TRANSACTIONAL|PROMOTIONAL)$`), "must be either TRANSACTIONAL or PROMOTIONAL"), + }, + }, + names.AttrTags: tftags.TagsAttribute(), + names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), + }, + } +} + +func (r *configurationSetResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var data configurationSetResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + + conn := r.Meta().PinpointSMSVoiceV2Client(ctx) + + name := data.ConfigurationSetName.ValueString() + input := &pinpointsmsvoicev2.CreateConfigurationSetInput{ + ClientToken: aws.String(sdkid.UniqueId()), + ConfigurationSetName: aws.String(name), + Tags: getTagsIn(ctx), + } + + output, err := conn.CreateConfigurationSet(ctx, input) + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("creating End User Messaging SMS Configuration Set (%s)", name), err.Error()) + + return + } + + if !data.DefaultSenderId.IsNull() { + err = setDefaultSenderId(ctx, conn, fwflex.StringFromFramework(ctx, data.DefaultSenderId), fwflex.StringFromFramework(ctx, data.DefaultSenderId)) + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("setting default sender ID for End User Messaging SMS Configuration Set (%s) to %s", name, data.DefaultMessageType.ValueString()), err.Error()) + + return + } + } + if !data.DefaultMessageType.IsNull() { + err = setDefaultMessageType(ctx, conn, fwflex.StringFromFramework(ctx, data.DefaultMessageType), data.DefaultMessageType.ValueEnum()) + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("setting default message type for End User Messaging SMS Configuration Set (%s) to %s", name, data.DefaultMessageType.ValueString()), err.Error()) + + return + } + } + + // Set values for unknowns. + data.ConfigurationSetARN = fwflex.StringToFramework(ctx, output.ConfigurationSetArn) + data.setID() + + response.Diagnostics.Append(response.State.Set(ctx, data)...) +} + +func (r *configurationSetResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var data configurationSetResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + + if err := data.InitFromID(); err != nil { + response.Diagnostics.AddError("parsing resource ID", err.Error()) + + return + } + + conn := r.Meta().PinpointSMSVoiceV2Client(ctx) + + out, err := findConfigurationSetByID(ctx, conn, data.ID.ValueString()) + + if tfresource.NotFound(err) { + response.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) + response.State.RemoveResource(ctx) + + return + } + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("reading End User Messaging SMS Configuration Set (%s)", data.ID.ValueString()), err.Error()) + + return + } + + // Set attributes for import. + response.Diagnostics.Append(fwflex.Flatten(ctx, out, &data)...) + if response.Diagnostics.HasError() { + return + } + + response.Diagnostics.Append(response.State.Set(ctx, &data)...) +} + +func (r *configurationSetResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + var old, new configurationSetResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &new)...) + if response.Diagnostics.HasError() { + return + } + response.Diagnostics.Append(request.State.Get(ctx, &old)...) + if response.Diagnostics.HasError() { + return + } + + conn := r.Meta().PinpointSMSVoiceV2Client(ctx) + + if !new.DefaultSenderId.Equal(old.DefaultSenderId) { + err := setDefaultSenderId(ctx, conn, new.ID.ValueStringPointer(), new.DefaultSenderId.ValueStringPointer()) + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("setting default sender ID for End User Messaging SMS Configuration Set (%s)", new.ID.ValueString()), err.Error()) + + return + } + } + if !new.DefaultMessageType.Equal(old.DefaultMessageType) { + err := setDefaultMessageType(ctx, conn, new.ID.ValueStringPointer(), new.DefaultMessageType.ValueEnum()) + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("setting default sender ID for End User Messaging SMS Configuration Set (%s)", new.ID.ValueString()), err.Error()) + + return + } + } + + response.Diagnostics.Append(response.State.Set(ctx, &new)...) +} + +func (r *configurationSetResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + var data configurationSetResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + + conn := r.Meta().PinpointSMSVoiceV2Client(ctx) + + _, err := conn.DeleteConfigurationSet(ctx, &pinpointsmsvoicev2.DeleteConfigurationSetInput{ + ConfigurationSetName: data.ID.ValueStringPointer(), + }) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return + } + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("deleting End User Messaging SMS Configuration Set (%s)", data.ID.ValueString()), err.Error()) + + return + } +} + +func (r *configurationSetResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { + r.SetTagsAll(ctx, request, response) +} + +type configurationSetResourceModel struct { + ID types.String `tfsdk:"id"` + ConfigurationSetARN types.String `tfsdk:"arn"` + ConfigurationSetName types.String `tfsdk:"name"` + DefaultSenderId types.String `tfsdk:"default_sender_id"` + DefaultMessageType fwtypes.StringEnum[awstypes.MessageType] `tfsdk:"default_message_type"` + Tags tftags.Map `tfsdk:"tags"` + TagsAll tftags.Map `tfsdk:"tags_all"` +} + +func (model *configurationSetResourceModel) InitFromID() error { + model.ConfigurationSetName = model.ID + + return nil +} + +func (model *configurationSetResourceModel) setID() { + model.ID = model.ConfigurationSetName +} + +func findConfigurationSetByID(ctx context.Context, conn *pinpointsmsvoicev2.Client, id string) (*awstypes.ConfigurationSetInformation, error) { + input := &pinpointsmsvoicev2.DescribeConfigurationSetsInput{ + ConfigurationSetNames: []string{id}, + } + + return findConfigurationSet(ctx, conn, input) +} + +func findConfigurationSet(ctx context.Context, conn *pinpointsmsvoicev2.Client, input *pinpointsmsvoicev2.DescribeConfigurationSetsInput) (*awstypes.ConfigurationSetInformation, error) { + output, err := findConfigurationSets(ctx, conn, input) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findConfigurationSets(ctx context.Context, conn *pinpointsmsvoicev2.Client, input *pinpointsmsvoicev2.DescribeConfigurationSetsInput) ([]awstypes.ConfigurationSetInformation, error) { + var output []awstypes.ConfigurationSetInformation + + pages := pinpointsmsvoicev2.NewDescribeConfigurationSetsPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + output = append(output, page.ConfigurationSets...) + } + + return output, nil +} + +func setDefaultSenderId(ctx context.Context, conn *pinpointsmsvoicev2.Client, configurationSetName *string, senderId *string) error { + input := &pinpointsmsvoicev2.SetDefaultSenderIdInput{ + ConfigurationSetName: configurationSetName, + SenderId: senderId, + } + + _, err := conn.SetDefaultSenderId(ctx, input) + + return err +} + +func setDefaultMessageType(ctx context.Context, conn *pinpointsmsvoicev2.Client, configurationSetName *string, messageType awstypes.MessageType) error { + input := &pinpointsmsvoicev2.SetDefaultMessageTypeInput{ + ConfigurationSetName: configurationSetName, + MessageType: messageType, + } + + _, err := conn.SetDefaultMessageType(ctx, input) + + return err +} diff --git a/internal/service/pinpointsmsvoicev2/configuration_set_test.go b/internal/service/pinpointsmsvoicev2/configuration_set_test.go new file mode 100644 index 00000000000..66b4d3ea5c2 --- /dev/null +++ b/internal/service/pinpointsmsvoicev2/configuration_set_test.go @@ -0,0 +1,241 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package pinpointsmsvoicev2_test + +import ( + "context" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/pinpointsmsvoicev2" + awstypes "github.com/aws/aws-sdk-go-v2/service/pinpointsmsvoicev2/types" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfpinpointsmsvoicev2 "github.com/hashicorp/terraform-provider-aws/internal/service/pinpointsmsvoicev2" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccPinpointSMSVoiceV2ConfigurationSet_basic(t *testing.T) { + ctx := acctest.Context(t) + var configurationSet awstypes.ConfigurationSetInformation + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_pinpointsmsvoicev2_configuration_set.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckConfigurationSet(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.PinpointSMSVoiceV2ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckConfigurationSetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccConfigurationSetConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigurationSetExists(ctx, resourceName, &configurationSet), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrARN), knownvalue.NotNull()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrName), knownvalue.StringExact(rName)), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.Null()), + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTagsAll), knownvalue.MapExact(map[string]knownvalue.Check{})), + }, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccPinpointSMSVoiceV2ConfigurationSet_disappears(t *testing.T) { + ctx := acctest.Context(t) + var configurationSet awstypes.ConfigurationSetInformation + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_pinpointsmsvoicev2_configuration_set.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckConfigurationSet(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.PinpointSMSVoiceV2ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckConfigurationSetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccConfigurationSetConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigurationSetExists(ctx, resourceName, &configurationSet), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfpinpointsmsvoicev2.ResourceConfigurationSet, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccPinpointSMSVoiceV2ConfigurationSet_tags(t *testing.T) { + ctx := acctest.Context(t) + var configurationSet awstypes.ConfigurationSetInformation + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_pinpointsmsvoicev2_configuration_set.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheckConfigurationSet(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.PinpointSMSVoiceV2ServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckConfigurationSetDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccConfigurationSetConfig_tags1(rName, acctest.CtKey1, acctest.CtValue1), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigurationSetExists(ctx, resourceName, &configurationSet), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccConfigurationSetConfig_tags2(rName, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, acctest.CtValue2), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigurationSetExists(ctx, resourceName, &configurationSet), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1Updated), + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + }, + }, + { + Config: testAccConfigurationSetConfig_tags1(rName, acctest.CtKey2, acctest.CtValue2), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigurationSetExists(ctx, resourceName, &configurationSet), + ), + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + }, + }, + }, + }) +} + +func testAccCheckConfigurationSetDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).PinpointSMSVoiceV2Client(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_pinpointsmsvoicev2_configuration_set" { + continue + } + + _, err := tfpinpointsmsvoicev2.FindConfigurationSetByID(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("End User Messaging SMS Configuration Set %s still exists", rs.Primary.ID) + } + + return nil + } +} + +func testAccCheckConfigurationSetExists(ctx context.Context, n string, v *awstypes.ConfigurationSetInformation) 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).PinpointSMSVoiceV2Client(ctx) + + output, err := tfpinpointsmsvoicev2.FindConfigurationSetByID(ctx, conn, rs.Primary.ID) + + if err != nil { + return err + } + + *v = *output + + return nil + } +} + +func testAccPreCheckConfigurationSet(ctx context.Context, t *testing.T) { + conn := acctest.Provider.Meta().(*conns.AWSClient).PinpointSMSVoiceV2Client(ctx) + + input := &pinpointsmsvoicev2.DescribeConfigurationSetsInput{} + + _, err := conn.DescribeConfigurationSets(ctx, input) + + if acctest.PreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + +func testAccConfigurationSetConfig_basic(rName string) string { + return fmt.Sprintf(` +resource "aws_pinpointsmsvoicev2_configuration_set" "test" { + name = %[1]q +} +`, rName) +} + +func testAccConfigurationSetConfig_tags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_pinpointsmsvoicev2_configuration_set" "test" { + name = %[1]q + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccConfigurationSetConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_pinpointsmsvoicev2_configuration_set" "test" { + name = %[1]q + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} diff --git a/internal/service/pinpointsmsvoicev2/exports_test.go b/internal/service/pinpointsmsvoicev2/exports_test.go index 3268085cb12..c1782cb12f2 100644 --- a/internal/service/pinpointsmsvoicev2/exports_test.go +++ b/internal/service/pinpointsmsvoicev2/exports_test.go @@ -5,9 +5,11 @@ package pinpointsmsvoicev2 // Exports for use in tests only. var ( - ResourceOptOutList = newOptOutListResource - ResourcePhoneNumber = newPhoneNumberResource + ResourceOptOutList = newOptOutListResource + ResourcePhoneNumber = newPhoneNumberResource + ResourceConfigurationSet = newConfigurationSetResource - FindOptOutListByID = findOptOutListByID - FindPhoneNumberByID = findPhoneNumberByID + FindOptOutListByID = findOptOutListByID + FindPhoneNumberByID = findPhoneNumberByID + FindConfigurationSetByID = findConfigurationSetByID ) diff --git a/internal/service/pinpointsmsvoicev2/service_package_gen.go b/internal/service/pinpointsmsvoicev2/service_package_gen.go index da87ba07388..bbee0ee1056 100644 --- a/internal/service/pinpointsmsvoicev2/service_package_gen.go +++ b/internal/service/pinpointsmsvoicev2/service_package_gen.go @@ -34,6 +34,13 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic IdentifierAttribute: names.AttrARN, }, }, + { + Factory: newConfigurationSetResource, + Name: "Configuration Set", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: names.AttrARN, + }, + }, } } diff --git a/website/docs/r/pinpointsmsvoicev2_configuration_set.html.markdown b/website/docs/r/pinpointsmsvoicev2_configuration_set.html.markdown new file mode 100644 index 00000000000..2981330138c --- /dev/null +++ b/website/docs/r/pinpointsmsvoicev2_configuration_set.html.markdown @@ -0,0 +1,54 @@ +--- +subcategory: "End User Messaging SMS" +layout: "aws" +page_title: "AWS: aws_pinpointsmsvoicev2_configuration_set" +description: |- + Manages an AWS End User Messaging SMS Configuration Set. +--- + +# Resource: aws_pinpointsmsvoicev2_configuration_set + +Manages an AWS End User Messaging SMS Configuration Set. + +## Example Usage + +```terraform +resource "aws_pinpointsmsvoicev2_configuration_set" "example" { + name = "example-configuration-set" + default_sender_id = "example" + default_message_type = "TRANSACTIONAL" +} +``` + +## Argument Reference + +This resource supports the following arguments: + +* `name` - (Required) The name of the configuration set. +* `default_sender_id` - (Optional) The default sender ID to use for this configuration set. +* `default_message_type` - (Optional) The default message type. Must either be "TRANSACTIONAL" or "PROMOTIONAL" +* `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. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the configuration set. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import -out lists using the `name`. For example: + +```terraform +import { + to = aws_pinpointsmsvoicev2_configuration_set.example + id = "example-configuration-set" +} +``` + +Using `terraform import`, import configuration sets using the `name`. For example: + +```console +% terraform import aws_pinpointsmsvoicev2_configuration_set.example example-configuration-set +``` From 1724a2b15b41c6da07667e29562adce4ffe60be4 Mon Sep 17 00:00:00 2001 From: Weston Reed Date: Mon, 7 Oct 2024 19:59:33 -0700 Subject: [PATCH 2/8] docs: add changelog entry for `aws_pinpointsmsvoicev2_configuration_set` --- .changelog/39620.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/39620.txt diff --git a/.changelog/39620.txt b/.changelog/39620.txt new file mode 100644 index 00000000000..ec1124d951f --- /dev/null +++ b/.changelog/39620.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_pinpointsmsvoicev2_configuration_set +``` \ No newline at end of file From 421e8cc948cac334ede9d66fef4a6ad940bf978f Mon Sep 17 00:00:00 2001 From: Weston Reed Date: Mon, 7 Oct 2024 20:03:31 -0700 Subject: [PATCH 3/8] fix: linter --- .../docs/r/pinpointsmsvoicev2_configuration_set.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/pinpointsmsvoicev2_configuration_set.html.markdown b/website/docs/r/pinpointsmsvoicev2_configuration_set.html.markdown index 2981330138c..546d244a6e4 100644 --- a/website/docs/r/pinpointsmsvoicev2_configuration_set.html.markdown +++ b/website/docs/r/pinpointsmsvoicev2_configuration_set.html.markdown @@ -14,8 +14,8 @@ Manages an AWS End User Messaging SMS Configuration Set. ```terraform resource "aws_pinpointsmsvoicev2_configuration_set" "example" { - name = "example-configuration-set" - default_sender_id = "example" + name = "example-configuration-set" + default_sender_id = "example" default_message_type = "TRANSACTIONAL" } ``` From 368f5dcad4ad9d4bcc666c1879830870f880a5dc Mon Sep 17 00:00:00 2001 From: Weston Reed Date: Mon, 7 Oct 2024 21:56:32 -0700 Subject: [PATCH 4/8] fix: add copyright --- internal/service/pinpointsmsvoicev2/configuration_set.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/service/pinpointsmsvoicev2/configuration_set.go b/internal/service/pinpointsmsvoicev2/configuration_set.go index 58e81ce99a9..6ab02487c32 100644 --- a/internal/service/pinpointsmsvoicev2/configuration_set.go +++ b/internal/service/pinpointsmsvoicev2/configuration_set.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package pinpointsmsvoicev2 import ( From 43ffe7a01cf30cc2cd55dbe0190a015f58b18715 Mon Sep 17 00:00:00 2001 From: Weston Reed Date: Mon, 7 Oct 2024 22:13:17 -0700 Subject: [PATCH 5/8] fix: run `make gen` --- .../pinpointsmsvoicev2/service_package_gen.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/service/pinpointsmsvoicev2/service_package_gen.go b/internal/service/pinpointsmsvoicev2/service_package_gen.go index bbee0ee1056..8a7120482e0 100644 --- a/internal/service/pinpointsmsvoicev2/service_package_gen.go +++ b/internal/service/pinpointsmsvoicev2/service_package_gen.go @@ -21,22 +21,22 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { return []*types.ServicePackageFrameworkResource{ { - Factory: newOptOutListResource, - Name: "Opt-out List", + Factory: newConfigurationSetResource, + Name: "Configuration Set", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: names.AttrARN, }, }, { - Factory: newPhoneNumberResource, - Name: "Phone Number", + Factory: newOptOutListResource, + Name: "Opt-out List", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: names.AttrARN, }, }, { - Factory: newConfigurationSetResource, - Name: "Configuration Set", + Factory: newPhoneNumberResource, + Name: "Phone Number", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: names.AttrARN, }, From dd7e513dca22e3d0c526fb3afd7e36f7ad62704a Mon Sep 17 00:00:00 2001 From: Weston Reed Date: Mon, 7 Oct 2024 23:21:09 -0700 Subject: [PATCH 6/8] fix: fix error on default message type --- .../pinpointsmsvoicev2/configuration_set.go | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/internal/service/pinpointsmsvoicev2/configuration_set.go b/internal/service/pinpointsmsvoicev2/configuration_set.go index 6ab02487c32..629ef0e79d9 100644 --- a/internal/service/pinpointsmsvoicev2/configuration_set.go +++ b/internal/service/pinpointsmsvoicev2/configuration_set.go @@ -105,23 +105,6 @@ func (r *configurationSetResource) Create(ctx context.Context, request resource. return } - if !data.DefaultSenderId.IsNull() { - err = setDefaultSenderId(ctx, conn, fwflex.StringFromFramework(ctx, data.DefaultSenderId), fwflex.StringFromFramework(ctx, data.DefaultSenderId)) - if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("setting default sender ID for End User Messaging SMS Configuration Set (%s) to %s", name, data.DefaultMessageType.ValueString()), err.Error()) - - return - } - } - if !data.DefaultMessageType.IsNull() { - err = setDefaultMessageType(ctx, conn, fwflex.StringFromFramework(ctx, data.DefaultMessageType), data.DefaultMessageType.ValueEnum()) - if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("setting default message type for End User Messaging SMS Configuration Set (%s) to %s", name, data.DefaultMessageType.ValueString()), err.Error()) - - return - } - } - // Set values for unknowns. data.ConfigurationSetARN = fwflex.StringToFramework(ctx, output.ConfigurationSetArn) data.setID() @@ -181,11 +164,12 @@ func (r *configurationSetResource) Update(ctx context.Context, request resource. conn := r.Meta().PinpointSMSVoiceV2Client(ctx) + name := new.ConfigurationSetName.ValueString() if !new.DefaultSenderId.Equal(old.DefaultSenderId) { err := setDefaultSenderId(ctx, conn, new.ID.ValueStringPointer(), new.DefaultSenderId.ValueStringPointer()) if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("setting default sender ID for End User Messaging SMS Configuration Set (%s)", new.ID.ValueString()), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("setting default sender ID for End User Messaging SMS Configuration Set (%s) to %s", name, new.DefaultSenderId.ValueString()), err.Error()) return } @@ -194,7 +178,7 @@ func (r *configurationSetResource) Update(ctx context.Context, request resource. err := setDefaultMessageType(ctx, conn, new.ID.ValueStringPointer(), new.DefaultMessageType.ValueEnum()) if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("setting default sender ID for End User Messaging SMS Configuration Set (%s)", new.ID.ValueString()), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("setting default message type for End User Messaging SMS Configuration Set (%s) to %s", name, new.DefaultMessageType.ValueString()), err.Error()) return } From ff1263de327c306a91d8b75cd17375f589fc4a3f Mon Sep 17 00:00:00 2001 From: Weston Reed Date: Tue, 8 Oct 2024 10:28:26 -0700 Subject: [PATCH 7/8] fix: handle delete sender ID and message type --- .../pinpointsmsvoicev2/configuration_set.go | 56 ++++++++++++++++--- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/internal/service/pinpointsmsvoicev2/configuration_set.go b/internal/service/pinpointsmsvoicev2/configuration_set.go index 629ef0e79d9..9293ccf2db3 100644 --- a/internal/service/pinpointsmsvoicev2/configuration_set.go +++ b/internal/service/pinpointsmsvoicev2/configuration_set.go @@ -166,21 +166,41 @@ func (r *configurationSetResource) Update(ctx context.Context, request resource. name := new.ConfigurationSetName.ValueString() if !new.DefaultSenderId.Equal(old.DefaultSenderId) { - err := setDefaultSenderId(ctx, conn, new.ID.ValueStringPointer(), new.DefaultSenderId.ValueStringPointer()) + if new.DefaultSenderId.IsNull() { + err := deleteDefaultSenderId(ctx, conn, new.ID.ValueStringPointer()) - if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("setting default sender ID for End User Messaging SMS Configuration Set (%s) to %s", name, new.DefaultSenderId.ValueString()), err.Error()) + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("deleting default sender ID for End User Messaging SMS Configuration Set (%s)", name), err.Error()) + + return + } + } else { + err := setDefaultSenderId(ctx, conn, new.ID.ValueStringPointer(), new.DefaultSenderId.ValueStringPointer()) - return + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("setting default sender ID for End User Messaging SMS Configuration Set (%s) to %s", name, new.DefaultSenderId.ValueString()), err.Error()) + + return + } } } if !new.DefaultMessageType.Equal(old.DefaultMessageType) { - err := setDefaultMessageType(ctx, conn, new.ID.ValueStringPointer(), new.DefaultMessageType.ValueEnum()) + if new.DefaultMessageType.IsNull() { + err := deleteDefaultMessageType(ctx, conn, new.ID.ValueStringPointer()) - if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("setting default message type for End User Messaging SMS Configuration Set (%s) to %s", name, new.DefaultMessageType.ValueString()), err.Error()) + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("deleting default message type for End User Messaging SMS Configuration Set (%s)", name), err.Error()) - return + return + } + } else { + err := setDefaultMessageType(ctx, conn, new.ID.ValueStringPointer(), new.DefaultMessageType.ValueEnum()) + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("setting default message type for End User Messaging SMS Configuration Set (%s) to %s", name, new.DefaultMessageType.ValueString()), err.Error()) + + return + } } } @@ -298,3 +318,23 @@ func setDefaultMessageType(ctx context.Context, conn *pinpointsmsvoicev2.Client, return err } + +func deleteDefaultSenderId(ctx context.Context, conn *pinpointsmsvoicev2.Client, configurationSetName *string) error { + input := &pinpointsmsvoicev2.DeleteDefaultSenderIdInput{ + ConfigurationSetName: configurationSetName, + } + + _, err := conn.DeleteDefaultSenderId(ctx, input) + + return err +} + +func deleteDefaultMessageType(ctx context.Context, conn *pinpointsmsvoicev2.Client, configurationSetName *string) error { + input := &pinpointsmsvoicev2.DeleteDefaultMessageTypeInput{ + ConfigurationSetName: configurationSetName, + } + + _, err := conn.DeleteDefaultMessageType(ctx, input) + + return err +} From f8760e8b49f2d8f02ebe49064d78649976fe75af Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 8 Oct 2024 14:35:16 -0400 Subject: [PATCH 8/8] Cosmetics. --- .../pinpointsmsvoicev2/configuration_set.go | 57 +++++++++---------- .../pinpointsmsvoicev2/exports_test.go | 4 +- 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/internal/service/pinpointsmsvoicev2/configuration_set.go b/internal/service/pinpointsmsvoicev2/configuration_set.go index 9293ccf2db3..b4bd9f9d399 100644 --- a/internal/service/pinpointsmsvoicev2/configuration_set.go +++ b/internal/service/pinpointsmsvoicev2/configuration_set.go @@ -40,7 +40,6 @@ func newConfigurationSetResource(context.Context) (resource.ResourceWithConfigur type configurationSetResource struct { framework.ResourceWithConfigure - framework.WithNoOpUpdate[configurationSetResourceModel] framework.WithImportByID } @@ -52,14 +51,11 @@ func (r *configurationSetResource) Schema(ctx context.Context, request resource. response.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ names.AttrARN: framework.ARNAttributeComputedOnly(), - names.AttrID: framework.IDAttribute(), - names.AttrName: schema.StringAttribute{ - Required: true, + "default_message_type": schema.StringAttribute{ + Optional: true, + CustomType: fwtypes.StringEnumType[awstypes.MessageType](), Validators: []validator.String{ - stringvalidator.RegexMatches(regexache.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_\-]{0,63}$`), ""), - }, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), + stringvalidator.RegexMatches(regexache.MustCompile(`^(TRANSACTIONAL|PROMOTIONAL)$`), "must be either TRANSACTIONAL or PROMOTIONAL"), }, }, "default_sender_id": schema.StringAttribute{ @@ -68,11 +64,14 @@ func (r *configurationSetResource) Schema(ctx context.Context, request resource. stringvalidator.RegexMatches(regexache.MustCompile(`^[A-Za-z0-9_-]{1,11}$`), "must be between 1 and 11 characters long and contain only letters, numbers, underscores, and dashes"), }, }, - "default_message_type": schema.StringAttribute{ - Optional: true, - CustomType: fwtypes.StringEnumType[awstypes.MessageType](), + names.AttrID: framework.IDAttribute(), + names.AttrName: schema.StringAttribute{ + Required: true, Validators: []validator.String{ - stringvalidator.RegexMatches(regexache.MustCompile(`^(TRANSACTIONAL|PROMOTIONAL)$`), "must be either TRANSACTIONAL or PROMOTIONAL"), + stringvalidator.RegexMatches(regexache.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_\-]{0,63}$`), ""), + }, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), }, }, names.AttrTags: tftags.TagsAttribute(), @@ -165,9 +164,9 @@ func (r *configurationSetResource) Update(ctx context.Context, request resource. conn := r.Meta().PinpointSMSVoiceV2Client(ctx) name := new.ConfigurationSetName.ValueString() - if !new.DefaultSenderId.Equal(old.DefaultSenderId) { - if new.DefaultSenderId.IsNull() { - err := deleteDefaultSenderId(ctx, conn, new.ID.ValueStringPointer()) + if !new.DefaultSenderID.Equal(old.DefaultSenderID) { + if new.DefaultSenderID.IsNull() { + err := deleteDefaultSenderID(ctx, conn, name) if err != nil { response.Diagnostics.AddError(fmt.Sprintf("deleting default sender ID for End User Messaging SMS Configuration Set (%s)", name), err.Error()) @@ -175,10 +174,10 @@ func (r *configurationSetResource) Update(ctx context.Context, request resource. return } } else { - err := setDefaultSenderId(ctx, conn, new.ID.ValueStringPointer(), new.DefaultSenderId.ValueStringPointer()) + err := setDefaultSenderID(ctx, conn, name, new.DefaultSenderID.ValueString()) if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("setting default sender ID for End User Messaging SMS Configuration Set (%s) to %s", name, new.DefaultSenderId.ValueString()), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("setting default sender ID for End User Messaging SMS Configuration Set (%s) to %s", name, new.DefaultSenderID.ValueString()), err.Error()) return } @@ -186,7 +185,7 @@ func (r *configurationSetResource) Update(ctx context.Context, request resource. } if !new.DefaultMessageType.Equal(old.DefaultMessageType) { if new.DefaultMessageType.IsNull() { - err := deleteDefaultMessageType(ctx, conn, new.ID.ValueStringPointer()) + err := deleteDefaultMessageType(ctx, conn, name) if err != nil { response.Diagnostics.AddError(fmt.Sprintf("deleting default message type for End User Messaging SMS Configuration Set (%s)", name), err.Error()) @@ -194,7 +193,7 @@ func (r *configurationSetResource) Update(ctx context.Context, request resource. return } } else { - err := setDefaultMessageType(ctx, conn, new.ID.ValueStringPointer(), new.DefaultMessageType.ValueEnum()) + err := setDefaultMessageType(ctx, conn, name, new.DefaultMessageType.ValueEnum()) if err != nil { response.Diagnostics.AddError(fmt.Sprintf("setting default message type for End User Messaging SMS Configuration Set (%s) to %s", name, new.DefaultMessageType.ValueString()), err.Error()) @@ -239,8 +238,8 @@ type configurationSetResourceModel struct { ID types.String `tfsdk:"id"` ConfigurationSetARN types.String `tfsdk:"arn"` ConfigurationSetName types.String `tfsdk:"name"` - DefaultSenderId types.String `tfsdk:"default_sender_id"` DefaultMessageType fwtypes.StringEnum[awstypes.MessageType] `tfsdk:"default_message_type"` + DefaultSenderID types.String `tfsdk:"default_sender_id"` Tags tftags.Map `tfsdk:"tags"` TagsAll tftags.Map `tfsdk:"tags_all"` } @@ -297,10 +296,10 @@ func findConfigurationSets(ctx context.Context, conn *pinpointsmsvoicev2.Client, return output, nil } -func setDefaultSenderId(ctx context.Context, conn *pinpointsmsvoicev2.Client, configurationSetName *string, senderId *string) error { +func setDefaultSenderID(ctx context.Context, conn *pinpointsmsvoicev2.Client, configurationSetName, senderID string) error { input := &pinpointsmsvoicev2.SetDefaultSenderIdInput{ - ConfigurationSetName: configurationSetName, - SenderId: senderId, + ConfigurationSetName: aws.String(configurationSetName), + SenderId: aws.String(senderID), } _, err := conn.SetDefaultSenderId(ctx, input) @@ -308,9 +307,9 @@ func setDefaultSenderId(ctx context.Context, conn *pinpointsmsvoicev2.Client, co return err } -func setDefaultMessageType(ctx context.Context, conn *pinpointsmsvoicev2.Client, configurationSetName *string, messageType awstypes.MessageType) error { +func setDefaultMessageType(ctx context.Context, conn *pinpointsmsvoicev2.Client, configurationSetName string, messageType awstypes.MessageType) error { input := &pinpointsmsvoicev2.SetDefaultMessageTypeInput{ - ConfigurationSetName: configurationSetName, + ConfigurationSetName: aws.String(configurationSetName), MessageType: messageType, } @@ -319,9 +318,9 @@ func setDefaultMessageType(ctx context.Context, conn *pinpointsmsvoicev2.Client, return err } -func deleteDefaultSenderId(ctx context.Context, conn *pinpointsmsvoicev2.Client, configurationSetName *string) error { +func deleteDefaultSenderID(ctx context.Context, conn *pinpointsmsvoicev2.Client, configurationSetName string) error { input := &pinpointsmsvoicev2.DeleteDefaultSenderIdInput{ - ConfigurationSetName: configurationSetName, + ConfigurationSetName: aws.String(configurationSetName), } _, err := conn.DeleteDefaultSenderId(ctx, input) @@ -329,9 +328,9 @@ func deleteDefaultSenderId(ctx context.Context, conn *pinpointsmsvoicev2.Client, return err } -func deleteDefaultMessageType(ctx context.Context, conn *pinpointsmsvoicev2.Client, configurationSetName *string) error { +func deleteDefaultMessageType(ctx context.Context, conn *pinpointsmsvoicev2.Client, configurationSetName string) error { input := &pinpointsmsvoicev2.DeleteDefaultMessageTypeInput{ - ConfigurationSetName: configurationSetName, + ConfigurationSetName: aws.String(configurationSetName), } _, err := conn.DeleteDefaultMessageType(ctx, input) diff --git a/internal/service/pinpointsmsvoicev2/exports_test.go b/internal/service/pinpointsmsvoicev2/exports_test.go index c1782cb12f2..87890f1cae3 100644 --- a/internal/service/pinpointsmsvoicev2/exports_test.go +++ b/internal/service/pinpointsmsvoicev2/exports_test.go @@ -5,11 +5,11 @@ package pinpointsmsvoicev2 // Exports for use in tests only. var ( + ResourceConfigurationSet = newConfigurationSetResource ResourceOptOutList = newOptOutListResource ResourcePhoneNumber = newPhoneNumberResource - ResourceConfigurationSet = newConfigurationSetResource + FindConfigurationSetByID = findConfigurationSetByID FindOptOutListByID = findOptOutListByID FindPhoneNumberByID = findPhoneNumberByID - FindConfigurationSetByID = findConfigurationSetByID )