Skip to content

Commit

Permalink
Feature: AWSConnectInstance Resource
Browse files Browse the repository at this point in the history
  • Loading branch information
abebars committed Dec 16, 2020
1 parent 174fd13 commit d9854f6
Show file tree
Hide file tree
Showing 9 changed files with 1,128 additions and 0 deletions.
215 changes: 215 additions & 0 deletions aws/data_source_aws_connect_instance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package aws

import (
"context"
"errors"
"fmt"
"log"
"strconv"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/connect"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

var dataResourceConnectInstanceAttributesMapping = map[string]string{
connect.InstanceAttributeTypeInboundCalls: "inbound_calls_enabled",
connect.InstanceAttributeTypeOutboundCalls: "outbound_calls_enabled",
connect.InstanceAttributeTypeContactflowLogs: "contact_flow_logs_enabled",
connect.InstanceAttributeTypeContactLens: "contact_lens_enabled",
connect.InstanceAttributeTypeAutoResolveBestVoices: "auto_resolve_best_voices",
connect.InstanceAttributeTypeUseCustomTtsVoices: "use_custom_tts_voices",
connect.InstanceAttributeTypeEarlyMedia: "early_media_enabled",
}

func dataSourceAwsConnectInstance() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceAwsConnectInstanceRead,
Schema: map[string]*schema.Schema{
"instance_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"instance_alias": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"created_time": {
Type: schema.TypeString,
Computed: true,
},
"arn": {
Type: schema.TypeString,
Computed: true,
},
"identity_management_type": {
Type: schema.TypeString,
Computed: true,
},
"inbound_calls_enabled": {
Type: schema.TypeBool,
Computed: true,
},
"outbound_calls_enabled": {
Type: schema.TypeBool,
Computed: true,
},
"early_media_enabled": {
Type: schema.TypeBool,
Computed: true,
},
"contact_flow_logs_enabled": {
Type: schema.TypeBool,
Computed: true,
},
"contact_lens_enabled": {
Type: schema.TypeBool,
Computed: true,
},
"auto_resolve_best_voices": {
Type: schema.TypeBool,
Computed: true,
},
"use_custom_tts_voices": {
Type: schema.TypeBool,
Computed: true,
},
"status": {
Type: schema.TypeString,
Computed: true,
},
"service_role": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func dataSourceAwsConnectInstanceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*AWSClient).connectconn

var matchedInstance *connect.Instance

instanceId, instanceIdOk := d.GetOk("instance_id")
instanceAlias, instanceAliasOk := d.GetOk("instance_alias")

if !instanceIdOk && !instanceAliasOk {
return diag.FromErr(errors.New("error one instance_id or instance_alias of must be assigned"))
}

if instanceIdOk {
input := connect.DescribeInstanceInput{
InstanceId: aws.String(instanceId.(string)),
}

log.Printf("[DEBUG] Reading Connect Instance by instance_id: %s", input)

output, err := conn.DescribeInstance(&input)

if err != nil {
return diag.FromErr(fmt.Errorf("error getting Connect Instance by instance_id (%s): %s", instanceId, err))
}

matchedInstance = output.Instance

} else if instanceAliasOk {
instanceSummaryList, err := dataSourceAwsConnectGetAllConnectInstanceSummaries(ctx, conn)
if err != nil {
return diag.FromErr(fmt.Errorf("error listing Connect Instances: %s", err))
}

for _, instanceSummary := range instanceSummaryList {
log.Printf("[DEBUG] Connect Instance summary: %s", instanceSummary)
if aws.StringValue(instanceSummary.InstanceAlias) == instanceAlias.(string) {

matchedInstance = &connect.Instance{
Arn: instanceSummary.Arn,
CreatedTime: instanceSummary.CreatedTime,
Id: instanceSummary.Id,
IdentityManagementType: instanceSummary.IdentityManagementType,
InboundCallsEnabled: instanceSummary.InboundCallsEnabled,
InstanceAlias: instanceSummary.InstanceAlias,
InstanceStatus: instanceSummary.InstanceStatus,
OutboundCallsEnabled: instanceSummary.OutboundCallsEnabled,
ServiceRole: instanceSummary.ServiceRole,
}
break
}
}
}

