diff --git a/.changelog/20565.txt b/.changelog/20565.txt new file mode 100644 index 00000000000..24f612da966 --- /dev/null +++ b/.changelog/20565.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_chime_voice_connector_group +``` \ No newline at end of file diff --git a/aws/provider.go b/aws/provider.go index 1beb5e9c100..21be0ead53e 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -549,6 +549,7 @@ func Provider() *schema.Provider { "aws_budgets_budget": resourceAwsBudgetsBudget(), "aws_budgets_budget_action": resourceAwsBudgetsBudgetAction(), "aws_chime_voice_connector": resourceAwsChimeVoiceConnector(), + "aws_chime_voice_connector_group": resourceAwsChimeVoiceConnectorGroup(), "aws_cloud9_environment_ec2": resourceAwsCloud9EnvironmentEc2(), "aws_cloudformation_stack": resourceAwsCloudFormationStack(), "aws_cloudformation_stack_set": resourceAwsCloudFormationStackSet(), diff --git a/aws/resource_aws_chime_voice_connector_group.go b/aws/resource_aws_chime_voice_connector_group.go new file mode 100644 index 00000000000..594218cc525 --- /dev/null +++ b/aws/resource_aws_chime_voice_connector_group.go @@ -0,0 +1,177 @@ +package aws + +import ( + "context" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/chime" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceAwsChimeVoiceConnectorGroup() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceAwsChimeVoiceConnectorGroupCreate, + ReadContext: resourceAwsChimeVoiceConnectorGroupRead, + UpdateContext: resourceAwsChimeVoiceConnectorGroupUpdate, + DeleteContext: resourceAwsChimeVoiceConnectorGroupDelete, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "connector": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 3, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "voice_connector_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 256), + }, + "priority": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 99), + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 256), + }, + }, + } +} + +func resourceAwsChimeVoiceConnectorGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).chimeconn + + input := &chime.CreateVoiceConnectorGroupInput{ + Name: aws.String(d.Get("name").(string)), + } + + if v, ok := d.GetOk("connector"); ok && v.(*schema.Set).Len() > 0 { + input.VoiceConnectorItems = expandVoiceConnectorItems(v.(*schema.Set).List()) + } + + resp, err := conn.CreateVoiceConnectorGroupWithContext(ctx, input) + if err != nil || resp.VoiceConnectorGroup == nil { + return diag.Errorf("error creating Chime Voice Connector group: %s", err) + } + + d.SetId(aws.StringValue(resp.VoiceConnectorGroup.VoiceConnectorGroupId)) + + return resourceAwsChimeVoiceConnectorGroupRead(ctx, d, meta) +} + +func resourceAwsChimeVoiceConnectorGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).chimeconn + + getInput := &chime.GetVoiceConnectorGroupInput{ + VoiceConnectorGroupId: aws.String(d.Id()), + } + + resp, err := conn.GetVoiceConnectorGroupWithContext(ctx, getInput) + if !d.IsNewResource() && isAWSErr(err, chime.ErrCodeNotFoundException, "") { + log.Printf("[WARN] Chime Voice conector group %s not found", d.Id()) + d.SetId("") + return nil + } + if err != nil || resp.VoiceConnectorGroup == nil { + return diag.Errorf("error getting Chime Voice Connector group (%s): %s", d.Id(), err) + } + + d.Set("name", resp.VoiceConnectorGroup.Name) + + if err := d.Set("connector", flattenVoiceConnectorItems(resp.VoiceConnectorGroup.VoiceConnectorItems)); err != nil { + return diag.Errorf("error setting Chime Voice Connector group items (%s): %s", d.Id(), err) + } + return nil +} + +func resourceAwsChimeVoiceConnectorGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).chimeconn + + input := &chime.UpdateVoiceConnectorGroupInput{ + Name: aws.String(d.Get("name").(string)), + VoiceConnectorGroupId: aws.String(d.Id()), + } + + if d.HasChange("connector") { + if v, ok := d.GetOk("connector"); ok { + input.VoiceConnectorItems = expandVoiceConnectorItems(v.(*schema.Set).List()) + } + } else if !d.IsNewResource() { + input.VoiceConnectorItems = make([]*chime.VoiceConnectorItem, 0) + } + + if _, err := conn.UpdateVoiceConnectorGroupWithContext(ctx, input); err != nil { + if isAWSErr(err, chime.ErrCodeNotFoundException, "") { + log.Printf("[WARN] Chime Voice conector group %s not found", d.Id()) + d.SetId("") + return nil + } + return diag.Errorf("error updating Chime Voice Connector group (%s): %s", d.Id(), err) + } + + return resourceAwsChimeVoiceConnectorGroupRead(ctx, d, meta) +} + +func resourceAwsChimeVoiceConnectorGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).chimeconn + + if v, ok := d.GetOk("connector"); ok && v.(*schema.Set).Len() > 0 { + if err := resourceAwsChimeVoiceConnectorGroupUpdate(ctx, d, meta); err != nil { + return err + } + } + + input := &chime.DeleteVoiceConnectorGroupInput{ + VoiceConnectorGroupId: aws.String(d.Id()), + } + + if _, err := conn.DeleteVoiceConnectorGroupWithContext(ctx, input); err != nil { + if isAWSErr(err, chime.ErrCodeNotFoundException, "") { + log.Printf("[WARN] Chime Voice conector group %s not found", d.Id()) + return nil + } + return diag.Errorf("error deleting Chime Voice Connector group (%s): %s", d.Id(), err) + } + + return nil +} + +func expandVoiceConnectorItems(data []interface{}) []*chime.VoiceConnectorItem { + var connectorsItems []*chime.VoiceConnectorItem + + for _, rItem := range data { + item := rItem.(map[string]interface{}) + connectorsItems = append(connectorsItems, &chime.VoiceConnectorItem{ + VoiceConnectorId: aws.String(item["voice_connector_id"].(string)), + Priority: aws.Int64(int64(item["priority"].(int))), + }) + } + + return connectorsItems +} + +func flattenVoiceConnectorItems(connectors []*chime.VoiceConnectorItem) []interface{} { + var rawConnectors []interface{} + + for _, c := range connectors { + rawC := map[string]interface{}{ + "priority": aws.Int64Value(c.Priority), + "voice_connector_id": aws.StringValue(c.VoiceConnectorId), + } + rawConnectors = append(rawConnectors, rawC) + } + return rawConnectors +} diff --git a/aws/resource_aws_chime_voice_connector_group_test.go b/aws/resource_aws_chime_voice_connector_group_test.go new file mode 100644 index 00000000000..0b9c66e0e73 --- /dev/null +++ b/aws/resource_aws_chime_voice_connector_group_test.go @@ -0,0 +1,184 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/chime" + "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/terraform" +) + +func TestAccAWSChimeVoiceConnectorGroup_basic(t *testing.T) { + var voiceConnectorGroup *chime.VoiceConnectorGroup + + vcgName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_chime_voice_connector_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, chime.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSChimeVoiceConnectorGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSChimeVoiceConnectorGroupConfig(vcgName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSChimeVoiceConnectorGroupExists(resourceName, voiceConnectorGroup), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("vcg-%s", vcgName)), + resource.TestCheckResourceAttr(resourceName, "connector.#", "1"), + resource.TestCheckResourceAttr(resourceName, "connector.0.priority", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSChimeVoiceConnectorGroup_disappears(t *testing.T) { + var voiceConnectorGroup *chime.VoiceConnectorGroup + + vcgName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_chime_voice_connector_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, chime.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSChimeVoiceConnectorGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSChimeVoiceConnectorGroupConfig(vcgName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSChimeVoiceConnectorGroupExists(resourceName, voiceConnectorGroup), + testAccCheckResourceDisappears(testAccProvider, resourceAwsChimeVoiceConnectorGroup(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSChimeVoiceConnectorGroup_update(t *testing.T) { + var voiceConnectorGroup *chime.VoiceConnectorGroup + + vcgName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_chime_voice_connector_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, chime.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSChimeVoiceConnectorGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSChimeVoiceConnectorGroupConfig(vcgName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSChimeVoiceConnectorGroupExists(resourceName, voiceConnectorGroup), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("vcg-%s", vcgName)), + resource.TestCheckResourceAttr(resourceName, "connector.#", "1"), + ), + }, + { + Config: testAccAWSChimeVoiceConnectorGroupUpdated(vcgName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("vcg-updated-%s", vcgName)), + resource.TestCheckResourceAttr(resourceName, "connector.0.priority", "10"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAWSChimeVoiceConnectorGroupConfig(name string) string { + return fmt.Sprintf(` +resource "aws_chime_voice_connector" "chime" { + name = "vc-%[1]s" + require_encryption = true +} + +resource "aws_chime_voice_connector_group" "test" { + name = "vcg-%[1]s" + + connector { + voice_connector_id = aws_chime_voice_connector.chime.id + priority = 1 + } +} +`, name) +} + +func testAccAWSChimeVoiceConnectorGroupUpdated(name string) string { + return fmt.Sprintf(` +resource "aws_chime_voice_connector" "chime" { + name = "vc-%[1]s" + require_encryption = false +} + +resource "aws_chime_voice_connector_group" "test" { + name = "vcg-updated-%[1]s" + + connector { + voice_connector_id = aws_chime_voice_connector.chime.id + priority = 10 + } +} +`, name) +} + +func testAccCheckAWSChimeVoiceConnectorGroupExists(name string, vc *chime.VoiceConnectorGroup) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("not found: %s", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no Chime voice connector group ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).chimeconn + input := &chime.GetVoiceConnectorGroupInput{ + VoiceConnectorGroupId: aws.String(rs.Primary.ID), + } + + resp, err := conn.GetVoiceConnectorGroup(input) + if err != nil || resp.VoiceConnectorGroup == nil { + return err + } + + vc = resp.VoiceConnectorGroup + return nil + } +} + +func testAccCheckAWSChimeVoiceConnectorGroupDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_chime_voice_connector" { + continue + } + conn := testAccProvider.Meta().(*AWSClient).chimeconn + input := &chime.GetVoiceConnectorGroupInput{ + VoiceConnectorGroupId: aws.String(rs.Primary.ID), + } + resp, err := conn.GetVoiceConnectorGroup(input) + if err == nil { + if resp.VoiceConnectorGroup != nil && aws.StringValue(resp.VoiceConnectorGroup.Name) != "" { + return fmt.Errorf("error Chime Voice Connector still exists") + } + } + return nil + } + return nil +} diff --git a/website/docs/r/chime_voice_connector_group.html.markdown b/website/docs/r/chime_voice_connector_group.html.markdown new file mode 100644 index 00000000000..ed8e9991be5 --- /dev/null +++ b/website/docs/r/chime_voice_connector_group.html.markdown @@ -0,0 +1,71 @@ +--- +subcategory: "Chime" +layout: "aws" +page_title: "AWS: aws_chime_voice_connector_group" +description: |- + Creates an Amazon Chime Voice Connector group under the administrator's AWS account. +--- + +# Resource: aws_chime_voice_connector_group + +Creates an Amazon Chime Voice Connector group under the administrator's AWS account. You can associate Amazon Chime Voice Connectors with the Amazon Chime Voice Connector group by including VoiceConnectorItems in the request. + +You can include Amazon Chime Voice Connectors from different AWS Regions in your group. This creates a fault tolerant mechanism for fallback in case of availability events. + +## Example Usage + +```terraform +resource "aws_chime_voice_connector" "vc1" { + name = "connector-test-1" + require_encryption = true + aws_region = "us-east-1" +} + +resource "aws_chime_voice_connector" "vc2" { + name = "connector-test-2" + require_encryption = true + aws_region = "us-west-2" +} + +resource "aws_chime_voice_connector_group" "group" { + name = "test-group" + + connector { + voice_connector_id = aws_chime_voice_connector.vc1.id + priority = 1 + } + + connector { + voice_connector_id = aws_chime_voice_connector.vc2.id + priority = 3 + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Amazon Chime Voice Connector group. +* `connector` - (Optional) The Amazon Chime Voice Connectors to route inbound calls to. + +### `connector` + +For Amazon Chime Voice Connector groups, the Amazon Chime Voice Connectors to which to route inbound calls. Includes priority configuration settings. Limit: 3 VoiceConnectorItems per Amazon Chime Voice Connector group. + +* `voice_connector_id` - (Required) The Amazon Chime Voice Connector ID. +* `priority` - (Required) The priority associated with the Amazon Chime Voice Connector, with 1 being the highest priority. Higher priority Amazon Chime Voice Connectors are attempted first. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - Amazon Chime Voice Connector group ID. + +## Import + +Configuration Recorder can be imported using the name, e.g. + +``` +$ terraform import aws_chime_voice_connector_group.default example +```