From 13690c7ac4d9ceb199df051e0f391ee68d16ceeb Mon Sep 17 00:00:00 2001 From: James Lavoy Date: Thu, 12 May 2022 16:47:26 -0600 Subject: [PATCH 01/31] adding iot_domain_name_configuration support --- GNUmakefile | 2 +- internal/provider/provider.go | 1 + .../service/iot/domain_name_configuration.go | 234 ++++++++++++++++++ .../iot/domain_name_configuration_test.go | 40 +++ ...ot_domain_name_configuration.html.markdown | 47 ++++ 5 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 internal/service/iot/domain_name_configuration.go create mode 100644 internal/service/iot/domain_name_configuration_test.go create mode 100644 website/docs/r/iot_domain_name_configuration.html.markdown diff --git a/GNUmakefile b/GNUmakefile index 0e0c99864bd9..ecb7cc5b88c2 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -191,4 +191,4 @@ semgrep: @echo "==> Running Semgrep static analysis..." @docker run --rm --volume "${PWD}:/src" returntocorp/semgrep --config .semgrep.yml -.PHONY: providerlint build gen generate-changelog gh-workflows-lint golangci-lint sweep test testacc fmt fmtcheck lint tools test-compile website-link-check website-lint website-lint-fix depscheck docscheck semgrep +.PHONY: providerlint build gen generate-changelog gh-workflows-lint golangci-lint sweep test testacc fmt fmtcheck lint tools test-compile website-link-check website-lint website-lint-fix depscheck docscheck semgrep \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index e83cee508f30..d4464fe2e95d 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1550,6 +1550,7 @@ func Provider() *schema.Provider { "aws_iot_authorizer": iot.ResourceAuthorizer(), "aws_iot_certificate": iot.ResourceCertificate(), + "aws_iot_domain_name_configuration": iot.ResourceDomainNameConfiguration(), "aws_iot_indexing_configuration": iot.ResourceIndexingConfiguration(), "aws_iot_logging_options": iot.ResourceLoggingOptions(), "aws_iot_policy": iot.ResourcePolicy(), diff --git a/internal/service/iot/domain_name_configuration.go b/internal/service/iot/domain_name_configuration.go new file mode 100644 index 000000000000..9fbb54662ade --- /dev/null +++ b/internal/service/iot/domain_name_configuration.go @@ -0,0 +1,234 @@ +package iot + +import ( + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/iot" + "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/flex" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/verify" +) + +func ResourceDomainNameConfiguration() *schema.Resource { + return &schema.Resource{ + Create: resourceDomainNameConfigurationCreate, + Read: resourceDomainNameConfigurationRead, + Update: resourceDomainNameConfigurationUpdate, + Delete: resourceDomainNameConfigurationDelete, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "domain_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "server_certificate_arns": { + Type: schema.TypeList, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: verify.ValidARN, + }, + Optional: true, + ForceNew: true, + }, + "service_type": { + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + "DATA", + "CREDENTIAL_PROVIDER", + "JOBS", + }, false), + Optional: true, + ForceNew: true, + Default: "DATA", + }, + "validation_certificate_arn": { + Type: schema.TypeString, + ValidateFunc: verify.ValidARN, + Optional: true, + ForceNew: true, + }, + "authorizer_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allow_authorizer_override": { + Type: schema.TypeBool, + Optional: true, + }, + "default_authorizer_name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "tags": tftags.TagsSchema(), + "tags_all": tftags.TagsSchemaComputed(), + "status": { + Type: schema.TypeString, + Optional: true, + Default: "ENABLED", + ValidateFunc: validation.StringInSlice([]string{ + "ENABLED", + "DISABLED", + }, false), + }, + }, + } +} + +func resourceDomainNameConfigurationCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).IoTConn + defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) + + name := d.Get("name").(string) + + apiObject := &iot.CreateDomainConfigurationInput{ + DomainConfigurationName: aws.String(name), + } + + if v, ok := d.GetOk("domain_name"); ok { + apiObject.DomainName = aws.String(v.(string)) + } + + if v, ok := d.GetOk("server_certificate_arns"); ok { + apiObject.ServerCertificateArns = flex.ExpandStringList(v.([]interface{})) + } + + if v, ok := d.GetOk("service_type"); ok { + apiObject.ServiceType = aws.String(v.(string)) + } + + if v, ok := d.GetOk("validation_certificate_arn"); ok { + apiObject.ValidationCertificateArn = aws.String(v.(string)) + } + + if v, ok := d.GetOk("authorizer_config"); ok { + apiObject.AuthorizerConfig = expandIotDomainNameConfigurationAuthorizerConfig(v.([]interface{})) + } + + if len(tags) > 0 { + apiObject.Tags = Tags(tags.IgnoreAWS()) + } + + log.Printf("[DEBUG] Creating IoT Domain Configuration: %s", name) + output, err := conn.CreateDomainConfiguration(apiObject) + + if err != nil { + return fmt.Errorf("error creating IoT Domain Configuration (%s): %s", name, err) + } + + d.SetId(aws.StringValue(output.DomainConfigurationName)) + + return resourceDomainNameConfigurationRead(d, meta) +} + +func expandIotDomainNameConfigurationAuthorizerConfig(l []interface{}) *iot.AuthorizerConfig { + if len(l) < 1 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + iotAuthorizerConfig := &iot.AuthorizerConfig{ + AllowAuthorizerOverride: aws.Bool(m["allow_authorizer_override"].(bool)), + DefaultAuthorizerName: aws.String(m["allow_authorizer_override"].(string)), + } + + return iotAuthorizerConfig +} + +func resourceDomainNameConfigurationRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).IoTConn + defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig + + out, err := conn.DescribeDomainConfiguration(&iot.DescribeDomainConfigurationInput{ + DomainConfigurationName: aws.String(d.Id()), + }) + if err != nil { + return fmt.Errorf("error reading domain details: %v", err) + } + + d.Set("arn", out.DomainConfigurationArn) + + tags, err := ListTags(conn, d.Get("arn").(string)) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return fmt.Errorf("error setting tags_all: %w", err) + } + + return nil +} + +func resourceDomainNameConfigurationUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).IoTConn + + input := iot.UpdateDomainConfigurationInput{ + DomainConfigurationName: aws.String(d.Id()), + } + + if d.HasChange("authorizer_config") { + input.AuthorizerConfig = expandIotDomainNameConfigurationAuthorizerConfig(d.Get("authorizer_config").([]interface{})) + } + + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + if err := UpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating IoT Domain Name Configuration (%s) tags: %w", d.Id(), err) + } + } + + log.Printf("[INFO] Updating IoT Domain Configuration: %s", d.Id()) + _, err := conn.UpdateDomainConfiguration(&input) + + if err != nil { + return fmt.Errorf("error updating IoT Domain Configuration (%s): %w", d.Id(), err) + } + + return resourceCertificateRead(d, meta) +} + +func resourceDomainNameConfigurationDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).IoTConn + + if d.Get("status").(string) == "ENABLED" { + log.Printf("[INFO] Disabling IoT Domain Configuration: %s", d.Id()) + _, err := conn.UpdateDomainConfiguration(&iot.UpdateDomainConfigurationInput{ + DomainConfigurationName: aws.String(d.Id()), + DomainConfigurationStatus: aws.String("DISABLED"), + }) + + if err != nil { + return fmt.Errorf("error disabling IoT Domain Configuration (%s): %s", d.Id(), err) + } + } + + _, err := conn.DeleteDomainConfiguration(&iot.DeleteDomainConfigurationInput{ + DomainConfigurationName: aws.String(d.Id()), + }) + + if err != nil { + return fmt.Errorf("error deleting certificate: %v", err) + } + + return nil +} diff --git a/internal/service/iot/domain_name_configuration_test.go b/internal/service/iot/domain_name_configuration_test.go new file mode 100644 index 000000000000..a549e165c7d1 --- /dev/null +++ b/internal/service/iot/domain_name_configuration_test.go @@ -0,0 +1,40 @@ +package iot_test + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/iot" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" +) + +func TestAccIotDomainNameConfiguration_main(t *testing.T) { + resourceName := "aws_iot_domain_name_configuration.test" + domain := acctest.RandomDomainName() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, iot.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccIotDomainNameConfig(domain), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "status", "ENABLED"), + ), + }, + }, + }) +} + +func testAccIotDomainNameConfig(domain string) string { + return acctest.ConfigCompose(fmt.Sprintf(` +resource "aws_iot_domain_name_configuration" "test" { + name = "test" + domain_name = "%[1]s" + service_type = "DATA" +} +`, domain)) +} diff --git a/website/docs/r/iot_domain_name_configuration.html.markdown b/website/docs/r/iot_domain_name_configuration.html.markdown new file mode 100644 index 000000000000..cd109419bdc5 --- /dev/null +++ b/website/docs/r/iot_domain_name_configuration.html.markdown @@ -0,0 +1,47 @@ +--- +subcategory: "IoT Core" +layout: "aws" +page_title: "AWS: aws_iot_domain_name_configuration" +description: |- + Creates and manages an AWS IoT domain name configuration. +--- + +# Resource: aws_iot_domain_name_configuration + +Creates and manages an AWS IoT domain name configuration. + +## Example Usage + +```terraform +resource "aws_iot_domain_name_configuration" "iot" { + name = "iot-" + domain_name = "iot.example.com" + service_type = "DATA" + server_certificate_arns = [ + aws_acm_certificate.cert.arn + ] +} +``` + + +## Argument Reference + +* `name` = (Required) The name of the domain configuration. This value must be unique to a region. +* `domain_name` = (Required) Fully-qualified domain name. +* `server_certificate_arns` = (Optional) The ARNs of the certificates that IoT passes to the device during the TLS handshake. Currently you can specify only one certificate ARN. This value is not required for Amazon Web Services-managed domains. When using a custom `domain_name`, the cert must include it. +* `service_type` = (Optional) The type of service delivered by the endpoint. Note: Amazon Web Services IoT Core currently supports only the DATA service type. +* `validation_certificate_arn` = (Optional) The certificate used to validate the server certificate and prove domain name ownership. This certificate must be signed by a public certificate authority. This value is not required for Amazon Web Services-managed domains. +* `authorizer_config` = (Optional) an object that specifies the authorization service for a domain. See Below. +* `tags` = (Optional) Key-value map of resource tags. If configured with a provider default_tags configuration block present, tags with matching keys will overwrite those defined at the provider-level. + +### authorizer_config + +* `allow_authorizer_override` = (Optional) A Boolean that specifies whether the domain configuration's authorization service can be overridden. +* `default_authorizer_name` = (Optional) The name of the authorization service for a domain configuration. + + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `name` - The name of the created domain name configuration. From 930ff6f4f3dd3cbfb96d9ebfbdbcb48d4ca7c32a Mon Sep 17 00:00:00 2001 From: James Lavoy Date: Fri, 13 May 2022 10:41:23 -0600 Subject: [PATCH 02/31] golang lint --- internal/service/iot/domain_name_configuration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/iot/domain_name_configuration.go b/internal/service/iot/domain_name_configuration.go index 9fbb54662ade..4da615ae4c48 100644 --- a/internal/service/iot/domain_name_configuration.go +++ b/internal/service/iot/domain_name_configuration.go @@ -165,7 +165,7 @@ func resourceDomainNameConfigurationRead(d *schema.ResourceData, meta interface{ d.Set("arn", out.DomainConfigurationArn) - tags, err := ListTags(conn, d.Get("arn").(string)) + tags, _ := ListTags(conn, d.Get("arn").(string)) //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { From 828320d8296226cdd528f99690e1cb8a743ac385 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 27 May 2022 10:47:32 -0400 Subject: [PATCH 03/31] Add CHANGELOG entry. --- .changelog/24765.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/24765.txt diff --git a/.changelog/24765.txt b/.changelog/24765.txt new file mode 100644 index 000000000000..8ad757d38054 --- /dev/null +++ b/.changelog/24765.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_iot_domain_name_configuration +``` \ No newline at end of file From dbc59c1538639f8c783ae710ae91494c9e771ae7 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 27 May 2022 10:52:55 -0400 Subject: [PATCH 04/31] r/aws_iot_domain_name_configuration: Alphabetize attributes. --- .../service/iot/domain_name_configuration.go | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/internal/service/iot/domain_name_configuration.go b/internal/service/iot/domain_name_configuration.go index 4da615ae4c48..9ae6184de615 100644 --- a/internal/service/iot/domain_name_configuration.go +++ b/internal/service/iot/domain_name_configuration.go @@ -22,24 +22,41 @@ func ResourceDomainNameConfiguration() *schema.Resource { Delete: resourceDomainNameConfigurationDelete, Schema: map[string]*schema.Schema{ - "name": { + "authorizer_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "allow_authorizer_override": { + Type: schema.TypeBool, + Optional: true, + }, + "default_authorizer_name": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "domain_name": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "domain_name": { + "name": { Type: schema.TypeString, Required: true, ForceNew: true, }, "server_certificate_arns": { - Type: schema.TypeList, + Type: schema.TypeList, + Optional: true, + ForceNew: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: verify.ValidARN, }, - Optional: true, - ForceNew: true, }, "service_type": { Type: schema.TypeString, @@ -52,31 +69,6 @@ func ResourceDomainNameConfiguration() *schema.Resource { ForceNew: true, Default: "DATA", }, - "validation_certificate_arn": { - Type: schema.TypeString, - ValidateFunc: verify.ValidARN, - Optional: true, - ForceNew: true, - }, - "authorizer_config": { - Type: schema.TypeList, - Optional: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "allow_authorizer_override": { - Type: schema.TypeBool, - Optional: true, - }, - "default_authorizer_name": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - "tags": tftags.TagsSchema(), - "tags_all": tftags.TagsSchemaComputed(), "status": { Type: schema.TypeString, Optional: true, @@ -86,6 +78,14 @@ func ResourceDomainNameConfiguration() *schema.Resource { "DISABLED", }, false), }, + "tags": tftags.TagsSchema(), + "tags_all": tftags.TagsSchemaComputed(), + "validation_certificate_arn": { + Type: schema.TypeString, + ValidateFunc: verify.ValidARN, + Optional: true, + ForceNew: true, + }, }, } } From c99bc1e951c56375f2eaa35fe492cc3dcdc8bc38 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 27 May 2022 10:53:52 -0400 Subject: [PATCH 05/31] r/aws_iot_domain_name_configuration: Add CustomizeDiff. --- internal/service/iot/domain_name_configuration.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/service/iot/domain_name_configuration.go b/internal/service/iot/domain_name_configuration.go index 9ae6184de615..6bcba3908146 100644 --- a/internal/service/iot/domain_name_configuration.go +++ b/internal/service/iot/domain_name_configuration.go @@ -21,6 +21,8 @@ func ResourceDomainNameConfiguration() *schema.Resource { Update: resourceDomainNameConfigurationUpdate, Delete: resourceDomainNameConfigurationDelete, + CustomizeDiff: verify.SetTagsDiff, + Schema: map[string]*schema.Schema{ "authorizer_config": { Type: schema.TypeList, From 3e7e4a37205f65189ac019707d8d00de1ccdaf5e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 27 May 2022 10:56:30 -0400 Subject: [PATCH 06/31] r/aws_iot_domain_name_configuration: Use '_Values()' (#14601). --- .../service/iot/domain_name_configuration.go | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/internal/service/iot/domain_name_configuration.go b/internal/service/iot/domain_name_configuration.go index 6bcba3908146..a2a70dec23c1 100644 --- a/internal/service/iot/domain_name_configuration.go +++ b/internal/service/iot/domain_name_configuration.go @@ -61,32 +61,25 @@ func ResourceDomainNameConfiguration() *schema.Resource { }, }, "service_type": { - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice([]string{ - "DATA", - "CREDENTIAL_PROVIDER", - "JOBS", - }, false), - Optional: true, - ForceNew: true, - Default: "DATA", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: iot.ServiceTypeData, + ValidateFunc: validation.StringInSlice(iot.ServiceType_Values(), false), }, "status": { - Type: schema.TypeString, - Optional: true, - Default: "ENABLED", - ValidateFunc: validation.StringInSlice([]string{ - "ENABLED", - "DISABLED", - }, false), + Type: schema.TypeString, + Optional: true, + Default: iot.DomainConfigurationStatusEnabled, + ValidateFunc: validation.StringInSlice(iot.DomainConfigurationStatus_Values(), false), }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), "validation_certificate_arn": { Type: schema.TypeString, - ValidateFunc: verify.ValidARN, Optional: true, ForceNew: true, + ValidateFunc: verify.ValidARN, }, }, } @@ -212,11 +205,11 @@ func resourceDomainNameConfigurationUpdate(d *schema.ResourceData, meta interfac func resourceDomainNameConfigurationDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).IoTConn - if d.Get("status").(string) == "ENABLED" { + if d.Get("status").(string) == iot.DomainConfigurationStatusEnabled { log.Printf("[INFO] Disabling IoT Domain Configuration: %s", d.Id()) _, err := conn.UpdateDomainConfiguration(&iot.UpdateDomainConfigurationInput{ DomainConfigurationName: aws.String(d.Id()), - DomainConfigurationStatus: aws.String("DISABLED"), + DomainConfigurationStatus: aws.String(iot.DomainConfigurationStatusDisabled), }) if err != nil { From 6ee9759faed7be06c357039d565183bc87479038 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 27 May 2022 11:11:35 -0400 Subject: [PATCH 07/31] r/aws_iot_domain_name_configuration: Use Plugin SDK v2 WithoutTimeout CRUD handler signatures (#15090). --- .../service/iot/domain_name_configuration.go | 129 +++++++++--------- 1 file changed, 67 insertions(+), 62 deletions(-) diff --git a/internal/service/iot/domain_name_configuration.go b/internal/service/iot/domain_name_configuration.go index a2a70dec23c1..cbecdf09d167 100644 --- a/internal/service/iot/domain_name_configuration.go +++ b/internal/service/iot/domain_name_configuration.go @@ -1,11 +1,12 @@ package iot import ( - "fmt" + "context" "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iot" + "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" @@ -16,10 +17,10 @@ import ( func ResourceDomainNameConfiguration() *schema.Resource { return &schema.Resource{ - Create: resourceDomainNameConfigurationCreate, - Read: resourceDomainNameConfigurationRead, - Update: resourceDomainNameConfigurationUpdate, - Delete: resourceDomainNameConfigurationDelete, + CreateWithoutTimeout: resourceDomainNameConfigurationCreate, + ReadWithoutTimeout: resourceDomainNameConfigurationRead, + UpdateWithoutTimeout: resourceDomainNameConfigurationUpdate, + DeleteWithoutTimeout: resourceDomainNameConfigurationDelete, CustomizeDiff: verify.SetTagsDiff, @@ -85,77 +86,62 @@ func ResourceDomainNameConfiguration() *schema.Resource { } } -func resourceDomainNameConfigurationCreate(d *schema.ResourceData, meta interface{}) error { +func resourceDomainNameConfigurationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).IoTConn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) name := d.Get("name").(string) - - apiObject := &iot.CreateDomainConfigurationInput{ + input := &iot.CreateDomainConfigurationInput{ DomainConfigurationName: aws.String(name), } + if v, ok := d.GetOk("authorizer_config"); ok { + input.AuthorizerConfig = expandIotDomainNameConfigurationAuthorizerConfig(v.([]interface{})) + } + if v, ok := d.GetOk("domain_name"); ok { - apiObject.DomainName = aws.String(v.(string)) + input.DomainName = aws.String(v.(string)) } if v, ok := d.GetOk("server_certificate_arns"); ok { - apiObject.ServerCertificateArns = flex.ExpandStringList(v.([]interface{})) + input.ServerCertificateArns = flex.ExpandStringList(v.([]interface{})) } if v, ok := d.GetOk("service_type"); ok { - apiObject.ServiceType = aws.String(v.(string)) + input.ServiceType = aws.String(v.(string)) } if v, ok := d.GetOk("validation_certificate_arn"); ok { - apiObject.ValidationCertificateArn = aws.String(v.(string)) - } - - if v, ok := d.GetOk("authorizer_config"); ok { - apiObject.AuthorizerConfig = expandIotDomainNameConfigurationAuthorizerConfig(v.([]interface{})) + input.ValidationCertificateArn = aws.String(v.(string)) } if len(tags) > 0 { - apiObject.Tags = Tags(tags.IgnoreAWS()) + input.Tags = Tags(tags.IgnoreAWS()) } - log.Printf("[DEBUG] Creating IoT Domain Configuration: %s", name) - output, err := conn.CreateDomainConfiguration(apiObject) + log.Printf("[DEBUG] Creating IoT Domain Configuration: %s", input) + output, err := conn.CreateDomainConfigurationWithContext(ctx, input) if err != nil { - return fmt.Errorf("error creating IoT Domain Configuration (%s): %s", name, err) + return diag.Errorf("creating IoT Domain Configuration (%s): %s", name, err) } d.SetId(aws.StringValue(output.DomainConfigurationName)) - return resourceDomainNameConfigurationRead(d, meta) + return resourceDomainNameConfigurationRead(ctx, d, meta) } -func expandIotDomainNameConfigurationAuthorizerConfig(l []interface{}) *iot.AuthorizerConfig { - if len(l) < 1 || l[0] == nil { - return nil - } - - m := l[0].(map[string]interface{}) - - iotAuthorizerConfig := &iot.AuthorizerConfig{ - AllowAuthorizerOverride: aws.Bool(m["allow_authorizer_override"].(bool)), - DefaultAuthorizerName: aws.String(m["allow_authorizer_override"].(string)), - } - - return iotAuthorizerConfig -} - -func resourceDomainNameConfigurationRead(d *schema.ResourceData, meta interface{}) error { +func resourceDomainNameConfigurationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).IoTConn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig - out, err := conn.DescribeDomainConfiguration(&iot.DescribeDomainConfigurationInput{ + out, err := conn.DescribeDomainConfigurationWithContext(ctx, &iot.DescribeDomainConfigurationInput{ DomainConfigurationName: aws.String(d.Id()), }) + if err != nil { - return fmt.Errorf("error reading domain details: %v", err) + return diag.Errorf("reading IoT Domain Configuration (%s): %s", d.Id(), err) } d.Set("arn", out.DomainConfigurationArn) @@ -164,66 +150,85 @@ func resourceDomainNameConfigurationRead(d *schema.ResourceData, meta interface{ //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %w", err) + return diag.Errorf("setting tags: %s", err) } if err := d.Set("tags_all", tags.Map()); err != nil { - return fmt.Errorf("error setting tags_all: %w", err) + return diag.Errorf("setting tags_all: %s", err) } return nil } -func resourceDomainNameConfigurationUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceDomainNameConfigurationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).IoTConn - input := iot.UpdateDomainConfigurationInput{ - DomainConfigurationName: aws.String(d.Id()), - } - if d.HasChange("authorizer_config") { - input.AuthorizerConfig = expandIotDomainNameConfigurationAuthorizerConfig(d.Get("authorizer_config").([]interface{})) + input := iot.UpdateDomainConfigurationInput{ + DomainConfigurationName: aws.String(d.Id()), + } + + if d.HasChange("authorizer_config") { + input.AuthorizerConfig = expandIotDomainNameConfigurationAuthorizerConfig(d.Get("authorizer_config").([]interface{})) + } + + log.Printf("[DEBUG] Updating IoT Domain Configuration: %s", d.Id()) + _, err := conn.UpdateDomainConfigurationWithContext(ctx, &input) + + if err != nil { + return diag.Errorf("updating IoT Domain Configuration (%s): %s", d.Id(), err) + } } if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") + if err := UpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("error updating IoT Domain Name Configuration (%s) tags: %w", d.Id(), err) + return diag.Errorf("updating IoT Domain Configuration (%s) tags: %s", d.Id(), err) } } - log.Printf("[INFO] Updating IoT Domain Configuration: %s", d.Id()) - _, err := conn.UpdateDomainConfiguration(&input) - - if err != nil { - return fmt.Errorf("error updating IoT Domain Configuration (%s): %w", d.Id(), err) - } - - return resourceCertificateRead(d, meta) + return resourceDomainNameConfigurationRead(ctx, d, meta) } -func resourceDomainNameConfigurationDelete(d *schema.ResourceData, meta interface{}) error { +func resourceDomainNameConfigurationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).IoTConn if d.Get("status").(string) == iot.DomainConfigurationStatusEnabled { - log.Printf("[INFO] Disabling IoT Domain Configuration: %s", d.Id()) - _, err := conn.UpdateDomainConfiguration(&iot.UpdateDomainConfigurationInput{ + log.Printf("[DEBUG] Disabling IoT Domain Configuration: %s", d.Id()) + _, err := conn.UpdateDomainConfigurationWithContext(ctx, &iot.UpdateDomainConfigurationInput{ DomainConfigurationName: aws.String(d.Id()), DomainConfigurationStatus: aws.String(iot.DomainConfigurationStatusDisabled), }) if err != nil { - return fmt.Errorf("error disabling IoT Domain Configuration (%s): %s", d.Id(), err) + return diag.Errorf("disabling IoT Domain Configuration (%s): %s", d.Id(), err) } } - _, err := conn.DeleteDomainConfiguration(&iot.DeleteDomainConfigurationInput{ + log.Printf("[DEBUG] Deleting IoT Domain Configuration: %s", d.Id()) + _, err := conn.DeleteDomainConfigurationWithContext(ctx, &iot.DeleteDomainConfigurationInput{ DomainConfigurationName: aws.String(d.Id()), }) if err != nil { - return fmt.Errorf("error deleting certificate: %v", err) + return diag.Errorf("deleting IoT Domain Configuration (%s): %s", d.Id(), err) } return nil } + +func expandIotDomainNameConfigurationAuthorizerConfig(l []interface{}) *iot.AuthorizerConfig { + if len(l) < 1 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + iotAuthorizerConfig := &iot.AuthorizerConfig{ + AllowAuthorizerOverride: aws.Bool(m["allow_authorizer_override"].(bool)), + DefaultAuthorizerName: aws.String(m["allow_authorizer_override"].(string)), + } + + return iotAuthorizerConfig +} From f70650fa4303fff04163d9c3986e7e0c7fde1191 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 27 May 2022 11:23:17 -0400 Subject: [PATCH 08/31] 'aws_iot_domain_name_configuration' -> 'aws_iot_domain_configuration'. --- .changelog/24765.txt | 2 +- internal/provider/provider.go | 2 +- ...nfiguration.go => domain_configuration.go} | 28 +++++++++---------- ...n_test.go => domain_configuration_test.go} | 16 +++++------ ...=> iot_domain_configuration.html.markdown} | 22 ++++++++------- 5 files changed, 36 insertions(+), 34 deletions(-) rename internal/service/iot/{domain_name_configuration.go => domain_configuration.go} (82%) rename internal/service/iot/{domain_name_configuration_test.go => domain_configuration_test.go} (67%) rename website/docs/r/{iot_domain_name_configuration.html.markdown => iot_domain_configuration.html.markdown} (65%) diff --git a/.changelog/24765.txt b/.changelog/24765.txt index 8ad757d38054..d192c96cef6e 100644 --- a/.changelog/24765.txt +++ b/.changelog/24765.txt @@ -1,3 +1,3 @@ ```release-note:new-resource -aws_iot_domain_name_configuration +aws_iot_domain_configuration ``` \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 785d1a53fd22..d759314f69df 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1556,7 +1556,7 @@ func Provider() *schema.Provider { "aws_iot_authorizer": iot.ResourceAuthorizer(), "aws_iot_certificate": iot.ResourceCertificate(), - "aws_iot_domain_name_configuration": iot.ResourceDomainNameConfiguration(), + "aws_iot_domain_configuration": iot.ResourceDomainConfiguration(), "aws_iot_indexing_configuration": iot.ResourceIndexingConfiguration(), "aws_iot_logging_options": iot.ResourceLoggingOptions(), "aws_iot_policy": iot.ResourcePolicy(), diff --git a/internal/service/iot/domain_name_configuration.go b/internal/service/iot/domain_configuration.go similarity index 82% rename from internal/service/iot/domain_name_configuration.go rename to internal/service/iot/domain_configuration.go index cbecdf09d167..3b8e93344275 100644 --- a/internal/service/iot/domain_name_configuration.go +++ b/internal/service/iot/domain_configuration.go @@ -15,12 +15,12 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/verify" ) -func ResourceDomainNameConfiguration() *schema.Resource { +func ResourceDomainConfiguration() *schema.Resource { return &schema.Resource{ - CreateWithoutTimeout: resourceDomainNameConfigurationCreate, - ReadWithoutTimeout: resourceDomainNameConfigurationRead, - UpdateWithoutTimeout: resourceDomainNameConfigurationUpdate, - DeleteWithoutTimeout: resourceDomainNameConfigurationDelete, + CreateWithoutTimeout: resourceDomainConfigurationCreate, + ReadWithoutTimeout: resourceDomainConfigurationRead, + UpdateWithoutTimeout: resourceDomainConfigurationUpdate, + DeleteWithoutTimeout: resourceDomainConfigurationDelete, CustomizeDiff: verify.SetTagsDiff, @@ -86,7 +86,7 @@ func ResourceDomainNameConfiguration() *schema.Resource { } } -func resourceDomainNameConfigurationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceDomainConfigurationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).IoTConn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) @@ -97,7 +97,7 @@ func resourceDomainNameConfigurationCreate(ctx context.Context, d *schema.Resour } if v, ok := d.GetOk("authorizer_config"); ok { - input.AuthorizerConfig = expandIotDomainNameConfigurationAuthorizerConfig(v.([]interface{})) + input.AuthorizerConfig = expandAuthorizerConfig(v.([]interface{})) } if v, ok := d.GetOk("domain_name"); ok { @@ -129,10 +129,10 @@ func resourceDomainNameConfigurationCreate(ctx context.Context, d *schema.Resour d.SetId(aws.StringValue(output.DomainConfigurationName)) - return resourceDomainNameConfigurationRead(ctx, d, meta) + return resourceDomainConfigurationRead(ctx, d, meta) } -func resourceDomainNameConfigurationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceDomainConfigurationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).IoTConn defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig @@ -160,7 +160,7 @@ func resourceDomainNameConfigurationRead(ctx context.Context, d *schema.Resource return nil } -func resourceDomainNameConfigurationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceDomainConfigurationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).IoTConn if d.HasChange("authorizer_config") { @@ -169,7 +169,7 @@ func resourceDomainNameConfigurationUpdate(ctx context.Context, d *schema.Resour } if d.HasChange("authorizer_config") { - input.AuthorizerConfig = expandIotDomainNameConfigurationAuthorizerConfig(d.Get("authorizer_config").([]interface{})) + input.AuthorizerConfig = expandAuthorizerConfig(d.Get("authorizer_config").([]interface{})) } log.Printf("[DEBUG] Updating IoT Domain Configuration: %s", d.Id()) @@ -188,10 +188,10 @@ func resourceDomainNameConfigurationUpdate(ctx context.Context, d *schema.Resour } } - return resourceDomainNameConfigurationRead(ctx, d, meta) + return resourceDomainConfigurationRead(ctx, d, meta) } -func resourceDomainNameConfigurationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceDomainConfigurationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).IoTConn if d.Get("status").(string) == iot.DomainConfigurationStatusEnabled { @@ -218,7 +218,7 @@ func resourceDomainNameConfigurationDelete(ctx context.Context, d *schema.Resour return nil } -func expandIotDomainNameConfigurationAuthorizerConfig(l []interface{}) *iot.AuthorizerConfig { +func expandAuthorizerConfig(l []interface{}) *iot.AuthorizerConfig { if len(l) < 1 || l[0] == nil { return nil } diff --git a/internal/service/iot/domain_name_configuration_test.go b/internal/service/iot/domain_configuration_test.go similarity index 67% rename from internal/service/iot/domain_name_configuration_test.go rename to internal/service/iot/domain_configuration_test.go index a549e165c7d1..2ce7d1fc3846 100644 --- a/internal/service/iot/domain_name_configuration_test.go +++ b/internal/service/iot/domain_configuration_test.go @@ -9,8 +9,8 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" ) -func TestAccIotDomainNameConfiguration_main(t *testing.T) { - resourceName := "aws_iot_domain_name_configuration.test" +func TestAccIotDomainConfiguration_basic(t *testing.T) { + resourceName := "aws_iot_domain_configuration.test" domain := acctest.RandomDomainName() resource.Test(t, resource.TestCase{ @@ -20,7 +20,7 @@ func TestAccIotDomainNameConfiguration_main(t *testing.T) { CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { - Config: testAccIotDomainNameConfig(domain), + Config: testAccDomainConfigurationConfig(domain), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "status", "ENABLED"), ), @@ -29,12 +29,12 @@ func TestAccIotDomainNameConfiguration_main(t *testing.T) { }) } -func testAccIotDomainNameConfig(domain string) string { - return acctest.ConfigCompose(fmt.Sprintf(` -resource "aws_iot_domain_name_configuration" "test" { +func testAccDomainConfigurationConfig(domain string) string { + return fmt.Sprintf(` +resource "aws_iot_domain_configuration" "test" { name = "test" - domain_name = "%[1]s" + domain_name = %[1]q service_type = "DATA" } -`, domain)) +`, domain) } diff --git a/website/docs/r/iot_domain_name_configuration.html.markdown b/website/docs/r/iot_domain_configuration.html.markdown similarity index 65% rename from website/docs/r/iot_domain_name_configuration.html.markdown rename to website/docs/r/iot_domain_configuration.html.markdown index cd109419bdc5..e4f2a096bf26 100644 --- a/website/docs/r/iot_domain_name_configuration.html.markdown +++ b/website/docs/r/iot_domain_configuration.html.markdown @@ -1,22 +1,23 @@ --- subcategory: "IoT Core" layout: "aws" -page_title: "AWS: aws_iot_domain_name_configuration" +page_title: "AWS: aws_iot_domain_configuration" description: |- - Creates and manages an AWS IoT domain name configuration. + Creates and manages an AWS IoT domain configuration. --- -# Resource: aws_iot_domain_name_configuration +# Resource: aws_iot_domain_configuration -Creates and manages an AWS IoT domain name configuration. +Creates and manages an AWS IoT domain configuration. ## Example Usage ```terraform -resource "aws_iot_domain_name_configuration" "iot" { +resource "aws_iot_domain_configuration" "iot" { name = "iot-" domain_name = "iot.example.com" service_type = "DATA" + server_certificate_arns = [ aws_acm_certificate.cert.arn ] @@ -26,22 +27,23 @@ resource "aws_iot_domain_name_configuration" "iot" { ## Argument Reference -* `name` = (Required) The name of the domain configuration. This value must be unique to a region. +* `authorizer_config` = (Optional) an object that specifies the authorization service for a domain. See Below. * `domain_name` = (Required) Fully-qualified domain name. +* `name` = (Required) The name of the domain configuration. This value must be unique to a region. * `server_certificate_arns` = (Optional) The ARNs of the certificates that IoT passes to the device during the TLS handshake. Currently you can specify only one certificate ARN. This value is not required for Amazon Web Services-managed domains. When using a custom `domain_name`, the cert must include it. * `service_type` = (Optional) The type of service delivered by the endpoint. Note: Amazon Web Services IoT Core currently supports only the DATA service type. +* `tags` - (Optional) Map of tags to assign to this resource. If configured with a provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. * `validation_certificate_arn` = (Optional) The certificate used to validate the server certificate and prove domain name ownership. This certificate must be signed by a public certificate authority. This value is not required for Amazon Web Services-managed domains. -* `authorizer_config` = (Optional) an object that specifies the authorization service for a domain. See Below. -* `tags` = (Optional) Key-value map of resource tags. If configured with a provider default_tags configuration block present, tags with matching keys will overwrite those defined at the provider-level. ### authorizer_config * `allow_authorizer_override` = (Optional) A Boolean that specifies whether the domain configuration's authorization service can be overridden. * `default_authorizer_name` = (Optional) The name of the authorization service for a domain configuration. - ## Attributes Reference In addition to all arguments above, the following attributes are exported: -* `name` - The name of the created domain name configuration. +* `arn` - The ARN of the domain configuration.. +* `id` - The name of the created domain configuration. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block). \ No newline at end of file From 205e91fb5b1537586b0497cbbd181e60adff6068 Mon Sep 17 00:00:00 2001 From: James Lavoy Date: Tue, 18 Jul 2023 08:35:31 -0600 Subject: [PATCH 09/31] adjusting website docs --- website/docs/r/iot_domain_configuration.html.markdown | 1 - 1 file changed, 1 deletion(-) diff --git a/website/docs/r/iot_domain_configuration.html.markdown b/website/docs/r/iot_domain_configuration.html.markdown index e4f2a096bf26..178944188bea 100644 --- a/website/docs/r/iot_domain_configuration.html.markdown +++ b/website/docs/r/iot_domain_configuration.html.markdown @@ -24,7 +24,6 @@ resource "aws_iot_domain_configuration" "iot" { } ``` - ## Argument Reference * `authorizer_config` = (Optional) an object that specifies the authorization service for a domain. See Below. From b9a6220a261523281fe0bca648d84cf2ce0662fa Mon Sep 17 00:00:00 2001 From: James Lavoy Date: Tue, 18 Jul 2023 08:38:36 -0600 Subject: [PATCH 10/31] renaming test --- internal/service/iot/domain_configuration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/iot/domain_configuration_test.go b/internal/service/iot/domain_configuration_test.go index 2ce7d1fc3846..1f25b276b782 100644 --- a/internal/service/iot/domain_configuration_test.go +++ b/internal/service/iot/domain_configuration_test.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" ) -func TestAccIotDomainConfiguration_basic(t *testing.T) { +func TestAccIoTDomainConfiguration_basic(t *testing.T) { resourceName := "aws_iot_domain_configuration.test" domain := acctest.RandomDomainName() From 58a8d2a6598a5271b9943031e942d67e1c984170 Mon Sep 17 00:00:00 2001 From: James Lavoy Date: Tue, 18 Jul 2023 08:45:26 -0600 Subject: [PATCH 11/31] adding copyright --- internal/service/iot/domain_configuration.go | 3 +++ internal/service/iot/domain_configuration_test.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/internal/service/iot/domain_configuration.go b/internal/service/iot/domain_configuration.go index 3b8e93344275..afe495cb9acf 100644 --- a/internal/service/iot/domain_configuration.go +++ b/internal/service/iot/domain_configuration.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package iot import ( diff --git a/internal/service/iot/domain_configuration_test.go b/internal/service/iot/domain_configuration_test.go index 1f25b276b782..fb4a4d4c0d8f 100644 --- a/internal/service/iot/domain_configuration_test.go +++ b/internal/service/iot/domain_configuration_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package iot_test import ( From e7a9b23f5638ac571e4f71fc5fe811885a595456 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 24 Oct 2023 16:41:23 -0400 Subject: [PATCH 12/31] r/aws_iot_domain_configuration: Modernize. --- internal/service/iot/domain_configuration.go | 147 +++++++++++------- .../service/iot/domain_configuration_test.go | 72 +++++++-- internal/service/iot/service_package_gen.go | 8 + 3 files changed, 158 insertions(+), 69 deletions(-) diff --git a/internal/service/iot/domain_configuration.go b/internal/service/iot/domain_configuration.go index afe495cb9acf..aef81880641b 100644 --- a/internal/service/iot/domain_configuration.go +++ b/internal/service/iot/domain_configuration.go @@ -9,15 +9,21 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iot" + "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/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/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) +// @SDKResource("aws_iot_domain_configuration", name="Domain Configuration") +// @Tags(identifierAttribute="arn") func ResourceDomainConfiguration() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceDomainConfigurationCreate, @@ -28,6 +34,10 @@ func ResourceDomainConfiguration() *schema.Resource { CustomizeDiff: verify.SetTagsDiff, Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, "authorizer_config": { Type: schema.TypeList, Optional: true, @@ -56,7 +66,7 @@ func ResourceDomainConfiguration() *schema.Resource { ForceNew: true, }, "server_certificate_arns": { - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, ForceNew: true, Elem: &schema.Schema{ @@ -90,112 +100,105 @@ func ResourceDomainConfiguration() *schema.Resource { } func resourceDomainConfigurationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*conns.AWSClient).IoTConn - defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig - tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).IoTConn(ctx) name := d.Get("name").(string) input := &iot.CreateDomainConfigurationInput{ DomainConfigurationName: aws.String(name), + Tags: getTagsIn(ctx), } - if v, ok := d.GetOk("authorizer_config"); ok { - input.AuthorizerConfig = expandAuthorizerConfig(v.([]interface{})) + if v, ok := d.GetOk("authorizer_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.AuthorizerConfig = expandAuthorizerConfig(v.([]interface{})[0].(map[string]interface{})) } if v, ok := d.GetOk("domain_name"); ok { input.DomainName = aws.String(v.(string)) } - if v, ok := d.GetOk("server_certificate_arns"); ok { - input.ServerCertificateArns = flex.ExpandStringList(v.([]interface{})) + if v, ok := d.GetOk("server_certificate_arns"); ok && v.(*schema.Set).Len() > 0 { + input.ServerCertificateArns = flex.ExpandStringSet(v.(*schema.Set)) } if v, ok := d.GetOk("service_type"); ok { input.ServiceType = aws.String(v.(string)) } + // TODO tls_config + if v, ok := d.GetOk("validation_certificate_arn"); ok { input.ValidationCertificateArn = aws.String(v.(string)) } - if len(tags) > 0 { - input.Tags = Tags(tags.IgnoreAWS()) - } - - log.Printf("[DEBUG] Creating IoT Domain Configuration: %s", input) output, err := conn.CreateDomainConfigurationWithContext(ctx, input) if err != nil { - return diag.Errorf("creating IoT Domain Configuration (%s): %s", name, err) + return sdkdiag.AppendErrorf(diags, "creating IoT Domain Configuration (%s): %s", name, err) } d.SetId(aws.StringValue(output.DomainConfigurationName)) - return resourceDomainConfigurationRead(ctx, d, meta) + return append(diags, resourceDomainConfigurationRead(ctx, d, meta)...) } func resourceDomainConfigurationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*conns.AWSClient).IoTConn - defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).IoTConn(ctx) - out, err := conn.DescribeDomainConfigurationWithContext(ctx, &iot.DescribeDomainConfigurationInput{ - DomainConfigurationName: aws.String(d.Id()), - }) + output, err := FindDomainConfigurationByName(ctx, conn, d.Id()) - if err != nil { - return diag.Errorf("reading IoT Domain Configuration (%s): %s", d.Id(), err) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] IoT Domain Configuration (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - d.Set("arn", out.DomainConfigurationArn) - - tags, _ := ListTags(conn, d.Get("arn").(string)) - - //lintignore:AWSR002 - if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { - return diag.Errorf("setting tags: %s", err) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading IoT Domain Configuration (%s): %s", d.Id(), err) } - if err := d.Set("tags_all", tags.Map()); err != nil { - return diag.Errorf("setting tags_all: %s", err) - } + d.Set("arn", output.DomainConfigurationArn) + // TODO authorizer_config + d.Set("domain_name", output.DomainName) + // TODO domain_type + d.Set("name", output.DomainConfigurationName) + // TODO server_certificate_arns + // TODO service_type + d.Set("status", output.DomainConfigurationStatus) + // TODO validation_certificate_arn return nil } func resourceDomainConfigurationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*conns.AWSClient).IoTConn + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).IoTConn(ctx) if d.HasChange("authorizer_config") { - input := iot.UpdateDomainConfigurationInput{ + input := &iot.UpdateDomainConfigurationInput{ DomainConfigurationName: aws.String(d.Id()), } if d.HasChange("authorizer_config") { - input.AuthorizerConfig = expandAuthorizerConfig(d.Get("authorizer_config").([]interface{})) + if v, ok := d.GetOk("authorizer_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.AuthorizerConfig = expandAuthorizerConfig(v.([]interface{})[0].(map[string]interface{})) + } } - log.Printf("[DEBUG] Updating IoT Domain Configuration: %s", d.Id()) - _, err := conn.UpdateDomainConfigurationWithContext(ctx, &input) + _, err := conn.UpdateDomainConfigurationWithContext(ctx, input) if err != nil { - return diag.Errorf("updating IoT Domain Configuration (%s): %s", d.Id(), err) - } - } - - if d.HasChange("tags_all") { - o, n := d.GetChange("tags_all") - - if err := UpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return diag.Errorf("updating IoT Domain Configuration (%s) tags: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "updating IoT Domain Configuration (%s): %s", d.Id(), err) } } - return resourceDomainConfigurationRead(ctx, d, meta) + return append(diags, resourceDomainConfigurationRead(ctx, d, meta)...) } func resourceDomainConfigurationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - conn := meta.(*conns.AWSClient).IoTConn + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).IoTConn(ctx) if d.Get("status").(string) == iot.DomainConfigurationStatusEnabled { log.Printf("[DEBUG] Disabling IoT Domain Configuration: %s", d.Id()) @@ -205,7 +208,7 @@ func resourceDomainConfigurationDelete(ctx context.Context, d *schema.ResourceDa }) if err != nil { - return diag.Errorf("disabling IoT Domain Configuration (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "disabling IoT Domain Configuration (%s): %s", d.Id(), err) } } @@ -215,23 +218,51 @@ func resourceDomainConfigurationDelete(ctx context.Context, d *schema.ResourceDa }) if err != nil { - return diag.Errorf("deleting IoT Domain Configuration (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "deleting IoT Domain Configuration (%s): %s", d.Id(), err) } - return nil + return diags } -func expandAuthorizerConfig(l []interface{}) *iot.AuthorizerConfig { - if len(l) < 1 || l[0] == nil { +func FindDomainConfigurationByName(ctx context.Context, conn *iot.IoT, name string) (*iot.DescribeDomainConfigurationOutput, error) { + input := &iot.DescribeDomainConfigurationInput{ + DomainConfigurationName: aws.String(name), + } + + output, err := conn.DescribeDomainConfigurationWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { + 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 +} + +func expandAuthorizerConfig(tfMap map[string]interface{}) *iot.AuthorizerConfig { + if tfMap == nil { return nil } - m := l[0].(map[string]interface{}) + apiObject := &iot.AuthorizerConfig{} + + if v, ok := tfMap["allow_authorizer_override"].(bool); ok { + apiObject.AllowAuthorizerOverride = aws.Bool(v) + } - iotAuthorizerConfig := &iot.AuthorizerConfig{ - AllowAuthorizerOverride: aws.Bool(m["allow_authorizer_override"].(bool)), - DefaultAuthorizerName: aws.String(m["allow_authorizer_override"].(string)), + if v, ok := tfMap["default_authorizer_name"].(string); ok && v != "" { + apiObject.DefaultAuthorizerName = aws.String(v) } - return iotAuthorizerConfig + return apiObject } diff --git a/internal/service/iot/domain_configuration_test.go b/internal/service/iot/domain_configuration_test.go index fb4a4d4c0d8f..af5cd91a8728 100644 --- a/internal/service/iot/domain_configuration_test.go +++ b/internal/service/iot/domain_configuration_test.go @@ -4,27 +4,36 @@ package iot_test import ( + "context" "fmt" "testing" "github.com/aws/aws-sdk-go/service/iot" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "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" + tfiot "github.com/hashicorp/terraform-provider-aws/internal/service/iot" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccIoTDomainConfiguration_basic(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + domainName := acctest.RandomDomainName() resourceName := "aws_iot_domain_configuration.test" - domain := acctest.RandomDomainName() resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - ErrorCheck: acctest.ErrorCheck(t, iot.EndpointsID), - ProviderFactories: acctest.ProviderFactories, - CheckDestroy: acctest.CheckDestroyNoop, + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iot.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDomainConfigurationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccDomainConfigurationConfig(domain), + Config: testAccDomainConfigurationConfig_basic(rName, domainName), Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDomainConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "status", "ENABLED"), ), }, @@ -32,12 +41,53 @@ func TestAccIoTDomainConfiguration_basic(t *testing.T) { }) } -func testAccDomainConfigurationConfig(domain string) string { +func testAccCheckDomainConfigurationExists(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).IoTConn(ctx) + + _, err := tfiot.FindDomainConfigurationByName(ctx, conn, rs.Primary.ID) + + return err + } +} + +func testAccCheckDomainConfigurationDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).IoTConn(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_iot_domain_configuration" { + continue + } + + _, err := tfiot.FindDomainConfigurationByName(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("IoT Domain Configuration %s still exists", rs.Primary.ID) + } + + return nil + } +} + +func testAccDomainConfigurationConfig_basic(rName, domainName string) string { return fmt.Sprintf(` resource "aws_iot_domain_configuration" "test" { - name = "test" - domain_name = %[1]q + name = %[1]q + domain_name = %[2]q service_type = "DATA" } -`, domain) +`, rName, domainName) } diff --git a/internal/service/iot/service_package_gen.go b/internal/service/iot/service_package_gen.go index 2577b9f4945c..a8613b1ab5e5 100644 --- a/internal/service/iot/service_package_gen.go +++ b/internal/service/iot/service_package_gen.go @@ -42,6 +42,14 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka Factory: ResourceCertificate, TypeName: "aws_iot_certificate", }, + { + Factory: ResourceDomainConfiguration, + TypeName: "aws_iot_domain_configuration", + Name: "Domain Configuration", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: "arn", + }, + }, { Factory: ResourceIndexingConfiguration, TypeName: "aws_iot_indexing_configuration", From 448ad1630a50ad39e47f0802d3d0b42a848504bb Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 24 Oct 2023 16:51:39 -0400 Subject: [PATCH 13/31] Fix markdown-lint 'MD047/single-trailing-newline Files should end with a single newline character'. --- .../r/iot_domain_configuration.html.markdown | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/website/docs/r/iot_domain_configuration.html.markdown b/website/docs/r/iot_domain_configuration.html.markdown index 178944188bea..b73d98a47947 100644 --- a/website/docs/r/iot_domain_configuration.html.markdown +++ b/website/docs/r/iot_domain_configuration.html.markdown @@ -26,23 +26,23 @@ resource "aws_iot_domain_configuration" "iot" { ## Argument Reference -* `authorizer_config` = (Optional) an object that specifies the authorization service for a domain. See Below. -* `domain_name` = (Required) Fully-qualified domain name. -* `name` = (Required) The name of the domain configuration. This value must be unique to a region. -* `server_certificate_arns` = (Optional) The ARNs of the certificates that IoT passes to the device during the TLS handshake. Currently you can specify only one certificate ARN. This value is not required for Amazon Web Services-managed domains. When using a custom `domain_name`, the cert must include it. -* `service_type` = (Optional) The type of service delivered by the endpoint. Note: Amazon Web Services IoT Core currently supports only the DATA service type. +* `authorizer_config` - (Optional) an object that specifies the authorization service for a domain. See Below. +* `domain_name` - (Required) Fully-qualified domain name. +* `name` - (Required) The name of the domain configuration. This value must be unique to a region. +* `server_certificate_arns` - (Optional) The ARNs of the certificates that IoT passes to the device during the TLS handshake. Currently you can specify only one certificate ARN. This value is not required for Amazon Web Services-managed domains. When using a custom `domain_name`, the cert must include it. +* `service_type` - (Optional) The type of service delivered by the endpoint. Note: Amazon Web Services IoT Core currently supports only the `DATA` service type. * `tags` - (Optional) Map of tags to assign to this resource. If configured with a provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. -* `validation_certificate_arn` = (Optional) The certificate used to validate the server certificate and prove domain name ownership. This certificate must be signed by a public certificate authority. This value is not required for Amazon Web Services-managed domains. +* `validation_certificate_arn` - (Optional) The certificate used to validate the server certificate and prove domain name ownership. This certificate must be signed by a public certificate authority. This value is not required for Amazon Web Services-managed domains. ### authorizer_config -* `allow_authorizer_override` = (Optional) A Boolean that specifies whether the domain configuration's authorization service can be overridden. -* `default_authorizer_name` = (Optional) The name of the authorization service for a domain configuration. +* `allow_authorizer_override` - (Optional) A Boolean that specifies whether the domain configuration's authorization service can be overridden. +* `default_authorizer_name` - (Optional) The name of the authorization service for a domain configuration. -## Attributes Reference +## Attribute Reference -In addition to all arguments above, the following attributes are exported: +This resource exports the following attributes in addition to the arguments above: -* `arn` - The ARN of the domain configuration.. +* `arn` - The ARN of the domain configuration. * `id` - The name of the created domain configuration. -* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block). \ No newline at end of file +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block). From 4246f613f236b487ea78f0f9adb51e3a98866540 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 08:10:52 -0400 Subject: [PATCH 14/31] r/aws_iot_domain_configuration: Add 'tls_config' attribute. --- internal/service/iot/domain_configuration.go | 66 ++++++++++++++++++- .../r/iot_domain_configuration.html.markdown | 7 +- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/internal/service/iot/domain_configuration.go b/internal/service/iot/domain_configuration.go index aef81880641b..9a0336311330 100644 --- a/internal/service/iot/domain_configuration.go +++ b/internal/service/iot/domain_configuration.go @@ -89,6 +89,19 @@ func ResourceDomainConfiguration() *schema.Resource { }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), + "tls_config": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "security_policy": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, "validation_certificate_arn": { Type: schema.TypeString, Optional: true, @@ -125,7 +138,9 @@ func resourceDomainConfigurationCreate(ctx context.Context, d *schema.ResourceDa input.ServiceType = aws.String(v.(string)) } - // TODO tls_config + if v, ok := d.GetOk("tls_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.TlsConfig = expandTlsConfig(v.([]interface{})[0].(map[string]interface{})) + } if v, ok := d.GetOk("validation_certificate_arn"); ok { input.ValidationCertificateArn = aws.String(v.(string)) @@ -166,6 +181,13 @@ func resourceDomainConfigurationRead(ctx context.Context, d *schema.ResourceData // TODO server_certificate_arns // TODO service_type d.Set("status", output.DomainConfigurationStatus) + if output.TlsConfig != nil { + if err := d.Set("tls_config", []interface{}{flattenTlsConfig(output.TlsConfig)}); err != nil { + return diag.Errorf("setting tls_config: %s", err) + } + } else { + d.Set("tls_config", nil) + } // TODO validation_certificate_arn return nil @@ -175,7 +197,7 @@ func resourceDomainConfigurationUpdate(ctx context.Context, d *schema.ResourceDa var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IoTConn(ctx) - if d.HasChange("authorizer_config") { + if d.HasChangesExcept("tags", "tags_all") { input := &iot.UpdateDomainConfigurationInput{ DomainConfigurationName: aws.String(d.Id()), } @@ -183,6 +205,18 @@ func resourceDomainConfigurationUpdate(ctx context.Context, d *schema.ResourceDa if d.HasChange("authorizer_config") { if v, ok := d.GetOk("authorizer_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { input.AuthorizerConfig = expandAuthorizerConfig(v.([]interface{})[0].(map[string]interface{})) + } else { + input.RemoveAuthorizerConfig = aws.Bool(true) + } + } + + if d.HasChange("status") { + input.DomainConfigurationStatus = aws.String(d.Get("status").(string)) + } + + if d.HasChange("tls_config") { + if v, ok := d.GetOk("tls_config"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.TlsConfig = expandTlsConfig(v.([]interface{})[0].(map[string]interface{})) } } @@ -266,3 +300,31 @@ func expandAuthorizerConfig(tfMap map[string]interface{}) *iot.AuthorizerConfig return apiObject } + +func expandTlsConfig(tfMap map[string]interface{}) *iot.TlsConfig { + if tfMap == nil { + return nil + } + + apiObject := &iot.TlsConfig{} + + if v, ok := tfMap["security_policy"].(string); ok && v != "" { + apiObject.SecurityPolicy = aws.String(v) + } + + return apiObject +} + +func flattenTlsConfig(apiObject *iot.TlsConfig) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.SecurityPolicy; v != nil { + tfMap["security_policy"] = aws.StringValue(v) + } + + return tfMap +} diff --git a/website/docs/r/iot_domain_configuration.html.markdown b/website/docs/r/iot_domain_configuration.html.markdown index b73d98a47947..4697ba25d83d 100644 --- a/website/docs/r/iot_domain_configuration.html.markdown +++ b/website/docs/r/iot_domain_configuration.html.markdown @@ -26,12 +26,13 @@ resource "aws_iot_domain_configuration" "iot" { ## Argument Reference -* `authorizer_config` - (Optional) an object that specifies the authorization service for a domain. See Below. +* `authorizer_config` - (Optional) An object that specifies the authorization service for a domain. See below. * `domain_name` - (Required) Fully-qualified domain name. * `name` - (Required) The name of the domain configuration. This value must be unique to a region. * `server_certificate_arns` - (Optional) The ARNs of the certificates that IoT passes to the device during the TLS handshake. Currently you can specify only one certificate ARN. This value is not required for Amazon Web Services-managed domains. When using a custom `domain_name`, the cert must include it. * `service_type` - (Optional) The type of service delivered by the endpoint. Note: Amazon Web Services IoT Core currently supports only the `DATA` service type. * `tags` - (Optional) Map of tags to assign to this resource. If configured with a provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. +* `tls_config` - (Optional) An object that specifies the TLS configuration for a domain. See below. * `validation_certificate_arn` - (Optional) The certificate used to validate the server certificate and prove domain name ownership. This certificate must be signed by a public certificate authority. This value is not required for Amazon Web Services-managed domains. ### authorizer_config @@ -39,6 +40,10 @@ resource "aws_iot_domain_configuration" "iot" { * `allow_authorizer_override` - (Optional) A Boolean that specifies whether the domain configuration's authorization service can be overridden. * `default_authorizer_name` - (Optional) The name of the authorization service for a domain configuration. +### tls_config + +* `security_policy` - (Optional) The security policy for a domain configuration. + ## Attribute Reference This resource exports the following attributes in addition to the arguments above: From 40e4d7afe61d8c398de1b4ea1e580d843746b7ed Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 08:22:08 -0400 Subject: [PATCH 15/31] r/aws_iot_domain_configuration: Add 'domain_type' attribute. --- internal/service/iot/domain_configuration.go | 41 ++++++++++++++++--- .../r/iot_domain_configuration.html.markdown | 1 + 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/internal/service/iot/domain_configuration.go b/internal/service/iot/domain_configuration.go index 9a0336311330..a4937d0f5def 100644 --- a/internal/service/iot/domain_configuration.go +++ b/internal/service/iot/domain_configuration.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -60,6 +61,10 @@ func ResourceDomainConfiguration() *schema.Resource { Required: true, ForceNew: true, }, + "domain_type": { + Type: schema.TypeString, + Computed: true, + }, "name": { Type: schema.TypeString, Required: true, @@ -174,12 +179,20 @@ func resourceDomainConfigurationRead(ctx context.Context, d *schema.ResourceData } d.Set("arn", output.DomainConfigurationArn) - // TODO authorizer_config + if output.AuthorizerConfig != nil { + if err := d.Set("authorizer_config", []interface{}{flattenAuthorizerConfig(output.AuthorizerConfig)}); err != nil { + return diag.Errorf("setting authorizer_config: %s", err) + } + } else { + d.Set("authorizer_config", nil) + } d.Set("domain_name", output.DomainName) - // TODO domain_type + d.Set("domain_type", output.DomainType) d.Set("name", output.DomainConfigurationName) - // TODO server_certificate_arns - // TODO service_type + d.Set("server_certificate_arns", tfslices.ApplyToAll(output.ServerCertificates, func(v *iot.ServerCertificateSummary) string { + return aws.StringValue(v.ServerCertificateArn) + })) + d.Set("service_type", output.ServiceType) d.Set("status", output.DomainConfigurationStatus) if output.TlsConfig != nil { if err := d.Set("tls_config", []interface{}{flattenTlsConfig(output.TlsConfig)}); err != nil { @@ -188,7 +201,7 @@ func resourceDomainConfigurationRead(ctx context.Context, d *schema.ResourceData } else { d.Set("tls_config", nil) } - // TODO validation_certificate_arn + d.Set("validation_certificate_arn", d.Get("validation_certificate_arn")) return nil } @@ -315,6 +328,24 @@ func expandTlsConfig(tfMap map[string]interface{}) *iot.TlsConfig { return apiObject } +func flattenAuthorizerConfig(apiObject *iot.AuthorizerConfig) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.AllowAuthorizerOverride; v != nil { + tfMap["allow_authorizer_override"] = aws.BoolValue(v) + } + + if v := apiObject.DefaultAuthorizerName; v != nil { + tfMap["default_authorizer_name"] = aws.StringValue(v) + } + + return tfMap +} + func flattenTlsConfig(apiObject *iot.TlsConfig) map[string]interface{} { if apiObject == nil { return nil diff --git a/website/docs/r/iot_domain_configuration.html.markdown b/website/docs/r/iot_domain_configuration.html.markdown index 4697ba25d83d..20a665f567bf 100644 --- a/website/docs/r/iot_domain_configuration.html.markdown +++ b/website/docs/r/iot_domain_configuration.html.markdown @@ -49,5 +49,6 @@ resource "aws_iot_domain_configuration" "iot" { This resource exports the following attributes in addition to the arguments above: * `arn` - The ARN of the domain configuration. +* `domain_type` - The type of the domain. * `id` - The name of the created domain configuration. * `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block). From 009a95a05b48212d049090f5b78710d8904f3606 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 08:50:38 -0400 Subject: [PATCH 16/31] r/aws_iot_domain_configuration: 'domain_name' is Optional. --- internal/service/iot/domain_configuration.go | 2 +- .../service/iot/domain_configuration_test.go | 21 ++++++++++++------- .../r/iot_domain_configuration.html.markdown | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/internal/service/iot/domain_configuration.go b/internal/service/iot/domain_configuration.go index a4937d0f5def..566699858614 100644 --- a/internal/service/iot/domain_configuration.go +++ b/internal/service/iot/domain_configuration.go @@ -58,7 +58,7 @@ func ResourceDomainConfiguration() *schema.Resource { }, "domain_name": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, }, "domain_type": { diff --git a/internal/service/iot/domain_configuration_test.go b/internal/service/iot/domain_configuration_test.go index af5cd91a8728..10fb0d3a6e8b 100644 --- a/internal/service/iot/domain_configuration_test.go +++ b/internal/service/iot/domain_configuration_test.go @@ -21,7 +21,6 @@ import ( func TestAccIoTDomainConfiguration_basic(t *testing.T) { ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - domainName := acctest.RandomDomainName() resourceName := "aws_iot_domain_configuration.test" resource.Test(t, resource.TestCase{ @@ -31,10 +30,20 @@ func TestAccIoTDomainConfiguration_basic(t *testing.T) { CheckDestroy: testAccCheckDomainConfigurationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccDomainConfigurationConfig_basic(rName, domainName), + Config: testAccDomainConfigurationConfig_basic(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckDomainConfigurationExists(ctx, resourceName), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "authorizer_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "domain_name", ""), + resource.TestCheckResourceAttr(resourceName, "domain_type", "AWS_MANAGED"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "server_certificate_arns.#", "0"), + resource.TestCheckResourceAttr(resourceName, "service_type", "DATA"), resource.TestCheckResourceAttr(resourceName, "status", "ENABLED"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tls_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "validation_certificate_arn", ""), ), }, }, @@ -82,12 +91,10 @@ func testAccCheckDomainConfigurationDestroy(ctx context.Context) resource.TestCh } } -func testAccDomainConfigurationConfig_basic(rName, domainName string) string { +func testAccDomainConfigurationConfig_basic(rName string) string { return fmt.Sprintf(` resource "aws_iot_domain_configuration" "test" { - name = %[1]q - domain_name = %[2]q - service_type = "DATA" + name = %[1]q } -`, rName, domainName) +`, rName) } diff --git a/website/docs/r/iot_domain_configuration.html.markdown b/website/docs/r/iot_domain_configuration.html.markdown index 20a665f567bf..63ca2a8081ac 100644 --- a/website/docs/r/iot_domain_configuration.html.markdown +++ b/website/docs/r/iot_domain_configuration.html.markdown @@ -27,7 +27,7 @@ resource "aws_iot_domain_configuration" "iot" { ## Argument Reference * `authorizer_config` - (Optional) An object that specifies the authorization service for a domain. See below. -* `domain_name` - (Required) Fully-qualified domain name. +* `domain_name` - (Optional) Fully-qualified domain name. * `name` - (Required) The name of the domain configuration. This value must be unique to a region. * `server_certificate_arns` - (Optional) The ARNs of the certificates that IoT passes to the device during the TLS handshake. Currently you can specify only one certificate ARN. This value is not required for Amazon Web Services-managed domains. When using a custom `domain_name`, the cert must include it. * `service_type` - (Optional) The type of service delivered by the endpoint. Note: Amazon Web Services IoT Core currently supports only the `DATA` service type. From 137f45c4adb8441ca55d052e8aa4fab6fea436bf Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 09:39:10 -0400 Subject: [PATCH 17/31] r/aws_iot_domain_configuration: Add sweeper. --- internal/service/iot/sweep.go | 358 +++++++++++++++++++--------------- 1 file changed, 201 insertions(+), 157 deletions(-) diff --git a/internal/service/iot/sweep.go b/internal/service/iot/sweep.go index 2b6159d80dda..9331fb134bdd 100644 --- a/internal/service/iot/sweep.go +++ b/internal/service/iot/sweep.go @@ -6,10 +6,10 @@ package iot import ( "fmt" "log" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iot" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/sweep" @@ -19,7 +19,7 @@ import ( func RegisterSweepers() { resource.AddTestSweepers("aws_iot_certificate", &resource.Sweeper{ Name: "aws_iot_certificate", - F: sweepCertifcates, + F: sweepCertificates, Dependencies: []string{ "aws_iot_policy_attachment", "aws_iot_thing_principal_attachment", @@ -76,32 +76,32 @@ func RegisterSweepers() { Name: "aws_iot_topic_rule_destination", F: sweepTopicRuleDestinations, }) + + resource.AddTestSweepers("aws_iot_domain_configuration", &resource.Sweeper{ + Name: "aws_iot_domain_configuration", + F: sweepDomainConfigurations, + }) } -func sweepCertifcates(region string) error { +func sweepCertificates(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) - if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.IoTConn(ctx) - sweepResources := make([]sweep.Sweepable, 0) - var errs *multierror.Error - input := &iot.ListCertificatesInput{} + sweepResources := make([]sweep.Sweepable, 0) err = conn.ListCertificatesPagesWithContext(ctx, input, func(page *iot.ListCertificatesOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, certificate := range page.Certificates { + for _, v := range page.Certificates { r := ResourceCertificate() d := r.Data(nil) - - d.SetId(aws.StringValue(certificate.CertificateId)) + d.SetId(aws.StringValue(v.CertificateId)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } @@ -109,44 +109,44 @@ func sweepCertifcates(region string) error { return !lastPage }) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("error listing IoT Certificate for %s: %w", region, err)) + if awsv1.SkipSweepError(err) { + log.Printf("[WARN] Skipping IoT Certificate sweep for %s: %s", region, err) + return nil } - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - errs = multierror.Append(errs, fmt.Errorf("error sweeping IoT Certificate for %s: %w", region, err)) + if err != nil { + return fmt.Errorf("error listing IoT Certificates (%s): %w", region, err) } - if awsv1.SkipSweepError(errs.ErrorOrNil()) { - log.Printf("[WARN] Skipping IoT Certificate sweep for %s: %s", region, errs) - return nil + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping IoT Certificates (%s): %w", region, err) } - return errs.ErrorOrNil() + return nil } func sweepPolicyAttachments(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) - if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.IoTConn(ctx) - sweepResources := make([]sweep.Sweepable, 0) - var errs *multierror.Error - input := &iot.ListPoliciesInput{} + sweepResources := make([]sweep.Sweepable, 0) + var sweeperErrs *multierror.Error err = conn.ListPoliciesPagesWithContext(ctx, input, func(page *iot.ListPoliciesOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, policy := range page.Policies { + for _, v := range page.Policies { + policyName := aws.StringValue(v.PolicyName) input := &iot.ListTargetsForPolicyInput{ - PolicyName: policy.PolicyName, + PolicyName: aws.String(policyName), } err := conn.ListTargetsForPolicyPagesWithContext(ctx, input, func(page *iot.ListTargetsForPolicyOutput, lastPage bool) bool { @@ -154,13 +154,12 @@ func sweepPolicyAttachments(region string) error { return !lastPage } - for _, target := range page.Targets { + for _, v := range page.Targets { r := ResourcePolicyAttachment() d := r.Data(nil) - - d.SetId(fmt.Sprintf("%s|%s", aws.StringValue(policy.PolicyName), aws.StringValue(target))) - d.Set("policy", policy.PolicyName) - d.Set("target", target) + d.SetId(fmt.Sprintf("%s|%s", policyName, aws.StringValue(v))) + d.Set("policy", policyName) + d.Set("target", v) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } @@ -168,54 +167,55 @@ func sweepPolicyAttachments(region string) error { return !lastPage }) + if awsv1.SkipSweepError(err) { + continue + } + if err != nil { - errs = multierror.Append(errs, fmt.Errorf("error listing IoT Policy Attachment for %s: %w", region, err)) + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing IoT Targets For Policy (%s): %w", region, err)) } } return !lastPage }) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("error listing IoT Policy Attachment for %s: %w", region, err)) + if awsv1.SkipSweepError(err) { + log.Printf("[WARN] Skipping IoT Policy Attachment sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors } - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - errs = multierror.Append(errs, fmt.Errorf("error sweeping IoT Policy Attachment for %s: %w", region, err)) + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing IoT Policies (%s): %w", region, err)) } - if awsv1.SkipSweepError(errs.ErrorOrNil()) { - log.Printf("[WARN] Skipping IoT Policy Attachment sweep for %s: %s", region, errs) - return nil + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping IoT Policy Attachments (%s): %w", region, err)) } - return errs.ErrorOrNil() + return sweeperErrs.ErrorOrNil() } func sweepPolicies(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) - if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.IoTConn(ctx) - sweepResources := make([]sweep.Sweepable, 0) - var errs *multierror.Error - input := &iot.ListPoliciesInput{} + sweepResources := make([]sweep.Sweepable, 0) err = conn.ListPoliciesPagesWithContext(ctx, input, func(page *iot.ListPoliciesOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, policy := range page.Policies { + for _, v := range page.Policies { r := ResourcePolicy() d := r.Data(nil) - - d.SetId(aws.StringValue(policy.PolicyName)) + d.SetId(aws.StringValue(v.PolicyName)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } @@ -223,46 +223,43 @@ func sweepPolicies(region string) error { return !lastPage }) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("error listing IoT Policy for %s: %w", region, err)) + if awsv1.SkipSweepError(err) { + log.Printf("[WARN] Skipping IoT Policy sweep for %s: %s", region, err) + return nil } - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - errs = multierror.Append(errs, fmt.Errorf("error sweeping IoT Policy for %s: %w", region, err)) + if err != nil { + return fmt.Errorf("error listing IoT Policies (%s): %w", region, err) } - if awsv1.SkipSweepError(errs.ErrorOrNil()) { - log.Printf("[WARN] Skipping IoT Policy sweep for %s: %s", region, errs) - return nil + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping IoT Policies (%s): %w", region, err) } - return errs.ErrorOrNil() + return nil } func sweepRoleAliases(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) - if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.IoTConn(ctx) - sweepResources := make([]sweep.Sweepable, 0) - var errs *multierror.Error - input := &iot.ListRoleAliasesInput{} + sweepResources := make([]sweep.Sweepable, 0) err = conn.ListRoleAliasesPagesWithContext(ctx, input, func(page *iot.ListRoleAliasesOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, roleAlias := range page.RoleAliases { + for _, v := range page.RoleAliases { r := ResourceRoleAlias() d := r.Data(nil) - - d.SetId(aws.StringValue(roleAlias)) + d.SetId(aws.StringValue(v)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } @@ -270,44 +267,44 @@ func sweepRoleAliases(region string) error { return !lastPage }) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("error listing IoT Role Alias for %s: %w", region, err)) + if awsv1.SkipSweepError(err) { + log.Printf("[WARN] Skipping Role Alias sweep for %s: %s", region, err) + return nil } - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - errs = multierror.Append(errs, fmt.Errorf("error sweeping IoT Role Alias for %s: %w", region, err)) + if err != nil { + return fmt.Errorf("error listing Role Aliases (%s): %w", region, err) } - if awsv1.SkipSweepError(errs.ErrorOrNil()) { - log.Printf("[WARN] Skipping IoT Role Alias sweep for %s: %s", region, errs) - return nil + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping IoT Role Aliases (%s): %w", region, err) } - return errs.ErrorOrNil() + return nil } func sweepThingPrincipalAttachments(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) - if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.IoTConn(ctx) - sweepResources := make([]sweep.Sweepable, 0) - var errs *multierror.Error - input := &iot.ListThingsInput{} + sweepResources := make([]sweep.Sweepable, 0) + var sweeperErrs *multierror.Error err = conn.ListThingsPagesWithContext(ctx, input, func(page *iot.ListThingsOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, thing := range page.Things { + for _, v := range page.Things { + thingName := aws.StringValue(v.ThingName) input := &iot.ListThingPrincipalsInput{ - ThingName: thing.ThingName, + ThingName: aws.String(thingName), } err := conn.ListThingPrincipalsPagesWithContext(ctx, input, func(page *iot.ListThingPrincipalsOutput, lastPage bool) bool { @@ -315,13 +312,12 @@ func sweepThingPrincipalAttachments(region string) error { return !lastPage } - for _, principal := range page.Principals { + for _, v := range page.Principals { r := ResourceThingPrincipalAttachment() d := r.Data(nil) - - d.SetId(fmt.Sprintf("%s|%s", aws.StringValue(thing.ThingName), aws.StringValue(principal))) - d.Set("principal", principal) - d.Set("thing", thing.ThingName) + d.SetId(fmt.Sprintf("%s|%s", thingName, aws.StringValue(v))) + d.Set("principal", v) + d.Set("thing", thingName) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } @@ -329,54 +325,55 @@ func sweepThingPrincipalAttachments(region string) error { return !lastPage }) + if awsv1.SkipSweepError(err) { + continue + } + if err != nil { - errs = multierror.Append(errs, fmt.Errorf("error listing IoT Thing Principal Attachment for %s: %w", region, err)) + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing IoT Thing Principals (%s): %w", region, err)) } } return !lastPage }) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("error listing IoT Thing Principal Attachment for %s: %w", region, err)) + if awsv1.SkipSweepError(err) { + log.Printf("[WARN] Skipping IoT Thing Principal Attachment sweep for %s: %s", region, err) + return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors } - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - errs = multierror.Append(errs, fmt.Errorf("error sweeping IoT Thing Principal Attachment for %s: %w", region, err)) + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing IoT Things (%s): %w", region, err)) } - if awsv1.SkipSweepError(errs.ErrorOrNil()) { - log.Printf("[WARN] Skipping IoT Thing Principal Attachment sweep for %s: %s", region, errs) - return nil + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping IoT Thing Principal Attachments (%s): %w", region, err)) } - return errs.ErrorOrNil() + return sweeperErrs.ErrorOrNil() } func sweepThings(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) - if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.IoTConn(ctx) - sweepResources := make([]sweep.Sweepable, 0) - var errs *multierror.Error - input := &iot.ListThingsInput{} + sweepResources := make([]sweep.Sweepable, 0) err = conn.ListThingsPagesWithContext(ctx, input, func(page *iot.ListThingsOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, thing := range page.Things { + for _, v := range page.Things { r := ResourceThing() d := r.Data(nil) - - d.SetId(aws.StringValue(thing.ThingName)) + d.SetId(aws.StringValue(v.ThingName)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } @@ -384,46 +381,43 @@ func sweepThings(region string) error { return !lastPage }) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("error listing IoT Thing for %s: %w", region, err)) + if awsv1.SkipSweepError(err) { + log.Printf("[WARN] Skipping IoT Thing sweep for %s: %s", region, err) + return nil } - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - errs = multierror.Append(errs, fmt.Errorf("error sweeping IoT Thing for %s: %w", region, err)) + if err != nil { + return fmt.Errorf("error listing IoT Things (%s): %w", region, err) } - if awsv1.SkipSweepError(errs.ErrorOrNil()) { - log.Printf("[WARN] Skipping IoT Thing sweep for %s: %s", region, errs) - return nil + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping IoT Things (%s): %w", region, err) } - return errs.ErrorOrNil() + return nil } func sweepThingTypes(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) - if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.IoTConn(ctx) - sweepResources := make([]sweep.Sweepable, 0) - var errs *multierror.Error - input := &iot.ListThingTypesInput{} + sweepResources := make([]sweep.Sweepable, 0) err = conn.ListThingTypesPagesWithContext(ctx, input, func(page *iot.ListThingTypesOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, thingTypes := range page.ThingTypes { + for _, v := range page.ThingTypes { r := ResourceThingType() d := r.Data(nil) - - d.SetId(aws.StringValue(thingTypes.ThingTypeName)) + d.SetId(aws.StringValue(v.ThingTypeName)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } @@ -431,20 +425,22 @@ func sweepThingTypes(region string) error { return !lastPage }) - if err != nil { - errs = multierror.Append(errs, fmt.Errorf("error listing IoT Thing Type for %s: %w", region, err)) + if awsv1.SkipSweepError(err) { + log.Printf("[WARN] Skipping IoT Thing Type sweep for %s: %s", region, err) + return nil } - if err := sweep.SweepOrchestrator(ctx, sweepResources); err != nil { - errs = multierror.Append(errs, fmt.Errorf("error sweeping IoT Thing Type for %s: %w", region, err)) + if err != nil { + return fmt.Errorf("error listing IoT Thing Types (%s): %w", region, err) } - if awsv1.SkipSweepError(errs.ErrorOrNil()) { - log.Printf("[WARN] Skipping IoT Thing Type sweep for %s: %s", region, errs) - return nil + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping IoT Thing Types (%s): %w", region, err) } - return errs.ErrorOrNil() + return nil } func sweepTopicRules(region string) error { @@ -455,44 +451,40 @@ func sweepTopicRules(region string) error { } conn := client.IoTConn(ctx) input := &iot.ListTopicRulesInput{} - var sweeperErrs *multierror.Error + sweepResources := make([]sweep.Sweepable, 0) - for { - output, err := conn.ListTopicRulesWithContext(ctx, input) - if awsv1.SkipSweepError(err) { - log.Printf("[WARN] Skipping IoT Topic Rules sweep for %s: %s", region, err) - return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors - } - if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving IoT Topic Rules: %w", err)) - return sweeperErrs + err = conn.ListTopicRulesPagesWithContext(ctx, input, func(page *iot.ListTopicRulesOutput, lastPage bool) bool { + if page == nil { + return !lastPage } - for _, rule := range output.Rules { - name := aws.StringValue(rule.RuleName) + for _, v := range page.Rules { + r := ResourceTopicRule() + d := r.Data(nil) + d.SetId(aws.StringValue(v.RuleName)) - log.Printf("[INFO] Deleting IoT Topic Rule: %s", name) - _, err := conn.DeleteTopicRuleWithContext(ctx, &iot.DeleteTopicRuleInput{ - RuleName: aws.String(name), - }) - if tfawserr.ErrCodeEquals(err, iot.ErrCodeUnauthorizedException) { - continue - } - if err != nil { - sweeperErr := fmt.Errorf("error deleting IoT Topic Rule (%s): %w", name, err) - log.Printf("[ERROR] %s", sweeperErr) - sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) - continue - } + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } - if aws.StringValue(output.NextToken) == "" { - break - } - input.NextToken = output.NextToken + return !lastPage + }) + + if awsv1.SkipSweepError(err) { + log.Printf("[WARN] Skipping IoT Topic Rule sweep for %s: %s", region, err) + return nil } - return sweeperErrs.ErrorOrNil() + if err != nil { + return fmt.Errorf("error listing IoT Topic Rules (%s): %w", region, err) + } + + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping IoT Topic Rules (%s): %w", region, err) + } + + return nil } func sweepThingGroups(region string) error { @@ -510,10 +502,10 @@ func sweepThingGroups(region string) error { return !lastPage } - for _, group := range page.ThingGroups { + for _, v := range page.ThingGroups { r := ResourceThingGroup() d := r.Data(nil) - d.SetId(aws.StringValue(group.GroupName)) + d.SetId(aws.StringValue(v.GroupName)) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } @@ -582,3 +574,55 @@ func sweepTopicRuleDestinations(region string) error { return nil } + +func sweepDomainConfigurations(region string) error { + ctx := sweep.Context(region) + client, err := sweep.SharedRegionalSweepClient(ctx, region) + if err != nil { + return fmt.Errorf("error getting client: %w", err) + } + conn := client.IoTConn(ctx) + input := &iot.ListDomainConfigurationsInput{} + sweepResources := make([]sweep.Sweepable, 0) + + err = conn.ListDomainConfigurationsPagesWithContext(ctx, input, func(page *iot.ListDomainConfigurationsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.DomainConfigurations { + name := aws.StringValue(v.DomainConfigurationName) + + if strings.HasPrefix(name, "iot:") { + log.Printf("[INFO] Skipping IoT Domain Configuration %s", name) + continue + } + + r := ResourceDomainConfiguration() + d := r.Data(nil) + d.SetId(name) + d.Set("status", iot.DomainConfigurationStatusEnabled) + + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) + } + + return !lastPage + }) + + if awsv1.SkipSweepError(err) { + log.Printf("[WARN] Skipping IoT Domain Configuration sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing IoT Domain Configurations (%s): %w", region, err) + } + + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping IoT Domain Configurations (%s): %w", region, err) + } + + return nil +} From da652b3ad205ac082ec4914b9f5c09b2475f5a0f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 11:13:32 -0400 Subject: [PATCH 18/31] Add 'acctest.SkipIfEnvVarNotSet'. --- internal/acctest/acctest.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/internal/acctest/acctest.go b/internal/acctest/acctest.go index 25e0073293b5..b4674aaa9d0a 100644 --- a/internal/acctest/acctest.go +++ b/internal/acctest/acctest.go @@ -2442,6 +2442,16 @@ func CheckResourceAttrIsJSONString(n, key string) resource.TestCheckFunc { }) } +// SkipIfEnvVarNotSet skips the current test if the specified environment variable is not set. +// The variable's value is returned. +func SkipIfEnvVarNotSet(t *testing.T, key string) string { + v := os.Getenv(key) + if v == "" { + t.Skipf("Environment variable %s is not set, skipping test", key) + } + return v +} + // RunSerialTests1Level runs test cases in parallel, optionally sleeping between each. func RunSerialTests1Level(t *testing.T, testCases map[string]func(t *testing.T), d time.Duration) { t.Helper() From ceafffc68048be560eb0dd2a50928eb059900aa6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 11:14:21 -0400 Subject: [PATCH 19/31] r/aws_iot_domain_configuration: 'tls_config' is Optional+Computed. --- internal/service/iot/domain_configuration.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/service/iot/domain_configuration.go b/internal/service/iot/domain_configuration.go index 566699858614..63bd6fc654d6 100644 --- a/internal/service/iot/domain_configuration.go +++ b/internal/service/iot/domain_configuration.go @@ -97,12 +97,14 @@ func ResourceDomainConfiguration() *schema.Resource { "tls_config": { Type: schema.TypeList, Optional: true, + Computed: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "security_policy": { Type: schema.TypeString, Optional: true, + Computed: true, }, }, }, From a86aaf58f8f2afdd0d77500c508b8f57254bbc93 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 11:14:46 -0400 Subject: [PATCH 20/31] 'TestAccIoTDomainConfiguration_basic' -> 'TestAccIoTDomainConfiguration_awsManaged'. --- internal/service/iot/domain_configuration_test.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/internal/service/iot/domain_configuration_test.go b/internal/service/iot/domain_configuration_test.go index 10fb0d3a6e8b..85454194d61a 100644 --- a/internal/service/iot/domain_configuration_test.go +++ b/internal/service/iot/domain_configuration_test.go @@ -18,31 +18,34 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -func TestAccIoTDomainConfiguration_basic(t *testing.T) { +func TestAccIoTDomainConfiguration_awsManaged(t *testing.T) { ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_iot_domain_configuration.test" + acctest.SkipIfEnvVarNotSet(t, "IOT_DOMAIN_CONFIGURATION_TEST_AWS_MANAGED") + resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, iot.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckDomainConfigurationDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { - Config: testAccDomainConfigurationConfig_basic(rName), + Config: testAccDomainConfigurationConfig_awsManaged(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckDomainConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttrSet(resourceName, "arn"), resource.TestCheckResourceAttr(resourceName, "authorizer_config.#", "0"), - resource.TestCheckResourceAttr(resourceName, "domain_name", ""), + resource.TestCheckResourceAttrSet(resourceName, "domain_name"), resource.TestCheckResourceAttr(resourceName, "domain_type", "AWS_MANAGED"), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "server_certificate_arns.#", "0"), resource.TestCheckResourceAttr(resourceName, "service_type", "DATA"), resource.TestCheckResourceAttr(resourceName, "status", "ENABLED"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), - resource.TestCheckResourceAttr(resourceName, "tls_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tls_config.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "tls_config.0.security_policy"), resource.TestCheckResourceAttr(resourceName, "validation_certificate_arn", ""), ), }, @@ -91,7 +94,7 @@ func testAccCheckDomainConfigurationDestroy(ctx context.Context) resource.TestCh } } -func testAccDomainConfigurationConfig_basic(rName string) string { +func testAccDomainConfigurationConfig_awsManaged(rName string) string { return fmt.Sprintf(` resource "aws_iot_domain_configuration" "test" { name = %[1]q From c4f522b9d58946d258d5ea0bba4f9801cfb0301a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 11:22:53 -0400 Subject: [PATCH 21/31] r/aws_iot_authorizer: Add sweeper. --- internal/service/iot/sweep.go | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/internal/service/iot/sweep.go b/internal/service/iot/sweep.go index 9331fb134bdd..0c55debb8ef1 100644 --- a/internal/service/iot/sweep.go +++ b/internal/service/iot/sweep.go @@ -77,6 +77,12 @@ func RegisterSweepers() { F: sweepTopicRuleDestinations, }) + resource.AddTestSweepers("aws_iot_authorizer", &resource.Sweeper{ + Name: "aws_iot_authorizer", + F: sweepAuthorizers, + Dependencies: []string{"aws_iot_domain_configuration"}, + }) + resource.AddTestSweepers("aws_iot_domain_configuration", &resource.Sweeper{ Name: "aws_iot_domain_configuration", F: sweepDomainConfigurations, @@ -575,6 +581,51 @@ func sweepTopicRuleDestinations(region string) error { return nil } +func sweepAuthorizers(region string) error { + ctx := sweep.Context(region) + client, err := sweep.SharedRegionalSweepClient(ctx, region) + if err != nil { + return fmt.Errorf("error getting client: %w", err) + } + conn := client.IoTConn(ctx) + input := &iot.ListAuthorizersInput{} + sweepResources := make([]sweep.Sweepable, 0) + + err = conn.ListAuthorizersPagesWithContext(ctx, input, func(page *iot.ListAuthorizersOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.Authorizers { + r := ResourceAuthorizer() + d := r.Data(nil) + d.SetId(aws.StringValue(v.AuthorizerName)) + d.Set("status", iot.AuthorizerStatusActive) + + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) + } + + return !lastPage + }) + + if awsv1.SkipSweepError(err) { + log.Printf("[WARN] Skipping IoT Authorizer sweep for %s: %s", region, err) + return nil + } + + if err != nil { + return fmt.Errorf("error listing IoT Authorizers (%s): %w", region, err) + } + + err = sweep.SweepOrchestrator(ctx, sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping IoT Authorizers (%s): %w", region, err) + } + + return nil +} + func sweepDomainConfigurations(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(ctx, region) From 5ddd71de51cfbbc7cad77f2334f341d7f31fc698 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 11:28:09 -0400 Subject: [PATCH 22/31] sweepers: aws_acm_certificate has dependency on aws_iot_domain_configuration. --- internal/service/acm/sweep.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/service/acm/sweep.go b/internal/service/acm/sweep.go index 079d56bd9bc0..a33e72f960dd 100644 --- a/internal/service/acm/sweep.go +++ b/internal/service/acm/sweep.go @@ -32,6 +32,7 @@ func RegisterSweepers() { "aws_elb", "aws_iam_server_certificate", "aws_iam_signing_certificate", + "aws_iot_domain_configuration", "aws_lb", "aws_lb_listener", }, From 247071e1957abd63309431e9e28acc6b5c82a346 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 11:40:10 -0400 Subject: [PATCH 23/31] r/aws_iot_domain_configuration: Avoid 'AWS Managed Domain Configuration must be disabled for at least 7 days before it can be deleted' errors in sweeper. --- internal/service/iot/sweep.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/internal/service/iot/sweep.go b/internal/service/iot/sweep.go index 0c55debb8ef1..5fe51dedf784 100644 --- a/internal/service/iot/sweep.go +++ b/internal/service/iot/sweep.go @@ -7,6 +7,7 @@ import ( "fmt" "log" "strings" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iot" @@ -649,10 +650,25 @@ func sweepDomainConfigurations(region string) error { continue } + output, err := FindDomainConfigurationByName(ctx, conn, name) + + if err != nil { + log.Printf("[WARN] IoT Domain Configuration (%s): %s", name, err) + continue + } + + if aws.StringValue(output.DomainType) == iot.DomainTypeAwsManaged && aws.StringValue(output.DomainConfigurationStatus) == iot.DomainConfigurationStatusDisabled { + // AWS Managed Domain Configuration must be disabled for at least 7 days before it can be deleted. + if output.LastStatusChangeDate.After(time.Now().AddDate(0, 0, -7)) { + log.Printf("[INFO] Skipping IoT Domain Configuration %s", name) + continue + } + } + r := ResourceDomainConfiguration() d := r.Data(nil) d.SetId(name) - d.Set("status", iot.DomainConfigurationStatusEnabled) + d.Set("status", output.DomainConfigurationStatus) sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } From 746a784b7193437249a057b7ab8fda3adc258ec7 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 13:01:55 -0400 Subject: [PATCH 24/31] Add 'TestAccIoTDomainConfiguration_basic'. --- .../service/iot/domain_configuration_test.go | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/internal/service/iot/domain_configuration_test.go b/internal/service/iot/domain_configuration_test.go index 85454194d61a..eddb2905c1aa 100644 --- a/internal/service/iot/domain_configuration_test.go +++ b/internal/service/iot/domain_configuration_test.go @@ -18,6 +18,40 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) +func TestAccIoTDomainConfiguration_basic(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + rootDomain := acctest.ACMCertificateDomainFromEnv(t) + domain := acctest.ACMCertificateRandomSubDomain(rootDomain) + resourceName := "aws_iot_domain_configuration.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iot.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDomainConfigurationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDomainConfigurationConfig_basic(rName, rootDomain, domain), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckDomainConfigurationExists(ctx, resourceName), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "authorizer_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "domain_name", domain), + resource.TestCheckResourceAttr(resourceName, "domain_type", "CUSTOMER_MANAGED"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "server_certificate_arns.#", "1"), + resource.TestCheckResourceAttr(resourceName, "service_type", "DATA"), + resource.TestCheckResourceAttr(resourceName, "status", "ENABLED"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "tls_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "validation_certificate_arn", ""), + ), + }, + }, + }) +} + func TestAccIoTDomainConfiguration_awsManaged(t *testing.T) { ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -94,6 +128,43 @@ func testAccCheckDomainConfigurationDestroy(ctx context.Context) resource.TestCh } } +func testAccDomainConfigurationConfig_basic(rName, rootDomain, domain string) string { + return fmt.Sprintf(` +resource "aws_acm_certificate" "test" { + domain_name = %[3]q + validation_method = "DNS" +} + +data "aws_route53_zone" "test" { + name = %[2]q + private_zone = false +} + +resource "aws_route53_record" "test" { + allow_overwrite = true + name = tolist(aws_acm_certificate.test.domain_validation_options)[0].resource_record_name + records = [tolist(aws_acm_certificate.test.domain_validation_options)[0].resource_record_value] + ttl = 60 + type = tolist(aws_acm_certificate.test.domain_validation_options)[0].resource_record_type + zone_id = data.aws_route53_zone.test.zone_id +} + +resource "aws_acm_certificate_validation" "test" { + depends_on = [aws_route53_record.test] + + certificate_arn = aws_acm_certificate.test.arn +} + +resource "aws_iot_domain_configuration" "test" { + depends_on = [aws_acm_certificate_validation.test] + + name = %[1]q + domain_name = %[3]q + server_certificate_arns = [aws_acm_certificate.test.arn] +} +`, rName, rootDomain, domain) +} + func testAccDomainConfigurationConfig_awsManaged(rName string) string { return fmt.Sprintf(` resource "aws_iot_domain_configuration" "test" { From 53de5b84c687ceff8a25131f6c206d229c5ba574 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 13:02:21 -0400 Subject: [PATCH 25/31] Acceptance test output: % ACM_CERTIFICATE_ROOT_DOMAIN=terraform-provider-aws-acctest-acm.com make testacc TESTARGS='-run=TestAccIoTDomainConfiguration_basic' PKG=iot ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/iot/... -v -count 1 -parallel 20 -run=TestAccIoTDomainConfiguration_basic -timeout 360m === RUN TestAccIoTDomainConfiguration_basic --- PASS: TestAccIoTDomainConfiguration_basic (108.42s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/iot 113.441s From 1503d78f95b99473c2e21d37f2bdc2d26c60374b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 13:19:03 -0400 Subject: [PATCH 26/31] r/aws_iot_domain_configuration: Support import. --- internal/service/iot/domain_configuration.go | 4 ++++ .../service/iot/domain_configuration_test.go | 19 ++++++++++++++----- .../r/iot_domain_configuration.html.markdown | 18 ++++++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/internal/service/iot/domain_configuration.go b/internal/service/iot/domain_configuration.go index 63bd6fc654d6..2daf5cc1f44d 100644 --- a/internal/service/iot/domain_configuration.go +++ b/internal/service/iot/domain_configuration.go @@ -32,6 +32,10 @@ func ResourceDomainConfiguration() *schema.Resource { UpdateWithoutTimeout: resourceDomainConfigurationUpdate, DeleteWithoutTimeout: resourceDomainConfigurationDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + CustomizeDiff: verify.SetTagsDiff, Schema: map[string]*schema.Schema{ diff --git a/internal/service/iot/domain_configuration_test.go b/internal/service/iot/domain_configuration_test.go index eddb2905c1aa..5326753215e3 100644 --- a/internal/service/iot/domain_configuration_test.go +++ b/internal/service/iot/domain_configuration_test.go @@ -48,6 +48,11 @@ func TestAccIoTDomainConfiguration_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "validation_certificate_arn", ""), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -128,15 +133,15 @@ func testAccCheckDomainConfigurationDestroy(ctx context.Context) resource.TestCh } } -func testAccDomainConfigurationConfig_basic(rName, rootDomain, domain string) string { +func testAccDomainConfigurationConfig_base(rootDomain, domain string) string { return fmt.Sprintf(` resource "aws_acm_certificate" "test" { - domain_name = %[3]q + domain_name = %[2]q validation_method = "DNS" } data "aws_route53_zone" "test" { - name = %[2]q + name = %[1]q private_zone = false } @@ -154,15 +159,19 @@ resource "aws_acm_certificate_validation" "test" { certificate_arn = aws_acm_certificate.test.arn } +`, rootDomain, domain) +} +func testAccDomainConfigurationConfig_basic(rName, rootDomain, domain string) string { + return acctest.ConfigCompose(testAccDomainConfigurationConfig_base(rootDomain, domain), fmt.Sprintf(` resource "aws_iot_domain_configuration" "test" { depends_on = [aws_acm_certificate_validation.test] name = %[1]q - domain_name = %[3]q + domain_name = %[2]q server_certificate_arns = [aws_acm_certificate.test.arn] } -`, rName, rootDomain, domain) +`, rName, domain)) } func testAccDomainConfigurationConfig_awsManaged(rName string) string { diff --git a/website/docs/r/iot_domain_configuration.html.markdown b/website/docs/r/iot_domain_configuration.html.markdown index 63ca2a8081ac..10c421c1b642 100644 --- a/website/docs/r/iot_domain_configuration.html.markdown +++ b/website/docs/r/iot_domain_configuration.html.markdown @@ -52,3 +52,21 @@ This resource exports the following attributes in addition to the arguments abov * `domain_type` - The type of the domain. * `id` - The name of the created domain configuration. * `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#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 IOT domain configurations using the name. For example: + +```terraform +import { + to = aws_iot_domain_configuration.example + id = "example" +} +``` + +Using `terraform import`, import domain configurations using the name. For example: + +```console +% terraform import aws_iot_domain_configuration.example example +``` From 6728dc0ee18c41c668ae42dca1bf1256d165a529 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 14:24:15 -0400 Subject: [PATCH 27/31] r/aws_iot_domain_configuration: Additional acceptance tests. --- internal/service/iot/authorizer_test.go | 8 +- internal/service/iot/domain_configuration.go | 5 +- .../service/iot/domain_configuration_test.go | 170 ++++++++++++++++++ 3 files changed, 177 insertions(+), 6 deletions(-) diff --git a/internal/service/iot/authorizer_test.go b/internal/service/iot/authorizer_test.go index 68794778d685..bc0781043b00 100644 --- a/internal/service/iot/authorizer_test.go +++ b/internal/service/iot/authorizer_test.go @@ -206,7 +206,7 @@ func testAccCheckAuthorizerDestroy(ctx context.Context) resource.TestCheckFunc { } } -func testAccAuthorizerBaseConfig(rName string) string { +func testAccAuthorizerConfig_base(rName string) string { return fmt.Sprintf(` resource "aws_iam_role" "test" { name = %[1]q @@ -240,7 +240,7 @@ resource "aws_lambda_function" "test" { } func testAccAuthorizerConfig_basic(rName string) string { - return acctest.ConfigCompose(testAccAuthorizerBaseConfig(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccAuthorizerConfig_base(rName), fmt.Sprintf(` resource "aws_iot_authorizer" "test" { name = %[1]q authorizer_function_arn = aws_lambda_function.test.arn @@ -254,7 +254,7 @@ resource "aws_iot_authorizer" "test" { } func testAccAuthorizerConfig_updated(rName string) string { - return acctest.ConfigCompose(testAccAuthorizerBaseConfig(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccAuthorizerConfig_base(rName), fmt.Sprintf(` resource "aws_iot_authorizer" "test" { name = %[1]q authorizer_function_arn = aws_lambda_function.test.arn @@ -272,7 +272,7 @@ resource "aws_iot_authorizer" "test" { } func testAccAuthorizerConfig_signingDisabled(rName string) string { - return acctest.ConfigCompose(testAccAuthorizerBaseConfig(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccAuthorizerConfig_base(rName), fmt.Sprintf(` resource "aws_iot_authorizer" "test" { name = %[1]q authorizer_function_arn = aws_lambda_function.test.arn diff --git a/internal/service/iot/domain_configuration.go b/internal/service/iot/domain_configuration.go index 2daf5cc1f44d..5a7944e7c962 100644 --- a/internal/service/iot/domain_configuration.go +++ b/internal/service/iot/domain_configuration.go @@ -21,6 +21,7 @@ import ( tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" + "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_iot_domain_configuration", name="Domain Configuration") @@ -96,8 +97,8 @@ func ResourceDomainConfiguration() *schema.Resource { Default: iot.DomainConfigurationStatusEnabled, ValidateFunc: validation.StringInSlice(iot.DomainConfigurationStatus_Values(), false), }, - "tags": tftags.TagsSchema(), - "tags_all": tftags.TagsSchemaComputed(), + names.AttrTags: tftags.TagsSchema(), + names.AttrTagsAll: tftags.TagsSchemaComputed(), "tls_config": { Type: schema.TypeList, Optional: true, diff --git a/internal/service/iot/domain_configuration_test.go b/internal/service/iot/domain_configuration_test.go index 5326753215e3..83cbe58635f1 100644 --- a/internal/service/iot/domain_configuration_test.go +++ b/internal/service/iot/domain_configuration_test.go @@ -45,6 +45,7 @@ func TestAccIoTDomainConfiguration_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "status", "ENABLED"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), resource.TestCheckResourceAttr(resourceName, "tls_config.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "tls_config.0.security_policy"), resource.TestCheckResourceAttr(resourceName, "validation_certificate_arn", ""), ), }, @@ -57,6 +58,121 @@ func TestAccIoTDomainConfiguration_basic(t *testing.T) { }) } +func TestAccIoTDomainConfiguration_disappears(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + rootDomain := acctest.ACMCertificateDomainFromEnv(t) + domain := acctest.ACMCertificateRandomSubDomain(rootDomain) + resourceName := "aws_iot_domain_configuration.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iot.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDomainConfigurationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDomainConfigurationConfig_basic(rName, rootDomain, domain), + Check: resource.ComposeTestCheckFunc( + testAccCheckDomainConfigurationExists(ctx, resourceName), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiot.ResourceDomainConfiguration(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccIoTDomainConfiguration_tags(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + rootDomain := acctest.ACMCertificateDomainFromEnv(t) + domain := acctest.ACMCertificateRandomSubDomain(rootDomain) + resourceName := "aws_iot_domain_configuration.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iot.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDomainConfigurationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDomainConfigurationConfig_tags1(rName, rootDomain, domain, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckDomainConfigurationExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccDomainConfigurationConfig_tags2(rName, rootDomain, domain, "key2", "value2", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckDomainConfigurationExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccDomainConfigurationConfig_tags1(rName, rootDomain, domain, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckDomainConfigurationExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func TestAccIoTDomainConfiguration_update(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + rootDomain := acctest.ACMCertificateDomainFromEnv(t) + domain := acctest.ACMCertificateRandomSubDomain(rootDomain) + resourceName := "aws_iot_domain_configuration.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, iot.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDomainConfigurationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDomainConfigurationConfig_securityPolicy(rName, rootDomain, domain, "TLS12_1_2_2022_10", true), + Check: resource.ComposeTestCheckFunc( + testAccCheckDomainConfigurationExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "authorizer_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "authorizer_config.0.allow_authorizer_override", "true"), + resource.TestCheckResourceAttr(resourceName, "tls_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "tls_config.0.security_policy", "TLS12_1_2_2022_10"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccDomainConfigurationConfig_securityPolicy(rName, rootDomain, domain, "IoTSecurityPolicy_TLS13_1_2_2022_10", false), + Check: resource.ComposeTestCheckFunc( + testAccCheckDomainConfigurationExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "authorizer_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "authorizer_config.0.allow_authorizer_override", "false"), + resource.TestCheckResourceAttr(resourceName, "tls_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "tls_config.0.security_policy", "IoTSecurityPolicy_TLS13_1_2_2022_10"), + ), + }, + }, + }) +} + func TestAccIoTDomainConfiguration_awsManaged(t *testing.T) { ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -174,6 +290,60 @@ resource "aws_iot_domain_configuration" "test" { `, rName, domain)) } +func testAccDomainConfigurationConfig_tags1(rName, rootDomain, domain, tagKey1, tagValue1 string) string { + return acctest.ConfigCompose(testAccDomainConfigurationConfig_base(rootDomain, domain), fmt.Sprintf(` +resource "aws_iot_domain_configuration" "test" { + depends_on = [aws_acm_certificate_validation.test] + + name = %[1]q + domain_name = %[2]q + server_certificate_arns = [aws_acm_certificate.test.arn] + + tags = { + %[3]q = %[4]q + } +} +`, rName, domain, tagKey1, tagValue1)) +} + +func testAccDomainConfigurationConfig_tags2(rName, rootDomain, domain, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return acctest.ConfigCompose(testAccDomainConfigurationConfig_base(rootDomain, domain), fmt.Sprintf(` +resource "aws_iot_domain_configuration" "test" { + depends_on = [aws_acm_certificate_validation.test] + + name = %[1]q + domain_name = %[2]q + server_certificate_arns = [aws_acm_certificate.test.arn] + + tags = { + %[3]q = %[4]q + %[5]q = %[6]q + } +} +`, rName, domain, tagKey1, tagValue1, tagKey2, tagValue2)) +} + +func testAccDomainConfigurationConfig_securityPolicy(rName, rootDomain, domain, securityPolicy string, allowAuthorizerOverride bool) string { + return acctest.ConfigCompose(testAccAuthorizerConfig_basic(rName), testAccDomainConfigurationConfig_base(rootDomain, domain), fmt.Sprintf(` +resource "aws_iot_domain_configuration" "test" { + depends_on = [aws_acm_certificate_validation.test] + + authorizer_config { + allow_authorizer_override = %[4]t + default_authorizer_name = aws_iot_authorizer.test.name + } + + name = %[1]q + domain_name = %[2]q + server_certificate_arns = [aws_acm_certificate.test.arn] + + tls_config { + security_policy = %[3]q + } +} +`, rName, domain, securityPolicy, allowAuthorizerOverride)) +} + func testAccDomainConfigurationConfig_awsManaged(rName string) string { return fmt.Sprintf(` resource "aws_iot_domain_configuration" "test" { From 123e28dad11282f04e024a77be71631ba3cefca5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 14:36:29 -0400 Subject: [PATCH 28/31] Fix markdown-lint 'MD012/no-multiple-blanks Multiple consecutive blank lines [Expected: 1; Actual: 2]'. --- website/docs/r/iot_domain_configuration.html.markdown | 1 - 1 file changed, 1 deletion(-) diff --git a/website/docs/r/iot_domain_configuration.html.markdown b/website/docs/r/iot_domain_configuration.html.markdown index 10c421c1b642..bc92dbc89d1e 100644 --- a/website/docs/r/iot_domain_configuration.html.markdown +++ b/website/docs/r/iot_domain_configuration.html.markdown @@ -53,7 +53,6 @@ This resource exports the following attributes in addition to the arguments abov * `id` - The name of the created domain configuration. * `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#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 IOT domain configurations using the name. For example: From 3a41c172795c7aaae3189dd87582c49a063ef289 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 14:36:51 -0400 Subject: [PATCH 29/31] Fix semgrep 'ci.aws-in-func-name'. --- internal/service/iot/domain_configuration_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/iot/domain_configuration_test.go b/internal/service/iot/domain_configuration_test.go index 83cbe58635f1..6babcaae6a06 100644 --- a/internal/service/iot/domain_configuration_test.go +++ b/internal/service/iot/domain_configuration_test.go @@ -173,7 +173,7 @@ func TestAccIoTDomainConfiguration_update(t *testing.T) { }) } -func TestAccIoTDomainConfiguration_awsManaged(t *testing.T) { +func TestAccIoTDomainConfiguration_awsManaged(t *testing.T) { // nosemgrep:ci.aws-in-func-name ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_iot_domain_configuration.test" @@ -344,7 +344,7 @@ resource "aws_iot_domain_configuration" "test" { `, rName, domain, securityPolicy, allowAuthorizerOverride)) } -func testAccDomainConfigurationConfig_awsManaged(rName string) string { +func testAccDomainConfigurationConfig_awsManaged(rName string) string { // nosemgrep:ci.aws-in-func-name return fmt.Sprintf(` resource "aws_iot_domain_configuration" "test" { name = %[1]q From c8eb41f5df87243d7b7c4a29b53ccecd57fd6cb9 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 14:37:11 -0400 Subject: [PATCH 30/31] Fix semgrep 'ci.caps5-in-func-name'. --- internal/service/iot/domain_configuration.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/iot/domain_configuration.go b/internal/service/iot/domain_configuration.go index 5a7944e7c962..6d9ffa0fd5a6 100644 --- a/internal/service/iot/domain_configuration.go +++ b/internal/service/iot/domain_configuration.go @@ -321,7 +321,7 @@ func expandAuthorizerConfig(tfMap map[string]interface{}) *iot.AuthorizerConfig return apiObject } -func expandTlsConfig(tfMap map[string]interface{}) *iot.TlsConfig { +func expandTlsConfig(tfMap map[string]interface{}) *iot.TlsConfig { // nosemgrep:ci.caps5-in-func-name if tfMap == nil { return nil } @@ -353,7 +353,7 @@ func flattenAuthorizerConfig(apiObject *iot.AuthorizerConfig) map[string]interfa return tfMap } -func flattenTlsConfig(apiObject *iot.TlsConfig) map[string]interface{} { +func flattenTlsConfig(apiObject *iot.TlsConfig) map[string]interface{} { // nosemgrep:ci.caps5-in-func-name if apiObject == nil { return nil } From 2bc52ff9dbce159d952a01aaaf4ada81e172c3e8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 25 Oct 2023 14:58:32 -0400 Subject: [PATCH 31/31] Fix acceptance tests. --- internal/service/iot/domain_configuration_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/service/iot/domain_configuration_test.go b/internal/service/iot/domain_configuration_test.go index 6babcaae6a06..bc0b17543165 100644 --- a/internal/service/iot/domain_configuration_test.go +++ b/internal/service/iot/domain_configuration_test.go @@ -111,7 +111,7 @@ func TestAccIoTDomainConfiguration_tags(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccDomainConfigurationConfig_tags2(rName, rootDomain, domain, "key2", "value2", "key2", "value2"), + Config: testAccDomainConfigurationConfig_tags2(rName, rootDomain, domain, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( testAccCheckDomainConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), @@ -145,13 +145,13 @@ func TestAccIoTDomainConfiguration_update(t *testing.T) { CheckDestroy: testAccCheckDomainConfigurationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccDomainConfigurationConfig_securityPolicy(rName, rootDomain, domain, "TLS12_1_2_2022_10", true), + Config: testAccDomainConfigurationConfig_securityPolicy(rName, rootDomain, domain, "IoTSecurityPolicy_TLS13_1_3_2022_10", true), Check: resource.ComposeTestCheckFunc( testAccCheckDomainConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "authorizer_config.#", "1"), resource.TestCheckResourceAttr(resourceName, "authorizer_config.0.allow_authorizer_override", "true"), resource.TestCheckResourceAttr(resourceName, "tls_config.#", "1"), - resource.TestCheckResourceAttr(resourceName, "tls_config.0.security_policy", "TLS12_1_2_2022_10"), + resource.TestCheckResourceAttr(resourceName, "tls_config.0.security_policy", "IoTSecurityPolicy_TLS13_1_3_2022_10"), ), }, {