diff --git a/.changelog/19504.txt b/.changelog/19504.txt new file mode 100644 index 000000000000..268780f68da9 --- /dev/null +++ b/.changelog/19504.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_chime_voice_connector +``` \ No newline at end of file diff --git a/aws/provider.go b/aws/provider.go index f9a7d1f47a13..886cb4927a76 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -548,6 +548,7 @@ func Provider() *schema.Provider { "aws_backup_vault_policy": resourceAwsBackupVaultPolicy(), "aws_budgets_budget": resourceAwsBudgetsBudget(), "aws_budgets_budget_action": resourceAwsBudgetsBudgetAction(), + "aws_chime_voice_connector": resourceAwsChimeVoiceConnector(), "aws_cloud9_environment_ec2": resourceAwsCloud9EnvironmentEc2(), "aws_cloudformation_stack": resourceAwsCloudFormationStack(), "aws_cloudformation_stack_set": resourceAwsCloudFormationStackSet(), diff --git a/aws/resource_aws_chime_voice_connector.go b/aws/resource_aws_chime_voice_connector.go new file mode 100644 index 000000000000..53a6e19edbf0 --- /dev/null +++ b/aws/resource_aws_chime_voice_connector.go @@ -0,0 +1,135 @@ +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 resourceAwsChimeVoiceConnector() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceAwsChimeVoiceConnectorCreate, + ReadContext: resourceAwsChimeVoiceConnectorRead, + UpdateContext: resourceAwsChimeVoiceConnectorUpdate, + DeleteContext: resourceAwsChimeVoiceConnectorDelete, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "aws_region": { + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Default: chime.VoiceConnectorAwsRegionUsEast1, + ValidateFunc: validation.StringInSlice([]string{chime.VoiceConnectorAwsRegionUsEast1, chime.VoiceConnectorAwsRegionUsWest2}, false), + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.NoZeroValues, + }, + "outbound_host_name": { + Type: schema.TypeString, + Computed: true, + }, + "require_encryption": { + Type: schema.TypeBool, + Required: true, + }, + }, + } +} + +func resourceAwsChimeVoiceConnectorCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).chimeconn + + createInput := &chime.CreateVoiceConnectorInput{ + Name: aws.String(d.Get("name").(string)), + RequireEncryption: aws.Bool(d.Get("require_encryption").(bool)), + } + + if v, ok := d.GetOk("aws_region"); ok { + createInput.AwsRegion = aws.String(v.(string)) + } + + resp, err := conn.CreateVoiceConnectorWithContext(ctx, createInput) + if err != nil || resp.VoiceConnector == nil { + return diag.Errorf("Error creating Chime Voice connector: %s", err) + } + + d.SetId(aws.StringValue(resp.VoiceConnector.VoiceConnectorId)) + + return resourceAwsChimeVoiceConnectorRead(ctx, d, meta) +} + +func resourceAwsChimeVoiceConnectorRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).chimeconn + + getInput := &chime.GetVoiceConnectorInput{ + VoiceConnectorId: aws.String(d.Id()), + } + + resp, err := conn.GetVoiceConnectorWithContext(ctx, getInput) + if !d.IsNewResource() && isAWSErr(err, chime.ErrCodeNotFoundException, "") { + log.Printf("[WARN] Chime Voice connector %s not found", d.Id()) + d.SetId("") + return nil + } + + if err != nil || resp.VoiceConnector == nil { + return diag.Errorf("Error getting Voice connector (%s): %s", d.Id(), err) + } + + d.Set("aws_region", resp.VoiceConnector.AwsRegion) + d.Set("outbound_host_name", resp.VoiceConnector.OutboundHostName) + d.Set("require_encryption", resp.VoiceConnector.RequireEncryption) + d.Set("name", resp.VoiceConnector.Name) + + return nil +} + +func resourceAwsChimeVoiceConnectorUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).chimeconn + + if d.HasChanges("name", "require_encryption") { + updateInput := &chime.UpdateVoiceConnectorInput{ + VoiceConnectorId: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + RequireEncryption: aws.Bool(d.Get("require_encryption").(bool)), + } + + if _, err := conn.UpdateVoiceConnectorWithContext(ctx, updateInput); err != nil { + if isAWSErr(err, chime.ErrCodeNotFoundException, "") { + log.Printf("[WARN] Chime Voice connector %s not found", d.Id()) + d.SetId("") + return nil + } + return diag.Errorf("Error updating Voice connector (%s): %s", d.Id(), err) + } + } + return resourceAwsChimeVoiceConnectorRead(ctx, d, meta) +} + +func resourceAwsChimeVoiceConnectorDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).chimeconn + + input := &chime.DeleteVoiceConnectorInput{ + VoiceConnectorId: aws.String(d.Id()), + } + + if _, err := conn.DeleteVoiceConnectorWithContext(ctx, input); err != nil { + if isAWSErr(err, chime.ErrCodeNotFoundException, "") { + log.Printf("[WARN] Chime Voice connector %s not found", d.Id()) + return nil + } + return diag.Errorf("Error deleting Voice connector (%s)", d.Id()) + } + return nil +} diff --git a/aws/resource_aws_chime_voice_connector_test.go b/aws/resource_aws_chime_voice_connector_test.go new file mode 100644 index 000000000000..061f2d6c4f29 --- /dev/null +++ b/aws/resource_aws_chime_voice_connector_test.go @@ -0,0 +1,166 @@ +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 TestAccAWSChimeVoiceConnector_basic(t *testing.T) { + var voiceConnector *chime.VoiceConnector + + vcName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_chime_voice_connector.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, chime.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSChimeVoiceConnectorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSChimeVoiceConnectorConfig(vcName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSChimeVoiceConnectorExists(resourceName, voiceConnector), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("vc-%s", vcName)), + resource.TestCheckResourceAttr(resourceName, "aws_region", chime.VoiceConnectorAwsRegionUsEast1), + resource.TestCheckResourceAttr(resourceName, "require_encryption", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSChimeVoiceConnector_disappears(t *testing.T) { + var voiceConnector *chime.VoiceConnector + + vcName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_chime_voice_connector.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, chime.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSChimeVoiceConnectorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSChimeVoiceConnectorConfig(vcName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSChimeVoiceConnectorExists(resourceName, voiceConnector), + testAccCheckResourceDisappears(testAccProvider, resourceAwsChimeVoiceConnector(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccAWSChimeVoiceConnector_update(t *testing.T) { + var voiceConnector *chime.VoiceConnector + + vcName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_chime_voice_connector.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, chime.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSChimeVoiceConnectorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSChimeVoiceConnectorConfig(vcName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSChimeVoiceConnectorExists(resourceName, voiceConnector), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("vc-%s", vcName)), + resource.TestCheckResourceAttr(resourceName, "aws_region", chime.VoiceConnectorAwsRegionUsEast1), + resource.TestCheckResourceAttr(resourceName, "require_encryption", "true"), + ), + }, + { + Config: testAccAWSChimeVoiceConnectorUpdated(vcName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "require_encryption", "false"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAWSChimeVoiceConnectorConfig(name string) string { + return fmt.Sprintf(` +resource "aws_chime_voice_connector" "test" { + name = "vc-%s" + require_encryption = true +} +`, name) +} + +func testAccAWSChimeVoiceConnectorUpdated(name string) string { + return fmt.Sprintf(` +resource "aws_chime_voice_connector" "test" { + name = "vc-%s" + require_encryption = false +} +`, name) +} + +func testAccCheckAWSChimeVoiceConnectorExists(name string, vc *chime.VoiceConnector) 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 ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).chimeconn + input := &chime.GetVoiceConnectorInput{ + VoiceConnectorId: aws.String(rs.Primary.ID), + } + resp, err := conn.GetVoiceConnector(input) + if err != nil { + return err + } + + vc = resp.VoiceConnector + + return nil + } +} + +func testAccCheckAWSChimeVoiceConnectorDestroy(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.GetVoiceConnectorInput{ + VoiceConnectorId: aws.String(rs.Primary.ID), + } + resp, err := conn.GetVoiceConnector(input) + if err == nil { + if resp.VoiceConnector != nil && aws.StringValue(resp.VoiceConnector.Name) != "" { + return fmt.Errorf("error Chime Voice Connector still exists") + } + } + return nil + } + return nil +} diff --git a/website/docs/r/chime_voice_connector.html.markdown b/website/docs/r/chime_voice_connector.html.markdown new file mode 100644 index 000000000000..4cb3eacaaffb --- /dev/null +++ b/website/docs/r/chime_voice_connector.html.markdown @@ -0,0 +1,43 @@ +--- +subcategory: "Chime" +layout: "aws" +page_title: "AWS: aws_chime_voice_connector" +description: |- + Enables you to connect your phone system to the telephone network at a substantial cost savings by using SIP trunking. +--- + +# Resource: aws_chime_voice_connector + +Enables you to connect your phone system to the telephone network at a substantial cost savings by using SIP trunking. + +## Example Usage + +```terraform +resource "aws_chime_voice_connector" "test" { + name = "connector-test-1" + require_encryption = true + aws_region = "us-east-1" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the Amazon Chime Voice Connector. +* `require_encryption` - (Required) When enabled, requires encryption for the Amazon Chime Voice Connector. +* `aws_region` - (Optional) The AWS Region in which the Amazon Chime Voice Connector is created. Default value: `us-east-1` + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `outbound_host_name` - The outbound host name for the Amazon Chime Voice Connector. + +## Import + +Configuration Recorder can be imported using the name, e.g. + +``` +$ terraform import aws_chime_voice_connector.test example +```