Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changelog/44750.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_connect_routing_profile: Add `media_concurrencies.cross_channel_behavior` argument
```

```release-note:enhancement
data-source/aws_connect_routing_profile: Add `media_concurrencies.cross_channel_behavior` attribute
```
1 change: 1 addition & 0 deletions internal/service/connect/connect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ func TestAccConnect_serial(t *testing.T) {
acctest.CtDisappears: testAccRoutingProfile_disappears,
"tags": testAccRoutingProfile_updateTags,
"concurrency": testAccRoutingProfile_updateConcurrency,
"crossChannelBehavior": testAccRoutingProfile_crossChannelBehavior,
"defaultOutboundQueue": testAccRoutingProfile_updateDefaultOutboundQueue,
"queues": testAccRoutingProfile_updateQueues,
"createQueueBatchAssociations": testAccRoutingProfile_createQueueConfigsBatchedAssociateDisassociate,
Expand Down
49 changes: 47 additions & 2 deletions internal/service/connect/routing_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,21 @@ func resourceRoutingProfile() *schema.Resource {
Required: true,
ValidateFunc: validation.IntBetween(1, 10),
},
"cross_channel_behavior": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"behavior_type": {
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: enum.Validate[awstypes.BehaviorType](),
},
},
},
},
},
},
},
Expand Down Expand Up @@ -201,7 +216,7 @@ func resourceRoutingProfileRead(ctx context.Context, d *schema.ResourceData, met
d.Set("default_outbound_queue_id", routingProfile.DefaultOutboundQueueId)
d.Set(names.AttrDescription, routingProfile.Description)
d.Set(names.AttrInstanceID, instanceID)
if err := d.Set("media_concurrencies", flattenMediaConcurrencies(routingProfile.MediaConcurrencies)); err != nil {
if err := d.Set("media_concurrencies", flattenMediaConcurrencies(routingProfile.MediaConcurrencies, d.Get("media_concurrencies").(*schema.Set).List())); err != nil {
return sdkdiag.AppendErrorf(diags, "setting media_concurrencies: %s", err)
}
d.Set(names.AttrName, routingProfile.Name)
Expand Down Expand Up @@ -477,21 +492,51 @@ func expandMediaConcurrencies(tfList []any) []awstypes.MediaConcurrency {
Channel: awstypes.Channel(tfMap["channel"].(string)),
Concurrency: aws.Int32(int32(tfMap["concurrency"].(int))),
}

if v, ok := tfMap["cross_channel_behavior"].([]any); ok && len(v) > 0 {
crossChannelBehaviorMap := v[0].(map[string]any)
apiObject.CrossChannelBehavior = &awstypes.CrossChannelBehavior{
BehaviorType: awstypes.BehaviorType(crossChannelBehaviorMap["behavior_type"].(string)),
}
}
Comment on lines +496 to +501
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be moved into it's own, distinct expander expandCrossChannelBehavior.

Each flatten/expander is typically only responsible for one level of attributes. Because cross_channel_behavior is a nested list, we can split that out into it's own expand function which gets called from here. This helps keep the function body of each individual flex function compact, even for large nested structures.


apiObjects = append(apiObjects, apiObject)
}

return apiObjects
}

func flattenMediaConcurrencies(apiObjects []awstypes.MediaConcurrency) []any {
func flattenMediaConcurrencies(apiObjects []awstypes.MediaConcurrency, configList ...[]any) []any {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason the configured values are now passed in here? I'm not quite following why it is being used below.

tfList := []any{}

configuredBehaviors := make(map[string]bool)
if len(configList) > 0 && configList[0] != nil {
for _, configRaw := range configList[0] {
configMap := configRaw.(map[string]any)
channel := configMap["channel"].(string)
if crossChannelBehaviorList, ok := configMap["cross_channel_behavior"].([]any); ok && len(crossChannelBehaviorList) > 0 {
configuredBehaviors[channel] = true
}
}
}

for _, apiObject := range apiObjects {
tfMap := map[string]any{
"channel": apiObject.Channel,
"concurrency": aws.ToInt32(apiObject.Concurrency),
}

channel := string(apiObject.Channel)
if apiObject.CrossChannelBehavior != nil {
if len(configList) == 0 || configuredBehaviors[channel] {
tfMap["cross_channel_behavior"] = []map[string]any{
{
"behavior_type": string(apiObject.CrossChannelBehavior.BehaviorType),
},
}
}
}
Comment on lines +529 to +538
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be a separate flattener.


tfList = append(tfList, tfMap)
}

Expand Down
12 changes: 12 additions & 0 deletions internal/service/connect/routing_profile_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ func dataSourceRoutingProfile() *schema.Resource {
Type: schema.TypeInt,
Computed: true,
},
"cross_channel_behavior": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"behavior_type": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
},
},
},
Expand Down
167 changes: 167 additions & 0 deletions internal/service/connect/routing_profile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,78 @@ func testAccRoutingProfile_updateQueues(t *testing.T) {
})
}

func testAccRoutingProfile_crossChannelBehavior(t *testing.T) {
ctx := acctest.Context(t)
var v awstypes.RoutingProfile
rName := sdkacctest.RandomWithPrefix("resource-test-terraform")
rName2 := sdkacctest.RandomWithPrefix("resource-test-terraform")
rName3 := sdkacctest.RandomWithPrefix("resource-test-terraform")
resourceName := "aws_connect_routing_profile.test"

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.ConnectServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckRoutingProfileDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccRoutingProfileConfig_crossChannelBehaviorDefault(rName, rName2, rName3),
Check: resource.ComposeTestCheckFunc(
testAccCheckRoutingProfileExists(ctx, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, "media_concurrencies.#", "2"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "media_concurrencies.*", map[string]string{
"channel": string(awstypes.ChannelVoice),
"concurrency": "1",
}),
Comment on lines +357 to +360
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check should also verify the AWS server-side default for cross_channel_behavior is being written to state as expected.

resource.TestCheckTypeSetElemNestedAttrs(resourceName, "media_concurrencies.*", map[string]string{
"channel": string(awstypes.ChannelChat),
"concurrency": "2",
}),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccRoutingProfileConfig_crossChannelBehaviorCurrentChannelOnly(rName, rName2, rName3),
Check: resource.ComposeTestCheckFunc(
testAccCheckRoutingProfileExists(ctx, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, "media_concurrencies.#", "2"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "media_concurrencies.*", map[string]string{
"channel": string(awstypes.ChannelVoice),
"concurrency": "1",
"cross_channel_behavior.0.behavior_type": string(awstypes.BehaviorTypeRouteCurrentChannelOnly),
}),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "media_concurrencies.*", map[string]string{
"channel": string(awstypes.ChannelChat),
"concurrency": "3",
"cross_channel_behavior.0.behavior_type": string(awstypes.BehaviorTypeRouteCurrentChannelOnly),
}),
),
},
{
Config: testAccRoutingProfileConfig_crossChannelBehaviorMixed(rName, rName2, rName3),
Check: resource.ComposeTestCheckFunc(
testAccCheckRoutingProfileExists(ctx, resourceName, &v),
resource.TestCheckResourceAttr(resourceName, "media_concurrencies.#", "2"),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "media_concurrencies.*", map[string]string{
"channel": string(awstypes.ChannelVoice),
"concurrency": "1",
"cross_channel_behavior.0.behavior_type": string(awstypes.BehaviorTypeRouteAnyChannel),
}),
resource.TestCheckTypeSetElemNestedAttrs(resourceName, "media_concurrencies.*", map[string]string{
"channel": string(awstypes.ChannelChat),
"concurrency": "3",
"cross_channel_behavior.0.behavior_type": string(awstypes.BehaviorTypeRouteCurrentChannelOnly),
}),
),
},
},
})
}

func testAccRoutingProfile_updateTags(t *testing.T) {
ctx := acctest.Context(t)
var v awstypes.RoutingProfile
Expand Down Expand Up @@ -997,3 +1069,98 @@ resource "aws_connect_routing_profile" "test" {
}
`, rName3))
}

