diff --git a/.changelog/21162.txt b/.changelog/21162.txt new file mode 100644 index 00000000000..76e925c0f10 --- /dev/null +++ b/.changelog/21162.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_chime_voice_connector_termination_credentials +``` \ No newline at end of file diff --git a/aws/provider.go b/aws/provider.go index 960a3df1822..5b33d65bfd6 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -584,6 +584,7 @@ func Provider() *schema.Provider { "aws_chime_voice_connector_streaming": resourceAwsChimeVoiceConnectorStreaming(), "aws_chime_voice_connector_origination": resourceAwsChimeVoiceConnectorOrigination(), "aws_chime_voice_connector_termination": resourceAwsChimeVoiceConnectorTermination(), + "aws_chime_voice_connector_termination_credentials": resourceAwsChimeVoiceConnectorTerminationCredentials(), "aws_cloud9_environment_ec2": resourceAwsCloud9EnvironmentEc2(), "aws_cloudcontrolapi_resource": resourceAwsCloudControlApiResource(), "aws_cloudformation_stack": resourceAwsCloudFormationStack(), diff --git a/aws/resource_aws_chime_voice_connector_termination_credentials.go b/aws/resource_aws_chime_voice_connector_termination_credentials.go new file mode 100644 index 00000000000..503bc6c6d9d --- /dev/null +++ b/aws/resource_aws_chime_voice_connector_termination_credentials.go @@ -0,0 +1,162 @@ +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 resourceAwsChimeVoiceConnectorTerminationCredentials() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceAwsChimeVoiceConnectorTerminationCredentialsCreate, + ReadWithoutTimeout: resourceAwsChimeVoiceConnectorTerminationCredentialsRead, + UpdateWithoutTimeout: resourceAwsChimeVoiceConnectorTerminationCredentialsUpdate, + DeleteWithoutTimeout: resourceAwsChimeVoiceConnectorTerminationCredentialsDelete, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "credentials": { + Type: schema.TypeSet, + Required: true, + MinItems: 1, + MaxItems: 10, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "username": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "password": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + "voice_connector_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceAwsChimeVoiceConnectorTerminationCredentialsCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).chimeconn + + vcId := d.Get("voice_connector_id").(string) + + input := &chime.PutVoiceConnectorTerminationCredentialsInput{ + VoiceConnectorId: aws.String(vcId), + Credentials: expandCredentials(d.Get("credentials").(*schema.Set).List()), + } + + if _, err := conn.PutVoiceConnectorTerminationCredentialsWithContext(ctx, input); err != nil { + return diag.Errorf("error creating Chime Voice Connector (%s) termination credentials: %s", vcId, err) + + } + + d.SetId(vcId) + + return resourceAwsChimeVoiceConnectorTerminationCredentialsRead(ctx, d, meta) +} + +func resourceAwsChimeVoiceConnectorTerminationCredentialsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).chimeconn + + input := &chime.ListVoiceConnectorTerminationCredentialsInput{ + VoiceConnectorId: aws.String(d.Id()), + } + + _, err := conn.ListVoiceConnectorTerminationCredentialsWithContext(ctx, input) + if !d.IsNewResource() && isAWSErr(err, chime.ErrCodeNotFoundException, "") { + log.Printf("[WARN] Chime Voice Connector (%s) termination credentials not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return diag.Errorf("error getting Chime Voice Connector (%s) termination credentials: %s", d.Id(), err) + } + + d.Set("voice_connector_id", d.Id()) + + return nil +} + +func resourceAwsChimeVoiceConnectorTerminationCredentialsUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).chimeconn + + if d.HasChanges("credentials") { + input := &chime.PutVoiceConnectorTerminationCredentialsInput{ + VoiceConnectorId: aws.String(d.Id()), + Credentials: expandCredentials(d.Get("credentials").(*schema.Set).List()), + } + + _, err := conn.PutVoiceConnectorTerminationCredentialsWithContext(ctx, input) + + if err != nil { + return diag.Errorf("error updating Chime Voice Connector (%s) termination credentials: %s", d.Id(), err) + } + } + + return resourceAwsChimeVoiceConnectorTerminationCredentialsRead(ctx, d, meta) +} + +func resourceAwsChimeVoiceConnectorTerminationCredentialsDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).chimeconn + + input := &chime.DeleteVoiceConnectorTerminationCredentialsInput{ + VoiceConnectorId: aws.String(d.Id()), + Usernames: expandCredentialsUsernames(d.Get("credentials").(*schema.Set).List()), + } + + _, err := conn.DeleteVoiceConnectorTerminationCredentialsWithContext(ctx, input) + + if isAWSErr(err, chime.ErrCodeNotFoundException, "") { + return nil + } + + if err != nil { + return diag.Errorf("error deleting Chime Voice Connector (%s) termination credentials: %s", d.Id(), err) + } + + return nil +} + +func expandCredentialsUsernames(data []interface{}) []*string { + var rawNames []*string + + for _, rData := range data { + item := rData.(map[string]interface{}) + rawNames = append(rawNames, aws.String(item["username"].(string))) + } + + return rawNames +} + +func expandCredentials(data []interface{}) []*chime.Credential { + var credentials []*chime.Credential + + for _, rItem := range data { + item := rItem.(map[string]interface{}) + credentials = append(credentials, &chime.Credential{ + Username: aws.String(item["username"].(string)), + Password: aws.String(item["password"].(string)), + }) + } + + return credentials +} diff --git a/aws/resource_aws_chime_voice_connector_termination_credentials_test.go b/aws/resource_aws_chime_voice_connector_termination_credentials_test.go new file mode 100644 index 00000000000..5b2d0084ffb --- /dev/null +++ b/aws/resource_aws_chime_voice_connector_termination_credentials_test.go @@ -0,0 +1,196 @@ +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 TestAccAWSChimeVoiceConnectorTerminationCredentials_basic(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_chime_voice_connector_termination_credentials.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, chime.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSChimeVoiceConnectorTerminationCredentialsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSChimeVoiceConnectorTerminationCredentialsConfig(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSChimeVoiceConnectorTerminationCredentialsExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "credentials.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"credentials"}, + }, + }, + }) +} + +func TestAccAWSChimeVoiceConnectorTerminationCredentials_disappears(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_chime_voice_connector_termination_credentials.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, chime.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSChimeVoiceConnectorTerminationCredentialsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSChimeVoiceConnectorTerminationCredentialsConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSChimeVoiceConnectorTerminationCredentialsExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsChimeVoiceConnectorTerminationCredentials(), resourceName), + ), + ExpectNonEmptyPlan: false, + }, + }, + }) +} + +func TestAccAWSChimeVoiceConnectorTerminationCredentials_update(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_chime_voice_connector_termination_credentials.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, chime.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSChimeVoiceConnectorTerminationCredentialsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSChimeVoiceConnectorTerminationCredentialsConfig(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSChimeVoiceConnectorTerminationCredentialsExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "credentials.#", "1"), + ), + }, + { + Config: testAccAWSChimeVoiceConnectorTerminationCredentialsConfigUpdated(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAWSChimeVoiceConnectorTerminationCredentialsExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "credentials.#", "2"), + ), + }, + }, + }) +} + +func testAccCheckAWSChimeVoiceConnectorTerminationCredentialsExists(name string) 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 termination credentials ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).chimeconn + input := &chime.ListVoiceConnectorTerminationCredentialsInput{ + VoiceConnectorId: aws.String(rs.Primary.ID), + } + + resp, err := conn.ListVoiceConnectorTerminationCredentials(input) + if err != nil { + return err + } + + if resp == nil || resp.Usernames == nil { + return fmt.Errorf("no Chime Voice Connector Termintation credentials (%s) found", rs.Primary.ID) + } + + return nil + } +} + +func testAccCheckAWSChimeVoiceConnectorTerminationCredentialsDestroy(s *terraform.State) error { + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_chime_voice_connector_termination_credentials" { + continue + } + conn := testAccProvider.Meta().(*AWSClient).chimeconn + input := &chime.ListVoiceConnectorTerminationCredentialsInput{ + VoiceConnectorId: aws.String(rs.Primary.ID), + } + resp, err := conn.ListVoiceConnectorTerminationCredentials(input) + + if isAWSErr(err, chime.ErrCodeNotFoundException, "") { + continue + } + + if err != nil { + return err + } + + if resp != nil && resp.Usernames != nil { + return fmt.Errorf("error Chime Voice Connector Termination credentials still exists") + } + } + + return nil +} + +func testAccAWSChimeVoiceConnectorTerminationCredentialsConfigBase(rName string) string { + return fmt.Sprintf(` +resource "aws_chime_voice_connector" "chime" { + name = "vc-%[1]s" + require_encryption = true +} + +resource "aws_chime_voice_connector_termination" "test" { + voice_connector_id = aws_chime_voice_connector.chime.id + + calling_regions = ["US"] + cidr_allow_list = ["50.35.78.0/27"] +} +`, rName) +} + +func testAccAWSChimeVoiceConnectorTerminationCredentialsConfig(rName string) string { + return composeConfig(testAccAWSChimeVoiceConnectorTerminationCredentialsConfigBase(rName), ` +resource "aws_chime_voice_connector_termination_credentials" "test" { + voice_connector_id = aws_chime_voice_connector.chime.id + + credentials { + username = "test1" + password = "test1!" + } + + depends_on = [aws_chime_voice_connector_termination.test] +} +`) +} + +func testAccAWSChimeVoiceConnectorTerminationCredentialsConfigUpdated(rName string) string { + return composeConfig(testAccAWSChimeVoiceConnectorTerminationCredentialsConfigBase(rName), ` +resource "aws_chime_voice_connector_termination_credentials" "test" { + voice_connector_id = aws_chime_voice_connector.chime.id + + credentials { + username = "test1" + password = "test1!" + } + + credentials { + username = "test2" + password = "test2!" + } + + depends_on = [aws_chime_voice_connector_termination.test] +} +`) +} diff --git a/website/docs/r/chime_voice_connector_termination_credentials.html.markdown b/website/docs/r/chime_voice_connector_termination_credentials.html.markdown new file mode 100644 index 00000000000..cfe498867d4 --- /dev/null +++ b/website/docs/r/chime_voice_connector_termination_credentials.html.markdown @@ -0,0 +1,69 @@ +--- +subcategory: "Chime" +layout: "aws" +page_title: "AWS: aws_chime_voice_connector_termination_credentials" +description: |- + Adds termination SIP credentials for the specified Amazon Chime Voice Connector. +--- + +# Resource: aws_chime_voice_connector_termination_credentials + +Adds termination SIP credentials for the specified Amazon Chime Voice Connector. + +~> **Note:** Voice Connector Termination Credentials requires a [Voice Connector Termination](/docs/providers/aws/r/chime_voice_connector_termination.html) to be present. Use of `depends_on` (as shown below) is recommended to avoid race conditions. + +## Example Usage + +```terraform +resource "aws_chime_voice_connector" "default" { + name = "test" + require_encryption = true +} + +resource "aws_chime_voice_connector_termination" "default" { + disabled = true + cps_limit = 1 + cidr_allow_list = ["50.35.78.96/31"] + calling_regions = ["US", "CA"] + voice_connector_id = aws_chime_voice_connector.default.id +} + +resource "aws_chime_voice_connector_termination_credentials" "default" { + voice_connector_id = aws_chime_voice_connector.default.id + + credentials { + username = "test" + password = "test!" + } + + depends_on = [aws_chime_voice_connector_termination.default] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `voice_connector_id` - (Required) Amazon Chime Voice Connector ID. +* `credentials` - (Required) List of termination SIP credentials. + +### `credentials` + +The SIP credentials used to authenticate requests to your Amazon Chime Voice Connector. + +* `username` - (Required) RFC2617 compliant username associated with the SIP credentials. +* `password` - (Required) RFC2617 compliant password associated with the SIP credentials. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - Amazon Chime Voice Connector ID. + +## Import + +Chime Voice Connector Termination Credentials can be imported using the `voice_connector_id`, e.g. + +``` +$ terraform import aws_chime_voice_connector_termination_credentials.default abcdef1ghij2klmno3pqr4 +```