From e5e552f5ff3ee25e5febf3b626a0c88686ad85ea Mon Sep 17 00:00:00 2001 From: Yarden Sachs Date: Thu, 19 Nov 2020 07:06:54 -0800 Subject: [PATCH 01/21] resource/aws_mq_broker: Added RabbitMQ engine type --- aws/resource_aws_mq_broker.go | 29 ++++++++++---- aws/resource_aws_mq_broker_test.go | 55 ++++++++++++++++++++++++++ website/docs/r/mq_broker.html.markdown | 29 ++++++++------ 3 files changed, 94 insertions(+), 19 deletions(-) diff --git a/aws/resource_aws_mq_broker.go b/aws/resource_aws_mq_broker.go index 4a4d3938a37..c07a322a01a 100644 --- a/aws/resource_aws_mq_broker.go +++ b/aws/resource_aws_mq_broker.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "reflect" + "strings" "time" "github.com/aws/aws-sdk-go/aws" @@ -72,6 +73,7 @@ func resourceAwsMqBroker() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{ mq.DeploymentModeSingleInstance, mq.DeploymentModeActiveStandbyMultiAz, + mq.DeploymentModeClusterMultiAz, }, true), }, "encryption_options": { @@ -104,6 +106,7 @@ func resourceAwsMqBroker() *schema.Resource { ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ mq.EngineTypeActivemq, + mq.EngineTypeRabbitmq, }, true), }, "engine_version": { @@ -192,10 +195,24 @@ func resourceAwsMqBroker() *schema.Resource { Computed: true, ForceNew: true, }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, "user": { Type: schema.TypeSet, Required: true, Set: resourceAwsMqUserHash, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + // AWS currently does not support updating the RabbitMQ users beyond resource creation. + // User list is not returned back after creation. + // Updates to users can only be in the RabbitMQ UI. + if v := d.Get("engine_type").(string); strings.EqualFold(v, mq.EngineTypeRabbitmq) && d.Get("arn").(string) != "" { + return true + } + + return false + }, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "console_access": { @@ -227,10 +244,6 @@ func resourceAwsMqBroker() *schema.Resource { }, }, }, - "arn": { - Type: schema.TypeString, - Computed: true, - }, "instances": { Type: schema.TypeList, Computed: true, @@ -375,9 +388,11 @@ func resourceAwsMqBrokerRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("error setting logs: %s", err) } - err = d.Set("configuration", flattenMqConfigurationId(out.Configurations.Current)) - if err != nil { - return err + if out.Configurations != nil { + err = d.Set("configuration", flattenMqConfigurationId(out.Configurations.Current)) + if err != nil { + return err + } } rawUsers := make([]*mq.User, len(out.Users)) diff --git a/aws/resource_aws_mq_broker_test.go b/aws/resource_aws_mq_broker_test.go index bb4e3a2e676..391d15bc616 100644 --- a/aws/resource_aws_mq_broker_test.go +++ b/aws/resource_aws_mq_broker_test.go @@ -815,6 +815,40 @@ func TestAccAWSMqBroker_disappears(t *testing.T) { }) } +func TestAccAWSMqBroker_rabbitmq(t *testing.T) { + var broker mq.DescribeBrokerResponse + sgName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + brokerName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + resourceName := "aws_mq_broker.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSMq(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsMqBrokerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccMqBrokerConfigRabbit(sgName, brokerName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsMqBrokerExists(resourceName, &broker), + resource.TestCheckResourceAttr(resourceName, "configuration.#", "0"), + resource.TestCheckResourceAttr(resourceName, "engine_type", "RabbitMQ"), + resource.TestCheckResourceAttr(resourceName, "engine_version", "3.8.6"), + resource.TestCheckResourceAttr(resourceName, "host_instance_type", "mq.t3.micro"), + resource.TestCheckResourceAttr(resourceName, "instances.#", "1"), + resource.TestCheckResourceAttr(resourceName, "instances.0.endpoints.#", "1"), + resource.TestMatchResourceAttr(resourceName, "instances.0.endpoints.0", regexp.MustCompile(`^amqps://[a-z0-9-\.]+:5671$`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"apply_immediately", "user"}, + }, + }, + }) +} + func testAccCheckAwsMqBrokerDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).mqconn @@ -1369,3 +1403,24 @@ resource "aws_mq_broker" "test" { } `, sgName, sgName, brokerName) } + +func testAccMqBrokerConfigRabbit(sgName, brokerName string) string { + return fmt.Sprintf(` +resource "aws_security_group" "test" { + name = "%s" +} + +resource "aws_mq_broker" "test" { + broker_name = "%s" + engine_type = "RabbitMQ" + engine_version = "3.8.6" + host_instance_type = "mq.t3.micro" + security_groups = [aws_security_group.test.id] + + user { + username = "Test" + password = "TestTest1234" + } +} +`, sgName, brokerName) +} diff --git a/website/docs/r/mq_broker.html.markdown b/website/docs/r/mq_broker.html.markdown index a9d16acc33c..fb8c49fa8dc 100644 --- a/website/docs/r/mq_broker.html.markdown +++ b/website/docs/r/mq_broker.html.markdown @@ -12,7 +12,7 @@ Provides an MQ Broker Resource. This resources also manages users for the broker For more information on Amazon MQ, see [Amazon MQ documentation](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/welcome.html). -Changes to an MQ Broker can occur when you change a +For brokers with ActiveMQ as engine type: changes to an MQ Broker can occur when you change a parameter, such as `configuration` or `user`, and are reflected in the next maintenance window. Because of this, Terraform may report a difference in its planning phase because a modification has not yet taken place. You can use the @@ -56,18 +56,18 @@ The following arguments are supported: are applied immediately, or during the next maintenance window. Default is `false`. * `auto_minor_version_upgrade` - (Optional) Enables automatic upgrades to new minor versions for brokers, as Apache releases the versions. * `broker_name` - (Required) The name of the broker. -* `configuration` - (Optional) Configuration of the broker. See below. +* `configuration` - (Optional) Configuration of the broker. See below. (Applies to `ActiveMQ` only) * `deployment_mode` - (Optional) The deployment mode of the broker. Supported: `SINGLE_INSTANCE` and `ACTIVE_STANDBY_MULTI_AZ`. Defaults to `SINGLE_INSTANCE`. * `encryption_options` - (Optional) Configuration block containing encryption options. See below. -* `engine_type` - (Required) The type of broker engine. Currently, Amazon MQ supports only `ActiveMQ`. +* `engine_type` - (Required) The type of broker engine. Currently, Amazon MQ supports `ActiveMQ` and `RabbitMQ`. * `engine_version` - (Required) The version of the broker engine. See the [AmazonMQ Broker Engine docs](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/broker-engine.html) for supported versions. * `host_instance_type` - (Required) The broker's instance type. e.g. `mq.t2.micro` or `mq.m4.large` * `publicly_accessible` - (Optional) Whether to enable connections from applications outside of the VPC that hosts the broker's subnets. * `security_groups` - (Required) The list of security group IDs assigned to the broker. * `subnet_ids` - (Optional) The list of subnet IDs in which to launch the broker. A `SINGLE_INSTANCE` deployment requires one subnet. An `ACTIVE_STANDBY_MULTI_AZ` deployment requires two subnets. * `maintenance_window_start_time` - (Optional) Maintenance window start time. See below. -* `logs` - (Optional) Logging configuration of the broker. See below. -* `user` - (Required) The list of all ActiveMQ usernames for the specified broker. See below. +* `logs` - (Optional) Logging configuration of the broker. See below. (Applies to `ActiveMQ` only) +* `user` - (Optional) The list of all broker usernames for the specified broker. See below. RabbitMQ users can only be specified in the creation of the resource. Updates to users can only be in the RabbitMQ UI. * `tags` - (Optional) A map of tags to assign to the resource. ### Nested Fields @@ -97,11 +97,13 @@ The following arguments are supported: #### `user` -* `console_access` - (Optional) Whether to enable access to the [ActiveMQ Web Console](http://activemq.apache.org/web-console.html) for the user. -* `groups` - (Optional) The list of groups (20 maximum) to which the ActiveMQ user belongs. +* `console_access` - (Optional) Whether to enable access to the [ActiveMQ Web Console](http://activemq.apache.org/web-console.html) for the user. (Applies to `ActiveMQ` only) +* `groups` - (Optional) The list of groups (20 maximum) to which the ActiveMQ user belongs. (Applies to `ActiveMQ` only) * `password` - (Required) The password of the user. It must be 12 to 250 characters long, at least 4 unique characters, and must not contain commas. * `username` - (Required) The username of the user. +~> **NOTE:** AWS currently does not support updating the RabbitMQ users beyond resource creation. Updates to users can only be in the RabbitMQ UI. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: @@ -112,11 +114,14 @@ In addition to all arguments above, the following attributes are exported: * `instances.0.console_url` - The URL of the broker's [ActiveMQ Web Console](http://activemq.apache.org/web-console.html). * `instances.0.ip_address` - The IP Address of the broker. * `instances.0.endpoints` - The broker's wire-level protocol endpoints in the following order & format referenceable e.g. as `instances.0.endpoints.0` (SSL): - * `ssl://broker-id.mq.us-west-2.amazonaws.com:61617` - * `amqp+ssl://broker-id.mq.us-west-2.amazonaws.com:5671` - * `stomp+ssl://broker-id.mq.us-west-2.amazonaws.com:61614` - * `mqtt+ssl://broker-id.mq.us-west-2.amazonaws.com:8883` - * `wss://broker-id.mq.us-west-2.amazonaws.com:61619` + * For `ActiveMQ`: + * `ssl://broker-id.mq.us-west-2.amazonaws.com:61617` + * `amqp+ssl://broker-id.mq.us-west-2.amazonaws.com:5671` + * `stomp+ssl://broker-id.mq.us-west-2.amazonaws.com:61614` + * `mqtt+ssl://broker-id.mq.us-west-2.amazonaws.com:8883` + * `wss://broker-id.mq.us-west-2.amazonaws.com:61619` + * For `RabbitMQ`: + * `amqps://broker-id.mq.us-west-2.amazonaws.com:5671` ## Import From e3e43dfcbcdb150658f27baa10beacbf45c7b0ba Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 4 Mar 2021 21:40:57 -0500 Subject: [PATCH 02/21] resource/aws_mq_broker: Add changelog --- .changelog/16108.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/16108.txt diff --git a/.changelog/16108.txt b/.changelog/16108.txt new file mode 100644 index 00000000000..63c78194a52 --- /dev/null +++ b/.changelog/16108.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_mq_broker: Add RabbitMQ as option for `engine_type` +``` \ No newline at end of file From e2ddcddd3ca3d6583d0145fb0888027c6f29e33b Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 08:35:47 -0500 Subject: [PATCH 03/21] docs/resource/mq_broker: Describe RabbitMQ limits --- website/docs/r/mq_broker.html.markdown | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/website/docs/r/mq_broker.html.markdown b/website/docs/r/mq_broker.html.markdown index fb8c49fa8dc..84da976afbb 100644 --- a/website/docs/r/mq_broker.html.markdown +++ b/website/docs/r/mq_broker.html.markdown @@ -8,22 +8,16 @@ description: |- # Resource: aws_mq_broker -Provides an MQ Broker Resource. This resources also manages users for the broker. +Provides an Amazon MQ broker resource. This resources also manages users for the broker. -For more information on Amazon MQ, see [Amazon MQ documentation](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/welcome.html). +-> For more information on Amazon MQ, see [Amazon MQ documentation](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/welcome.html). -For brokers with ActiveMQ as engine type: changes to an MQ Broker can occur when you change a -parameter, such as `configuration` or `user`, and are reflected in the next maintenance -window. Because of this, Terraform may report a difference in its planning -phase because a modification has not yet taken place. You can use the -`apply_immediately` flag to instruct the service to apply the change immediately -(see documentation below). +~> **NOTE:** Amazon MQ currently places limits on **RabbitMQ** brokers. For example, a RabbitMQ broker cannot have: instances with an associated IP address of an ENI attached to the broker, an associated LDAP server to authenticate and authorize broker connections, storage type `EFS`, audit logging, or `configuration` blocks. Although this resource allows you to create RabbitMQ users, RabbitMQ users cannot have console access or groups. Also, Amazon MQ does not return information about RabbitMQ users so drift detection is not possible. -~> **Note:** using `apply_immediately` can result in a -brief downtime as the broker reboots. +~> **NOTE:** Changes to an MQ Broker can occur when you change a parameter, such as `configuration` or `user`, and are reflected in the next maintenance window. Because of this, Terraform may report a difference in its planning phase because a modification has not yet taken place. You can use the `apply_immediately` flag to instruct the service to apply the change immediately (see documentation below). Using `apply_immediately` can result in a brief downtime as the broker reboots. + +~> **NOTE:** All arguments including the username and password will be stored in the raw state as plain-text. [Read more about sensitive data in state](https://www.terraform.io/docs/state/sensitive-data.html). -~> **Note:** All arguments including the username and password will be stored in the raw state as plain-text. -[Read more about sensitive data in state](https://www.terraform.io/docs/state/sensitive-data.html). ## Example Usage From 2e17c5e619ba5b150552ca4eafb1f298a7fc76e9 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 09:21:12 -0500 Subject: [PATCH 04/21] docs/resource/mq_broker: Clean up docs --- website/docs/r/mq_broker.html.markdown | 92 ++++++++++++++------------ 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/website/docs/r/mq_broker.html.markdown b/website/docs/r/mq_broker.html.markdown index 84da976afbb..8857d5e5e89 100644 --- a/website/docs/r/mq_broker.html.markdown +++ b/website/docs/r/mq_broker.html.markdown @@ -44,70 +44,78 @@ resource "aws_mq_broker" "example" { ## Argument Reference -The following arguments are supported: - -* `apply_immediately` - (Optional) Specifies whether any broker modifications - are applied immediately, or during the next maintenance window. Default is `false`. -* `auto_minor_version_upgrade` - (Optional) Enables automatic upgrades to new minor versions for brokers, as Apache releases the versions. -* `broker_name` - (Required) The name of the broker. -* `configuration` - (Optional) Configuration of the broker. See below. (Applies to `ActiveMQ` only) -* `deployment_mode` - (Optional) The deployment mode of the broker. Supported: `SINGLE_INSTANCE` and `ACTIVE_STANDBY_MULTI_AZ`. Defaults to `SINGLE_INSTANCE`. -* `encryption_options` - (Optional) Configuration block containing encryption options. See below. -* `engine_type` - (Required) The type of broker engine. Currently, Amazon MQ supports `ActiveMQ` and `RabbitMQ`. -* `engine_version` - (Required) The version of the broker engine. See the [AmazonMQ Broker Engine docs](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/broker-engine.html) for supported versions. -* `host_instance_type` - (Required) The broker's instance type. e.g. `mq.t2.micro` or `mq.m4.large` +The following arguments are required: + +* `broker_name` - (Required) Name of the broker. +* `engine_type` - (Required) Type of broker engine. Valid values are `ACTIVEMQ` and `RABBITMQ`. +* `engine_version` - (Required) Version of the broker engine. See the [AmazonMQ Broker Engine docs](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/broker-engine.html) for supported versions. For example, `5.15.0`. +* `host_instance_type` - (Required) Broker's instance type. For example, `mq.t2.micro`, `mq.m4.large`. +* `user` - (Required) Configuration block for broker users. For `engine_type` of `RABBITMQ`, Amazon MQ does not return broker users preventing this resource from making user updates and drift detection. Detailed below. + +The following arguments are optional: + +* `apply_immediately` - (Optional) Specifies whether any broker modifications are applied immediately, or during the next maintenance window. Default is `false`. +* `auto_minor_version_upgrade` - (Optional) Whether to automatically upgrade to new minor versions of brokers as Amazon MQ makes releases available. +* `configuration` - (Optional) Configuration block for broker configuration. Applies to `engine_type` of `ACTIVEMQ` only. Detailed below. +* `deployment_mode` - (Optional) Deployment mode of the broker. Valid values are `SINGLE_INSTANCE` and `ACTIVE_STANDBY_MULTI_AZ`. Default is `SINGLE_INSTANCE`. +* `encryption_options` - (Optional) Configuration block containing encryption options. Detailed below. +* `logs` - (Optional) Configuration block for the logging configuration of the broker. Detailed below. +* `maintenance_window_start_time` - (Optional) Configuration block for the maintenance window start time. Detailed below. * `publicly_accessible` - (Optional) Whether to enable connections from applications outside of the VPC that hosts the broker's subnets. -* `security_groups` - (Required) The list of security group IDs assigned to the broker. -* `subnet_ids` - (Optional) The list of subnet IDs in which to launch the broker. A `SINGLE_INSTANCE` deployment requires one subnet. An `ACTIVE_STANDBY_MULTI_AZ` deployment requires two subnets. -* `maintenance_window_start_time` - (Optional) Maintenance window start time. See below. -* `logs` - (Optional) Logging configuration of the broker. See below. (Applies to `ActiveMQ` only) -* `user` - (Optional) The list of all broker usernames for the specified broker. See below. RabbitMQ users can only be specified in the creation of the resource. Updates to users can only be in the RabbitMQ UI. -* `tags` - (Optional) A map of tags to assign to the resource. +* `security_groups` - (Optional) List of security group IDs assigned to the broker. +* `subnet_ids` - (Optional) List of subnet IDs in which to launch the broker. A `SINGLE_INSTANCE` deployment requires one subnet. An `ACTIVE_STANDBY_MULTI_AZ` deployment requires multiple subnets. +* `tags` - (Optional) Map of tags to assign to the broker. -### Nested Fields +### configuration -#### `configuration` +The following arguments are optional: * `id` - (Optional) The Configuration ID. * `revision` - (Optional) Revision of the Configuration. -#### `encryption_options` +### encryption_options -* `kms_key_id` - (Optional) Amazon Resource Name (ARN) of Key Management Service (KMS) Customer Master Key (CMK) to use for encryption at rest. Requires setting `use_aws_owned_key` to `false`. To perform drift detection when AWS managed CMKs or customer managed CMKs are in use, this value must be configured. -* `use_aws_owned_key` - (Optional) Boolean to enable an AWS owned Key Management Service (KMS) Customer Master Key (CMK) that is not in your account. Defaults to `true`. Setting to `false` without configuring `kms_key_id` will create an AWS managed Customer Master Key (CMK) aliased to `aws/mq` in your account. +The following arguments are optional: -#### `maintenance_window_start_time` +* `kms_key_id` - (Optional) Amazon Resource Name (ARN) of Key Management Service (KMS) Customer Master Key (CMK) to use for encryption at rest. Requires setting `use_aws_owned_key` to `false`. To perform drift detection when AWS-managed CMKs or customer-managed CMKs are in use, this value must be configured. +* `use_aws_owned_key` - (Optional) Whether to enable an AWS-owned KMS CMK that is not in your account. Defaults to `true`. Setting to `false` without configuring `kms_key_id` will create an AWS-managed CMK aliased to `aws/mq` in your account. -* `day_of_week` - (Required) The day of the week. e.g. `MONDAY`, `TUESDAY`, or `WEDNESDAY` -* `time_of_day` - (Required) The time, in 24-hour format. e.g. `02:00` -* `time_zone` - (Required) The time zone, UTC by default, in either the Country/City format, or the UTC offset format. e.g. `CET` +### logs -~> **NOTE:** AWS currently does not support updating the maintenance window beyond resource creation. - -### `logs` +The following arguments are optional: +* `audit` - (Optional) Enables audit logging. Auditing is only possible for `engine_type` of `ACTIVEMQ`. User management action made using JMX or the ActiveMQ Web Console is logged. Defaults to `false`. * `general` - (Optional) Enables general logging via CloudWatch. Defaults to `false`. -* `audit` - (Optional) Enables audit logging. User management action made using JMX or the ActiveMQ Web Console is logged. Defaults to `false`. -#### `user` +### maintenance_window_start_time + +The following arguments are required: + +* `day_of_week` - (Required) Day of the week, e.g. `MONDAY`, `TUESDAY`, or `WEDNESDAY`. +* `time_of_day` - (Required) Time, in 24-hour format, e.g. `02:00`. +* `time_zone` - (Required) Time zone in either the Country/City format or the UTC offset format, e.g. `CET`. + +~> **NOTE:** Amazon MQ currently does not support updating the maintenance window. Changes to the maintenance window start time will force a new broker to be created. + +### user -* `console_access` - (Optional) Whether to enable access to the [ActiveMQ Web Console](http://activemq.apache.org/web-console.html) for the user. (Applies to `ActiveMQ` only) -* `groups` - (Optional) The list of groups (20 maximum) to which the ActiveMQ user belongs. (Applies to `ActiveMQ` only) -* `password` - (Required) The password of the user. It must be 12 to 250 characters long, at least 4 unique characters, and must not contain commas. -* `username` - (Required) The username of the user. +* `console_access` - (Optional) Whether to enable access to the [ActiveMQ Web Console](http://activemq.apache.org/web-console.html) for the user. Applies to `engine_type` of `ActiveMQ` only. +* `groups` - (Optional) List of groups (20 maximum) to which the ActiveMQ user belongs. Applies to `engine_type` of `ACTIVEMQ` only. +* `password` - (Required) Password of the user. It must be 12 to 250 characters long, at least 4 unique characters, and must not contain commas. +* `username` - (Required) Username of the user. -~> **NOTE:** AWS currently does not support updating the RabbitMQ users beyond resource creation. Updates to users can only be in the RabbitMQ UI. +~> **NOTE:** AWS currently does not support updating RabbitMQ users. Updates to users can only be in the RabbitMQ UI. ## Attributes Reference In addition to all arguments above, the following attributes are exported: -* `id` - The unique ID that Amazon MQ generates for the broker. -* `arn` - The ARN of the broker. -* `instances` - A list of information about allocated brokers (both active & standby). +* `arn` - ARN of the broker. +* `id` - Unique ID that Amazon MQ generates for the broker. +* `instances` - List of information about allocated brokers (both active & standby). * `instances.0.console_url` - The URL of the broker's [ActiveMQ Web Console](http://activemq.apache.org/web-console.html). - * `instances.0.ip_address` - The IP Address of the broker. - * `instances.0.endpoints` - The broker's wire-level protocol endpoints in the following order & format referenceable e.g. as `instances.0.endpoints.0` (SSL): + * `instances.0.ip_address` - IP Address of the broker. + * `instances.0.endpoints` - Broker's wire-level protocol endpoints in the following order & format referenceable e.g. as `instances.0.endpoints.0` (SSL): * For `ActiveMQ`: * `ssl://broker-id.mq.us-west-2.amazonaws.com:61617` * `amqp+ssl://broker-id.mq.us-west-2.amazonaws.com:5671` From b37c5e5bdce38df2777e92f292415de44ec39746 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 09:27:23 -0500 Subject: [PATCH 05/21] resource/mq_broker: Make security groups optional --- aws/resource_aws_mq_broker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_mq_broker.go b/aws/resource_aws_mq_broker.go index c07a322a01a..d316e6c20f4 100644 --- a/aws/resource_aws_mq_broker.go +++ b/aws/resource_aws_mq_broker.go @@ -186,7 +186,7 @@ func resourceAwsMqBroker() *schema.Resource { "security_groups": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, - Required: true, + Optional: true, }, "subnet_ids": { Type: schema.TypeSet, From 367400209e77d63060f3f24a596231df9df22c7d Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 09:30:01 -0500 Subject: [PATCH 06/21] resource/mq_broker: Move expanders/flatteners to resource file --- aws/resource_aws_mq_broker.go | 161 +++++++++++++++++++++++++++++++++ aws/structure.go | 162 ---------------------------------- 2 files changed, 161 insertions(+), 162 deletions(-) diff --git a/aws/resource_aws_mq_broker.go b/aws/resource_aws_mq_broker.go index d316e6c20f4..b37c9fb7d3d 100644 --- a/aws/resource_aws_mq_broker.go +++ b/aws/resource_aws_mq_broker.go @@ -739,3 +739,164 @@ func validateMqBrokerPassword(v interface{}, k string) (ws []string, errors []er } return } + +func expandMqUsers(cfg []interface{}) []*mq.User { + users := make([]*mq.User, len(cfg)) + for i, m := range cfg { + u := m.(map[string]interface{}) + user := mq.User{ + Username: aws.String(u["username"].(string)), + Password: aws.String(u["password"].(string)), + } + if v, ok := u["console_access"]; ok { + user.ConsoleAccess = aws.Bool(v.(bool)) + } + if v, ok := u["groups"]; ok { + user.Groups = expandStringSet(v.(*schema.Set)) + } + users[i] = &user + } + return users +} + +// We use cfgdUsers to get & set the password +func flattenMqUsers(users []*mq.User, cfgUsers []interface{}) *schema.Set { + existingPairs := make(map[string]string) + for _, u := range cfgUsers { + user := u.(map[string]interface{}) + username := user["username"].(string) + existingPairs[username] = user["password"].(string) + } + + out := make([]interface{}, 0) + for _, u := range users { + m := map[string]interface{}{ + "username": *u.Username, + } + password := "" + if p, ok := existingPairs[*u.Username]; ok { + password = p + } + if password != "" { + m["password"] = password + } + if u.ConsoleAccess != nil { + m["console_access"] = *u.ConsoleAccess + } + if len(u.Groups) > 0 { + m["groups"] = flattenStringSet(u.Groups) + } + out = append(out, m) + } + return schema.NewSet(resourceAwsMqUserHash, out) +} + +func expandMqWeeklyStartTime(cfg []interface{}) *mq.WeeklyStartTime { + if len(cfg) < 1 { + return nil + } + + m := cfg[0].(map[string]interface{}) + return &mq.WeeklyStartTime{ + DayOfWeek: aws.String(m["day_of_week"].(string)), + TimeOfDay: aws.String(m["time_of_day"].(string)), + TimeZone: aws.String(m["time_zone"].(string)), + } +} + +func flattenMqWeeklyStartTime(wst *mq.WeeklyStartTime) []interface{} { + if wst == nil { + return []interface{}{} + } + m := make(map[string]interface{}) + if wst.DayOfWeek != nil { + m["day_of_week"] = *wst.DayOfWeek + } + if wst.TimeOfDay != nil { + m["time_of_day"] = *wst.TimeOfDay + } + if wst.TimeZone != nil { + m["time_zone"] = *wst.TimeZone + } + return []interface{}{m} +} + +func expandMqConfigurationId(cfg []interface{}) *mq.ConfigurationId { + if len(cfg) < 1 { + return nil + } + + m := cfg[0].(map[string]interface{}) + out := mq.ConfigurationId{ + Id: aws.String(m["id"].(string)), + } + if v, ok := m["revision"].(int); ok && v > 0 { + out.Revision = aws.Int64(int64(v)) + } + + return &out +} + +func flattenMqConfigurationId(cid *mq.ConfigurationId) []interface{} { + if cid == nil { + return []interface{}{} + } + m := make(map[string]interface{}) + if cid.Id != nil { + m["id"] = *cid.Id + } + if cid.Revision != nil { + m["revision"] = *cid.Revision + } + return []interface{}{m} +} + +func flattenMqBrokerInstances(instances []*mq.BrokerInstance) []interface{} { + if len(instances) == 0 { + return []interface{}{} + } + l := make([]interface{}, len(instances)) + for i, instance := range instances { + m := make(map[string]interface{}) + if instance.ConsoleURL != nil { + m["console_url"] = *instance.ConsoleURL + } + if len(instance.Endpoints) > 0 { + m["endpoints"] = aws.StringValueSlice(instance.Endpoints) + } + if instance.IpAddress != nil { + m["ip_address"] = *instance.IpAddress + } + l[i] = m + } + + return l +} + +func flattenMqLogs(logs *mq.LogsSummary) []interface{} { + if logs == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "general": aws.BoolValue(logs.General), + "audit": aws.BoolValue(logs.Audit), + } + + return []interface{}{m} +} + +func expandMqLogs(l []interface{}) *mq.Logs { + if len(l) == 0 || l[0] == nil { + return nil + } + + m := l[0].(map[string]interface{}) + + logs := &mq.Logs{ + Audit: aws.Bool(m["audit"].(bool)), + General: aws.Bool(m["general"].(bool)), + } + + return logs +} diff --git a/aws/structure.go b/aws/structure.go index 6c715cc8203..dca6f400f6d 100644 --- a/aws/structure.go +++ b/aws/structure.go @@ -35,7 +35,6 @@ import ( "github.com/aws/aws-sdk-go/service/kinesis" "github.com/aws/aws-sdk-go/service/lambda" "github.com/aws/aws-sdk-go/service/macie" - "github.com/aws/aws-sdk-go/service/mq" "github.com/aws/aws-sdk-go/service/neptune" "github.com/aws/aws-sdk-go/service/rds" "github.com/aws/aws-sdk-go/service/redshift" @@ -3576,167 +3575,6 @@ func canonicalXML(s string) (string, error) { return results, nil } -func expandMqUsers(cfg []interface{}) []*mq.User { - users := make([]*mq.User, len(cfg)) - for i, m := range cfg { - u := m.(map[string]interface{}) - user := mq.User{ - Username: aws.String(u["username"].(string)), - Password: aws.String(u["password"].(string)), - } - if v, ok := u["console_access"]; ok { - user.ConsoleAccess = aws.Bool(v.(bool)) - } - if v, ok := u["groups"]; ok { - user.Groups = expandStringSet(v.(*schema.Set)) - } - users[i] = &user - } - return users -} - -// We use cfgdUsers to get & set the password -func flattenMqUsers(users []*mq.User, cfgUsers []interface{}) *schema.Set { - existingPairs := make(map[string]string) - for _, u := range cfgUsers { - user := u.(map[string]interface{}) - username := user["username"].(string) - existingPairs[username] = user["password"].(string) - } - - out := make([]interface{}, 0) - for _, u := range users { - m := map[string]interface{}{ - "username": *u.Username, - } - password := "" - if p, ok := existingPairs[*u.Username]; ok { - password = p - } - if password != "" { - m["password"] = password - } - if u.ConsoleAccess != nil { - m["console_access"] = *u.ConsoleAccess - } - if len(u.Groups) > 0 { - m["groups"] = flattenStringSet(u.Groups) - } - out = append(out, m) - } - return schema.NewSet(resourceAwsMqUserHash, out) -} - -func expandMqWeeklyStartTime(cfg []interface{}) *mq.WeeklyStartTime { - if len(cfg) < 1 { - return nil - } - - m := cfg[0].(map[string]interface{}) - return &mq.WeeklyStartTime{ - DayOfWeek: aws.String(m["day_of_week"].(string)), - TimeOfDay: aws.String(m["time_of_day"].(string)), - TimeZone: aws.String(m["time_zone"].(string)), - } -} - -func flattenMqWeeklyStartTime(wst *mq.WeeklyStartTime) []interface{} { - if wst == nil { - return []interface{}{} - } - m := make(map[string]interface{}) - if wst.DayOfWeek != nil { - m["day_of_week"] = *wst.DayOfWeek - } - if wst.TimeOfDay != nil { - m["time_of_day"] = *wst.TimeOfDay - } - if wst.TimeZone != nil { - m["time_zone"] = *wst.TimeZone - } - return []interface{}{m} -} - -func expandMqConfigurationId(cfg []interface{}) *mq.ConfigurationId { - if len(cfg) < 1 { - return nil - } - - m := cfg[0].(map[string]interface{}) - out := mq.ConfigurationId{ - Id: aws.String(m["id"].(string)), - } - if v, ok := m["revision"].(int); ok && v > 0 { - out.Revision = aws.Int64(int64(v)) - } - - return &out -} - -func flattenMqConfigurationId(cid *mq.ConfigurationId) []interface{} { - if cid == nil { - return []interface{}{} - } - m := make(map[string]interface{}) - if cid.Id != nil { - m["id"] = *cid.Id - } - if cid.Revision != nil { - m["revision"] = *cid.Revision - } - return []interface{}{m} -} - -func flattenMqBrokerInstances(instances []*mq.BrokerInstance) []interface{} { - if len(instances) == 0 { - return []interface{}{} - } - l := make([]interface{}, len(instances)) - for i, instance := range instances { - m := make(map[string]interface{}) - if instance.ConsoleURL != nil { - m["console_url"] = *instance.ConsoleURL - } - if len(instance.Endpoints) > 0 { - m["endpoints"] = aws.StringValueSlice(instance.Endpoints) - } - if instance.IpAddress != nil { - m["ip_address"] = *instance.IpAddress - } - l[i] = m - } - - return l -} - -func flattenMqLogs(logs *mq.LogsSummary) []interface{} { - if logs == nil { - return []interface{}{} - } - - m := map[string]interface{}{ - "general": aws.BoolValue(logs.General), - "audit": aws.BoolValue(logs.Audit), - } - - return []interface{}{m} -} - -func expandMqLogs(l []interface{}) *mq.Logs { - if len(l) == 0 || l[0] == nil { - return nil - } - - m := l[0].(map[string]interface{}) - - logs := &mq.Logs{ - Audit: aws.Bool(m["audit"].(bool)), - General: aws.Bool(m["general"].(bool)), - } - - return logs -} - func flattenResourceLifecycleConfig(rlc *elasticbeanstalk.ApplicationResourceLifecycleConfig) []map[string]interface{} { result := make([]map[string]interface{}, 0, 1) From 0af1a36704ec86f770e1eb9a7726f3cb7c86bb0e Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 09:41:03 -0500 Subject: [PATCH 07/21] resource/mq_broker: Allow logging to be unset (AWS provides default) --- aws/resource_aws_mq_broker.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/aws/resource_aws_mq_broker.go b/aws/resource_aws_mq_broker.go index b37c9fb7d3d..929c7de128c 100644 --- a/aws/resource_aws_mq_broker.go +++ b/aws/resource_aws_mq_broker.go @@ -135,12 +135,10 @@ func resourceAwsMqBroker() *schema.Resource { "general": { Type: schema.TypeBool, Optional: true, - Default: false, }, "audit": { Type: schema.TypeBool, Optional: true, - Default: false, }, }, }, @@ -878,9 +876,14 @@ func flattenMqLogs(logs *mq.LogsSummary) []interface{} { return []interface{}{} } - m := map[string]interface{}{ - "general": aws.BoolValue(logs.General), - "audit": aws.BoolValue(logs.Audit), + m := map[string]interface{}{} + + if logs.General != nil { + m["general"] = aws.BoolValue(logs.General) + } + + if logs.Audit != nil { + m["audit"] = aws.BoolValue(logs.Audit) } return []interface{}{m} @@ -893,9 +896,14 @@ func expandMqLogs(l []interface{}) *mq.Logs { m := l[0].(map[string]interface{}) - logs := &mq.Logs{ - Audit: aws.Bool(m["audit"].(bool)), - General: aws.Bool(m["general"].(bool)), + logs := &mq.Logs{} + + if v, ok := m["general"]; ok { + logs.General = aws.Bool(v.(bool)) + } + + if v, ok := m["audit"]; ok { + logs.Audit = aws.Bool(v.(bool)) } return logs From db133d605e63afc17ed5f18c592fbd80a84cf674 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 10:40:07 -0500 Subject: [PATCH 08/21] resource/mq_broker: Add storage and authentication arguments --- aws/resource_aws_mq_broker.go | 163 ++++++++++++------------- website/docs/r/mq_broker.html.markdown | 2 +- 2 files changed, 81 insertions(+), 84 deletions(-) diff --git a/aws/resource_aws_mq_broker.go b/aws/resource_aws_mq_broker.go index 929c7de128c..0569382c40f 100644 --- a/aws/resource_aws_mq_broker.go +++ b/aws/resource_aws_mq_broker.go @@ -34,6 +34,16 @@ func resourceAwsMqBroker() *schema.Resource { Optional: true, Default: false, }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "authentication_strategy": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(mq.AuthenticationStrategy_Values(), true), + }, "auto_minor_version_upgrade": { Type: schema.TypeBool, Optional: true, @@ -66,15 +76,11 @@ func resourceAwsMqBroker() *schema.Resource { }, }, "deployment_mode": { - Type: schema.TypeString, - Optional: true, - Default: mq.DeploymentModeSingleInstance, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - mq.DeploymentModeSingleInstance, - mq.DeploymentModeActiveStandbyMultiAz, - mq.DeploymentModeClusterMultiAz, - }, true), + Type: schema.TypeString, + Optional: true, + Default: mq.DeploymentModeSingleInstance, + ForceNew: true, + ValidateFunc: validation.StringInSlice(mq.DeploymentMode_Values(), true), }, "encryption_options": { Type: schema.TypeList, @@ -101,13 +107,10 @@ func resourceAwsMqBroker() *schema.Resource { }, }, "engine_type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - mq.EngineTypeActivemq, - mq.EngineTypeRabbitmq, - }, true), + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(mq.EngineType_Values(), true), }, "engine_version": { Type: schema.TypeString, @@ -119,6 +122,27 @@ func resourceAwsMqBroker() *schema.Resource { Required: true, ForceNew: true, }, + "instances": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "console_url": { + Type: schema.TypeString, + Computed: true, + }, + "endpoints": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "ip_address": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, "logs": { Type: schema.TypeList, Optional: true, @@ -152,17 +176,9 @@ func resourceAwsMqBroker() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "day_of_week": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - mq.DayOfWeekSunday, - mq.DayOfWeekMonday, - mq.DayOfWeekTuesday, - mq.DayOfWeekWednesday, - mq.DayOfWeekThursday, - mq.DayOfWeekFriday, - mq.DayOfWeekSaturday, - }, true), + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(mq.DayOfWeek_Values(), true), }, "time_of_day": { Type: schema.TypeString, @@ -186,6 +202,11 @@ func resourceAwsMqBroker() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, }, + "storage_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(mq.BrokerStorageType_Values(), false), + }, "subnet_ids": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, @@ -193,10 +214,7 @@ func resourceAwsMqBroker() *schema.Resource { Computed: true, ForceNew: true, }, - "arn": { - Type: schema.TypeString, - Computed: true, - }, + "tags": tagsSchema(), "user": { Type: schema.TypeSet, Required: true, @@ -242,28 +260,6 @@ func resourceAwsMqBroker() *schema.Resource { }, }, }, - "instances": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "console_url": { - Type: schema.TypeString, - Computed: true, - }, - "ip_address": { - Type: schema.TypeString, - Computed: true, - }, - "endpoints": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - }, - }, - }, - "tags": tagsSchema(), }, } } @@ -281,12 +277,14 @@ func resourceAwsMqBrokerCreate(d *schema.ResourceData, meta interface{}) error { EngineType: aws.String(d.Get("engine_type").(string)), EngineVersion: aws.String(d.Get("engine_version").(string)), HostInstanceType: aws.String(d.Get("host_instance_type").(string)), + Logs: expandMqLogs(d.Get("logs").([]interface{})), PubliclyAccessible: aws.Bool(d.Get("publicly_accessible").(bool)), - SecurityGroups: expandStringSet(d.Get("security_groups").(*schema.Set)), Users: expandMqUsers(d.Get("user").(*schema.Set).List()), - Logs: expandMqLogs(d.Get("logs").([]interface{})), } + if v, ok := d.GetOk("authentication_strategy"); ok { + input.AuthenticationStrategy = aws.String(v.(string)) + } if v, ok := d.GetOk("configuration"); ok { input.Configuration = expandMqConfigurationId(v.([]interface{})) } @@ -296,6 +294,12 @@ func resourceAwsMqBrokerCreate(d *schema.ResourceData, meta interface{}) error { if v, ok := d.GetOk("maintenance_window_start_time"); ok { input.MaintenanceWindowStartTime = expandMqWeeklyStartTime(v.([]interface{})) } + if v, ok := d.GetOk("security_groups"); ok && v.(*schema.Set).Len() > 0 { + input.SecurityGroups = expandStringSet(d.Get("security_groups").(*schema.Set)) + } + if v, ok := d.GetOk("storage_type"); ok { + input.StorageType = aws.String(v.(string)) + } if v, ok := d.GetOk("subnet_ids"); ok { input.SubnetIds = expandStringSet(v.(*schema.Set)) } @@ -361,36 +365,31 @@ func resourceAwsMqBrokerRead(d *schema.ResourceData, meta interface{}) error { return err } - d.Set("auto_minor_version_upgrade", out.AutoMinorVersionUpgrade) d.Set("arn", out.BrokerArn) - d.Set("instances", flattenMqBrokerInstances(out.BrokerInstances)) + d.Set("authentication_strategy", out.AuthenticationStrategy) + d.Set("auto_minor_version_upgrade", out.AutoMinorVersionUpgrade) d.Set("broker_name", out.BrokerName) d.Set("deployment_mode", out.DeploymentMode) - - if err := d.Set("encryption_options", flattenMqEncryptionOptions(out.EncryptionOptions)); err != nil { - return fmt.Errorf("error setting encryption_options: %s", err) - } - d.Set("engine_type", out.EngineType) d.Set("engine_version", out.EngineVersion) d.Set("host_instance_type", out.HostInstanceType) + d.Set("instances", flattenMqBrokerInstances(out.BrokerInstances)) d.Set("publicly_accessible", out.PubliclyAccessible) - err = d.Set("maintenance_window_start_time", flattenMqWeeklyStartTime(out.MaintenanceWindowStartTime)) - if err != nil { - return err - } d.Set("security_groups", aws.StringValueSlice(out.SecurityGroups)) + d.Set("storage_type", out.StorageType) d.Set("subnet_ids", aws.StringValueSlice(out.SubnetIds)) + if err := d.Set("configuration", flattenMqConfigurationId(out.Configurations.Current)); err != nil { + return fmt.Errorf("error setting configuration: %w", err) + } + if err := d.Set("encryption_options", flattenMqEncryptionOptions(out.EncryptionOptions)); err != nil { + return fmt.Errorf("error setting encryption_options: %w", err) + } if err := d.Set("logs", flattenMqLogs(out.Logs)); err != nil { - return fmt.Errorf("error setting logs: %s", err) + return fmt.Errorf("error setting logs: %w", err) } - - if out.Configurations != nil { - err = d.Set("configuration", flattenMqConfigurationId(out.Configurations.Current)) - if err != nil { - return err - } + if err := d.Set("maintenance_window_start_time", flattenMqWeeklyStartTime(out.MaintenanceWindowStartTime)); err != nil { + return fmt.Errorf("error setting maintenance_window_start_time: %w", err) } rawUsers := make([]*mq.User, len(out.Users)) @@ -410,13 +409,11 @@ func resourceAwsMqBrokerRead(d *schema.ResourceData, meta interface{}) error { } } - users := flattenMqUsers(rawUsers, d.Get("user").(*schema.Set).List()) - if err = d.Set("user", users); err != nil { - return err + if err := d.Set("user", flattenMqUsers(rawUsers, d.Get("user").(*schema.Set).List())); err != nil { + return fmt.Errorf("error setting user: %w", err) } - if err := d.Set("tags", keyvaluetags.MqKeyValueTags(out.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %s", err) + return fmt.Errorf("error setting tags: %w", err) } return nil @@ -433,7 +430,7 @@ func resourceAwsMqBrokerUpdate(d *schema.ResourceData, meta interface{}) error { SecurityGroups: expandStringSet(d.Get("security_groups").(*schema.Set)), }) if err != nil { - return fmt.Errorf("error updating MQ Broker (%s) security groups: %s", d.Id(), err) + return fmt.Errorf("error updating MQ Broker (%s) security groups: %w", d.Id(), err) } } @@ -444,7 +441,7 @@ func resourceAwsMqBrokerUpdate(d *schema.ResourceData, meta interface{}) error { Logs: expandMqLogs(d.Get("logs").([]interface{})), }) if err != nil { - return fmt.Errorf("error updating MQ Broker (%s) configuration: %s", d.Id(), err) + return fmt.Errorf("error updating MQ Broker (%s) configuration: %w", d.Id(), err) } requiresReboot = true } @@ -458,7 +455,7 @@ func resourceAwsMqBrokerUpdate(d *schema.ResourceData, meta interface{}) error { usersUpdated, err = updateAwsMqBrokerUsers(conn, d.Id(), o.(*schema.Set).List(), n.(*schema.Set).List()) if err != nil { - return fmt.Errorf("error updating MQ Broker (%s) user: %s", d.Id(), err) + return fmt.Errorf("error updating MQ Broker (%s) user: %w", d.Id(), err) } if usersUpdated { @@ -471,7 +468,7 @@ func resourceAwsMqBrokerUpdate(d *schema.ResourceData, meta interface{}) error { BrokerId: aws.String(d.Id()), }) if err != nil { - return fmt.Errorf("error rebooting MQ Broker (%s): %s", d.Id(), err) + return fmt.Errorf("error rebooting MQ Broker (%s): %w", d.Id(), err) } stateConf := resource.StateChangeConf{ @@ -502,7 +499,7 @@ func resourceAwsMqBrokerUpdate(d *schema.ResourceData, meta interface{}) error { o, n := d.GetChange("tags") if err := keyvaluetags.MqUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("error updating MQ Broker (%s) tags: %s", d.Get("arn").(string), err) + return fmt.Errorf("error updating MQ Broker (%s) tags: %w", d.Get("arn").(string), err) } } diff --git a/website/docs/r/mq_broker.html.markdown b/website/docs/r/mq_broker.html.markdown index 8857d5e5e89..e62b42961c2 100644 --- a/website/docs/r/mq_broker.html.markdown +++ b/website/docs/r/mq_broker.html.markdown @@ -57,7 +57,7 @@ The following arguments are optional: * `apply_immediately` - (Optional) Specifies whether any broker modifications are applied immediately, or during the next maintenance window. Default is `false`. * `auto_minor_version_upgrade` - (Optional) Whether to automatically upgrade to new minor versions of brokers as Amazon MQ makes releases available. * `configuration` - (Optional) Configuration block for broker configuration. Applies to `engine_type` of `ACTIVEMQ` only. Detailed below. -* `deployment_mode` - (Optional) Deployment mode of the broker. Valid values are `SINGLE_INSTANCE` and `ACTIVE_STANDBY_MULTI_AZ`. Default is `SINGLE_INSTANCE`. +* `deployment_mode` - (Optional) Deployment mode of the broker. Valid values are `SINGLE_INSTANCE`, `ACTIVE_STANDBY_MULTI_AZ`, and `CLUSTER_MULTI_AZ`. Default is `SINGLE_INSTANCE`. * `encryption_options` - (Optional) Configuration block containing encryption options. Detailed below. * `logs` - (Optional) Configuration block for the logging configuration of the broker. Detailed below. * `maintenance_window_start_time` - (Optional) Configuration block for the maintenance window start time. Detailed below. From fefe3a2c6085e5a85fbea21317155589d77b5297 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 10:48:31 -0500 Subject: [PATCH 09/21] docs/resource/mq_broker: Add storage and authentication arguments --- website/docs/r/mq_broker.html.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/docs/r/mq_broker.html.markdown b/website/docs/r/mq_broker.html.markdown index e62b42961c2..738596499d4 100644 --- a/website/docs/r/mq_broker.html.markdown +++ b/website/docs/r/mq_broker.html.markdown @@ -55,6 +55,7 @@ The following arguments are required: The following arguments are optional: * `apply_immediately` - (Optional) Specifies whether any broker modifications are applied immediately, or during the next maintenance window. Default is `false`. +* `authentication_strategy` - (Optional) Authentication strategy used to secure the broker. Valid values are `SIMPLE` and `LDAP`. `LDAP` is not supported for `engine_type` `RABBITMQ`. * `auto_minor_version_upgrade` - (Optional) Whether to automatically upgrade to new minor versions of brokers as Amazon MQ makes releases available. * `configuration` - (Optional) Configuration block for broker configuration. Applies to `engine_type` of `ACTIVEMQ` only. Detailed below. * `deployment_mode` - (Optional) Deployment mode of the broker. Valid values are `SINGLE_INSTANCE`, `ACTIVE_STANDBY_MULTI_AZ`, and `CLUSTER_MULTI_AZ`. Default is `SINGLE_INSTANCE`. @@ -63,6 +64,7 @@ The following arguments are optional: * `maintenance_window_start_time` - (Optional) Configuration block for the maintenance window start time. Detailed below. * `publicly_accessible` - (Optional) Whether to enable connections from applications outside of the VPC that hosts the broker's subnets. * `security_groups` - (Optional) List of security group IDs assigned to the broker. +* `storage_type` - (Optional) Storage type of the broker. For `engine_type` `ACTIVEMQ`, the valid values are `EFS` and `EBS`. For `engine_type` of `RABBITMQ`, only `EBS` is supported. * `subnet_ids` - (Optional) List of subnet IDs in which to launch the broker. A `SINGLE_INSTANCE` deployment requires one subnet. An `ACTIVE_STANDBY_MULTI_AZ` deployment requires multiple subnets. * `tags` - (Optional) Map of tags to assign to the broker. From 8492260b9ceb132b56559db423c8298c70649bf6 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 10:53:47 -0500 Subject: [PATCH 10/21] tests/resource/mq_broker: Add storage and authentication arguments --- aws/resource_aws_mq_broker_test.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_mq_broker_test.go b/aws/resource_aws_mq_broker_test.go index 391d15bc616..ac3ad724420 100644 --- a/aws/resource_aws_mq_broker_test.go +++ b/aws/resource_aws_mq_broker_test.go @@ -249,6 +249,7 @@ func TestAccAWSMqBroker_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAwsMqBrokerExists(resourceName, &broker), resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), + resource.TestCheckResourceAttr(resourceName, "authentication_strategy", mq.AuthenticationStrategySimple), resource.TestCheckResourceAttr(resourceName, "broker_name", brokerName), resource.TestCheckResourceAttr(resourceName, "configuration.#", "1"), resource.TestMatchResourceAttr(resourceName, "configuration.0.id", regexp.MustCompile(`^c-[a-z0-9-]+$`)), @@ -268,6 +269,7 @@ func TestAccAWSMqBroker_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "maintenance_window_start_time.0.time_zone", "UTC"), resource.TestCheckResourceAttr(resourceName, "publicly_accessible", "false"), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + resource.TestCheckResourceAttr(resourceName, "storage_type", mq.BrokerStorageTypeEbs), resource.TestCheckResourceAttr(resourceName, "subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "user.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ @@ -931,15 +933,17 @@ func testAccPreCheckAWSMq(t *testing.T) { func testAccMqBrokerConfig(sgName, brokerName string) string { return fmt.Sprintf(` resource "aws_security_group" "test" { - name = "%s" + name = %[1]q } resource "aws_mq_broker" "test" { - broker_name = "%s" - engine_type = "ActiveMQ" - engine_version = "5.15.0" - host_instance_type = "mq.t2.micro" - security_groups = [aws_security_group.test.id] + broker_name = %[1]q + engine_type = "ActiveMQ" + engine_version = "5.15.0" + host_instance_type = "mq.t2.micro" + security_groups = [aws_security_group.test.id] + authentication_strategy = "SIMPLE" + storage_type = "EBS" logs { general = true From 2dbc9b7148c3b6012e3a5211f1dea1f7d6fbe3d0 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 11:06:03 -0500 Subject: [PATCH 11/21] data-source/mq_broker: Add storage and authentication arguments --- aws/data_source_aws_mq_broker.go | 32 +++++++++++++++++++----------- aws/resource_aws_mq_broker_test.go | 4 ++-- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/aws/data_source_aws_mq_broker.go b/aws/data_source_aws_mq_broker.go index 15e31403828..ea6047c0748 100644 --- a/aws/data_source_aws_mq_broker.go +++ b/aws/data_source_aws_mq_broker.go @@ -13,6 +13,18 @@ func dataSourceAwsMqBroker() *schema.Resource { Read: dataSourceAwsmQBrokerRead, Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "authentication_strategy": { + Type: schema.TypeString, + Computed: true, + }, + "auto_minor_version_upgrade": { + Type: schema.TypeBool, + Computed: true, + }, "broker_id": { Type: schema.TypeString, Optional: true, @@ -25,14 +37,6 @@ func dataSourceAwsMqBroker() *schema.Resource { Computed: true, ConflictsWith: []string{"broker_id"}, }, - "auto_minor_version_upgrade": { - Type: schema.TypeBool, - Computed: true, - }, - "arn": { - Type: schema.TypeString, - Computed: true, - }, "configuration": { Type: schema.TypeList, Computed: true, @@ -90,15 +94,15 @@ func dataSourceAwsMqBroker() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "ip_address": { - Type: schema.TypeString, - Computed: true, - }, "endpoints": { Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + "ip_address": { + Type: schema.TypeString, + Computed: true, + }, }, }, }, @@ -155,6 +159,10 @@ func dataSourceAwsMqBroker() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, }, + "storage_type": { + Type: schema.TypeString, + Computed: true, + }, "subnet_ids": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, diff --git a/aws/resource_aws_mq_broker_test.go b/aws/resource_aws_mq_broker_test.go index ac3ad724420..bde9ac9aa14 100644 --- a/aws/resource_aws_mq_broker_test.go +++ b/aws/resource_aws_mq_broker_test.go @@ -269,7 +269,7 @@ func TestAccAWSMqBroker_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "maintenance_window_start_time.0.time_zone", "UTC"), resource.TestCheckResourceAttr(resourceName, "publicly_accessible", "false"), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), - resource.TestCheckResourceAttr(resourceName, "storage_type", mq.BrokerStorageTypeEbs), + resource.TestCheckResourceAttr(resourceName, "storage_type", mq.BrokerStorageTypeEfs), resource.TestCheckResourceAttr(resourceName, "subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "user.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ @@ -943,7 +943,7 @@ resource "aws_mq_broker" "test" { host_instance_type = "mq.t2.micro" security_groups = [aws_security_group.test.id] authentication_strategy = "SIMPLE" - storage_type = "EBS" + storage_type = "EFS" logs { general = true From 97387c31eeae58e0807b74450aece5ce9f48baa7 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 11:09:37 -0500 Subject: [PATCH 12/21] tests/data-source/mq_broker: Add storage and authentication arguments --- aws/data_source_aws_mq_broker_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aws/data_source_aws_mq_broker_test.go b/aws/data_source_aws_mq_broker_test.go index 523ebc36829..051225180ad 100644 --- a/aws/data_source_aws_mq_broker_test.go +++ b/aws/data_source_aws_mq_broker_test.go @@ -27,6 +27,7 @@ func TestAccDataSourceAWSMqBroker_basic(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrPair(dataSourceByIdName, "arn", resourceName, "arn"), resource.TestCheckResourceAttrPair(dataSourceByIdName, "broker_name", resourceName, "broker_name"), + resource.TestCheckResourceAttrPair(dataSourceByIdName, "authentication_strategy", resourceName, "authentication_strategy"), resource.TestCheckResourceAttrPair(dataSourceByIdName, "auto_minor_version_upgrade", resourceName, "auto_minor_version_upgrade"), resource.TestCheckResourceAttrPair(dataSourceByIdName, "deployment_mode", resourceName, "deployment_mode"), resource.TestCheckResourceAttrPair(dataSourceByIdName, "configuration.#", resourceName, "configuration.#"), @@ -40,6 +41,7 @@ func TestAccDataSourceAWSMqBroker_basic(t *testing.T) { resource.TestCheckResourceAttrPair(dataSourceByIdName, "maintenance_window_start_time.#", resourceName, "maintenance_window_start_time.#"), resource.TestCheckResourceAttrPair(dataSourceByIdName, "publicly_accessible", resourceName, "publicly_accessible"), resource.TestCheckResourceAttrPair(dataSourceByIdName, "security_groups.#", resourceName, "security_groups.#"), + resource.TestCheckResourceAttrPair(dataSourceByIdName, "storage_type", resourceName, "storage_type"), resource.TestCheckResourceAttrPair(dataSourceByIdName, "subnet_ids.#", resourceName, "subnet_ids.#"), resource.TestCheckResourceAttrPair(dataSourceByIdName, "tags.%", resourceName, "tags.%"), resource.TestCheckResourceAttrPair(dataSourceByIdName, "user.#", resourceName, "user.#"), From 19dd6ed452a2ae530d9379e1916fbd9a9af4027d Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 11:15:17 -0500 Subject: [PATCH 13/21] tests/resource/mq_broker: MQ capitalization issues --- aws/resource_aws_mq_broker.go | 2 +- aws/resource_aws_mq_broker_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_mq_broker.go b/aws/resource_aws_mq_broker.go index 0569382c40f..257f697266c 100644 --- a/aws/resource_aws_mq_broker.go +++ b/aws/resource_aws_mq_broker.go @@ -42,7 +42,7 @@ func resourceAwsMqBroker() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, - ValidateFunc: validation.StringInSlice(mq.AuthenticationStrategy_Values(), true), + ValidateFunc: validation.StringInSlice(mq.AuthenticationStrategy_Values(), false), }, "auto_minor_version_upgrade": { Type: schema.TypeBool, diff --git a/aws/resource_aws_mq_broker_test.go b/aws/resource_aws_mq_broker_test.go index bde9ac9aa14..2c60b18c34e 100644 --- a/aws/resource_aws_mq_broker_test.go +++ b/aws/resource_aws_mq_broker_test.go @@ -249,7 +249,7 @@ func TestAccAWSMqBroker_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAwsMqBrokerExists(resourceName, &broker), resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), - resource.TestCheckResourceAttr(resourceName, "authentication_strategy", mq.AuthenticationStrategySimple), + resource.TestCheckResourceAttr(resourceName, "authentication_strategy", "simple"), resource.TestCheckResourceAttr(resourceName, "broker_name", brokerName), resource.TestCheckResourceAttr(resourceName, "configuration.#", "1"), resource.TestMatchResourceAttr(resourceName, "configuration.0.id", regexp.MustCompile(`^c-[a-z0-9-]+$`)), @@ -942,7 +942,7 @@ resource "aws_mq_broker" "test" { engine_version = "5.15.0" host_instance_type = "mq.t2.micro" security_groups = [aws_security_group.test.id] - authentication_strategy = "SIMPLE" + authentication_strategy = "simple" storage_type = "EFS" logs { From c78fc4e5a93f2cf1be3c83c1a57379bea7ccdf18 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 13:32:45 -0500 Subject: [PATCH 14/21] resource/mq_broker: Rework with waiter --- aws/data_source_aws_mq_broker.go | 53 +++++ aws/internal/service/mq/waiter/status.go | 27 +++ aws/internal/service/mq/waiter/waiter.go | 31 +++ aws/resource_aws_mq_broker.go | 281 ++++++++++++++++++----- aws/resource_aws_mq_broker_test.go | 6 +- 5 files changed, 339 insertions(+), 59 deletions(-) create mode 100644 aws/internal/service/mq/waiter/status.go create mode 100644 aws/internal/service/mq/waiter/waiter.go diff --git a/aws/data_source_aws_mq_broker.go b/aws/data_source_aws_mq_broker.go index ea6047c0748..5edfd915943 100644 --- a/aws/data_source_aws_mq_broker.go +++ b/aws/data_source_aws_mq_broker.go @@ -106,6 +106,59 @@ func dataSourceAwsMqBroker() *schema.Resource { }, }, }, + "ldap_server_metadata": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "hosts": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "role_base": { + Type: schema.TypeString, + Computed: true, + }, + "role_name": { + Type: schema.TypeString, + Computed: true, + }, + "role_search_matching": { + Type: schema.TypeString, + Computed: true, + }, + "role_search_subtree": { + Type: schema.TypeBool, + Computed: true, + }, + "service_account_password": { + Type: schema.TypeString, + Computed: true, + }, + "service_account_username": { + Type: schema.TypeString, + Computed: true, + }, + "user_base": { + Type: schema.TypeString, + Computed: true, + }, + "user_role_name": { + Type: schema.TypeString, + Computed: true, + }, + "user_search_matching": { + Type: schema.TypeString, + Computed: true, + }, + "user_search_subtree": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, "logs": { Type: schema.TypeList, Optional: true, diff --git a/aws/internal/service/mq/waiter/status.go b/aws/internal/service/mq/waiter/status.go new file mode 100644 index 00000000000..23ae61330b1 --- /dev/null +++ b/aws/internal/service/mq/waiter/status.go @@ -0,0 +1,27 @@ +package waiter + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/mq" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func BrokerStatus(conn *mq.MQ, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + input := &mq.DescribeBrokerInput{ + BrokerId: aws.String(id), + } + + output, err := conn.DescribeBroker(input) + + if err != nil { + return nil, aws.StringValue(output.BrokerState), err + } + + if output == nil { + return output, aws.StringValue(output.BrokerState), nil + } + + return output, aws.StringValue(output.BrokerState), nil + } +} diff --git a/aws/internal/service/mq/waiter/waiter.go b/aws/internal/service/mq/waiter/waiter.go new file mode 100644 index 00000000000..e6dead8714f --- /dev/null +++ b/aws/internal/service/mq/waiter/waiter.go @@ -0,0 +1,31 @@ +package waiter + +import ( + "time" + + "github.com/aws/aws-sdk-go/service/mq" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +const ( + BrokerCreateTimeout = 30 * time.Minute +) + +func BrokerCreated(conn *mq.MQ, id string) (*mq.DescribeBrokerResponse, error) { + stateConf := resource.StateChangeConf{ + Pending: []string{ + mq.BrokerStateCreationInProgress, + mq.BrokerStateRebootInProgress, + }, + Target: []string{mq.BrokerStateRunning}, + Timeout: BrokerCreateTimeout, + Refresh: BrokerStatus(conn, id), + } + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*mq.DescribeBrokerResponse); ok { + return output, err + } + + return nil, err +} diff --git a/aws/resource_aws_mq_broker.go b/aws/resource_aws_mq_broker.go index 257f697266c..9e2eb0c3936 100644 --- a/aws/resource_aws_mq_broker.go +++ b/aws/resource_aws_mq_broker.go @@ -16,6 +16,7 @@ import ( "github.com/mitchellh/copystructure" "github.com/terraform-providers/terraform-provider-aws/aws/internal/hashcode" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/mq/waiter" ) func resourceAwsMqBroker() *schema.Resource { @@ -41,8 +42,8 @@ func resourceAwsMqBroker() *schema.Resource { "authentication_strategy": { Type: schema.TypeString, Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(mq.AuthenticationStrategy_Values(), false), + Computed: true, + ValidateFunc: validation.StringInSlice(mq.AuthenticationStrategy_Values(), true), }, "auto_minor_version_upgrade": { Type: schema.TypeBool, @@ -143,6 +144,60 @@ func resourceAwsMqBroker() *schema.Resource { }, }, }, + "ldap_server_metadata": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "hosts": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "role_base": { + Type: schema.TypeString, + Optional: true, + }, + "role_name": { + Type: schema.TypeString, + Optional: true, + }, + "role_search_matching": { + Type: schema.TypeString, + Optional: true, + }, + "role_search_subtree": { + Type: schema.TypeBool, + Optional: true, + }, + "service_account_password": { + Type: schema.TypeString, + Optional: true, + }, + "service_account_username": { + Type: schema.TypeString, + Optional: true, + }, + "user_base": { + Type: schema.TypeString, + Optional: true, + }, + "user_role_name": { + Type: schema.TypeString, + Optional: true, + }, + "user_search_matching": { + Type: schema.TypeString, + Optional: true, + }, + "user_search_subtree": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, "logs": { Type: schema.TypeList, Optional: true, @@ -205,7 +260,8 @@ func resourceAwsMqBroker() *schema.Resource { "storage_type": { Type: schema.TypeString, Optional: true, - ValidateFunc: validation.StringInSlice(mq.BrokerStorageType_Values(), false), + Computed: true, + ValidateFunc: validation.StringInSlice(mq.BrokerStorageType_Values(), true), }, "subnet_ids": { Type: schema.TypeSet, @@ -277,7 +333,6 @@ func resourceAwsMqBrokerCreate(d *schema.ResourceData, meta interface{}) error { EngineType: aws.String(d.Get("engine_type").(string)), EngineVersion: aws.String(d.Get("engine_version").(string)), HostInstanceType: aws.String(d.Get("host_instance_type").(string)), - Logs: expandMqLogs(d.Get("logs").([]interface{})), PubliclyAccessible: aws.Bool(d.Get("publicly_accessible").(bool)), Users: expandMqUsers(d.Get("user").(*schema.Set).List()), } @@ -291,6 +346,12 @@ func resourceAwsMqBrokerCreate(d *schema.ResourceData, meta interface{}) error { if v, ok := d.GetOk("deployment_mode"); ok { input.DeploymentMode = aws.String(v.(string)) } + if v, ok := d.GetOk("logs"); ok && len(v.([]interface{})) > 0 { + input.Logs = expandMqLogs(d.Get("logs").([]interface{})) + } + if v, ok := d.GetOk("ldap_server_metadata"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.LdapServerMetadata = expandMQLDAPServerMetadata(v.([]interface{})[0].(map[string]interface{})) + } if v, ok := d.GetOk("maintenance_window_start_time"); ok { input.MaintenanceWindowStartTime = expandMqWeeklyStartTime(v.([]interface{})) } @@ -316,27 +377,8 @@ func resourceAwsMqBrokerCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(aws.StringValue(out.BrokerId)) d.Set("arn", out.BrokerArn) - stateConf := resource.StateChangeConf{ - Pending: []string{ - mq.BrokerStateCreationInProgress, - mq.BrokerStateRebootInProgress, - }, - Target: []string{mq.BrokerStateRunning}, - Timeout: 30 * time.Minute, - Refresh: func() (interface{}, string, error) { - out, err := conn.DescribeBroker(&mq.DescribeBrokerInput{ - BrokerId: aws.String(d.Id()), - }) - if err != nil { - return 42, "", err - } - - return out, *out.BrokerState, nil - }, - } - _, err = stateConf.WaitForState() - if err != nil { - return err + if _, err := waiter.BrokerCreated(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for MQ Broker (%s) creation: %w", d.Id(), err) } return resourceAwsMqBrokerRead(d, meta) @@ -347,7 +389,7 @@ func resourceAwsMqBrokerRead(d *schema.ResourceData, meta interface{}) error { ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig log.Printf("[INFO] Reading MQ Broker: %s", d.Id()) - out, err := conn.DescribeBroker(&mq.DescribeBrokerInput{ + output, err := conn.DescribeBroker(&mq.DescribeBrokerInput{ BrokerId: aws.String(d.Id()), }) if err != nil { @@ -365,35 +407,62 @@ func resourceAwsMqBrokerRead(d *schema.ResourceData, meta interface{}) error { return err } - d.Set("arn", out.BrokerArn) - d.Set("authentication_strategy", out.AuthenticationStrategy) - d.Set("auto_minor_version_upgrade", out.AutoMinorVersionUpgrade) - d.Set("broker_name", out.BrokerName) - d.Set("deployment_mode", out.DeploymentMode) - d.Set("engine_type", out.EngineType) - d.Set("engine_version", out.EngineVersion) - d.Set("host_instance_type", out.HostInstanceType) - d.Set("instances", flattenMqBrokerInstances(out.BrokerInstances)) - d.Set("publicly_accessible", out.PubliclyAccessible) - d.Set("security_groups", aws.StringValueSlice(out.SecurityGroups)) - d.Set("storage_type", out.StorageType) - d.Set("subnet_ids", aws.StringValueSlice(out.SubnetIds)) - - if err := d.Set("configuration", flattenMqConfigurationId(out.Configurations.Current)); err != nil { - return fmt.Errorf("error setting configuration: %w", err) - } - if err := d.Set("encryption_options", flattenMqEncryptionOptions(out.EncryptionOptions)); err != nil { - return fmt.Errorf("error setting encryption_options: %w", err) - } - if err := d.Set("logs", flattenMqLogs(out.Logs)); err != nil { - return fmt.Errorf("error setting logs: %w", err) - } - if err := d.Set("maintenance_window_start_time", flattenMqWeeklyStartTime(out.MaintenanceWindowStartTime)); err != nil { - return fmt.Errorf("error setting maintenance_window_start_time: %w", err) - } - - rawUsers := make([]*mq.User, len(out.Users)) - for i, u := range out.Users { + d.Set("arn", output.BrokerArn) + d.Set("authentication_strategy", output.AuthenticationStrategy) + d.Set("auto_minor_version_upgrade", output.AutoMinorVersionUpgrade) + d.Set("broker_name", output.BrokerName) + d.Set("deployment_mode", output.DeploymentMode) + d.Set("engine_type", output.EngineType) + d.Set("engine_version", output.EngineVersion) + d.Set("host_instance_type", output.HostInstanceType) + d.Set("instances", flattenMqBrokerInstances(output.BrokerInstances)) + d.Set("publicly_accessible", output.PubliclyAccessible) + d.Set("security_groups", aws.StringValueSlice(output.SecurityGroups)) + d.Set("storage_type", output.StorageType) + d.Set("subnet_ids", aws.StringValueSlice(output.SubnetIds)) + + if output.Configurations.Current != nil { + if err := d.Set("configuration", flattenMqConfigurationId(output.Configurations.Current)); err != nil { + return fmt.Errorf("error setting configuration: %w", err) + } + } else { + d.Set("configuration", nil) + } + + if output.EncryptionOptions != nil { + if err := d.Set("encryption_options", flattenMqEncryptionOptions(output.EncryptionOptions)); err != nil { + return fmt.Errorf("error setting encryption_options: %w", err) + } + } else { + d.Set("encryption_options", nil) + } + + if output.LdapServerMetadata != nil { + if err := d.Set("ldap_server_metadata", flattenMQLDAPServerMetadata(output.LdapServerMetadata)); err != nil { + return fmt.Errorf("error setting lda_server_metadata: %w", err) + } + } else { + d.Set("ldap_server_metadata", nil) + } + + if output.Logs != nil { + if err := d.Set("logs", flattenMqLogs(output.Logs)); err != nil { + return fmt.Errorf("error setting logs: %w", err) + } + } else { + d.Set("logs", nil) + } + + if output.MaintenanceWindowStartTime != nil { + if err := d.Set("maintenance_window_start_time", flattenMqWeeklyStartTime(output.MaintenanceWindowStartTime)); err != nil { + return fmt.Errorf("error setting maintenance_window_start_time: %w", err) + } + } else { + d.Set("maintenance_window_start_time", nil) + } + + rawUsers := make([]*mq.User, len(output.Users)) + for i, u := range output.Users { uOut, err := conn.DescribeUser(&mq.DescribeUserInput{ BrokerId: aws.String(d.Id()), Username: u.Username, @@ -412,7 +481,7 @@ func resourceAwsMqBrokerRead(d *schema.ResourceData, meta interface{}) error { if err := d.Set("user", flattenMqUsers(rawUsers, d.Get("user").(*schema.Set).List())); err != nil { return fmt.Errorf("error setting user: %w", err) } - if err := d.Set("tags", keyvaluetags.MqKeyValueTags(out.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + if err := d.Set("tags", keyvaluetags.MqKeyValueTags(output.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { return fmt.Errorf("error setting tags: %w", err) } @@ -424,6 +493,21 @@ func resourceAwsMqBrokerUpdate(d *schema.ResourceData, meta interface{}) error { requiresReboot := false + if d.HasChange("ldap_server_metadata") { + input := &mq.UpdateBrokerRequest{ + BrokerId: aws.String(d.Id()), + LdapServerMetadata: nil, + } + + if v, ok := d.GetOk("ldap_server_metadata"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.LdapServerMetadata = expandMQLDAPServerMetadata(v.([]interface{})[0].(map[string]interface{})) + } + + if _, err := conn.UpdateBroker(input); err != nil { + return fmt.Errorf("error updating MQ Broker (%s) LDAP server metadata: %w", d.Id(), err) + } + } + if d.HasChange("security_groups") { _, err := conn.UpdateBroker(&mq.UpdateBrokerRequest{ BrokerId: aws.String(d.Id()), @@ -905,3 +989,88 @@ func expandMqLogs(l []interface{}) *mq.Logs { return logs } + +func flattenMQLDAPServerMetadata(apiObject *mq.LdapServerMetadataOutput) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.Hosts; v != nil { + tfMap["hosts"] = aws.StringValueSlice(v) + } + if v := apiObject.RoleBase; v != nil { + tfMap["role_base"] = aws.StringValue(v) + } + if v := apiObject.RoleName; v != nil { + tfMap["role_name"] = aws.StringValue(v) + } + if v := apiObject.RoleSearchMatching; v != nil { + tfMap["role_search_matching"] = aws.StringValue(v) + } + if v := apiObject.RoleSearchSubtree; v != nil { + tfMap["role_search_subtree"] = aws.BoolValue(v) + } + if v := apiObject.ServiceAccountUsername; v != nil { + tfMap["service_account_username"] = aws.StringValue(v) + } + if v := apiObject.UserBase; v != nil { + tfMap["user_base"] = aws.StringValue(v) + } + if v := apiObject.UserRoleName; v != nil { + tfMap["user_role_name"] = aws.StringValue(v) + } + if v := apiObject.UserSearchMatching; v != nil { + tfMap["user_search_matching"] = aws.StringValue(v) + } + if v := apiObject.UserSearchSubtree; v != nil { + tfMap["user_search_subtree"] = aws.BoolValue(v) + } + + return tfMap +} + +func expandMQLDAPServerMetadata(tfMap map[string]interface{}) *mq.LdapServerMetadataInput { + if tfMap == nil { + return nil + } + + apiObject := &mq.LdapServerMetadataInput{} + + if v, ok := tfMap["hosts"]; ok && len(v.([]interface{})) > 0 { + apiObject.Hosts = expandStringList(v.([]interface{})) + } + if v, ok := tfMap["role_base"].(string); ok && v != "" { + apiObject.RoleBase = aws.String(v) + } + if v, ok := tfMap["role_name"].(string); ok && v != "" { + apiObject.RoleName = aws.String(v) + } + if v, ok := tfMap["role_search_matching"].(string); ok && v != "" { + apiObject.RoleSearchMatching = aws.String(v) + } + if v, ok := tfMap["role_search_subtree"].(bool); ok { + apiObject.RoleSearchSubtree = aws.Bool(v) + } + if v, ok := tfMap["service_account_password"].(string); ok && v != "" { + apiObject.ServiceAccountPassword = aws.String(v) + } + if v, ok := tfMap["service_account_username"].(string); ok && v != "" { + apiObject.ServiceAccountUsername = aws.String(v) + } + if v, ok := tfMap["user_base"].(string); ok && v != "" { + apiObject.UserBase = aws.String(v) + } + if v, ok := tfMap["user_role_name"].(string); ok && v != "" { + apiObject.UserRoleName = aws.String(v) + } + if v, ok := tfMap["user_search_matching"].(string); ok && v != "" { + apiObject.UserSearchMatching = aws.String(v) + } + if v, ok := tfMap["user_search_subtree"].(bool); ok { + apiObject.UserSearchSubtree = aws.Bool(v) + } + + return apiObject +} diff --git a/aws/resource_aws_mq_broker_test.go b/aws/resource_aws_mq_broker_test.go index 2c60b18c34e..1fb77271248 100644 --- a/aws/resource_aws_mq_broker_test.go +++ b/aws/resource_aws_mq_broker_test.go @@ -269,7 +269,7 @@ func TestAccAWSMqBroker_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "maintenance_window_start_time.0.time_zone", "UTC"), resource.TestCheckResourceAttr(resourceName, "publicly_accessible", "false"), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), - resource.TestCheckResourceAttr(resourceName, "storage_type", mq.BrokerStorageTypeEfs), + resource.TestCheckResourceAttr(resourceName, "storage_type", "efs"), resource.TestCheckResourceAttr(resourceName, "subnet_ids.#", "1"), resource.TestCheckResourceAttr(resourceName, "user.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ @@ -937,13 +937,13 @@ resource "aws_security_group" "test" { } resource "aws_mq_broker" "test" { - broker_name = %[1]q + broker_name = %[2]q engine_type = "ActiveMQ" engine_version = "5.15.0" host_instance_type = "mq.t2.micro" security_groups = [aws_security_group.test.id] authentication_strategy = "simple" - storage_type = "EFS" + storage_type = "efs" logs { general = true From 73433fc65653f95a56a2cf9a011ab931fb2cff65 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 13:50:19 -0500 Subject: [PATCH 15/21] docs/resource/mq_broker: Update docs with new attributes --- website/docs/r/mq_broker.html.markdown | 31 ++++++++++++++++++++------ 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/website/docs/r/mq_broker.html.markdown b/website/docs/r/mq_broker.html.markdown index 738596499d4..2a54373699b 100644 --- a/website/docs/r/mq_broker.html.markdown +++ b/website/docs/r/mq_broker.html.markdown @@ -47,24 +47,25 @@ resource "aws_mq_broker" "example" { The following arguments are required: * `broker_name` - (Required) Name of the broker. -* `engine_type` - (Required) Type of broker engine. Valid values are `ACTIVEMQ` and `RABBITMQ`. +* `engine_type` - (Required) Type of broker engine. Valid values are `ActiveMQ` and `RabbitMQ`. * `engine_version` - (Required) Version of the broker engine. See the [AmazonMQ Broker Engine docs](https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/broker-engine.html) for supported versions. For example, `5.15.0`. * `host_instance_type` - (Required) Broker's instance type. For example, `mq.t2.micro`, `mq.m4.large`. -* `user` - (Required) Configuration block for broker users. For `engine_type` of `RABBITMQ`, Amazon MQ does not return broker users preventing this resource from making user updates and drift detection. Detailed below. +* `user` - (Required) Configuration block for broker users. For `engine_type` of `RabbitMQ`, Amazon MQ does not return broker users preventing this resource from making user updates and drift detection. Detailed below. The following arguments are optional: * `apply_immediately` - (Optional) Specifies whether any broker modifications are applied immediately, or during the next maintenance window. Default is `false`. -* `authentication_strategy` - (Optional) Authentication strategy used to secure the broker. Valid values are `SIMPLE` and `LDAP`. `LDAP` is not supported for `engine_type` `RABBITMQ`. +* `authentication_strategy` - (Optional) Authentication strategy used to secure the broker. Valid values are `simple` and `ldap`. `ldap` is not supported for `engine_type` `RabbitMQ`. * `auto_minor_version_upgrade` - (Optional) Whether to automatically upgrade to new minor versions of brokers as Amazon MQ makes releases available. -* `configuration` - (Optional) Configuration block for broker configuration. Applies to `engine_type` of `ACTIVEMQ` only. Detailed below. +* `configuration` - (Optional) Configuration block for broker configuration. Applies to `engine_type` of `ActiveMQ` only. Detailed below. * `deployment_mode` - (Optional) Deployment mode of the broker. Valid values are `SINGLE_INSTANCE`, `ACTIVE_STANDBY_MULTI_AZ`, and `CLUSTER_MULTI_AZ`. Default is `SINGLE_INSTANCE`. * `encryption_options` - (Optional) Configuration block containing encryption options. Detailed below. +* `ldap_server_metadata` - (Optional) Configuration block for the LDAP server used to authenticate and authorize connections to the broker. Not supported for `engine_type` `RabbitMQ`. Detailed below. * `logs` - (Optional) Configuration block for the logging configuration of the broker. Detailed below. * `maintenance_window_start_time` - (Optional) Configuration block for the maintenance window start time. Detailed below. * `publicly_accessible` - (Optional) Whether to enable connections from applications outside of the VPC that hosts the broker's subnets. * `security_groups` - (Optional) List of security group IDs assigned to the broker. -* `storage_type` - (Optional) Storage type of the broker. For `engine_type` `ACTIVEMQ`, the valid values are `EFS` and `EBS`. For `engine_type` of `RABBITMQ`, only `EBS` is supported. +* `storage_type` - (Optional) Storage type of the broker. For `engine_type` `ActiveMQ`, the valid values are `efs` and `ebs`. For `engine_type` `RabbitMQ`, only `EBS` is supported. * `subnet_ids` - (Optional) List of subnet IDs in which to launch the broker. A `SINGLE_INSTANCE` deployment requires one subnet. An `ACTIVE_STANDBY_MULTI_AZ` deployment requires multiple subnets. * `tags` - (Optional) Map of tags to assign to the broker. @@ -82,11 +83,27 @@ The following arguments are optional: * `kms_key_id` - (Optional) Amazon Resource Name (ARN) of Key Management Service (KMS) Customer Master Key (CMK) to use for encryption at rest. Requires setting `use_aws_owned_key` to `false`. To perform drift detection when AWS-managed CMKs or customer-managed CMKs are in use, this value must be configured. * `use_aws_owned_key` - (Optional) Whether to enable an AWS-owned KMS CMK that is not in your account. Defaults to `true`. Setting to `false` without configuring `kms_key_id` will create an AWS-managed CMK aliased to `aws/mq` in your account. +### ldap_server_metadata + +The following arguments are optional: + +* `hosts` - (Optional) List of a fully qualified domain name of the LDAP server and an optional failover server. +* `role_base` - (Optional) Fully qualified name of the directory to search for a user’s groups. +* `role_name` - (Optional) Specifies the LDAP attribute that identifies the group name attribute in the object returned from the group membership query. +* `role_search_matching` - (Optional) Search criteria for groups. +* `role_search_subtree` - (Optional) Whether the directory search scope is the entire sub-tree. +* `service_account_password` - (Optional) Service account password. +* `service_account_username` - (Optional) Service account username. +* `user_base` - (Optional) Fully qualified name of the directory where you want to search for users. +* `user_role_name` - (Optional) Specifies the name of the LDAP attribute for the user group membership. +* `user_search_matching` - (Optional) Search criteria for users. +* `user_search_subtree` - (Optional) Whether the directory search scope is the entire sub-tree. + ### logs The following arguments are optional: -* `audit` - (Optional) Enables audit logging. Auditing is only possible for `engine_type` of `ACTIVEMQ`. User management action made using JMX or the ActiveMQ Web Console is logged. Defaults to `false`. +* `audit` - (Optional) Enables audit logging. Auditing is only possible for `engine_type` of `ActiveMQ`. User management action made using JMX or the ActiveMQ Web Console is logged. Defaults to `false`. * `general` - (Optional) Enables general logging via CloudWatch. Defaults to `false`. ### maintenance_window_start_time @@ -102,7 +119,7 @@ The following arguments are required: ### user * `console_access` - (Optional) Whether to enable access to the [ActiveMQ Web Console](http://activemq.apache.org/web-console.html) for the user. Applies to `engine_type` of `ActiveMQ` only. -* `groups` - (Optional) List of groups (20 maximum) to which the ActiveMQ user belongs. Applies to `engine_type` of `ACTIVEMQ` only. +* `groups` - (Optional) List of groups (20 maximum) to which the ActiveMQ user belongs. Applies to `engine_type` of `ActiveMQ` only. * `password` - (Required) Password of the user. It must be 12 to 250 characters long, at least 4 unique characters, and must not contain commas. * `username` - (Required) Username of the user. From 7798234535291f574595032107b9acad12b6aaf3 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 14:00:46 -0500 Subject: [PATCH 16/21] resource/mq_broker: Avoid nil pointer panic --- aws/resource_aws_mq_broker.go | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/aws/resource_aws_mq_broker.go b/aws/resource_aws_mq_broker.go index 9e2eb0c3936..54253bb252a 100644 --- a/aws/resource_aws_mq_broker.go +++ b/aws/resource_aws_mq_broker.go @@ -10,6 +10,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/mq" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -392,19 +393,19 @@ func resourceAwsMqBrokerRead(d *schema.ResourceData, meta interface{}) error { output, err := conn.DescribeBroker(&mq.DescribeBrokerInput{ BrokerId: aws.String(d.Id()), }) + + if !d.IsNewResource() && (tfawserr.ErrCodeEquals(err, "NotFoundException") || tfawserr.ErrCodeEquals(err, mq.ErrCodeForbiddenException)) { + log.Printf("[WARN] MQ broker (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + if err != nil { - if isAWSErr(err, mq.ErrCodeNotFoundException, "") { - log.Printf("[WARN] MQ Broker %q not found, removing from state", d.Id()) - d.SetId("") - return nil - } - // API docs say a 404 can also return a 403 - if isAWSErr(err, mq.ErrCodeForbiddenException, "Forbidden") { - log.Printf("[WARN] MQ Broker %q not found, removing from state", d.Id()) - d.SetId("") - return nil - } - return err + return fmt.Errorf("error reading MQ broker (%s): %w", d.Id(), err) + } + + if output == nil { + return fmt.Errorf("empty response while reading MQ broker (%s)", d.Id()) } d.Set("arn", output.BrokerArn) @@ -421,7 +422,7 @@ func resourceAwsMqBrokerRead(d *schema.ResourceData, meta interface{}) error { d.Set("storage_type", output.StorageType) d.Set("subnet_ids", aws.StringValueSlice(output.SubnetIds)) - if output.Configurations.Current != nil { + if output.Configurations != nil && output.Configurations.Current != nil { if err := d.Set("configuration", flattenMqConfigurationId(output.Configurations.Current)); err != nil { return fmt.Errorf("error setting configuration: %w", err) } From ed284c73ebcf7056293c9fd6c12929804917a6e1 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 14:29:22 -0500 Subject: [PATCH 17/21] resource/mq_broker: Move state waiting to waiter --- aws/internal/service/mq/waiter/status.go | 13 ++++-- aws/internal/service/mq/waiter/waiter.go | 40 +++++++++++++++++ aws/resource_aws_mq_broker.go | 57 +++--------------------- aws/resource_aws_mq_broker_test.go | 4 +- 4 files changed, 58 insertions(+), 56 deletions(-) diff --git a/aws/internal/service/mq/waiter/status.go b/aws/internal/service/mq/waiter/status.go index 23ae61330b1..a33f0b462fe 100644 --- a/aws/internal/service/mq/waiter/status.go +++ b/aws/internal/service/mq/waiter/status.go @@ -3,9 +3,14 @@ package waiter import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/mq" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) +const ( + BrokerNotFoundStatus = "NotFound" +) + func BrokerStatus(conn *mq.MQ, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { input := &mq.DescribeBrokerInput{ @@ -14,12 +19,12 @@ func BrokerStatus(conn *mq.MQ, id string) resource.StateRefreshFunc { output, err := conn.DescribeBroker(input) - if err != nil { - return nil, aws.StringValue(output.BrokerState), err + if err != nil && tfawserr.ErrCodeEquals(err, "NotFoundException") { + return nil, BrokerNotFoundStatus, nil } - if output == nil { - return output, aws.StringValue(output.BrokerState), nil + if err != nil { + return nil, aws.StringValue(output.BrokerState), err } return output, aws.StringValue(output.BrokerState), nil diff --git a/aws/internal/service/mq/waiter/waiter.go b/aws/internal/service/mq/waiter/waiter.go index e6dead8714f..ea0b5039924 100644 --- a/aws/internal/service/mq/waiter/waiter.go +++ b/aws/internal/service/mq/waiter/waiter.go @@ -9,6 +9,8 @@ import ( const ( BrokerCreateTimeout = 30 * time.Minute + BrokerDeleteTimeout = 30 * time.Minute + BrokerRebootTimeout = 30 * time.Minute ) func BrokerCreated(conn *mq.MQ, id string) (*mq.DescribeBrokerResponse, error) { @@ -29,3 +31,41 @@ func BrokerCreated(conn *mq.MQ, id string) (*mq.DescribeBrokerResponse, error) { return nil, err } + +func BrokerDeleted(conn *mq.MQ, id string) (*mq.DescribeBrokerResponse, error) { + stateConf := resource.StateChangeConf{ + Pending: []string{ + mq.BrokerStateRunning, + mq.BrokerStateRebootInProgress, + mq.BrokerStateDeletionInProgress, + }, + Target: []string{BrokerNotFoundStatus}, + Timeout: BrokerDeleteTimeout, + Refresh: BrokerStatus(conn, id), + } + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*mq.DescribeBrokerResponse); ok { + return output, err + } + + return nil, err +} + +func BrokerRebooted(conn *mq.MQ, id string) (*mq.DescribeBrokerResponse, error) { + stateConf := resource.StateChangeConf{ + Pending: []string{ + mq.BrokerStateRebootInProgress, + }, + Target: []string{mq.BrokerStateRunning}, + Timeout: BrokerRebootTimeout, + Refresh: BrokerStatus(conn, id), + } + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*mq.DescribeBrokerResponse); ok { + return output, err + } + + return nil, err +} diff --git a/aws/resource_aws_mq_broker.go b/aws/resource_aws_mq_broker.go index 54253bb252a..d25eb7742c1 100644 --- a/aws/resource_aws_mq_broker.go +++ b/aws/resource_aws_mq_broker.go @@ -6,7 +6,6 @@ import ( "log" "reflect" "strings" - "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/mq" @@ -556,27 +555,8 @@ func resourceAwsMqBrokerUpdate(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("error rebooting MQ Broker (%s): %w", d.Id(), err) } - stateConf := resource.StateChangeConf{ - Pending: []string{ - mq.BrokerStateRunning, - mq.BrokerStateRebootInProgress, - }, - Target: []string{mq.BrokerStateRunning}, - Timeout: 30 * time.Minute, - Refresh: func() (interface{}, string, error) { - out, err := conn.DescribeBroker(&mq.DescribeBrokerInput{ - BrokerId: aws.String(d.Id()), - }) - if err != nil { - return 42, "", err - } - - return out, *out.BrokerState, nil - }, - } - _, err = stateConf.WaitForState() - if err != nil { - return err + if _, err := waiter.BrokerRebooted(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for MQ Broker (%s) reboot: %w", d.Id(), err) } } @@ -602,7 +582,11 @@ func resourceAwsMqBrokerDelete(d *schema.ResourceData, meta interface{}) error { return err } - return waitForMqBrokerDeletion(conn, d.Id()) + if _, err := waiter.BrokerDeleted(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for MQ Broker (%s) deletion: %w", d.Id(), err) + } + + return nil } func resourceAwsMqUserHash(v interface{}) int { @@ -625,33 +609,6 @@ func resourceAwsMqUserHash(v interface{}) int { return hashcode.String(buf.String()) } -func waitForMqBrokerDeletion(conn *mq.MQ, id string) error { - stateConf := resource.StateChangeConf{ - Pending: []string{ - mq.BrokerStateRunning, - mq.BrokerStateRebootInProgress, - mq.BrokerStateDeletionInProgress, - }, - Target: []string{""}, - Timeout: 30 * time.Minute, - Refresh: func() (interface{}, string, error) { - out, err := conn.DescribeBroker(&mq.DescribeBrokerInput{ - BrokerId: aws.String(id), - }) - if err != nil { - if isAWSErr(err, "NotFoundException", "") { - return 42, "", nil - } - return 42, "", err - } - - return out, *out.BrokerState, nil - }, - } - _, err := stateConf.WaitForState() - return err -} - func updateAwsMqBrokerUsers(conn *mq.MQ, bId string, oldUsers, newUsers []interface{}) (bool, error) { // If there are any user creates/deletes/updates, updatedUsers will be set to true updatedUsers := false diff --git a/aws/resource_aws_mq_broker_test.go b/aws/resource_aws_mq_broker_test.go index 1fb77271248..90c57a0be73 100644 --- a/aws/resource_aws_mq_broker_test.go +++ b/aws/resource_aws_mq_broker_test.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/mq/waiter" ) func init() { @@ -102,8 +103,7 @@ func testSweepMqBrokers(region string) error { if err != nil { return err } - err = waitForMqBrokerDeletion(conn, *bs.BrokerId) - if err != nil { + if _, err = waiter.BrokerDeleted(conn, aws.StringValue(bs.BrokerId)); err != nil { return err } } From 05a5bc089aba0fe5703060b31de877ddfad9e2bc Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 15:01:09 -0500 Subject: [PATCH 18/21] resource/mq_broker: Fix waiter --- aws/internal/service/mq/waiter/status.go | 20 +++++++++----------- aws/internal/service/mq/waiter/waiter.go | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/aws/internal/service/mq/waiter/status.go b/aws/internal/service/mq/waiter/status.go index a33f0b462fe..a7c34267fe9 100644 --- a/aws/internal/service/mq/waiter/status.go +++ b/aws/internal/service/mq/waiter/status.go @@ -7,24 +7,22 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) -const ( - BrokerNotFoundStatus = "NotFound" -) - func BrokerStatus(conn *mq.MQ, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - input := &mq.DescribeBrokerInput{ + output, err := conn.DescribeBroker(&mq.DescribeBrokerInput{ BrokerId: aws.String(id), - } + }) - output, err := conn.DescribeBroker(input) - - if err != nil && tfawserr.ErrCodeEquals(err, "NotFoundException") { - return nil, BrokerNotFoundStatus, nil + if tfawserr.ErrCodeEquals(err, mq.ErrCodeNotFoundException) { + return nil, "", nil } if err != nil { - return nil, aws.StringValue(output.BrokerState), err + return nil, "", err + } + + if output == nil { + return nil, "", nil } return output, aws.StringValue(output.BrokerState), nil diff --git a/aws/internal/service/mq/waiter/waiter.go b/aws/internal/service/mq/waiter/waiter.go index ea0b5039924..3bc226dc58d 100644 --- a/aws/internal/service/mq/waiter/waiter.go +++ b/aws/internal/service/mq/waiter/waiter.go @@ -39,7 +39,7 @@ func BrokerDeleted(conn *mq.MQ, id string) (*mq.DescribeBrokerResponse, error) { mq.BrokerStateRebootInProgress, mq.BrokerStateDeletionInProgress, }, - Target: []string{BrokerNotFoundStatus}, + Target: []string{}, Timeout: BrokerDeleteTimeout, Refresh: BrokerStatus(conn, id), } From 1a4e8380173dfcd0a0a45ec1fbd615b2a836bf08 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 15:09:07 -0500 Subject: [PATCH 19/21] resource/mq_broker: Update changelog --- .changelog/16108.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changelog/16108.txt b/.changelog/16108.txt index 63c78194a52..8585c63642c 100644 --- a/.changelog/16108.txt +++ b/.changelog/16108.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/aws_mq_broker: Add RabbitMQ as option for `engine_type` +resource/aws_mq_broker: Add RabbitMQ as option for `engine_type`, and new arguments `authentication_strategy`, `ldap_server_metadata`, and `storage_type`. Improve handling of eventual consistency. ``` \ No newline at end of file From 785535a86a1c59c05d6a48910b8e24020c2385aa Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 15:32:31 -0500 Subject: [PATCH 20/21] tests/resource/mq_broker: Allow sweeper to retry in some cases --- aws/resource_aws_mq_broker_test.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_mq_broker_test.go b/aws/resource_aws_mq_broker_test.go index 90c57a0be73..a68b006ce94 100644 --- a/aws/resource_aws_mq_broker_test.go +++ b/aws/resource_aws_mq_broker_test.go @@ -3,12 +3,15 @@ package aws import ( "fmt" "log" + "math/rand" "regexp" "strings" "testing" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/mq" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -95,11 +98,27 @@ func testSweepMqBrokers(region string) error { } log.Printf("[DEBUG] %d MQ brokers found", len(resp.BrokerSummaries)) + rand.Seed(time.Now().UnixNano()) + rand.Shuffle(len(resp.BrokerSummaries), func(i, j int) { + resp.BrokerSummaries[i], resp.BrokerSummaries[j] = resp.BrokerSummaries[j], resp.BrokerSummaries[i] + }) + for _, bs := range resp.BrokerSummaries { - log.Printf("[INFO] Deleting MQ broker %s", *bs.BrokerId) + log.Printf("[INFO] Deleting MQ broker %s", aws.StringValue(bs.BrokerId)) _, err := conn.DeleteBroker(&mq.DeleteBrokerInput{ BrokerId: bs.BrokerId, }) + if tfawserr.ErrMessageContains(err, mq.ErrCodeBadRequestException, "while in state [CREATION_IN_PROGRESS") { + log.Printf("[WARN] Broker in state CREATION_IN_PROGRESS and must complete creation before deletion") + if _, err = waiter.BrokerCreated(conn, aws.StringValue(bs.BrokerId)); err != nil { + return err + } + + log.Printf("[WARN] Retrying deletion of broker %s", aws.StringValue(bs.BrokerId)) + _, err = conn.DeleteBroker(&mq.DeleteBrokerInput{ + BrokerId: bs.BrokerId, + }) + } if err != nil { return err } From b0df885da7033941d48d2c715d057f46d87596da Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 5 Mar 2021 18:28:57 -0500 Subject: [PATCH 21/21] resource/mq_broker: Use const for error code --- aws/resource_aws_mq_broker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_mq_broker.go b/aws/resource_aws_mq_broker.go index d25eb7742c1..623b6dd1e6f 100644 --- a/aws/resource_aws_mq_broker.go +++ b/aws/resource_aws_mq_broker.go @@ -393,7 +393,7 @@ func resourceAwsMqBrokerRead(d *schema.ResourceData, meta interface{}) error { BrokerId: aws.String(d.Id()), }) - if !d.IsNewResource() && (tfawserr.ErrCodeEquals(err, "NotFoundException") || tfawserr.ErrCodeEquals(err, mq.ErrCodeForbiddenException)) { + if !d.IsNewResource() && (tfawserr.ErrCodeEquals(err, mq.ErrCodeNotFoundException) || tfawserr.ErrCodeEquals(err, mq.ErrCodeForbiddenException)) { log.Printf("[WARN] MQ broker (%s) not found, removing from state", d.Id()) d.SetId("") return nil