if matchedInstance == nil {
return diag.FromErr(fmt.Errorf("error finding Connect Instance by instance_alias: %s", instanceAlias))
}

d.SetId(aws.StringValue(matchedInstance.Id))

d.Set("arn", matchedInstance.Arn)
d.Set("created_time", matchedInstance.CreatedTime.Format(time.RFC3339))
d.Set("identity_management_type", matchedInstance.IdentityManagementType)
d.Set("instance_alias", matchedInstance.InstanceAlias)
d.Set("inbound_calls_enabled", matchedInstance.InboundCallsEnabled)
d.Set("outbound_calls_enabled", matchedInstance.OutboundCallsEnabled)
d.Set("status", matchedInstance.InstanceStatus)
d.Set("service_role", matchedInstance.ServiceRole)

for att := range dataResourceConnectInstanceAttributesMapping {
value, err := dataResourceAwsConnectInstanceReadAttribute(ctx, conn, d.Id(), att)
if err != nil {
return diag.FromErr(fmt.Errorf("error reading Connect instance (%s) attribute (%s): %s", d.Id(), att, err))
}
d.Set(resourceConnectInstanceAttributesMapping[att], value)
}
return nil
}

func dataSourceAwsConnectGetAllConnectInstanceSummaries(ctx context.Context, conn *connect.Connect) ([]*connect.InstanceSummary, error) {
var instances []*connect.InstanceSummary
var nextToken string

for {
input := &connect.ListInstancesInput{
// MaxResults Valid Range: Minimum value of 1. Maximum value of 60
MaxResults: aws.Int64(int64(60)),
}
if nextToken != "" {
input.NextToken = aws.String(nextToken)
}

log.Printf("[DEBUG] Listing Connect Instances: %s", input)

output, err := conn.ListInstancesWithContext(ctx, input)
if err != nil {
return instances, err
}
instances = append(instances, output.InstanceSummaryList...)

if output.NextToken == nil {
break
}
nextToken = aws.StringValue(output.NextToken)
}

return instances, nil
}

func dataResourceAwsConnectInstanceReadAttribute(ctx context.Context, conn *connect.Connect, instanceID string, attributeType string) (bool, error) {
input := &connect.DescribeInstanceAttributeInput{
InstanceId: aws.String(instanceID),
AttributeType: aws.String(attributeType),
}

out, err := conn.DescribeInstanceAttributeWithContext(ctx, input)

if err != nil {
return false, err
}

result, parseerr := strconv.ParseBool(*out.Attribute.Value)
return result, parseerr
}
114 changes: 114 additions & 0 deletions aws/data_source_aws_connect_instance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package aws

import (
"fmt"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccAwsConnectInstanceDataSource_basic(t *testing.T) {
rInt := acctest.RandInt()
resourceName := "aws_connect_instance.foo"
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccAwsConnectInstanceDataSourceConfig_nonExistentId,
ExpectError: regexp.MustCompile(`error getting Connect Instance by instance_id`),
},
{
Config: testAccAwsConnectInstanceDataSourceConfig_nonExistentAlias,
ExpectError: regexp.MustCompile(`error finding Connect Instance by instance_alias`),
},
{
Config: testAccAwsConnectInstanceDataSourceConfigBasic(rInt),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttrSet(resourceName, "arn"),
resource.TestCheckResourceAttrSet(resourceName, "created_time"),
resource.TestCheckResourceAttrSet(resourceName, "identity_management_type"),
resource.TestCheckResourceAttrSet(resourceName, "instance_alias"),
resource.TestCheckResourceAttrSet(resourceName, "inbound_calls_enabled"),
resource.TestCheckResourceAttrSet(resourceName, "outbound_calls_enabled"),
resource.TestCheckResourceAttrSet(resourceName, "contact_flow_logs_enabled"),
resource.TestCheckResourceAttrSet(resourceName, "contact_lens_enabled"),
resource.TestCheckResourceAttrSet(resourceName, "auto_resolve_best_voices"),
resource.TestCheckResourceAttrSet(resourceName, "use_custom_tts_voices"),
resource.TestCheckResourceAttrSet(resourceName, "early_media_enabled"),
resource.TestCheckResourceAttrSet(resourceName, "status"),
resource.TestCheckResourceAttrSet(resourceName, "service_role"),
),
},
},
})
}

