diff --git a/.changelog/39567.txt b/.changelog/39567.txt new file mode 100644 index 00000000000..580fbca23d1 --- /dev/null +++ b/.changelog/39567.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_bedrockagent_knowledge_base: Change `knowledge_base_configuration` and `storage_configuration` to [ForceNew](https://developer.hashicorp.com/terraform/plugin/sdkv2/schemas/schema-behaviors#forcenew) +``` diff --git a/internal/service/bedrockagent/agent_knowledge_base_association_test.go b/internal/service/bedrockagent/agent_knowledge_base_association_test.go index 4c334395ce4..7aa1b457fa3 100644 --- a/internal/service/bedrockagent/agent_knowledge_base_association_test.go +++ b/internal/service/bedrockagent/agent_knowledge_base_association_test.go @@ -222,7 +222,7 @@ func testAccCheckAgentKnowledgeBaseAssociationExists(ctx context.Context, n stri func testAccAgentKnowledgeBaseAssociationConfig_basic(rName, agentModel, embeddingModel, description, state string) string { return acctest.ConfigCompose( testAccAgentConfig_basic(rName, agentModel, description), - testAccKnowledgeBaseConfig_basicRDS(rName, embeddingModel), + testAccKnowledgeBaseConfig_basicRDS(rName, embeddingModel, ""), fmt.Sprintf(` resource "aws_bedrockagent_agent_knowledge_base_association" "test" { agent_id = aws_bedrockagent_agent.test.id diff --git a/internal/service/bedrockagent/data_source_test.go b/internal/service/bedrockagent/data_source_test.go index f3fd86ea602..cf0bb02944a 100644 --- a/internal/service/bedrockagent/data_source_test.go +++ b/internal/service/bedrockagent/data_source_test.go @@ -561,7 +561,7 @@ func testAccCheckDataSourceExists(ctx context.Context, n string, v *types.DataSo } func testAccDataSourceConfig_base(rName, embeddingModel string) string { - return acctest.ConfigCompose(testAccKnowledgeBaseConfig_basicRDS(rName, embeddingModel), fmt.Sprintf(` + return acctest.ConfigCompose(testAccKnowledgeBaseConfig_basicRDS(rName, embeddingModel, ""), fmt.Sprintf(` resource "aws_s3_bucket" "test" { bucket = %[1]q } diff --git a/internal/service/bedrockagent/knowledge_base.go b/internal/service/bedrockagent/knowledge_base.go index d3d128d3567..b243a08bca4 100644 --- a/internal/service/bedrockagent/knowledge_base.go +++ b/internal/service/bedrockagent/knowledge_base.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -102,10 +103,16 @@ func (r *knowledgeBaseResource) Schema(ctx context.Context, request resource.Sch listvalidator.SizeAtLeast(1), listvalidator.SizeAtMost(1), }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ names.AttrType: schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, }, Blocks: map[string]schema.Block{ @@ -116,11 +123,17 @@ func (r *knowledgeBaseResource) Schema(ctx context.Context, request resource.Sch listvalidator.SizeAtLeast(1), listvalidator.SizeAtMost(1), }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "embedding_model_arn": schema.StringAttribute{ CustomType: fwtypes.ARNType, Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, }, }, @@ -135,10 +148,16 @@ func (r *knowledgeBaseResource) Schema(ctx context.Context, request resource.Sch listvalidator.SizeAtLeast(1), listvalidator.SizeAtMost(1), }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ names.AttrType: schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, }, Blocks: map[string]schema.Block{ @@ -147,17 +166,29 @@ func (r *knowledgeBaseResource) Schema(ctx context.Context, request resource.Sch Validators: []validator.List{ listvalidator.SizeAtMost(1), }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "connection_string": schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "credentials_secret_arn": schema.StringAttribute{ CustomType: fwtypes.ARNType, Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, names.AttrNamespace: schema.StringAttribute{ Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, }, Blocks: map[string]schema.Block{ @@ -166,13 +197,22 @@ func (r *knowledgeBaseResource) Schema(ctx context.Context, request resource.Sch Validators: []validator.List{ listvalidator.SizeAtMost(1), }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "metadata_field": schema.StringAttribute{ Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "text_field": schema.StringAttribute{ Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, }, }, @@ -185,21 +225,36 @@ func (r *knowledgeBaseResource) Schema(ctx context.Context, request resource.Sch Validators: []validator.List{ listvalidator.SizeAtMost(1), }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "credentials_secret_arn": schema.StringAttribute{ CustomType: fwtypes.ARNType, Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, names.AttrDatabaseName: schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, names.AttrResourceARN: schema.StringAttribute{ CustomType: fwtypes.ARNType, Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, names.AttrTableName: schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, }, Blocks: map[string]schema.Block{ @@ -208,19 +263,34 @@ func (r *knowledgeBaseResource) Schema(ctx context.Context, request resource.Sch Validators: []validator.List{ listvalidator.SizeAtMost(1), }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "metadata_field": schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "primary_key_field": schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "text_field": schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "vector_field": schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, }, }, @@ -233,17 +303,29 @@ func (r *knowledgeBaseResource) Schema(ctx context.Context, request resource.Sch Validators: []validator.List{ listvalidator.SizeAtMost(1), }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "credentials_secret_arn": schema.StringAttribute{ CustomType: fwtypes.ARNType, Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, names.AttrEndpoint: schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "vector_index_name": schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, }, Blocks: map[string]schema.Block{ @@ -252,16 +334,28 @@ func (r *knowledgeBaseResource) Schema(ctx context.Context, request resource.Sch Validators: []validator.List{ listvalidator.SizeAtMost(1), }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "metadata_field": schema.StringAttribute{ Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "text_field": schema.StringAttribute{ Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "vector_field": schema.StringAttribute{ Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, }, }, @@ -274,14 +368,23 @@ func (r *knowledgeBaseResource) Schema(ctx context.Context, request resource.Sch Validators: []validator.List{ listvalidator.SizeAtMost(1), }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "collection_arn": schema.StringAttribute{ CustomType: fwtypes.ARNType, Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "vector_index_name": schema.StringAttribute{ Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, }, Blocks: map[string]schema.Block{ @@ -290,16 +393,28 @@ func (r *knowledgeBaseResource) Schema(ctx context.Context, request resource.Sch Validators: []validator.List{ listvalidator.SizeAtMost(1), }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "metadata_field": schema.StringAttribute{ Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "text_field": schema.StringAttribute{ Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, "vector_field": schema.StringAttribute{ Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, }, }, }, @@ -414,9 +529,7 @@ func (r *knowledgeBaseResource) Update(ctx context.Context, request resource.Upd conn := r.Meta().BedrockAgentClient(ctx) if !new.Description.Equal(old.Description) || - !new.KnowledgeBaseConfiguration.Equal(old.KnowledgeBaseConfiguration) || - !new.Name.Equal(old.Name) || - !new.StorageConfiguration.Equal(old.StorageConfiguration) { + !new.Name.Equal(old.Name) { input := &bedrockagent.UpdateKnowledgeBaseInput{} response.Diagnostics.Append(fwflex.Expand(ctx, new, input)...) if response.Diagnostics.HasError() { diff --git a/internal/service/bedrockagent/knowledge_base_test.go b/internal/service/bedrockagent/knowledge_base_test.go index d771aba4175..57d1d5ea79a 100644 --- a/internal/service/bedrockagent/knowledge_base_test.go +++ b/internal/service/bedrockagent/knowledge_base_test.go @@ -6,12 +6,17 @@ package bedrockagent_test import ( "context" "fmt" + "strconv" "testing" "github.com/aws/aws-sdk-go-v2/service/bedrockagent/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/statecheck" "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfbedrockagent "github.com/hashicorp/terraform-provider-aws/internal/service/bedrockagent" @@ -48,13 +53,20 @@ func testAccKnowledgeBase_basicRDS(t *testing.T) { CheckDestroy: testAccCheckKnowledgeBaseDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccKnowledgeBaseConfig_basicRDS(rName, foundationModel), + Config: testAccKnowledgeBaseConfig_basicRDS(rName, foundationModel, ""), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + }, + }, Check: resource.ComposeTestCheckFunc( testAccCheckKnowledgeBaseExists(ctx, resourceName, &knowledgebase), - resource.TestCheckResourceAttrPair(resourceName, names.AttrRoleARN, "aws_iam_role.test", names.AttrARN), + resource.TestCheckNoResourceAttr(resourceName, names.AttrDescription), resource.TestCheckResourceAttr(resourceName, "knowledge_base_configuration.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "knowledge_base_configuration.0.vector_knowledge_base_configuration.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "knowledge_base_configuration.0.type", "VECTOR"), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + resource.TestCheckResourceAttrPair(resourceName, names.AttrRoleARN, "aws_iam_role.test", names.AttrARN), resource.TestCheckResourceAttr(resourceName, "storage_configuration.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "storage_configuration.0.type", "RDS"), resource.TestCheckResourceAttr(resourceName, "storage_configuration.0.rds_configuration.#", acctest.Ct1), @@ -71,6 +83,32 @@ func testAccKnowledgeBase_basicRDS(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + Config: testAccKnowledgeBaseConfig_basicRDS(rName, foundationModel, "test description"), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + }, + }, + Check: resource.ComposeTestCheckFunc( + testAccCheckKnowledgeBaseExists(ctx, resourceName, &knowledgebase), + resource.TestCheckResourceAttr(resourceName, names.AttrDescription, "test description"), + resource.TestCheckResourceAttr(resourceName, "knowledge_base_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "knowledge_base_configuration.0.vector_knowledge_base_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "knowledge_base_configuration.0.type", "VECTOR"), + resource.TestCheckResourceAttr(resourceName, names.AttrName, rName), + resource.TestCheckResourceAttrPair(resourceName, names.AttrRoleARN, "aws_iam_role.test", names.AttrARN), + resource.TestCheckResourceAttr(resourceName, "storage_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "storage_configuration.0.type", "RDS"), + resource.TestCheckResourceAttr(resourceName, "storage_configuration.0.rds_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "storage_configuration.0.rds_configuration.0.table_name", "bedrock_integration.bedrock_kb"), + resource.TestCheckResourceAttr(resourceName, "storage_configuration.0.rds_configuration.0.field_mapping.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "storage_configuration.0.rds_configuration.0.field_mapping.0.vector_field", "embedding"), + resource.TestCheckResourceAttr(resourceName, "storage_configuration.0.rds_configuration.0.field_mapping.0.text_field", "chunks"), + resource.TestCheckResourceAttr(resourceName, "storage_configuration.0.rds_configuration.0.field_mapping.0.metadata_field", "metadata"), + resource.TestCheckResourceAttr(resourceName, "storage_configuration.0.rds_configuration.0.field_mapping.0.primary_key_field", names.AttrID), + ), + }, }, }) } @@ -104,7 +142,7 @@ func testAccKnowledgeBase_disappears(t *testing.T) { CheckDestroy: testAccCheckKnowledgeBaseDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccKnowledgeBaseConfig_basicRDS(rName, foundationModel), + Config: testAccKnowledgeBaseConfig_basicRDS(rName, foundationModel, ""), Check: resource.ComposeTestCheckFunc( testAccCheckKnowledgeBaseExists(ctx, resourceName, &knowledgebase), acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfbedrockagent.ResourceKnowledgeBase, resourceName), @@ -145,10 +183,18 @@ func testAccKnowledgeBase_tags(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccKnowledgeBaseConfig_tags1(rName, foundationModel, acctest.CtKey1, acctest.CtValue1), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1), + })), + }, Check: resource.ComposeTestCheckFunc( testAccCheckKnowledgeBaseExists(ctx, resourceName, &knowledgebase), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1), ), }, { @@ -158,19 +204,35 @@ func testAccKnowledgeBase_tags(t *testing.T) { }, { Config: testAccKnowledgeBaseConfig_tags2(rName, foundationModel, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, acctest.CtValue2), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey1: knownvalue.StringExact(acctest.CtValue1Updated), + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + }, Check: resource.ComposeTestCheckFunc( testAccCheckKnowledgeBaseExists(ctx, resourceName, &knowledgebase), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1Updated), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), ), }, { Config: testAccKnowledgeBaseConfig_tags1(rName, foundationModel, acctest.CtKey2, acctest.CtValue2), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionUpdate), + }, + }, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue(resourceName, tfjsonpath.New(names.AttrTags), knownvalue.MapExact(map[string]knownvalue.Check{ + acctest.CtKey2: knownvalue.StringExact(acctest.CtValue2), + })), + }, Check: resource.ComposeTestCheckFunc( testAccCheckKnowledgeBaseExists(ctx, resourceName, &knowledgebase), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), - resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), ), }, }, @@ -505,12 +567,20 @@ resource "null_resource" "db_setup" { `, rName, model)) } -func testAccKnowledgeBaseConfig_basicRDS(rName, model string) string { +func testAccKnowledgeBaseConfig_basicRDS(rName, model, description string) string { + if description == "" { + description = "null" + } else { + description = strconv.Quote(description) + } + return acctest.ConfigCompose(testAccKnowledgeBase_baseRDS(rName, model), fmt.Sprintf(` resource "aws_bedrockagent_knowledge_base" "test" { name = %[1]q role_arn = aws_iam_role.test.arn + description = %[3]s + knowledge_base_configuration { vector_knowledge_base_configuration { embedding_model_arn = "arn:${data.aws_partition.current.partition}:bedrock:${data.aws_region.current.name}::foundation-model/%[2]s" @@ -536,7 +606,7 @@ resource "aws_bedrockagent_knowledge_base" "test" { depends_on = [aws_iam_role_policy.test, null_resource.db_setup] } -`, rName, model)) +`, rName, model, description)) } func testAccKnowledgeBaseConfig_tags1(rName, model, tag1Key, tag1Value string) string {