From 4e4610471954143f327f5dd190b432cc9e9b4d75 Mon Sep 17 00:00:00 2001 From: Richard Weerasinghe Date: Wed, 8 Nov 2023 15:07:45 +1300 Subject: [PATCH 01/17] add aws_bedrock_model_invocation_logging_configuration resource --- .../model_invocation_logging_configuration.go | 280 ++++++++++++++++++ ...l_invocation_logging_configuration_test.go | 167 +++++++++++ .../service/bedrock/service_package_gen.go | 6 +- 3 files changed, 452 insertions(+), 1 deletion(-) create mode 100644 internal/service/bedrock/model_invocation_logging_configuration.go create mode 100644 internal/service/bedrock/model_invocation_logging_configuration_test.go diff --git a/internal/service/bedrock/model_invocation_logging_configuration.go b/internal/service/bedrock/model_invocation_logging_configuration.go new file mode 100644 index 00000000000..142cefa1614 --- /dev/null +++ b/internal/service/bedrock/model_invocation_logging_configuration.go @@ -0,0 +1,280 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package bedrock + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/service/bedrock" + bedrock_types "github.com/aws/aws-sdk-go-v2/service/bedrock/types" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" +) + +type resourceModelInvocationLoggingConfigurationModel struct { + ID types.String `tfsdk:"id"` + LoggingConfig *loggingConfigModel `tfsdk:"logging_config"` +} + +type loggingConfigModel struct { + EmbeddingDataDeliveryEnabled types.Bool `tfsdk:"embedding_data_delivery_enabled"` + ImageDataDeliveryEnabled types.Bool `tfsdk:"image_data_delivery_enabled"` + TextDataDeliveryEnabled types.Bool `tfsdk:"text_data_delivery_enabled"` + CloudWatchConfig *cloudWatchConfigModel `tfsdk:"cloud_watch_config"` + S3Config *s3ConfigModel `tfsdk:"s3_config"` +} + +type cloudWatchConfigModel struct { + LogGroupName types.String `tfsdk:"log_group_name"` + RoleArn types.String `tfsdk:"role_arn"` + LargeDataDeliveryS3Config *s3ConfigModel `tfsdk:"large_data_delivery_s3_config"` +} + +type s3ConfigModel struct { + BucketName types.String `tfsdk:"bucket_name"` + KeyPrefix types.String `tfsdk:"key_prefix"` +} + +// @FrameworkResource +func newResourceModelInvocationLoggingConfiguration(context.Context) (resource.ResourceWithConfigure, error) { + return &resourceModelInvocationLoggingConfiguration{}, nil +} + +type resourceModelInvocationLoggingConfiguration struct { + framework.ResourceWithConfigure +} + +func (r *resourceModelInvocationLoggingConfiguration) Metadata(_ context.Context, request resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "aws_bedrock_model_invocation_logging_configuration" +} + +func (r *resourceModelInvocationLoggingConfiguration) Schema(ctx context.Context, request resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "id": framework.IDAttribute(), + }, + Blocks: map[string]schema.Block{ + "logging_config": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "embedding_data_delivery_enabled": schema.BoolAttribute{ + Required: true, + }, + "image_data_delivery_enabled": schema.BoolAttribute{ + Required: true, + }, + "text_data_delivery_enabled": schema.BoolAttribute{ + Required: true, + }, + }, + Blocks: map[string]schema.Block{ + "cloud_watch_config": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "log_group_name": schema.StringAttribute{ + Optional: true, + }, + "role_arn": schema.StringAttribute{ + Optional: true, + }, + }, + Blocks: map[string]schema.Block{ + "large_data_delivery_s3_config": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "bucket_name": schema.StringAttribute{ + Optional: true, + }, + "key_prefix": schema.StringAttribute{ + Optional: true, + }, + }, + }, + }, + }, + "s3_config": schema.SingleNestedBlock{ + Attributes: map[string]schema.Attribute{ + "bucket_name": schema.StringAttribute{ + Optional: true, + }, + "key_prefix": schema.StringAttribute{ + Optional: true, + }, + }, + }, + }, + }, + }, + } +} + +func (r *resourceModelInvocationLoggingConfiguration) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data resourceModelInvocationLoggingConfigurationModel + + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + lc := expandLoggingConfig(data.LoggingConfig) + + conn := r.Meta().BedrockClient(ctx) + input := bedrock.PutModelInvocationLoggingConfigurationInput{ + LoggingConfig: lc, + } + + _, err := conn.PutModelInvocationLoggingConfiguration(ctx, &input) + if err != nil { + resp.Diagnostics.AddError("failed to put model invocation logging configuration", err.Error()) + return + } + + data.ID = flex.StringValueToFramework(ctx, r.Meta().Region) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *resourceModelInvocationLoggingConfiguration) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state resourceModelInvocationLoggingConfigurationModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + conn := r.Meta().BedrockClient(ctx) + output, err := conn.GetModelInvocationLoggingConfiguration(ctx, &bedrock.GetModelInvocationLoggingConfigurationInput{}) + if err != nil { + resp.Diagnostics.AddError("failed to get model invocation logging configuration", err.Error()) + return + } + + state.ID = flex.StringValueToFramework(ctx, r.Meta().Region) + state.LoggingConfig = flattenLoggingConfig(ctx, output.LoggingConfig) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *resourceModelInvocationLoggingConfiguration) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data resourceModelInvocationLoggingConfigurationModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + + lc := expandLoggingConfig(data.LoggingConfig) + + conn := r.Meta().BedrockClient(ctx) + input := bedrock.PutModelInvocationLoggingConfigurationInput{ + LoggingConfig: lc, + } + + _, err := conn.PutModelInvocationLoggingConfiguration(ctx, &input) + if err != nil { + resp.Diagnostics.AddError("failed to put model invocation logging configuration", err.Error()) + return + } + + data.ID = flex.StringValueToFramework(ctx, r.Meta().Region) + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *resourceModelInvocationLoggingConfiguration) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + conn := r.Meta().BedrockClient(ctx) + + _, err := conn.DeleteModelInvocationLoggingConfiguration(ctx, &bedrock.DeleteModelInvocationLoggingConfigurationInput{}) + if err != nil { + resp.Diagnostics.AddError("failed to delete model invocation logging configuration", err.Error()) + return + } +} + +func (r *resourceModelInvocationLoggingConfiguration) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + +func flattenLoggingConfig(ctx context.Context, apiObject *bedrock_types.LoggingConfig) *loggingConfigModel { + if apiObject == nil { + return nil + } + + return &loggingConfigModel{ + EmbeddingDataDeliveryEnabled: flex.BoolToFramework(ctx, apiObject.EmbeddingDataDeliveryEnabled), + ImageDataDeliveryEnabled: flex.BoolToFramework(ctx, apiObject.ImageDataDeliveryEnabled), + TextDataDeliveryEnabled: flex.BoolToFramework(ctx, apiObject.TextDataDeliveryEnabled), + CloudWatchConfig: flattenCloudWatchConfig(ctx, apiObject.CloudWatchConfig), + S3Config: flattenS3Config(ctx, apiObject.S3Config), + } +} + +func flattenCloudWatchConfig(ctx context.Context, apiObject *bedrock_types.CloudWatchConfig) *cloudWatchConfigModel { + if apiObject == nil { + return nil + } + + return &cloudWatchConfigModel{ + LogGroupName: flex.StringToFramework(ctx, apiObject.LogGroupName), + RoleArn: flex.StringToFramework(ctx, apiObject.RoleArn), + LargeDataDeliveryS3Config: flattenS3Config(ctx, apiObject.LargeDataDeliveryS3Config), + } +} + +func flattenS3Config(ctx context.Context, apiObject *bedrock_types.S3Config) *s3ConfigModel { + if apiObject == nil { + return nil + } + + return &s3ConfigModel{ + BucketName: flex.StringToFramework(ctx, apiObject.BucketName), + KeyPrefix: flex.StringToFramework(ctx, apiObject.KeyPrefix), + } +} + +func expandLoggingConfig(model *loggingConfigModel) *bedrock_types.LoggingConfig { + if model == nil { + return nil + } + + apiObject := &bedrock_types.LoggingConfig{ + EmbeddingDataDeliveryEnabled: model.EmbeddingDataDeliveryEnabled.ValueBoolPointer(), + ImageDataDeliveryEnabled: model.ImageDataDeliveryEnabled.ValueBoolPointer(), + TextDataDeliveryEnabled: model.TextDataDeliveryEnabled.ValueBoolPointer(), + } + if model.CloudWatchConfig != nil { + apiObject.CloudWatchConfig = expandCloudWatchConfig(model.CloudWatchConfig) + } + if model.S3Config != nil { + apiObject.S3Config = expandS3Config(model.S3Config) + } + + return apiObject +} + +func expandCloudWatchConfig(model *cloudWatchConfigModel) *bedrock_types.CloudWatchConfig { + if model == nil { + return nil + } + + apiObject := &bedrock_types.CloudWatchConfig{ + LogGroupName: model.LogGroupName.ValueStringPointer(), + RoleArn: model.RoleArn.ValueStringPointer(), + LargeDataDeliveryS3Config: expandS3Config(model.LargeDataDeliveryS3Config), + } + + return apiObject +} + +func expandS3Config(model *s3ConfigModel) *bedrock_types.S3Config { + if model == nil { + return nil + } + + apiObject := &bedrock_types.S3Config{ + BucketName: model.BucketName.ValueStringPointer(), + KeyPrefix: model.KeyPrefix.ValueStringPointer(), + } + + return apiObject +} diff --git a/internal/service/bedrock/model_invocation_logging_configuration_test.go b/internal/service/bedrock/model_invocation_logging_configuration_test.go new file mode 100644 index 00000000000..f17916cfb4f --- /dev/null +++ b/internal/service/bedrock/model_invocation_logging_configuration_test.go @@ -0,0 +1,167 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package bedrock_test + +import ( + "fmt" + "testing" + + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" +) + +func TestAccBedrockModelInvocationLoggingConfiguration_basic(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_bedrock_model_invocation_logging_configuration.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccModelInvocationLoggingConfiguration_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "logging_config.embedding_data_delivery_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "logging_config.image_data_delivery_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "logging_config.text_data_delivery_enabled", "true"), + resource.TestCheckResourceAttrSet(resourceName, "logging_config.cloud_watch_config.log_group_name"), + resource.TestCheckResourceAttrSet(resourceName, "logging_config.cloud_watch_config.role_arn"), + resource.TestCheckResourceAttrSet(resourceName, "logging_config.s3_config.bucket_name"), + resource.TestCheckResourceAttr(resourceName, "logging_config.s3_config.key_prefix", "bedrock"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccModelInvocationLoggingConfiguration_basic(rName string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} +data "aws_region" "current" {} +data "aws_partition" "current" {} + +resource aws_s3_bucket bedrock_logging { + bucket = "bedrock-logging-%[1]s" + force_destroy = true + lifecycle { + ignore_changes = ["tags", "tags_all"] + } +} + +resource "aws_s3_bucket_policy" "bedrock_logging" { + bucket = aws_s3_bucket.bedrock_logging.bucket + + policy = < Date: Wed, 8 Nov 2023 16:56:29 +1300 Subject: [PATCH 02/17] add docs --- ...cation_logging_configuration.html.markdown | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown diff --git a/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown b/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown new file mode 100644 index 00000000000..a49938f9816 --- /dev/null +++ b/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown @@ -0,0 +1,132 @@ +--- +subcategory: "Amazon Bedrock" +layout: "aws" +page_title: "AWS: aws_bedrock_model_invocation_logging_configuration" +description: |- + Manages Bedrock model invocation logging configuration. +--- + +# Resource: aws_bedrock_model_invocation_logging_configuration + +Manages Bedrock model invocation logging configuration. + +## Example Usage + +```terraform +data "aws_caller_identity" "current" {} + +resource aws_s3_bucket bedrock_logging { + bucket = "bedrock-logging-%[1]s" + force_destroy = true + lifecycle { + ignore_changes = [ + tags["CreatorId"], tags["CreatorName"], + ] + } +} + +resource "aws_s3_bucket_policy" "bedrock_logging" { + bucket = aws_s3_bucket.bedrock_logging.bucket + + policy = < Date: Wed, 8 Nov 2023 21:01:33 +1300 Subject: [PATCH 03/17] terrafmt lint fixes --- ...l_invocation_logging_configuration_test.go | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/internal/service/bedrock/model_invocation_logging_configuration_test.go b/internal/service/bedrock/model_invocation_logging_configuration_test.go index f17916cfb4f..58cdc2a88f1 100644 --- a/internal/service/bedrock/model_invocation_logging_configuration_test.go +++ b/internal/service/bedrock/model_invocation_logging_configuration_test.go @@ -126,17 +126,17 @@ resource "aws_iam_policy" "bedrock_logging" { description = "BedrockCloudWatchPolicy" policy = jsonencode({ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "logs:CreateLogStream", - "logs:PutLogEvents" - ], - "Resource": "${aws_cloudwatch_log_group.bedrock_logging.arn}:log-stream:aws/bedrock/modelinvocations" - } - ] + "Version" : "2012-10-17", + "Statement" : [ + { + "Effect" : "Allow", + "Action" : [ + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Resource" : "${aws_cloudwatch_log_group.bedrock_logging.arn}:log-stream:aws/bedrock/modelinvocations" + } + ] }) } @@ -152,7 +152,7 @@ resource "aws_bedrock_model_invocation_logging_configuration" "test" { text_data_delivery_enabled = true cloud_watch_config { log_group_name = aws_cloudwatch_log_group.bedrock_logging.name - role_arn = aws_iam_role.bedrock_logging.arn + role_arn = aws_iam_role.bedrock_logging.arn } s3_config { bucket_name = aws_s3_bucket.bedrock_logging.id From 80894106cd13584244396bd8b076b4bdf96eebb1 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Thu, 9 Nov 2023 15:47:53 -0500 Subject: [PATCH 04/17] chore: changelog --- .changelog/34303.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/34303.txt diff --git a/.changelog/34303.txt b/.changelog/34303.txt new file mode 100644 index 00000000000..18f4a3b9465 --- /dev/null +++ b/.changelog/34303.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_bedrock_model_invocation_logging_configuration +`` From 6d4f02140b7f31512b0d0f1f58d3e5547db69ecf Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Thu, 9 Nov 2023 15:53:27 -0500 Subject: [PATCH 05/17] r/aws_bedrock_model_invocation_logging_configuration: add name to annotation --- .../service/bedrock/model_invocation_logging_configuration.go | 2 +- internal/service/bedrock/service_package_gen.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/service/bedrock/model_invocation_logging_configuration.go b/internal/service/bedrock/model_invocation_logging_configuration.go index 142cefa1614..046dca60039 100644 --- a/internal/service/bedrock/model_invocation_logging_configuration.go +++ b/internal/service/bedrock/model_invocation_logging_configuration.go @@ -40,7 +40,7 @@ type s3ConfigModel struct { KeyPrefix types.String `tfsdk:"key_prefix"` } -// @FrameworkResource +// @FrameworkResource(name="Model Invocation Logging Configuration") func newResourceModelInvocationLoggingConfiguration(context.Context) (resource.ResourceWithConfigure, error) { return &resourceModelInvocationLoggingConfiguration{}, nil } diff --git a/internal/service/bedrock/service_package_gen.go b/internal/service/bedrock/service_package_gen.go index 2fc8ed2183b..ae77fab1ea9 100644 --- a/internal/service/bedrock/service_package_gen.go +++ b/internal/service/bedrock/service_package_gen.go @@ -31,6 +31,7 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic return []*types.ServicePackageFrameworkResource{ { Factory: newResourceModelInvocationLoggingConfiguration, + Name: "Model Invocation Logging Configuration", }, } } From ee1c598e43269c9c10b9d2ee73ee8b04a2bcec77 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Fri, 10 Nov 2023 10:56:59 -0500 Subject: [PATCH 06/17] r/aws_bedrock_model_invocation_logging_configuration: standardize diag messages --- .../model_invocation_logging_configuration.go | 56 ++++++++++++------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/internal/service/bedrock/model_invocation_logging_configuration.go b/internal/service/bedrock/model_invocation_logging_configuration.go index 046dca60039..5b2af3d2af8 100644 --- a/internal/service/bedrock/model_invocation_logging_configuration.go +++ b/internal/service/bedrock/model_invocation_logging_configuration.go @@ -12,8 +12,10 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + "github.com/hashicorp/terraform-provider-aws/names" ) type resourceModelInvocationLoggingConfigurationModel struct { @@ -40,6 +42,8 @@ type s3ConfigModel struct { KeyPrefix types.String `tfsdk:"key_prefix"` } +const ResourceNameModelInvocationLoggingConfiguration = "Model Invocation Logging Configuration" + // @FrameworkResource(name="Model Invocation Logging Configuration") func newResourceModelInvocationLoggingConfiguration(context.Context) (resource.ResourceWithConfigure, error) { return &resourceModelInvocationLoggingConfiguration{}, nil @@ -111,83 +115,93 @@ func (r *resourceModelInvocationLoggingConfiguration) Schema(ctx context.Context } func (r *resourceModelInvocationLoggingConfiguration) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var data resourceModelInvocationLoggingConfigurationModel + conn := r.Meta().BedrockClient(ctx) + var data resourceModelInvocationLoggingConfigurationModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) if resp.Diagnostics.HasError() { return } + data.ID = flex.StringValueToFramework(ctx, r.Meta().Region) - lc := expandLoggingConfig(data.LoggingConfig) - - conn := r.Meta().BedrockClient(ctx) input := bedrock.PutModelInvocationLoggingConfigurationInput{ - LoggingConfig: lc, + LoggingConfig: expandLoggingConfig(data.LoggingConfig), } _, err := conn.PutModelInvocationLoggingConfiguration(ctx, &input) if err != nil { - resp.Diagnostics.AddError("failed to put model invocation logging configuration", err.Error()) + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Bedrock, create.ErrActionCreating, ResourceNameModelInvocationLoggingConfiguration, data.ID.String(), err), + err.Error(), + ) return } - data.ID = flex.StringValueToFramework(ctx, r.Meta().Region) - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } func (r *resourceModelInvocationLoggingConfiguration) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + conn := r.Meta().BedrockClient(ctx) + var state resourceModelInvocationLoggingConfigurationModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } - conn := r.Meta().BedrockClient(ctx) output, err := conn.GetModelInvocationLoggingConfiguration(ctx, &bedrock.GetModelInvocationLoggingConfigurationInput{}) if err != nil { - resp.Diagnostics.AddError("failed to get model invocation logging configuration", err.Error()) + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Bedrock, create.ErrActionReading, ResourceNameModelInvocationLoggingConfiguration, state.ID.String(), err), + err.Error(), + ) return } - state.ID = flex.StringValueToFramework(ctx, r.Meta().Region) state.LoggingConfig = flattenLoggingConfig(ctx, output.LoggingConfig) resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } func (r *resourceModelInvocationLoggingConfiguration) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + conn := r.Meta().BedrockClient(ctx) + var data resourceModelInvocationLoggingConfigurationModel resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) if resp.Diagnostics.HasError() { return } - lc := expandLoggingConfig(data.LoggingConfig) - - conn := r.Meta().BedrockClient(ctx) input := bedrock.PutModelInvocationLoggingConfigurationInput{ - LoggingConfig: lc, + LoggingConfig: expandLoggingConfig(data.LoggingConfig), } _, err := conn.PutModelInvocationLoggingConfiguration(ctx, &input) if err != nil { - resp.Diagnostics.AddError("failed to put model invocation logging configuration", err.Error()) - return + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Bedrock, create.ErrActionUpdating, ResourceNameModelInvocationLoggingConfiguration, data.ID.String(), err), + err.Error(), + ) } - data.ID = flex.StringValueToFramework(ctx, r.Meta().Region) - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } func (r *resourceModelInvocationLoggingConfiguration) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { conn := r.Meta().BedrockClient(ctx) + var data resourceModelInvocationLoggingConfigurationModel + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + if resp.Diagnostics.HasError() { + return + } + _, err := conn.DeleteModelInvocationLoggingConfiguration(ctx, &bedrock.DeleteModelInvocationLoggingConfigurationInput{}) if err != nil { - resp.Diagnostics.AddError("failed to delete model invocation logging configuration", err.Error()) - return + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.Bedrock, create.ErrActionDeleting, ResourceNameModelInvocationLoggingConfiguration, data.ID.String(), err), + err.Error(), + ) } } From a7e2ac3ab19ddc69927dc26cbdd2bf0aefaff2a3 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Fri, 10 Nov 2023 11:28:11 -0500 Subject: [PATCH 07/17] r/aws_bedrock_model_invocation_logging_configuration(docs): link to block definitions, tidy --- ...cation_logging_configuration.html.markdown | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown b/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown index a49938f9816..5e73ab967fd 100644 --- a/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown +++ b/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown @@ -77,46 +77,46 @@ resource "aws_bedrock_model_invocation_logging_configuration" "test" { The following arguments are required: -* `logging_config` - The logging configuration values to set. Type: LoggingConfig object. +* `logging_config` - The logging configuration values to set. See [`logging_config`](#logging_config-argument-reference). -## Attribute Reference - -This resource exports no additional attributes. - -### LoggingConfig Object +### `logging_config` Argument Reference The following arguments are optional: -* `cloud_watch_config` – CloudWatch logging configuration. Type: CloudWatchConfig object. -* `embedding_data_delivery_enabled` – Set to include embeddings data in the log delivery. Type: Boolean. Default: True -* `image_data_delivery_enabled` – Set to include image data in the log delivery. Type: Boolean. Default: True -* `textDataDeliveryEnabled` – Set to include text data in the log delivery. Type: Boolean. Default: True -* `s3_config` – S3 configuration for storing log data. Type: S3Config object. +* `cloudwatch_config` – CloudWatch logging configuration. See [`cloudwatch_config`](#cloudwatch_config-argument-reference). +* `embedding_data_delivery_enabled` – Set to include embeddings data in the log delivery. +* `image_data_delivery_enabled` – Set to include image data in the log delivery. +* `s3_config` – S3 configuration for storing log data. See [`s3_config`](#s3_config-argument-reference). +* `text_data_delivery_enabled` – Set to include text data in the log delivery. -### CloudWatchConfig Object +### `cloudwatch_config` Argument Reference The following arguments are required: -* `log_group_name` – The log group name. Type: String. Length Constraints: Minimum length of 1. Maximum length of 512. -* `role_arn` – The role ARN.. Type: String. Length Constraints: Minimum length of 0. Maximum length of 2048. Pattern: `^arn:aws(-[^:]+)?:iam::([0-9]{12})?:role/.+$` +* `log_group_name` – Log group name. +* `role_arn` – IAM Role ARN. The following arguments are optional: -* `large_data_delivery_s3_config` – S3 configuration for delivering a large amount of data. Type: S3Config. +* `large_data_delivery_s3_config` – S3 configuration for delivering a large amount of data. See [`s3_config`](#s3_config-argument-reference). -### S3Config Object +### `s3_config` Argument Reference The following arguments are required: -* `bucket_name` – S3 bucket name. Type: String. Length Constraints: Minimum length of 3. Maximum length of 63. +* `bucket_name` – S3 bucket name. The following arguments are optional: -* `key_prefix` – S3 prefix. Type: String. Length Constraints: Minimum length of 0. Maximum length of 1024. +* `key_prefix` – S3 object key prefix. + +## Attribute Reference + +* `id` - AWS region in which logging is configured. ## Import -In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import Bedrock Invocation Logging Configuration using the `id` set to the aws region. For example: +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import Bedrock Invocation Logging Configuration using the `id` set to the AWS region. For example: ```terraform import { @@ -125,7 +125,7 @@ import { } ``` -Using `terraform import`, import Bedrock custom model using the `id` set to the region. For example: +Using `terraform import`, import Bedrock custom model using the `id` set to the AWS region. For example: ```console % terraform import aws_bedrock_model_invocation_logging_configuration.my_config us-east-1 From 9ea13719995b49630231bfa0378997ebe4f1416a Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Fri, 10 Nov 2023 11:59:07 -0500 Subject: [PATCH 08/17] r/aws_bedrock_model_invocation_logging_configuration(docs): lintfix --- ...bedrock_model_invocation_logging_configuration.html.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown b/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown index 5e73ab967fd..af8fddbd382 100644 --- a/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown +++ b/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown @@ -112,6 +112,8 @@ The following arguments are optional: ## Attribute Reference +This resource exports the following attributes in addition to the arguments above: + * `id` - AWS region in which logging is configured. ## Import From 85362fa46dcd95c1c6126b6f5d103dc4c75eb643 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Fri, 10 Nov 2023 15:12:26 -0500 Subject: [PATCH 09/17] r/aws_bedrock_model_invocation_logging_configuration(docs): note on per region settings --- ...bedrock_model_invocation_logging_configuration.html.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown b/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown index af8fddbd382..c4610c613df 100644 --- a/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown +++ b/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown @@ -10,6 +10,8 @@ description: |- Manages Bedrock model invocation logging configuration. +~> Model invocation logging is configured per AWS region. To avoid overwriting settings, this resource should not be defined in multiple configurations. + ## Example Usage ```terraform From c23cd4e286fab02bcd257e89e2f6679690e4b3c7 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Fri, 10 Nov 2023 15:19:10 -0500 Subject: [PATCH 10/17] r/aws_bedrock_model_invocation_logging_configuration(docs): tidy example config --- ...cation_logging_configuration.html.markdown | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown b/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown index c4610c613df..d65976be471 100644 --- a/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown +++ b/website/docs/r/bedrock_model_invocation_logging_configuration.html.markdown @@ -14,11 +14,13 @@ Manages Bedrock model invocation logging configuration. ## Example Usage +### Basic Usage + ```terraform data "aws_caller_identity" "current" {} -resource aws_s3_bucket bedrock_logging { - bucket = "bedrock-logging-%[1]s" +resource aws_s3_bucket example { + bucket = "example" force_destroy = true lifecycle { ignore_changes = [ @@ -27,8 +29,8 @@ resource aws_s3_bucket bedrock_logging { } } -resource "aws_s3_bucket_policy" "bedrock_logging" { - bucket = aws_s3_bucket.bedrock_logging.bucket +resource "aws_s3_bucket_policy" "example" { + bucket = aws_s3_bucket.example.bucket policy = < Date: Fri, 10 Nov 2023 15:31:36 -0500 Subject: [PATCH 11/17] r/aws_bedrock_model_invocation_logging_configuration: use types.Object, refactor flex functions autoflex does not currently support SingleNestedBlock types, but once support is added this is potentially a candidate to be migrated --- .../model_invocation_logging_configuration.go | 185 +++++++++++------- ...l_invocation_logging_configuration_test.go | 6 +- 2 files changed, 117 insertions(+), 74 deletions(-) diff --git a/internal/service/bedrock/model_invocation_logging_configuration.go b/internal/service/bedrock/model_invocation_logging_configuration.go index 5b2af3d2af8..222ff2a310a 100644 --- a/internal/service/bedrock/model_invocation_logging_configuration.go +++ b/internal/service/bedrock/model_invocation_logging_configuration.go @@ -7,41 +7,21 @@ import ( "context" "github.com/aws/aws-sdk-go-v2/service/bedrock" - bedrock_types "github.com/aws/aws-sdk-go-v2/service/bedrock/types" + awstypes "github.com/aws/aws-sdk-go-v2/service/bedrock/types" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" "github.com/hashicorp/terraform-provider-aws/names" ) -type resourceModelInvocationLoggingConfigurationModel struct { - ID types.String `tfsdk:"id"` - LoggingConfig *loggingConfigModel `tfsdk:"logging_config"` -} - -type loggingConfigModel struct { - EmbeddingDataDeliveryEnabled types.Bool `tfsdk:"embedding_data_delivery_enabled"` - ImageDataDeliveryEnabled types.Bool `tfsdk:"image_data_delivery_enabled"` - TextDataDeliveryEnabled types.Bool `tfsdk:"text_data_delivery_enabled"` - CloudWatchConfig *cloudWatchConfigModel `tfsdk:"cloud_watch_config"` - S3Config *s3ConfigModel `tfsdk:"s3_config"` -} - -type cloudWatchConfigModel struct { - LogGroupName types.String `tfsdk:"log_group_name"` - RoleArn types.String `tfsdk:"role_arn"` - LargeDataDeliveryS3Config *s3ConfigModel `tfsdk:"large_data_delivery_s3_config"` -} - -type s3ConfigModel struct { - BucketName types.String `tfsdk:"bucket_name"` - KeyPrefix types.String `tfsdk:"key_prefix"` -} - const ResourceNameModelInvocationLoggingConfiguration = "Model Invocation Logging Configuration" // @FrameworkResource(name="Model Invocation Logging Configuration") @@ -76,7 +56,7 @@ func (r *resourceModelInvocationLoggingConfiguration) Schema(ctx context.Context }, }, Blocks: map[string]schema.Block{ - "cloud_watch_config": schema.SingleNestedBlock{ + "cloudwatch_config": schema.SingleNestedBlock{ Attributes: map[string]schema.Attribute{ "log_group_name": schema.StringAttribute{ Optional: true, @@ -124,8 +104,13 @@ func (r *resourceModelInvocationLoggingConfiguration) Create(ctx context.Context } data.ID = flex.StringValueToFramework(ctx, r.Meta().Region) + loggingConfig := expandLoggingConfig(ctx, data.LoggingConfig, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + input := bedrock.PutModelInvocationLoggingConfigurationInput{ - LoggingConfig: expandLoggingConfig(data.LoggingConfig), + LoggingConfig: loggingConfig, } _, err := conn.PutModelInvocationLoggingConfiguration(ctx, &input) @@ -172,8 +157,13 @@ func (r *resourceModelInvocationLoggingConfiguration) Update(ctx context.Context return } + loggingConfig := expandLoggingConfig(ctx, data.LoggingConfig, resp.Diagnostics) + if resp.Diagnostics.HasError() { + return + } + input := bedrock.PutModelInvocationLoggingConfigurationInput{ - LoggingConfig: expandLoggingConfig(data.LoggingConfig), + LoggingConfig: loggingConfig, } _, err := conn.PutModelInvocationLoggingConfiguration(ctx, &input) @@ -209,86 +199,139 @@ func (r *resourceModelInvocationLoggingConfiguration) ImportState(ctx context.Co resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } -func flattenLoggingConfig(ctx context.Context, apiObject *bedrock_types.LoggingConfig) *loggingConfigModel { +type resourceModelInvocationLoggingConfigurationModel struct { + ID types.String `tfsdk:"id"` + LoggingConfig types.Object `tfsdk:"logging_config"` +} + +type loggingConfigModel struct { + EmbeddingDataDeliveryEnabled types.Bool `tfsdk:"embedding_data_delivery_enabled"` + ImageDataDeliveryEnabled types.Bool `tfsdk:"image_data_delivery_enabled"` + TextDataDeliveryEnabled types.Bool `tfsdk:"text_data_delivery_enabled"` + CloudWatchConfig types.Object `tfsdk:"cloudwatch_config"` + S3Config types.Object `tfsdk:"s3_config"` +} + +type cloudWatchConfigModel struct { + LogGroupName types.String `tfsdk:"log_group_name"` + RoleArn types.String `tfsdk:"role_arn"` + LargeDataDeliveryS3Config types.Object `tfsdk:"large_data_delivery_s3_config"` +} + +type s3ConfigModel struct { + BucketName types.String `tfsdk:"bucket_name"` + KeyPrefix types.String `tfsdk:"key_prefix"` +} + +func flattenLoggingConfig(ctx context.Context, apiObject *awstypes.LoggingConfig) types.Object { + attributeTypes := fwtypes.AttributeTypesMust[loggingConfigModel](ctx) + // Reflection above cannot determine the nested object attribute types + cwAttrTypes := fwtypes.AttributeTypesMust[cloudWatchConfigModel](ctx) + cwAttrTypes["large_data_delivery_s3_config"] = types.ObjectType{AttrTypes: fwtypes.AttributeTypesMust[s3ConfigModel](ctx)} + attributeTypes["cloudwatch_config"] = types.ObjectType{AttrTypes: cwAttrTypes} + attributeTypes["s3_config"] = types.ObjectType{AttrTypes: fwtypes.AttributeTypesMust[s3ConfigModel](ctx)} + if apiObject == nil { - return nil + return types.ObjectNull(attributeTypes) + } - return &loggingConfigModel{ - EmbeddingDataDeliveryEnabled: flex.BoolToFramework(ctx, apiObject.EmbeddingDataDeliveryEnabled), - ImageDataDeliveryEnabled: flex.BoolToFramework(ctx, apiObject.ImageDataDeliveryEnabled), - TextDataDeliveryEnabled: flex.BoolToFramework(ctx, apiObject.TextDataDeliveryEnabled), - CloudWatchConfig: flattenCloudWatchConfig(ctx, apiObject.CloudWatchConfig), - S3Config: flattenS3Config(ctx, apiObject.S3Config), + attrs := map[string]attr.Value{ + "embedding_data_delivery_enabled": flex.BoolToFramework(ctx, apiObject.EmbeddingDataDeliveryEnabled), + "image_data_delivery_enabled": flex.BoolToFramework(ctx, apiObject.ImageDataDeliveryEnabled), + "text_data_delivery_enabled": flex.BoolToFramework(ctx, apiObject.TextDataDeliveryEnabled), + "cloudwatch_config": flattenCloudWatchConfig(ctx, apiObject.CloudWatchConfig), + "s3_config": flattenS3Config(ctx, apiObject.S3Config), } + + return types.ObjectValueMust(attributeTypes, attrs) } -func flattenCloudWatchConfig(ctx context.Context, apiObject *bedrock_types.CloudWatchConfig) *cloudWatchConfigModel { +func flattenCloudWatchConfig(ctx context.Context, apiObject *awstypes.CloudWatchConfig) types.Object { + attributeTypes := fwtypes.AttributeTypesMust[cloudWatchConfigModel](ctx) + // Reflection above cannot determine the nested object attribute types + attributeTypes["large_data_delivery_s3_config"] = types.ObjectType{AttrTypes: fwtypes.AttributeTypesMust[s3ConfigModel](ctx)} + if apiObject == nil { - return nil + return types.ObjectNull(attributeTypes) } - return &cloudWatchConfigModel{ - LogGroupName: flex.StringToFramework(ctx, apiObject.LogGroupName), - RoleArn: flex.StringToFramework(ctx, apiObject.RoleArn), - LargeDataDeliveryS3Config: flattenS3Config(ctx, apiObject.LargeDataDeliveryS3Config), + attrs := map[string]attr.Value{ + "log_group_name": flex.StringToFramework(ctx, apiObject.LogGroupName), + "role_arn": flex.StringToFramework(ctx, apiObject.RoleArn), + "large_data_delivery_s3_config": flattenS3Config(ctx, apiObject.LargeDataDeliveryS3Config), } + + return types.ObjectValueMust(attributeTypes, attrs) } -func flattenS3Config(ctx context.Context, apiObject *bedrock_types.S3Config) *s3ConfigModel { +func flattenS3Config(ctx context.Context, apiObject *awstypes.S3Config) types.Object { + attributeTypes := fwtypes.AttributeTypesMust[s3ConfigModel](ctx) if apiObject == nil { - return nil + return types.ObjectNull(attributeTypes) } - return &s3ConfigModel{ - BucketName: flex.StringToFramework(ctx, apiObject.BucketName), - KeyPrefix: flex.StringToFramework(ctx, apiObject.KeyPrefix), + attrs := map[string]attr.Value{ + "bucket_name": flex.StringToFramework(ctx, apiObject.BucketName), + "key_prefix": flex.StringToFramework(ctx, apiObject.KeyPrefix), } + + return types.ObjectValueMust(attributeTypes, attrs) } -func expandLoggingConfig(model *loggingConfigModel) *bedrock_types.LoggingConfig { - if model == nil { +func expandLoggingConfig(ctx context.Context, object types.Object, diags diag.Diagnostics) *awstypes.LoggingConfig { + if object.IsNull() { return nil } - apiObject := &bedrock_types.LoggingConfig{ - EmbeddingDataDeliveryEnabled: model.EmbeddingDataDeliveryEnabled.ValueBoolPointer(), - ImageDataDeliveryEnabled: model.ImageDataDeliveryEnabled.ValueBoolPointer(), - TextDataDeliveryEnabled: model.TextDataDeliveryEnabled.ValueBoolPointer(), - } - if model.CloudWatchConfig != nil { - apiObject.CloudWatchConfig = expandCloudWatchConfig(model.CloudWatchConfig) + var conf loggingConfigModel + diags.Append(object.As(ctx, &conf, basetypes.ObjectAsOptions{})...) + if diags.HasError() { + return nil } - if model.S3Config != nil { - apiObject.S3Config = expandS3Config(model.S3Config) + + apiObject := &awstypes.LoggingConfig{ + EmbeddingDataDeliveryEnabled: conf.EmbeddingDataDeliveryEnabled.ValueBoolPointer(), + ImageDataDeliveryEnabled: conf.ImageDataDeliveryEnabled.ValueBoolPointer(), + TextDataDeliveryEnabled: conf.TextDataDeliveryEnabled.ValueBoolPointer(), + CloudWatchConfig: expandCloudWatchConfig(ctx, conf.CloudWatchConfig, diags), + S3Config: expandS3Config(ctx, conf.S3Config, diags), } return apiObject } -func expandCloudWatchConfig(model *cloudWatchConfigModel) *bedrock_types.CloudWatchConfig { - if model == nil { +func expandCloudWatchConfig(ctx context.Context, object types.Object, diags diag.Diagnostics) *awstypes.CloudWatchConfig { + if object.IsNull() { return nil } - apiObject := &bedrock_types.CloudWatchConfig{ - LogGroupName: model.LogGroupName.ValueStringPointer(), - RoleArn: model.RoleArn.ValueStringPointer(), - LargeDataDeliveryS3Config: expandS3Config(model.LargeDataDeliveryS3Config), + var conf cloudWatchConfigModel + diags.Append(object.As(ctx, &conf, basetypes.ObjectAsOptions{})...) + if diags.HasError() { + return nil } - return apiObject + return &awstypes.CloudWatchConfig{ + LogGroupName: conf.LogGroupName.ValueStringPointer(), + RoleArn: conf.RoleArn.ValueStringPointer(), + LargeDataDeliveryS3Config: expandS3Config(ctx, conf.LargeDataDeliveryS3Config, diags), + } } -func expandS3Config(model *s3ConfigModel) *bedrock_types.S3Config { - if model == nil { +func expandS3Config(ctx context.Context, object types.Object, diags diag.Diagnostics) *awstypes.S3Config { + if object.IsNull() { return nil } - apiObject := &bedrock_types.S3Config{ - BucketName: model.BucketName.ValueStringPointer(), - KeyPrefix: model.KeyPrefix.ValueStringPointer(), + var conf s3ConfigModel + diags.Append(object.As(ctx, &conf, basetypes.ObjectAsOptions{})...) + if diags.HasError() { + return nil } - return apiObject + return &awstypes.S3Config{ + BucketName: conf.BucketName.ValueStringPointer(), + KeyPrefix: conf.KeyPrefix.ValueStringPointer(), + } } diff --git a/internal/service/bedrock/model_invocation_logging_configuration_test.go b/internal/service/bedrock/model_invocation_logging_configuration_test.go index 58cdc2a88f1..3a32ef153d2 100644 --- a/internal/service/bedrock/model_invocation_logging_configuration_test.go +++ b/internal/service/bedrock/model_invocation_logging_configuration_test.go @@ -28,8 +28,8 @@ func TestAccBedrockModelInvocationLoggingConfiguration_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "logging_config.embedding_data_delivery_enabled", "true"), resource.TestCheckResourceAttr(resourceName, "logging_config.image_data_delivery_enabled", "true"), resource.TestCheckResourceAttr(resourceName, "logging_config.text_data_delivery_enabled", "true"), - resource.TestCheckResourceAttrSet(resourceName, "logging_config.cloud_watch_config.log_group_name"), - resource.TestCheckResourceAttrSet(resourceName, "logging_config.cloud_watch_config.role_arn"), + resource.TestCheckResourceAttrSet(resourceName, "logging_config.cloudwatch_config.log_group_name"), + resource.TestCheckResourceAttrSet(resourceName, "logging_config.cloudwatch_config.role_arn"), resource.TestCheckResourceAttrSet(resourceName, "logging_config.s3_config.bucket_name"), resource.TestCheckResourceAttr(resourceName, "logging_config.s3_config.key_prefix", "bedrock"), ), @@ -150,7 +150,7 @@ resource "aws_bedrock_model_invocation_logging_configuration" "test" { embedding_data_delivery_enabled = true image_data_delivery_enabled = true text_data_delivery_enabled = true - cloud_watch_config { + cloudwatch_config { log_group_name = aws_cloudwatch_log_group.bedrock_logging.name role_arn = aws_iam_role.bedrock_logging.arn } From c7412418af030a8eddb13ba9e0b4b289c0dfebf7 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Fri, 10 Nov 2023 15:54:36 -0500 Subject: [PATCH 12/17] r/aws_bedrock_model_invocation_logging_configuration(test): tidy config, use AttrPair checks --- ...l_invocation_logging_configuration_test.go | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/internal/service/bedrock/model_invocation_logging_configuration_test.go b/internal/service/bedrock/model_invocation_logging_configuration_test.go index 3a32ef153d2..6f606ffefda 100644 --- a/internal/service/bedrock/model_invocation_logging_configuration_test.go +++ b/internal/service/bedrock/model_invocation_logging_configuration_test.go @@ -16,6 +16,9 @@ func TestAccBedrockModelInvocationLoggingConfiguration_basic(t *testing.T) { ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_bedrock_model_invocation_logging_configuration.test" + logGroupResourceName := "aws_cloudwatch_log_group.test" + iamRoleResourceName := "aws_iam_role.test" + s3BucketResourceName := "aws_s3_bucket.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -28,9 +31,9 @@ func TestAccBedrockModelInvocationLoggingConfiguration_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "logging_config.embedding_data_delivery_enabled", "true"), resource.TestCheckResourceAttr(resourceName, "logging_config.image_data_delivery_enabled", "true"), resource.TestCheckResourceAttr(resourceName, "logging_config.text_data_delivery_enabled", "true"), - resource.TestCheckResourceAttrSet(resourceName, "logging_config.cloudwatch_config.log_group_name"), - resource.TestCheckResourceAttrSet(resourceName, "logging_config.cloudwatch_config.role_arn"), - resource.TestCheckResourceAttrSet(resourceName, "logging_config.s3_config.bucket_name"), + resource.TestCheckResourceAttrPair(resourceName, "logging_config.cloudwatch_config.log_group_name", logGroupResourceName, "name"), + resource.TestCheckResourceAttrPair(resourceName, "logging_config.cloudwatch_config.role_arn", iamRoleResourceName, "arn"), + resource.TestCheckResourceAttrPair(resourceName, "logging_config.s3_config.bucket_name", s3BucketResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "logging_config.s3_config.key_prefix", "bedrock"), ), }, @@ -49,16 +52,16 @@ data "aws_caller_identity" "current" {} data "aws_region" "current" {} data "aws_partition" "current" {} -resource aws_s3_bucket bedrock_logging { - bucket = "bedrock-logging-%[1]s" +resource "aws_s3_bucket" "test" { + bucket = %[1]q force_destroy = true lifecycle { ignore_changes = ["tags", "tags_all"] } } -resource "aws_s3_bucket_policy" "bedrock_logging" { - bucket = aws_s3_bucket.bedrock_logging.bucket +resource "aws_s3_bucket_policy" "test" { + bucket = aws_s3_bucket.test.bucket policy = < Date: Fri, 10 Nov 2023 16:35:49 -0500 Subject: [PATCH 13/17] names: add bedrock endpoint ID --- names/names.go | 1 + 1 file changed, 1 insertion(+) diff --git a/names/names.go b/names/names.go index d3ba2a653cb..db4022c40ab 100644 --- a/names/names.go +++ b/names/names.go @@ -32,6 +32,7 @@ const ( ACMEndpointID = "acm" AthenaEndpointID = "athena" AuditManagerEndpointID = "auditmanager" + BedrockEndpointID = "bedrock" CleanRoomsEndpointID = "cleanrooms" CloudWatchLogsEndpointID = "logs" CodeStarConnectionsEndpointID = "codestar-connections" From 0975e018ac68115a6b5ddcf1d576e486d9796f54 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Fri, 10 Nov 2023 20:13:34 -0500 Subject: [PATCH 14/17] r/aws_bedrock_model_invocation_logging_configuration: add finder, handle empty result on read --- .../model_invocation_logging_configuration.go | 46 +++++++++++++++---- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/internal/service/bedrock/model_invocation_logging_configuration.go b/internal/service/bedrock/model_invocation_logging_configuration.go index 222ff2a310a..538b680478d 100644 --- a/internal/service/bedrock/model_invocation_logging_configuration.go +++ b/internal/service/bedrock/model_invocation_logging_configuration.go @@ -5,6 +5,7 @@ package bedrock import ( "context" + "errors" "github.com/aws/aws-sdk-go-v2/service/bedrock" awstypes "github.com/aws/aws-sdk-go-v2/service/bedrock/types" @@ -15,14 +16,16 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/framework" "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) -const ResourceNameModelInvocationLoggingConfiguration = "Model Invocation Logging Configuration" +const ResNameModelInvocationLoggingConfiguration = "Model Invocation Logging Configuration" // @FrameworkResource(name="Model Invocation Logging Configuration") func newResourceModelInvocationLoggingConfiguration(context.Context) (resource.ResourceWithConfigure, error) { @@ -116,7 +119,7 @@ func (r *resourceModelInvocationLoggingConfiguration) Create(ctx context.Context _, err := conn.PutModelInvocationLoggingConfiguration(ctx, &input) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Bedrock, create.ErrActionCreating, ResourceNameModelInvocationLoggingConfiguration, data.ID.String(), err), + create.ProblemStandardMessage(names.Bedrock, create.ErrActionCreating, ResNameModelInvocationLoggingConfiguration, data.ID.String(), err), err.Error(), ) return @@ -134,16 +137,22 @@ func (r *resourceModelInvocationLoggingConfiguration) Read(ctx context.Context, return } - output, err := conn.GetModelInvocationLoggingConfiguration(ctx, &bedrock.GetModelInvocationLoggingConfigurationInput{}) + output, err := FindModelInvocationLoggingConfigurationByID(ctx, conn) + var nfe *retry.NotFoundError + var ere *tfresource.EmptyResultError + if errors.As(err, &nfe) || errors.As(err, &ere) { + resp.State.RemoveResource(ctx) + return + } if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Bedrock, create.ErrActionReading, ResourceNameModelInvocationLoggingConfiguration, state.ID.String(), err), + create.ProblemStandardMessage(names.Bedrock, create.ErrActionReading, ResNameModelInvocationLoggingConfiguration, state.ID.String(), err), err.Error(), ) return } - state.LoggingConfig = flattenLoggingConfig(ctx, output.LoggingConfig) + state.LoggingConfig = flattenLoggingConfig(ctx, output) resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) } @@ -169,7 +178,7 @@ func (r *resourceModelInvocationLoggingConfiguration) Update(ctx context.Context _, err := conn.PutModelInvocationLoggingConfiguration(ctx, &input) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Bedrock, create.ErrActionUpdating, ResourceNameModelInvocationLoggingConfiguration, data.ID.String(), err), + create.ProblemStandardMessage(names.Bedrock, create.ErrActionUpdating, ResNameModelInvocationLoggingConfiguration, data.ID.String(), err), err.Error(), ) } @@ -189,7 +198,7 @@ func (r *resourceModelInvocationLoggingConfiguration) Delete(ctx context.Context _, err := conn.DeleteModelInvocationLoggingConfiguration(ctx, &bedrock.DeleteModelInvocationLoggingConfigurationInput{}) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.Bedrock, create.ErrActionDeleting, ResourceNameModelInvocationLoggingConfiguration, data.ID.String(), err), + create.ProblemStandardMessage(names.Bedrock, create.ErrActionDeleting, ResNameModelInvocationLoggingConfiguration, data.ID.String(), err), err.Error(), ) } @@ -199,6 +208,28 @@ func (r *resourceModelInvocationLoggingConfiguration) ImportState(ctx context.Co resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) } +func FindModelInvocationLoggingConfigurationByID(ctx context.Context, conn *bedrock.Client) (*awstypes.LoggingConfig, error) { + in := &bedrock.GetModelInvocationLoggingConfigurationInput{} + out, err := conn.GetModelInvocationLoggingConfiguration(ctx, in) + if err != nil { + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, + } + } + + return nil, err + } + + if out == nil || out.LoggingConfig == nil { + return nil, tfresource.NewEmptyResultError(in) + } + + return out.LoggingConfig, nil +} + type resourceModelInvocationLoggingConfigurationModel struct { ID types.String `tfsdk:"id"` LoggingConfig types.Object `tfsdk:"logging_config"` @@ -233,7 +264,6 @@ func flattenLoggingConfig(ctx context.Context, apiObject *awstypes.LoggingConfig if apiObject == nil { return types.ObjectNull(attributeTypes) - } attrs := map[string]attr.Value{ From 2f47062406aa5575db54b4cdaa78f31363a6650f Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Fri, 10 Nov 2023 20:14:05 -0500 Subject: [PATCH 15/17] r/aws_bedrock_model_invocation_logging_configuration(test): add exists, destroy checks --- ...l_invocation_logging_configuration_test.go | 66 ++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/internal/service/bedrock/model_invocation_logging_configuration_test.go b/internal/service/bedrock/model_invocation_logging_configuration_test.go index 6f606ffefda..e42b6f30c6b 100644 --- a/internal/service/bedrock/model_invocation_logging_configuration_test.go +++ b/internal/service/bedrock/model_invocation_logging_configuration_test.go @@ -4,12 +4,21 @@ package bedrock_test import ( + "context" + "errors" "fmt" "testing" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" + tfbedrock "github.com/hashicorp/terraform-provider-aws/internal/service/bedrock" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" ) func TestAccBedrockModelInvocationLoggingConfiguration_basic(t *testing.T) { @@ -21,12 +30,18 @@ func TestAccBedrockModelInvocationLoggingConfiguration_basic(t *testing.T) { s3BucketResourceName := "aws_s3_bucket.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(ctx, t) }, + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.BedrockEndpointID) + }, + ErrorCheck: acctest.ErrorCheck(t, names.BedrockEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckModelInvocationLoggingConfigurationDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccModelInvocationLoggingConfiguration_basic(rName), Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckModelInvocationLoggingConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttrSet(resourceName, "id"), resource.TestCheckResourceAttr(resourceName, "logging_config.embedding_data_delivery_enabled", "true"), resource.TestCheckResourceAttr(resourceName, "logging_config.image_data_delivery_enabled", "true"), @@ -46,6 +61,55 @@ func TestAccBedrockModelInvocationLoggingConfiguration_basic(t *testing.T) { }) } +func testAccCheckModelInvocationLoggingConfigurationExists(ctx context.Context, name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.Bedrock, create.ErrActionCheckingExistence, tfbedrock.ResNameModelInvocationLoggingConfiguration, name, errors.New("not found")) + } + + if rs.Primary.ID == "" { + return create.Error(names.Bedrock, create.ErrActionCheckingExistence, tfbedrock.ResNameModelInvocationLoggingConfiguration, name, errors.New("not set")) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).BedrockClient(ctx) + + _, err := tfbedrock.FindModelInvocationLoggingConfigurationByID(ctx, conn) + if err != nil { + return create.Error(names.Bedrock, create.ErrActionCheckingExistence, tfbedrock.ResNameModelInvocationLoggingConfiguration, name, err) + } + + return nil + } +} + +func testAccCheckModelInvocationLoggingConfigurationDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).BedrockClient(ctx) + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_bedrock_model_invocation_logging_configuration" { + continue + } + + output, err := tfbedrock.FindModelInvocationLoggingConfigurationByID(ctx, conn) + if err != nil { + var nfe *retry.NotFoundError + var ere *tfresource.EmptyResultError + if errors.As(err, &nfe) || errors.As(err, &ere) { + return nil + } + return err + } + + if output != nil { + return create.Error(names.Bedrock, create.ErrActionCheckingDestroyed, tfbedrock.ResNameModelInvocationLoggingConfiguration, rs.Primary.ID, err) + } + } + + return nil + } +} + func testAccModelInvocationLoggingConfiguration_basic(rName string) string { return fmt.Sprintf(` data "aws_caller_identity" "current" {} From afae00c0ce2103a22a23192bfe11a44f6caf54c2 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Fri, 10 Nov 2023 22:14:12 -0500 Subject: [PATCH 16/17] r/aws_bedrock_model_invocation_logging_configuration(test): add disappears test, serialize --- internal/service/bedrock/bedrock_test.go | 23 +++++++++++ internal/service/bedrock/exports_test.go | 9 +++++ ...l_invocation_logging_configuration_test.go | 39 ++++++++++++++++--- 3 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 internal/service/bedrock/bedrock_test.go create mode 100644 internal/service/bedrock/exports_test.go diff --git a/internal/service/bedrock/bedrock_test.go b/internal/service/bedrock/bedrock_test.go new file mode 100644 index 00000000000..b0652d3a1a5 --- /dev/null +++ b/internal/service/bedrock/bedrock_test.go @@ -0,0 +1,23 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package bedrock_test + +import ( + "testing" + + "github.com/hashicorp/terraform-provider-aws/internal/acctest" +) + +func TestAccBedrock_serial(t *testing.T) { + t.Parallel() + + testCases := map[string]map[string]func(t *testing.T){ + "ModelInvocationLoggingConfiguration": { + "basic": testAccModelInvocationLoggingConfiguration_basic, + "disappears": testAccModelInvocationLoggingConfiguration_disappears, + }, + } + + acctest.RunSerialTests2Levels(t, testCases, 0) +} diff --git a/internal/service/bedrock/exports_test.go b/internal/service/bedrock/exports_test.go new file mode 100644 index 00000000000..9575a71b862 --- /dev/null +++ b/internal/service/bedrock/exports_test.go @@ -0,0 +1,9 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package bedrock + +// Exports for use in tests only. +var ( + ResourceModelInvocationLoggingConfiguration = newResourceModelInvocationLoggingConfiguration +) diff --git a/internal/service/bedrock/model_invocation_logging_configuration_test.go b/internal/service/bedrock/model_invocation_logging_configuration_test.go index e42b6f30c6b..3406cc74492 100644 --- a/internal/service/bedrock/model_invocation_logging_configuration_test.go +++ b/internal/service/bedrock/model_invocation_logging_configuration_test.go @@ -21,7 +21,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccBedrockModelInvocationLoggingConfiguration_basic(t *testing.T) { +func testAccModelInvocationLoggingConfiguration_basic(t *testing.T) { ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_bedrock_model_invocation_logging_configuration.test" @@ -29,7 +29,7 @@ func TestAccBedrockModelInvocationLoggingConfiguration_basic(t *testing.T) { iamRoleResourceName := "aws_iam_role.test" s3BucketResourceName := "aws_s3_bucket.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckPartitionHasService(t, names.BedrockEndpointID) @@ -39,7 +39,7 @@ func TestAccBedrockModelInvocationLoggingConfiguration_basic(t *testing.T) { CheckDestroy: testAccCheckModelInvocationLoggingConfigurationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccModelInvocationLoggingConfiguration_basic(rName), + Config: testAccModelInvocationLoggingConfigurationConfig_basic(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckModelInvocationLoggingConfigurationExists(ctx, resourceName), resource.TestCheckResourceAttrSet(resourceName, "id"), @@ -61,6 +61,32 @@ func TestAccBedrockModelInvocationLoggingConfiguration_basic(t *testing.T) { }) } +func testAccModelInvocationLoggingConfiguration_disappears(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_bedrock_model_invocation_logging_configuration.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.BedrockEndpointID) + }, + ErrorCheck: acctest.ErrorCheck(t, names.BedrockEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckModelInvocationLoggingConfigurationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccModelInvocationLoggingConfigurationConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckModelInvocationLoggingConfigurationExists(ctx, resourceName), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfbedrock.ResourceModelInvocationLoggingConfiguration, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func testAccCheckModelInvocationLoggingConfigurationExists(ctx context.Context, name string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] @@ -110,7 +136,7 @@ func testAccCheckModelInvocationLoggingConfigurationDestroy(ctx context.Context) } } -func testAccModelInvocationLoggingConfiguration_basic(rName string) string { +func testAccModelInvocationLoggingConfigurationConfig_basic(rName string) string { return fmt.Sprintf(` data "aws_caller_identity" "current" {} data "aws_region" "current" {} @@ -213,7 +239,10 @@ resource "aws_iam_role_policy_attachment" "test" { } resource "aws_bedrock_model_invocation_logging_configuration" "test" { - depends_on = [aws_s3_bucket_policy.test] + depends_on = [ + aws_s3_bucket_policy.test, + aws_iam_role_policy_attachment.test, + ] logging_config { embedding_data_delivery_enabled = true From 81958f4c54fb1ee5f4a142f9528ca17765460949 Mon Sep 17 00:00:00 2001 From: Jared Baker Date: Mon, 13 Nov 2023 10:22:41 -0500 Subject: [PATCH 17/17] r/aws_bedrock_model_invocation_logging_configuration: retry iam propagation errors --- .../model_invocation_logging_configuration.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/internal/service/bedrock/model_invocation_logging_configuration.go b/internal/service/bedrock/model_invocation_logging_configuration.go index 538b680478d..7625eaa5729 100644 --- a/internal/service/bedrock/model_invocation_logging_configuration.go +++ b/internal/service/bedrock/model_invocation_logging_configuration.go @@ -6,6 +6,7 @@ package bedrock import ( "context" "errors" + "time" "github.com/aws/aws-sdk-go-v2/service/bedrock" awstypes "github.com/aws/aws-sdk-go-v2/service/bedrock/types" @@ -25,7 +26,10 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -const ResNameModelInvocationLoggingConfiguration = "Model Invocation Logging Configuration" +const ( + iamPropagationTimeout = time.Minute * 1 + ResNameModelInvocationLoggingConfiguration = "Model Invocation Logging Configuration" +) // @FrameworkResource(name="Model Invocation Logging Configuration") func newResourceModelInvocationLoggingConfiguration(context.Context) (resource.ResourceWithConfigure, error) { @@ -116,7 +120,17 @@ func (r *resourceModelInvocationLoggingConfiguration) Create(ctx context.Context LoggingConfig: loggingConfig, } - _, err := conn.PutModelInvocationLoggingConfiguration(ctx, &input) + // Retry handling to allow for IAM propagation + // + // Example: + // ValidationException: Failed to validate permissions for log group: , with role: . Verify + // the IAM role permissions are correct. + _, err := tfresource.RetryWhenIsAErrorMessageContains[*awstypes.ValidationException](ctx, iamPropagationTimeout, + func() (interface{}, error) { + return conn.PutModelInvocationLoggingConfiguration(ctx, &input) + }, + "Failed to validate permissions for log group", + ) if err != nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.Bedrock, create.ErrActionCreating, ResNameModelInvocationLoggingConfiguration, data.ID.String(), err),