diff --git a/.changelog/43411.txt b/.changelog/43411.txt new file mode 100644 index 000000000000..82b04734bd2b --- /dev/null +++ b/.changelog/43411.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_bedrockagent_agent_alias: Fix inconsistent result error when `routing_configuration` is not specified but AWS returns default `agent_version` +``` \ No newline at end of file diff --git a/internal/service/bedrockagent/agent_alias.go b/internal/service/bedrockagent/agent_alias.go index 7ec72d5ec87a..43813b9eca08 100644 --- a/internal/service/bedrockagent/agent_alias.go +++ b/internal/service/bedrockagent/agent_alias.go @@ -171,6 +171,19 @@ func (r *agentAliasResource) Create(ctx context.Context, request resource.Create return } + // Handle the case where routing_configuration was not provided in the plan but AWS returns it with a default agent_version + // This prevents the "Provider produced inconsistent result" error + var planData agentAliasResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &planData)...) + if response.Diagnostics.HasError() { + return + } + + if planData.RoutingConfiguration.IsNull() { + // If routing_configuration was not specified in the plan, keep it as null in the state + data.RoutingConfiguration = planData.RoutingConfiguration + } + response.Diagnostics.Append(response.State.Set(ctx, &data)...) } @@ -206,11 +219,24 @@ func (r *agentAliasResource) Read(ctx context.Context, request resource.ReadRequ return } + // Store the original state to check if routing_configuration should remain null + var originalData agentAliasResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &originalData)...) + if response.Diagnostics.HasError() { + return + } + response.Diagnostics.Append(fwflex.Flatten(ctx, output, &data)...) if response.Diagnostics.HasError() { return } + // Handle the case where routing_configuration was null in the original state + // This maintains consistency when AWS returns a default agent_version + if originalData.RoutingConfiguration.IsNull() { + data.RoutingConfiguration = originalData.RoutingConfiguration + } + response.Diagnostics.Append(response.State.Set(ctx, &data)...) } @@ -266,8 +292,18 @@ func (r *agentAliasResource) Update(ctx context.Context, request resource.Update } } - // set unknowns if a tags only update - if new.RoutingConfiguration.IsUnknown() { + // Handle routing_configuration state consistency + var planData agentAliasResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &planData)...) + if response.Diagnostics.HasError() { + return + } + + if planData.RoutingConfiguration.IsNull() { + // If routing_configuration was not specified in the plan, keep it as null in the state + new.RoutingConfiguration = planData.RoutingConfiguration + } else if new.RoutingConfiguration.IsUnknown() { + // set unknowns if a tags only update new.RoutingConfiguration = old.RoutingConfiguration } diff --git a/internal/service/bedrockagent/agent_alias_test.go b/internal/service/bedrockagent/agent_alias_test.go index 67c2ad0df4a7..deabdfbf131b 100644 --- a/internal/service/bedrockagent/agent_alias_test.go +++ b/internal/service/bedrockagent/agent_alias_test.go @@ -177,6 +177,76 @@ func TestAccBedrockAgentAgentAlias_routingUpdate(t *testing.T) { }) } +func TestAccBedrockAgentAgentAlias_routingConfigurationNull(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_bedrockagent_agent_alias.test" + var v awstypes.AgentAlias + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, names.BedrockEndpointID) }, + ErrorCheck: acctest.ErrorCheck(t, names.BedrockAgentServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAgentAliasDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAgentAliasConfig_routingConfigurationNull(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAgentAliasExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "agent_alias_name", rName), + resource.TestCheckResourceAttrSet(resourceName, "agent_alias_arn"), + resource.TestCheckResourceAttrSet(resourceName, "agent_alias_id"), + resource.TestCheckResourceAttrSet(resourceName, "agent_id"), + resource.TestCheckResourceAttr(resourceName, names.AttrDescription, "Test Alias"), + // The key assertion: routing_configuration should remain null even if AWS returns a default + resource.TestCheckResourceAttr(resourceName, "routing_configuration.#", "0"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccBedrockAgentAgentAlias_routingConfigurationExplicitNull(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_bedrockagent_agent_alias.test" + var v awstypes.AgentAlias + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, names.BedrockEndpointID) }, + ErrorCheck: acctest.ErrorCheck(t, names.BedrockAgentServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAgentAliasDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAgentAliasConfig_routingConfigurationExplicitNull(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAgentAliasExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "agent_alias_name", rName), + resource.TestCheckResourceAttrSet(resourceName, "agent_alias_arn"), + resource.TestCheckResourceAttrSet(resourceName, "agent_alias_id"), + resource.TestCheckResourceAttrSet(resourceName, "agent_id"), + resource.TestCheckResourceAttr(resourceName, names.AttrDescription, "Test Alias"), + // The key assertion: routing_configuration should remain null even when explicitly set to null + resource.TestCheckResourceAttr(resourceName, "routing_configuration.#", "0"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccBedrockAgentAgentAlias_tags(t *testing.T) { ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -412,6 +482,34 @@ resource "aws_bedrockagent_agent_alias" "test" { `, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } +func testAccAgentAliasConfig_routingConfigurationNull(rName string) string { + return acctest.ConfigCompose( + testAccAgentConfig_basic(rName, "anthropic.claude-v2", "basic claude"), + fmt.Sprintf(` +resource "aws_bedrockagent_agent_alias" "test" { + agent_alias_name = %[1]q + agent_id = aws_bedrockagent_agent.test.agent_id + description = "Test Alias" + # routing_configuration is intentionally not specified to test null handling +} +`, rName)) +} + +func testAccAgentAliasConfig_routingConfigurationExplicitNull(rName string) string { + return acctest.ConfigCompose( + testAccAgentConfig_basic(rName, "anthropic.claude-v2", "basic claude"), + fmt.Sprintf(` +resource "aws_bedrockagent_agent_alias" "test" { + agent_alias_name = %[1]q + agent_id = aws_bedrockagent_agent.test.agent_id + description = "Test Alias" + routing_configuration { + agent_version = null + } +} +`, rName)) +} + func testAccAgentAliasConfig_provisionedThroughout(rName, version string) string { return acctest.ConfigCompose( testAccAgentAliasConfig_provisionedModelThroughputBase(rName),