func testAccRoutingProfileConfig_crossChannelBehaviorDefault(rName, rName2, rName3 string) string {
return acctest.ConfigCompose(
testAccRoutingProfileConfig_base(rName, rName2),
fmt.Sprintf(`
resource "aws_connect_routing_profile" "test" {
instance_id = aws_connect_instance.test.id
name = %[1]q
default_outbound_queue_id = aws_connect_queue.default_outbound_queue.queue_id
description = "Test cross-channel behavior - default"

media_concurrencies {
channel = "VOICE"
concurrency = 1
# behaviour uses AWS server-side default
}

media_concurrencies {
channel = "CHAT"
concurrency = 2
# behaviour uses AWS server-side default
}

tags = {
"Name" = "Test Routing Profile Cross-Channel Behavior",
}
}
`, rName3))
}

func testAccRoutingProfileConfig_crossChannelBehaviorCurrentChannelOnly(rName, rName2, rName3 string) string {
return acctest.ConfigCompose(
testAccRoutingProfileConfig_base(rName, rName2),
fmt.Sprintf(`
resource "aws_connect_routing_profile" "test" {
instance_id = aws_connect_instance.test.id
name = %[1]q
default_outbound_queue_id = aws_connect_queue.default_outbound_queue.queue_id
description = "Test cross-channel behavior - current channel only"

media_concurrencies {
channel = "VOICE"
concurrency = 1
cross_channel_behavior {
behavior_type = "ROUTE_CURRENT_CHANNEL_ONLY"
}
}

media_concurrencies {
channel = "CHAT"
concurrency = 3
cross_channel_behavior {
behavior_type = "ROUTE_CURRENT_CHANNEL_ONLY"
}
}

tags = {
"Name" = "Test Routing Profile Cross-Channel Behavior",
}
}
`, rName3))
}

