diff --git a/.changelog/23525.txt b/.changelog/23525.txt new file mode 100644 index 00000000000..35cc9bd21a4 --- /dev/null +++ b/.changelog/23525.txt @@ -0,0 +1,3 @@ +```release-note:new-data-source +aws_connect_routing_profile +``` \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index de57c76deb2..667bbb524c3 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -503,6 +503,7 @@ func Provider() *schema.Provider { "aws_connect_prompt": connect.DataSourcePrompt(), "aws_connect_queue": connect.DataSourceQueue(), "aws_connect_quick_connect": connect.DataSourceQuickConnect(), + "aws_connect_routing_profile": connect.DataSourceRoutingProfile(), "aws_connect_security_profile": connect.DataSourceSecurityProfile(), "aws_connect_user_hierarchy_structure": connect.DataSourceUserHierarchyStructure(), diff --git a/internal/service/connect/enum.go b/internal/service/connect/enum.go index 3177d60f0a3..a2a8f397136 100644 --- a/internal/service/connect/enum.go +++ b/internal/service/connect/enum.go @@ -36,6 +36,9 @@ const ( // ListRoutingProfileQueuesMaxResults Valid Range: Minimum value of 1. Maximum value of 100. // https://docs.aws.amazon.com/connect/latest/APIReference/API_ListRoutingProfileQueues.html ListRoutingProfileQueuesMaxResults = 60 + // ListRoutingProfilesMaxResults Valid Range: Minimum value of 1. Maximum value of 1000. + // https://docs.aws.amazon.com/connect/latest/APIReference/API_ListRoutingProfiles.html + ListRoutingProfilesMaxResults = 60 // ListSecurityProfilePermissionsMaxResults Valid Range: Minimum value of 1. Maximum value of 1000. // https://docs.aws.amazon.com/connect/latest/APIReference/API_ListSecurityProfilePermissions.html ListSecurityProfilePermissionsMaxResults = 60 diff --git a/internal/service/connect/routing_profile_data_source.go b/internal/service/connect/routing_profile_data_source.go new file mode 100644 index 00000000000..519b8070e35 --- /dev/null +++ b/internal/service/connect/routing_profile_data_source.go @@ -0,0 +1,202 @@ +package connect + +import ( + "context" + "fmt" + + "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" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" +) + +func DataSourceRoutingProfile() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceRoutingProfileRead, + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "default_outbound_queue_id": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "instance_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 100), + }, + "media_concurrencies": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "channel": { + Type: schema.TypeString, + Computed: true, + }, + "concurrency": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ExactlyOneOf: []string{"name", "routing_profile_id"}, + }, + "queue_configs": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "channel": { + Type: schema.TypeString, + Computed: true, + }, + "delay": { + Type: schema.TypeInt, + Computed: true, + }, + "priority": { + Type: schema.TypeInt, + Computed: true, + }, + "queue_arn": { + Type: schema.TypeString, + Computed: true, + }, + "queue_id": { + Type: schema.TypeString, + Computed: true, + }, + "queue_name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "routing_profile_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ExactlyOneOf: []string{"routing_profile_id", "name"}, + }, + "tags": tftags.TagsSchemaComputed(), + }, + } +} + +func dataSourceRoutingProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).ConnectConn + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + + instanceID := d.Get("instance_id").(string) + + input := &connect.DescribeRoutingProfileInput{ + InstanceId: aws.String(instanceID), + } + + if v, ok := d.GetOk("routing_profile_id"); ok { + input.RoutingProfileId = aws.String(v.(string)) + } else if v, ok := d.GetOk("name"); ok { + name := v.(string) + routingProfileSummary, err := dataSourceGetConnectRoutingProfileSummaryByName(ctx, conn, instanceID, name) + + if err != nil { + return diag.FromErr(fmt.Errorf("error finding Connect Routing Profile Summary by name (%s): %w", name, err)) + } + + if routingProfileSummary == nil { + return diag.FromErr(fmt.Errorf("error finding Connect Routing Profile Summary by name (%s): not found", name)) + } + + input.RoutingProfileId = routingProfileSummary.Id + } + + resp, err := conn.DescribeRoutingProfileWithContext(ctx, input) + + if err != nil { + return diag.FromErr(fmt.Errorf("error getting Connect Routing Profile: %w", err)) + } + + if resp == nil || resp.RoutingProfile == nil { + return diag.FromErr(fmt.Errorf("error getting Connect Routing Profile: empty response")) + } + + routingProfile := resp.RoutingProfile + + if err := d.Set("media_concurrencies", flattenRoutingProfileMediaConcurrencies(routingProfile.MediaConcurrencies)); err != nil { + return diag.FromErr(err) + } + + d.Set("arn", routingProfile.RoutingProfileArn) + d.Set("default_outbound_queue_id", routingProfile.DefaultOutboundQueueId) + d.Set("description", routingProfile.Description) + d.Set("instance_id", instanceID) + d.Set("name", routingProfile.Name) + d.Set("routing_profile_id", routingProfile.RoutingProfileId) + + // getting the routing profile queues uses a separate API: ListRoutingProfileQueues + queueConfigs, err := getRoutingProfileQueueConfigs(ctx, conn, instanceID, *routingProfile.RoutingProfileId) + + if err != nil { + return diag.FromErr(fmt.Errorf("error finding Connect Routing Profile Queue Configs Summary by Routing Profile ID (%s): %w", *routingProfile.RoutingProfileId, err)) + } + + d.Set("queue_configs", queueConfigs) + + if err := d.Set("tags", KeyValueTags(routingProfile.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return diag.FromErr(fmt.Errorf("error setting tags: %s", err)) + } + + d.SetId(fmt.Sprintf("%s:%s", instanceID, aws.StringValue(routingProfile.RoutingProfileId))) + + return nil +} + +func dataSourceGetConnectRoutingProfileSummaryByName(ctx context.Context, conn *connect.Connect, instanceID, name string) (*connect.RoutingProfileSummary, error) { + var result *connect.RoutingProfileSummary + + input := &connect.ListRoutingProfilesInput{ + InstanceId: aws.String(instanceID), + MaxResults: aws.Int64(ListRoutingProfilesMaxResults), + } + + err := conn.ListRoutingProfilesPagesWithContext(ctx, input, func(page *connect.ListRoutingProfilesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, qs := range page.RoutingProfileSummaryList { + if qs == nil { + continue + } + + if aws.StringValue(qs.Name) == name { + result = qs + return false + } + } + + return !lastPage + }) + + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/internal/service/connect/routing_profile_data_source_test.go b/internal/service/connect/routing_profile_data_source_test.go new file mode 100644 index 00000000000..04e2c066446 --- /dev/null +++ b/internal/service/connect/routing_profile_data_source_test.go @@ -0,0 +1,202 @@ +package connect_test + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/connect" + sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" +) + +func TestAccConnectRoutingProfileDataSource_routingProfileID(t *testing.T) { + rName := sdkacctest.RandomWithPrefix("resource-test-terraform") + rName2 := sdkacctest.RandomWithPrefix("resource-test-terraform") + rName3 := sdkacctest.RandomWithPrefix("resource-test-terraform") + rName4 := sdkacctest.RandomWithPrefix("resource-test-terraform") + resourceName := "aws_connect_routing_profile.test" + datasourceName := "data.aws_connect_routing_profile.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, connect.EndpointsID), + Providers: acctest.Providers, + Steps: []resource.TestStep{ + { + Config: testAccRoutingProfileDataSourceConfig_RoutingProfileID(rName, rName2, rName3, rName4), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasourceName, "default_outbound_queue_id", resourceName, "default_outbound_queue_id"), + resource.TestCheckResourceAttrPair(datasourceName, "description", resourceName, "description"), + resource.TestCheckResourceAttrPair(datasourceName, "instance_id", resourceName, "instance_id"), + resource.TestCheckResourceAttrPair(datasourceName, "media_concurrencies.#", resourceName, "media_concurrencies.#"), + resource.TestCheckTypeSetElemNestedAttrs(datasourceName, "media_concurrencies.*", map[string]string{ + "channel": "VOICE", + "concurrency": "1", + }), + resource.TestCheckTypeSetElemNestedAttrs(datasourceName, "media_concurrencies.*", map[string]string{ + "channel": "CHAT", + "concurrency": "2", + }), + resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(datasourceName, "queue_configs.#", resourceName, "queue_configs.#"), + resource.TestCheckResourceAttrSet(datasourceName, "queue_configs.0.channel"), + resource.TestCheckResourceAttrSet(datasourceName, "queue_configs.0.delay"), + resource.TestCheckResourceAttrSet(datasourceName, "queue_configs.0.priority"), + resource.TestCheckTypeSetElemAttrPair(datasourceName, "queue_configs.*.queue_arn", "aws_connect_queue.default_outbound_queue", "arn"), + resource.TestCheckTypeSetElemAttrPair(datasourceName, "queue_configs.*.queue_id", "aws_connect_queue.default_outbound_queue", "queue_id"), + resource.TestCheckTypeSetElemAttrPair(datasourceName, "queue_configs.*.queue_name", "aws_connect_queue.default_outbound_queue", "name"), + resource.TestCheckResourceAttrSet(datasourceName, "queue_configs.1.channel"), + resource.TestCheckResourceAttrSet(datasourceName, "queue_configs.1.delay"), + resource.TestCheckResourceAttrSet(datasourceName, "queue_configs.1.priority"), + resource.TestCheckTypeSetElemAttrPair(datasourceName, "queue_configs.*.queue_arn", "aws_connect_queue.test", "arn"), + resource.TestCheckTypeSetElemAttrPair(datasourceName, "queue_configs.*.queue_id", "aws_connect_queue.test", "queue_id"), + resource.TestCheckTypeSetElemAttrPair(datasourceName, "queue_configs.*.queue_name", "aws_connect_queue.test", "name"), + resource.TestCheckResourceAttrPair(datasourceName, "routing_profile_id", resourceName, "routing_profile_id"), + resource.TestCheckResourceAttrPair(datasourceName, "tags.%", resourceName, "tags.%"), + resource.TestCheckResourceAttrPair(datasourceName, "tags.Name", resourceName, "tags.Name"), + ), + }, + }, + }) +} + +func TestAccConnectRoutingProfileDataSource_name(t *testing.T) { + rName := sdkacctest.RandomWithPrefix("resource-test-terraform") + rName2 := sdkacctest.RandomWithPrefix("resource-test-terraform") + rName3 := sdkacctest.RandomWithPrefix("resource-test-terraform") + rName4 := sdkacctest.RandomWithPrefix("resource-test-terraform") + resourceName := "aws_connect_routing_profile.test" + datasourceName := "data.aws_connect_routing_profile.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, connect.EndpointsID), + Providers: acctest.Providers, + Steps: []resource.TestStep{ + { + Config: testAccRoutingProfileDataSourceConfig_Name(rName, rName2, rName3, rName4), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasourceName, "default_outbound_queue_id", resourceName, "default_outbound_queue_id"), + resource.TestCheckResourceAttrPair(datasourceName, "description", resourceName, "description"), + resource.TestCheckResourceAttrPair(datasourceName, "instance_id", resourceName, "instance_id"), + resource.TestCheckResourceAttrPair(datasourceName, "media_concurrencies.#", resourceName, "media_concurrencies.#"), + resource.TestCheckTypeSetElemNestedAttrs(datasourceName, "media_concurrencies.*", map[string]string{ + "channel": "VOICE", + "concurrency": "1", + }), + resource.TestCheckTypeSetElemNestedAttrs(datasourceName, "media_concurrencies.*", map[string]string{ + "channel": "CHAT", + "concurrency": "2", + }), + resource.TestCheckResourceAttrPair(datasourceName, "name", resourceName, "name"), + resource.TestCheckResourceAttrPair(datasourceName, "queue_configs.#", resourceName, "queue_configs.#"), + resource.TestCheckResourceAttrSet(datasourceName, "queue_configs.0.channel"), + resource.TestCheckResourceAttr(datasourceName, "queue_configs.0.delay", "1"), + resource.TestCheckResourceAttrSet(datasourceName, "queue_configs.0.priority"), + resource.TestCheckTypeSetElemAttrPair(datasourceName, "queue_configs.*.queue_arn", "aws_connect_queue.default_outbound_queue", "arn"), + resource.TestCheckTypeSetElemAttrPair(datasourceName, "queue_configs.*.queue_id", "aws_connect_queue.default_outbound_queue", "queue_id"), + resource.TestCheckTypeSetElemAttrPair(datasourceName, "queue_configs.*.queue_name", "aws_connect_queue.default_outbound_queue", "name"), + resource.TestCheckResourceAttrSet(datasourceName, "queue_configs.1.channel"), + resource.TestCheckResourceAttr(datasourceName, "queue_configs.1.delay", "1"), + resource.TestCheckResourceAttrSet(datasourceName, "queue_configs.1.priority"), + resource.TestCheckTypeSetElemAttrPair(datasourceName, "queue_configs.*.queue_arn", "aws_connect_queue.test", "arn"), + resource.TestCheckTypeSetElemAttrPair(datasourceName, "queue_configs.*.queue_id", "aws_connect_queue.test", "queue_id"), + resource.TestCheckTypeSetElemAttrPair(datasourceName, "queue_configs.*.queue_name", "aws_connect_queue.test", "name"), + resource.TestCheckResourceAttrPair(datasourceName, "routing_profile_id", resourceName, "routing_profile_id"), + resource.TestCheckResourceAttrPair(datasourceName, "tags.%", resourceName, "tags.%"), + resource.TestCheckResourceAttrPair(datasourceName, "tags.Name", resourceName, "tags.Name"), + ), + }, + }, + }) +} + +func testAccRoutingProfileBaseDataSourceConfig(rName, rName2, rName3, rName4 string) string { + return fmt.Sprintf(` +resource "aws_connect_instance" "test" { + identity_management_type = "CONNECT_MANAGED" + inbound_calls_enabled = true + instance_alias = %[1]q + outbound_calls_enabled = true +} + +data "aws_connect_hours_of_operation" "test" { + instance_id = aws_connect_instance.test.id + name = "Basic Hours" +} + +resource "aws_connect_queue" "default_outbound_queue" { + instance_id = aws_connect_instance.test.id + name = %[2]q + description = "Default Outbound Queue for Routing Profiles" + hours_of_operation_id = data.aws_connect_hours_of_operation.test.hours_of_operation_id +} + +resource "aws_connect_queue" "test" { + instance_id = aws_connect_instance.test.id + name = %[3]q + description = "Additional queue to routing profile queue config" + hours_of_operation_id = data.aws_connect_hours_of_operation.test.hours_of_operation_id +} + +resource "aws_connect_routing_profile" "test" { + instance_id = aws_connect_instance.test.id + name = %[4]q + default_outbound_queue_id = aws_connect_queue.default_outbound_queue.queue_id + description = "Test Routing Profile Data Source" + + media_concurrencies { + channel = "VOICE" + concurrency = 1 + } + + media_concurrencies { + channel = "CHAT" + concurrency = 2 + } + + queue_configs { + channel = "VOICE" + delay = 1 + priority = 2 + queue_id = aws_connect_queue.default_outbound_queue.queue_id + } + + queue_configs { + channel = "CHAT" + delay = 1 + priority = 1 + queue_id = aws_connect_queue.test.queue_id + } + + tags = { + "Name" = "Test Routing Profile", + } +} +`, rName, rName2, rName3, rName4) +} + +func testAccRoutingProfileDataSourceConfig_RoutingProfileID(rName, rName2, rName3, rName4 string) string { + return acctest.ConfigCompose( + testAccRoutingProfileBaseDataSourceConfig(rName, rName2, rName3, rName4), + ` +data "aws_connect_routing_profile" "test" { + instance_id = aws_connect_instance.test.id + routing_profile_id = aws_connect_routing_profile.test.routing_profile_id +} +`) +} + +func testAccRoutingProfileDataSourceConfig_Name(rName, rName2, rName3, rName4 string) string { + return acctest.ConfigCompose( + testAccRoutingProfileBaseDataSourceConfig(rName, rName2, rName3, rName4), + ` +data "aws_connect_routing_profile" "test" { + instance_id = aws_connect_instance.test.id + name = aws_connect_routing_profile.test.name +} +`) +} diff --git a/website/docs/d/connect_routing_profile.html.markdown b/website/docs/d/connect_routing_profile.html.markdown new file mode 100644 index 00000000000..c8c92c8cda8 --- /dev/null +++ b/website/docs/d/connect_routing_profile.html.markdown @@ -0,0 +1,67 @@ +--- +subcategory: "Connect" +layout: "aws" +page_title: "AWS: aws_connect_routing_profile" +description: |- + Provides details about a specific Amazon Connect Routing Profile. +--- + +# Data Source: aws_connect_routing_profile + +Provides details about a specific Amazon Connect Routing Profile. + +## Example Usage + +By `name` + +```hcl +data "aws_connect_routing_profile" "example" { + instance_id = "aaaaaaaa-bbbb-cccc-dddd-111111111111" + name = "Example" +} +``` + +By `routing_profile_id` + +```hcl +data "aws_connect_routing_profile" "example" { + instance_id = "aaaaaaaa-bbbb-cccc-dddd-111111111111" + routing_profile_id = "cccccccc-bbbb-cccc-dddd-111111111111" +} +``` + +## Argument Reference + +~> **NOTE:** `instance_id` and one of either `name` or `routing_profile_id` is required. + +The following arguments are supported: + +* `instance_id` - Reference to the hosting Amazon Connect Instance +* `name` - (Optional) Returns information on a specific Routing Profile by name +* `routing_profile_id` - (Optional) Returns information on a specific Routing Profile by Routing Profile id + +## Attributes Reference + +In addition to all of the arguments above, the following attributes are exported: + +* `arn` - The Amazon Resource Name (ARN) of the Routing Profile. +* `default_outbound_queue_id` - Specifies the default outbound queue for the Routing Profile. +* `description` - Specifies the description of the Routing Profile. +* `id` - The identifier of the hosting Amazon Connect Instance and identifier of the Routing Profile separated by a colon (`:`). +* `media_concurrencies` - One or more `media_concurrencies` blocks that specify the channels that agents can handle in the Contact Control Panel (CCP) for this Routing Profile. The `media_concurrencies` block is documented below. +* `queue_configs` - One or more `queue_configs` blocks that specify the inbound queues associated with the routing profile. If no queue is added, the agent only can make outbound calls. The `queue_configs` block is documented below. +* `tags` - A map of tags to assign to the Routing Profile. + +A `media_concurrencies` block supports the following attributes: + +* `channel` - Specifies the channels that agents can handle in the Contact Control Panel (CCP). Valid values are `VOICE`, `CHAT`, `TASK`. +* `concurrency` - Specifies the number of contacts an agent can have on a channel simultaneously. Valid Range for `VOICE`: Minimum value of 1. Maximum value of 1. Valid Range for `CHAT`: Minimum value of 1. Maximum value of 10. Valid Range for `TASK`: Minimum value of 1. Maximum value of 10. + +A `queue_configs` block supports the following attributes: + +* `channel` - Specifies the channels agents can handle in the Contact Control Panel (CCP) for this routing profile. Valid values are `VOICE`, `CHAT`, `TASK`. +* `delay` - Specifies the delay, in seconds, that a contact should be in the queue before they are routed to an available agent +* `priority` - Specifies the order in which contacts are to be handled for the queue. +* `queue_arn` - Specifies the ARN for the queue. +* `queue_id` - Specifies the identifier for the queue. +* `queue_name` - Specifies the name for the queue.