func TestAccAwsConnectInstanceDataSource_alias(t *testing.T) {
rInt := acctest.RandInt()
resourceName := "aws_connect_instance.foo"
resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccAwsConnectInstanceDataSourceConfigAlias(rInt),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttrSet(resourceName, "arn"),
resource.TestCheckResourceAttrSet(resourceName, "created_time"),
resource.TestCheckResourceAttrSet(resourceName, "identity_management_type"),
resource.TestCheckResourceAttrSet(resourceName, "instance_alias"),
resource.TestCheckResourceAttrSet(resourceName, "inbound_calls_enabled"),
resource.TestCheckResourceAttrSet(resourceName, "outbound_calls_enabled"),
resource.TestCheckResourceAttrSet(resourceName, "contact_flow_logs_enabled"),
resource.TestCheckResourceAttrSet(resourceName, "contact_lens_enabled"),
resource.TestCheckResourceAttrSet(resourceName, "auto_resolve_best_voices"),
resource.TestCheckResourceAttrSet(resourceName, "use_custom_tts_voices"),
resource.TestCheckResourceAttrSet(resourceName, "early_media_enabled"),
resource.TestCheckResourceAttrSet(resourceName, "status"),
resource.TestCheckResourceAttrSet(resourceName, "service_role"),
),
},
},
})
}

const testAccAwsConnectInstanceDataSourceConfig_nonExistentId = `
data "aws_connect_instance" "foo" {
instance_id = "97afc98d-101a-ba98-ab97-ae114fc115ec"
}
`

const testAccAwsConnectInstanceDataSourceConfig_nonExistentAlias = `
data "aws_connect_instance" "foo" {
instance_alias = "tf-acc-test-does-not-exist"
}
`

func testAccAwsConnectInstanceDataSourceConfigBasic(rInt int) string {
return fmt.Sprintf(`
resource "aws_connect_instance" "foo" {
instance_alias = "datasource-test-terraform-%d"
}
data "aws_connect_instance" "foo" {
instance_id = aws_connect_instance.foo.id
}
`, rInt)
}

func testAccAwsConnectInstanceDataSourceConfigAlias(rInt int) string {
return fmt.Sprintf(`
resource "aws_connect_instance" "foo" {
instance_alias = "datasource-test-terraform-%d"
}
data "aws_connect_instance" "foo" {
instance_alias = aws_connect_instance.foo.instance_alias
}
`, rInt)
}
27 changes: 27 additions & 0 deletions aws/internal/service/connect/waiter/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package waiter

import (
"context"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/connect"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func InstanceStatus(ctx context.Context, conn *connect.Connect, instanceId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
input := &connect.DescribeInstanceInput{
InstanceId: aws.String(instanceId),
}

output, err := conn.DescribeInstanceWithContext(ctx, input)

if err != nil {
return nil, connect.ErrCodeResourceNotFoundException, err
}

state := aws.StringValue(output.Instance.InstanceStatus)

return output, state, nil
}
}
31 changes: 31 additions & 0 deletions aws/internal/service/connect/waiter/waiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package waiter

import (
"context"
"time"

"github.com/aws/aws-sdk-go/service/connect"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

const (
// ConnectInstanceCreateTimeout Timeout for connect instance creation
ConnectInstanceCreateTimeout = 5 * time.Minute
)

func InstanceCreated(ctx context.Context, conn *connect.Connect, instanceId string) (*connect.DescribeInstanceOutput, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{connect.InstanceStatusCreationInProgress},
Target: []string{connect.InstanceStatusActive},
Refresh: InstanceStatus(ctx, conn, instanceId),
Timeout: ConnectInstanceCreateTimeout,
}

outputRaw, err := stateConf.WaitForState()

if v, ok := outputRaw.(*connect.DescribeInstanceOutput); ok {
return v, err
}

return nil, err
}
Loading

0 comments on commit d9854f6

Please sign in to comment.