func testAccRoutingProfileConfig_crossChannelBehaviorMixed(rName, rName2, rName3 string) string {
return acctest.ConfigCompose(
testAccRoutingProfileConfig_base(rName, rName2),
fmt.Sprintf(`
resource "aws_connect_routing_profile" "test" {
instance_id = aws_connect_instance.test.id
name = %[1]q
default_outbound_queue_id = aws_connect_queue.default_outbound_queue.queue_id
description = "Test cross-channel behavior - mixed"

media_concurrencies {
channel = "VOICE"
concurrency = 1
cross_channel_behavior {
behavior_type = "ROUTE_ANY_CHANNEL"
}
}

media_concurrencies {
channel = "CHAT"
concurrency = 3
cross_channel_behavior {
behavior_type = "ROUTE_CURRENT_CHANNEL_ONLY"
}
}

tags = {
"Name" = "Test Routing Profile Cross-Channel Behavior",
}
}
`, rName3))
}
5 changes: 5 additions & 0 deletions website/docs/d/connect_routing_profile.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ A `media_concurrencies` block supports the following attributes:

* `channel` - Channels that agents can handle in the Contact Control Panel (CCP). Valid values are `VOICE`, `CHAT`, `TASK`.
* `concurrency` - 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.
* `cross_channel_behavior` - Configuration block for cross-channel behavior. Documented below.

A `cross_channel_behavior` block supports the following attributes:

* `behavior_type` - Cross-channel behavior for routing contacts across multiple channels. Valid values are `ROUTE_CURRENT_CHANNEL_ONLY`, `ROUTE_ANY_CHANNEL`.

A `queue_configs` block supports the following attributes:

Expand Down
25 changes: 25 additions & 0 deletions website/docs/r/connect_routing_profile.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ resource "aws_connect_routing_profile" "example" {
media_concurrencies {
channel = "VOICE"
concurrency = 1
cross_channel_behavior {
behavior_type = "ROUTE_ANY_CHANNEL"
}
}

media_concurrencies {
channel = "CHAT"
concurrency = 3
cross_channel_behavior {
behavior_type = "ROUTE_CURRENT_CHANNEL_ONLY"
}
}

queue_configs {
Expand All @@ -38,6 +49,15 @@ resource "aws_connect_routing_profile" "example" {
}
```

### Cross-Channel Behavior

The `cross_channel_behavior` block in `media_concurrencies` controls how Amazon Connect routes contacts across different channels:

- `ROUTE_ANY_CHANNEL` (default): Allows agents to receive contacts from any channel, regardless of the channel they are currently handling.
- `ROUTE_CURRENT_CHANNEL_ONLY`: Restricts agents to receive contacts only from the channel they are currently handling.

This feature enables fine-grained control over agent workload distribution and helps optimize contact center operations.
Comment on lines +52 to +59
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block seems redundant with the argument reference below. If any of the additional details are necessary they can be included in the argument descriptions instead.


## Argument Reference

This resource supports the following arguments:
Expand All @@ -56,6 +76,11 @@ A `media_concurrencies` block supports the following arguments:

* `channel` - (Required) Specifies the channels that agents can handle in the Contact Control Panel (CCP). Valid values are `VOICE`, `CHAT`, `TASK`.
* `concurrency` - (Required) 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.
* `cross_channel_behavior` - (Optional) Configuration block for cross-channel behavior. If not specified, AWS will use the service default behavior. Documented below.

A `cross_channel_behavior` block supports the following arguments:

* `behavior_type` - (Required) Specifies the cross-channel behavior for routing contacts across multiple channels. Valid values are `ROUTE_CURRENT_CHANNEL_ONLY`, `ROUTE_ANY_CHANNEL`.

A `queue_configs` block supports the following arguments:

Expand Down
Loading