From b048264324f0ddb6d618b298c76c355a7a261f8c Mon Sep 17 00:00:00 2001 From: Ian Lodge Date: Tue, 21 May 2024 11:46:19 -0400 Subject: [PATCH 01/12] Adding AppFabric Ingestion Destination --- internal/service/appfabric/exports_test.go | 10 + .../appfabric/ingestion_destination.go | 816 ++++++++++++++++++ .../appfabric/ingestion_destination_test.go | 308 +++++++ .../service/appfabric/service_package_gen.go | 9 +- 4 files changed, 1141 insertions(+), 2 deletions(-) create mode 100644 internal/service/appfabric/exports_test.go create mode 100644 internal/service/appfabric/ingestion_destination.go create mode 100644 internal/service/appfabric/ingestion_destination_test.go diff --git a/internal/service/appfabric/exports_test.go b/internal/service/appfabric/exports_test.go new file mode 100644 index 00000000000..9e6892aeb0e --- /dev/null +++ b/internal/service/appfabric/exports_test.go @@ -0,0 +1,10 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package appfabric + +// Exports for use in tests only. +var ( + ResourceIngestionDestination = newResourceIngestionDestination + FindIngestionDestinationByID = findIngestionDestinationByID +) diff --git a/internal/service/appfabric/ingestion_destination.go b/internal/service/appfabric/ingestion_destination.go new file mode 100644 index 00000000000..047ce6eae7f --- /dev/null +++ b/internal/service/appfabric/ingestion_destination.go @@ -0,0 +1,816 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package appfabric + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/YakDriver/regexache" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/appfabric" + awstypes "github.com/aws/aws-sdk-go-v2/service/appfabric/types" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "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/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" + "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/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + fwflex "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" +) + +// Function annotations are used for resource registration to the Provider. DO NOT EDIT. +// @FrameworkResource(name="Ingestion Destination") +func newResourceIngestionDestination(context.Context) (resource.ResourceWithConfigure, error) { + r := &ingestionDestinationResource{} + + r.SetDefaultCreateTimeout(5 * time.Minute) + r.SetDefaultUpdateTimeout(5 * time.Minute) + r.SetDefaultDeleteTimeout(5 * time.Minute) + + return r, nil +} + +const ( + ResNameIngestionDestination = "Ingestion Destination" +) + +type ingestionDestinationResource struct { + framework.ResourceWithConfigure + framework.WithTimeouts +} + +func (r *ingestionDestinationResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "aws_appfabric_ingestion_destination" +} + +func (r *ingestionDestinationResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "app_bundle_identifier": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + names.AttrARN: framework.IDAttribute(), + names.AttrID: framework.IDAttribute(), + "ingestion_arn": framework.ARNAttributeComputedOnly(), + "ingestion_identifier": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + }, + Blocks: map[string]schema.Block{ + "destination_configuration": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.IsRequired(), + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "audit_log": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[destinationConfigurationAuditLogModel](ctx), + Validators: []validator.List{ + listvalidator.IsRequired(), + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "destination": schema.ListNestedBlock{ + Validators: []validator.List{ + listvalidator.IsRequired(), + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "firehose_stream": schema.ListAttribute{ + Optional: true, + CustomType: fwtypes.NewListNestedObjectTypeOf[firehoseStreamModel](ctx), + ElementType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "stream_name": types.StringType, + }, + }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.UseStateForUnknown(), + }, + }, + "s3_bucket": schema.ListAttribute{ + Optional: true, + CustomType: fwtypes.NewListNestedObjectTypeOf[s3BucketModel](ctx), + ElementType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "bucket_name": types.StringType, + "prefix": types.StringType, + }, + }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.UseStateForUnknown(), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + "processing_configuration": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[processingConfigurationModel](ctx), + Validators: []validator.List{ + listvalidator.IsRequired(), + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Blocks: map[string]schema.Block{ + "audit_log": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[processingConfigurationAuditLogModel](ctx), + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "format": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 255), + stringvalidator.RegexMatches( + regexache.MustCompile(`^json$|^parquet$`), + "Valid values one of JSON | PARQUET", + ), + }, + }, + "schema": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 255), + stringvalidator.RegexMatches( + regexache.MustCompile(`^ocsf$|^raw$`), + "Valid values one of OCSF | RAW", + ), + }, + }, + }, + }, + }, + }, + }, + }, + names.AttrTimeouts: timeouts.Block(ctx, timeouts.Opts{ + Create: true, + Update: true, + Delete: true, + }), + }, + } +} + +func (r *ingestionDestinationResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan resourceIngestionDestinationModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + conn := r.Meta().AppFabricClient(ctx) + + var destinationConfigurationData []destinationConfigurationModel + resp.Diagnostics.Append(plan.DestinationConfiguration.ElementsAs(ctx, &destinationConfigurationData, false)...) + if resp.Diagnostics.HasError() { + return + } + + destinationConfiguration, d := expandDestinationConfiguration(ctx, destinationConfigurationData) + resp.Diagnostics.Append(d...) + + var processingConfigurationData []processingConfigurationModel + resp.Diagnostics.Append(plan.ProcessingConfiguration.ElementsAs(ctx, &processingConfigurationData, false)...) + if resp.Diagnostics.HasError() { + return + } + + processingConfiguration, d := expandProcessingConfiguration(ctx, processingConfigurationData) + resp.Diagnostics.Append(d...) + + in := &appfabric.CreateIngestionDestinationInput{} + resp.Diagnostics.Append(fwflex.Expand(ctx, plan, in)...) + if resp.Diagnostics.HasError() { + return + } + + in.DestinationConfiguration = destinationConfiguration + in.ProcessingConfiguration = processingConfiguration + + out, err := conn.CreateIngestionDestination(ctx, in) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.AppFabric, create.ErrActionWaitingForCreation, ResNameIngestionDestination, plan.ID.String(), err), + err.Error(), + ) + return + } + + // Set values for unknowns. + ingestionDestination := out.IngestionDestination + plan.IngestionDestinationArn = fwflex.StringToFramework(ctx, ingestionDestination.Arn) + plan.setID() + + iDest, err := waitIngestionDestinationCreated(ctx, conn, plan.IngestionDestinationArn.ValueString(), plan.AppBundleIdentifier.ValueString(), plan.IngestionIdentifier.ValueString(), r.CreateTimeout(ctx, plan.Timeouts)) + if err != nil { + resp.Diagnostics.AddError(fmt.Sprintf("Waiting for Ingestion Destination (%s) to be created", plan.IngestionDestinationArn.ValueString()), err.Error()) + + return + } + + // Set values for unknowns after creation is complete. + plan.IngestionDestinationArn = fwflex.StringToFramework(ctx, iDest.Arn) + plan.IngestionArn = fwflex.StringToFramework(ctx, iDest.IngestionArn) + + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *ingestionDestinationResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var state resourceIngestionDestinationModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + conn := r.Meta().AppFabricClient(ctx) + + if err := state.InitFromID(); err != nil { + resp.Diagnostics.AddError("parsing resource ID", err.Error()) + + return + } + + out, err := findIngestionDestinationByID(ctx, conn, state.IngestionDestinationArn.ValueString(), state.AppBundleIdentifier.ValueString(), state.IngestionIdentifier.ValueString()) + + if tfresource.NotFound(err) { + resp.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) + resp.State.RemoveResource(ctx) + + return + } + + if err != nil { + resp.Diagnostics.AddError(fmt.Sprintf("reading Ingestion Destination ID (%s)", state.IngestionDestinationArn.ValueString()), err.Error()) + + return + } + + resp.Diagnostics.Append(fwflex.Flatten(ctx, out, &state)...) + if resp.Diagnostics.HasError() { + return + } + + destinationConfigurationOutput, d := flattenDestinationConfiguration(ctx, out.DestinationConfiguration) + resp.Diagnostics.Append(d...) + state.DestinationConfiguration = destinationConfigurationOutput + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *ingestionDestinationResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var old, new resourceIngestionDestinationModel + resp.Diagnostics.Append(req.Plan.Get(ctx, &new)...) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(req.State.Get(ctx, &old)...) + if resp.Diagnostics.HasError() { + return + } + + conn := r.Meta().AppFabricClient(ctx) + + // Check if updates are necessary based on the changed attributes + if !old.DestinationConfiguration.Equal(new.DestinationConfiguration) { + var destinationConfigurationData []destinationConfigurationModel + resp.Diagnostics.Append(new.DestinationConfiguration.ElementsAs(ctx, &destinationConfigurationData, false)...) + if resp.Diagnostics.HasError() { + return + } + + destinationConfiguration, diags := expandDestinationConfiguration(ctx, destinationConfigurationData) + resp.Diagnostics.Append(diags...) + if diags.HasError() { + return + } + + input := &appfabric.UpdateIngestionDestinationInput{ + IngestionDestinationIdentifier: aws.String(new.IngestionDestinationArn.ValueString()), + } + resp.Diagnostics.Append(fwflex.Expand(ctx, new, input)...) + if resp.Diagnostics.HasError() { + return + } + + input.DestinationConfiguration = destinationConfiguration + + _, err := conn.UpdateIngestionDestination(ctx, input) + if err != nil { + resp.Diagnostics.AddError( + "Failed to update Ingestion Destination", + fmt.Sprintf("Error updating Ingestion Destination with ID %s: %s", new.IngestionDestinationArn.String(), err.Error()), + ) + return + } + + if _, err = waitIngestionDestinationUpdated(ctx, conn, new.IngestionDestinationArn.ValueString(), new.AppBundleIdentifier.ValueString(), new.IngestionIdentifier.ValueString(), r.CreateTimeout(ctx, new.Timeouts)); err != nil { + resp.Diagnostics.AddError(fmt.Sprintf("Waiting for Ingestion Destination (%s) to be updated", new.IngestionDestinationArn.ValueString()), err.Error()) + + return + } + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &new)...) +} + +func (r *ingestionDestinationResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state resourceIngestionDestinationModel + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + conn := r.Meta().AppFabricClient(ctx) + + _, err := conn.DeleteIngestionDestination(ctx, &appfabric.DeleteIngestionDestinationInput{ + IngestionDestinationIdentifier: aws.String(state.IngestionDestinationArn.ValueString()), + AppBundleIdentifier: aws.String(state.AppBundleIdentifier.ValueString()), + IngestionIdentifier: aws.String(state.IngestionIdentifier.ValueString()), + }) + + if err != nil { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return + } + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.AppFabric, create.ErrActionDeleting, ResNameIngestionDestination, state.ID.String(), err), + err.Error(), + ) + return + } + + if _, err = waitIngestionDestinationDeleted(ctx, conn, state.IngestionDestinationArn.ValueString(), state.AppBundleIdentifier.ValueString(), state.IngestionIdentifier.ValueString(), r.CreateTimeout(ctx, state.Timeouts)); err != nil { + resp.Diagnostics.AddError(fmt.Sprintf("Waiting for Ingestion Destination (%s) to be deleted", state.IngestionDestinationArn.ValueString()), err.Error()) + + return + } + +} + +func (r *ingestionDestinationResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root(names.AttrID), req, resp) +} + +func waitIngestionDestinationCreated(ctx context.Context, conn *appfabric.Client, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier string, timeout time.Duration) (*awstypes.IngestionDestination, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{}, + Target: enum.Slice(awstypes.IngestionDestinationStatusActive, awstypes.IngestionDestinationStatusActive), + Refresh: statusIngestionDestination(ctx, conn, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*awstypes.IngestionDestination); ok { + return out, err + } + + return nil, err +} + +func waitIngestionDestinationUpdated(ctx context.Context, conn *appfabric.Client, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier string, timeout time.Duration) (*awstypes.IngestionDestination, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{}, + Target: enum.Slice(awstypes.IngestionDestinationStatusActive, awstypes.IngestionDestinationStatusActive), + Refresh: statusIngestionDestination(ctx, conn, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier), + Timeout: timeout, + NotFoundChecks: 20, + ContinuousTargetOccurence: 2, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*awstypes.IngestionDestination); ok { + return out, err + } + + return nil, err +} + +func waitIngestionDestinationDeleted(ctx context.Context, conn *appfabric.Client, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier string, timeout time.Duration) (*awstypes.IngestionDestination, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.IngestionDestinationStatusActive, awstypes.IngestionDestinationStatusActive), + Target: []string{}, + Refresh: statusIngestionDestination(ctx, conn, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + if out, ok := outputRaw.(*awstypes.IngestionDestination); ok { + return out, err + } + + return nil, err +} + +func statusIngestionDestination(ctx context.Context, conn *appfabric.Client, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + out, err := findIngestionDestinationByID(ctx, conn, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier) + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return out, string(out.Status), nil + } +} + +func findIngestionDestinationByID(ctx context.Context, conn *appfabric.Client, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier string) (*awstypes.IngestionDestination, error) { + in := &appfabric.GetIngestionDestinationInput{ + IngestionDestinationIdentifier: aws.String(ingestionDestinationArn), + AppBundleIdentifier: aws.String(appBundleIdentifier), + IngestionIdentifier: aws.String(ingestionIdentifier), + } + + out, err := conn.GetIngestionDestination(ctx, in) + if err != nil { + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, + } + } + + return nil, err + } + + if out == nil || out.IngestionDestination == nil { + return nil, tfresource.NewEmptyResultError(in) + } + + return out.IngestionDestination, nil +} + +func expandDestinationConfiguration(ctx context.Context, tfList []destinationConfigurationModel) (awstypes.DestinationConfiguration, diag.Diagnostics) { + auditLog := []awstypes.DestinationConfiguration{} + var diags diag.Diagnostics + + tfObj := tfList[0] + + if !tfObj.DestinationConfigurationAuditLog.IsNull() { + var destinationConfigurationAuditLog []destinationConfigurationAuditLogModel + diags.Append(tfObj.DestinationConfigurationAuditLog.ElementsAs(ctx, &destinationConfigurationAuditLog, false)...) + apiObject := expandDestinationConfigurationAuditLog(ctx, destinationConfigurationAuditLog) + auditLog = append(auditLog, apiObject) + } + return auditLog[0], diags +} + +func expandDestinationConfigurationAuditLog(ctx context.Context, tfList []destinationConfigurationAuditLogModel) *awstypes.DestinationConfigurationMemberAuditLog { + var diags diag.Diagnostics + + if len(tfList) == 0 { + return nil + } + + tfObj := tfList[0] + + var destinationData []destinationModel + diags.Append(tfObj.Destination.ElementsAs(ctx, &destinationData, false)...) + + destination := &awstypes.DestinationConfigurationMemberAuditLog{ + Value: awstypes.AuditLogDestinationConfiguration{ + Destination: expandDestination(ctx, destinationData), + }, + } + + return destination +} + +func expandDestination(ctx context.Context, tfList []destinationModel) awstypes.Destination { + destination := []awstypes.Destination{} + var diags diag.Diagnostics + + if len(tfList) == 0 { + return nil + } + + tfObj := tfList[0] + + for _, item := range tfList { + if !item.FirehoseStream.IsNull() && (len(item.FirehoseStream.Elements()) > 0) { + var firehoseStream []firehoseStreamModel + diags.Append(tfObj.FirehoseStream.ElementsAs(ctx, &firehoseStream, false)...) + streamName := expandFirehoseStream(ctx, firehoseStream) + destination = append(destination, streamName) + } + if !item.S3Bucket.IsNull() && (len(item.S3Bucket.Elements()) > 0) { + var s3Bucket []s3BucketModel + diags.Append(tfObj.S3Bucket.ElementsAs(ctx, &s3Bucket, false)...) + bucketName := expandS3Bucket(ctx, s3Bucket) + destination = append(destination, bucketName) + } + } + + return destination[0] +} + +func expandFirehoseStream(ctx context.Context, tfList []firehoseStreamModel) *awstypes.DestinationMemberFirehoseStream { + if len(tfList) == 0 { + return nil + } + + return &awstypes.DestinationMemberFirehoseStream{ + Value: awstypes.FirehoseStream{ + StreamName: fwflex.StringFromFramework(ctx, tfList[0].StreamName), + }, + } +} + +func expandS3Bucket(ctx context.Context, tfList []s3BucketModel) *awstypes.DestinationMemberS3Bucket { + if len(tfList) == 0 { + return nil + } + + return &awstypes.DestinationMemberS3Bucket{ + Value: awstypes.S3Bucket{ + BucketName: fwflex.StringFromFramework(ctx, tfList[0].BucketName), + Prefix: fwflex.StringFromFramework(ctx, tfList[0].Prefix), + }, + } +} + +func expandProcessingConfiguration(ctx context.Context, tfList []processingConfigurationModel) (awstypes.ProcessingConfiguration, diag.Diagnostics) { + auditLog := []awstypes.ProcessingConfiguration{} + var diags diag.Diagnostics + + tfObj := tfList[0] + + if !tfObj.ProcessingConfigurationAuditLog.IsNull() { + var processingConfigurationAuditLog []processingConfigurationAuditLogModel + diags.Append(tfObj.ProcessingConfigurationAuditLog.ElementsAs(ctx, &processingConfigurationAuditLog, false)...) + apiObject := expandProcessingConfigurationAuditLog(processingConfigurationAuditLog) + auditLog = append(auditLog, apiObject) + } + return auditLog[0], diags +} + +func expandProcessingConfigurationAuditLog(auditLog []processingConfigurationAuditLogModel) *awstypes.ProcessingConfigurationMemberAuditLog { + if len(auditLog) == 0 { + return nil + } + + return &awstypes.ProcessingConfigurationMemberAuditLog{ + Value: awstypes.AuditLogProcessingConfiguration{ + Format: auditLog[0].Format.ValueEnum(), + Schema: auditLog[0].Schema.ValueEnum(), + }, + } +} + +func flattenDestinationConfiguration(ctx context.Context, apiObject awstypes.DestinationConfiguration) (types.List, diag.Diagnostics) { + var diags diag.Diagnostics + elemType := types.ObjectType{AttrTypes: destinationConfigurationModelAttrTypes} + + if apiObject == nil { + return types.ListNull(elemType), diags + } + + obj := map[string]attr.Value{} + + switch v := apiObject.(type) { + case *awstypes.DestinationConfigurationMemberAuditLog: + auditLog, d := flattenDestinationConfigurationAuditLog(ctx, &v.Value) + diags.Append(d...) + obj = map[string]attr.Value{ + "audit_log": auditLog, + } + default: + log.Println("union is nil or unknown type") + } + + objVal, d := types.ObjectValue(destinationConfigurationModelAttrTypes, obj) + diags.Append(d...) + + listVal, d := types.ListValue(elemType, []attr.Value{objVal}) + diags.Append(d...) + + return listVal, diags +} + +func flattenDestinationConfigurationAuditLog(ctx context.Context, apiObject *awstypes.AuditLogDestinationConfiguration) (types.List, diag.Diagnostics) { + var diags diag.Diagnostics + elemType := types.ObjectType{AttrTypes: destinationConfigurationMemberAuditLogModelAttrTypes} + + if apiObject == nil { + return types.ListNull(elemType), diags + } + + obj := map[string]attr.Value{} + + destination, d := flattenDestination(ctx, apiObject.Destination) + diags.Append(d...) + obj["destination"] = destination + + objVal, d := types.ObjectValue(destinationConfigurationMemberAuditLogModelAttrTypes, obj) + diags.Append(d...) + + listVal, d := types.ListValue(elemType, []attr.Value{objVal}) + diags.Append(d...) + + return listVal, diags +} + +func flattenDestination(ctx context.Context, apiObject awstypes.Destination) (types.List, diag.Diagnostics) { + var diags diag.Diagnostics + elemType := types.ObjectType{AttrTypes: destinationModelAttrTypes} + + obj := map[string]attr.Value{} + + switch v := apiObject.(type) { + case *awstypes.DestinationMemberFirehoseStream: + destination, d := flattenDestinationModel(ctx, &v.Value, nil, "firehose") + diags.Append(d...) + obj = map[string]attr.Value{ + "firehose_stream": destination, + "s3_bucket": types.ListNull(s3BucketAttrTypes), + } + case *awstypes.DestinationMemberS3Bucket: + destination, d := flattenDestinationModel(ctx, nil, &v.Value, "s3") + diags.Append(d...) + obj = map[string]attr.Value{ + "firehose_stream": types.ListNull(firehoseStreamAttrTypes), + "s3_bucket": destination, + } + } + + objVal, d := types.ObjectValue(destinationModelAttrTypes, obj) + diags.Append(d...) + + listVal, d := types.ListValue(elemType, []attr.Value{objVal}) + diags.Append(d...) + + return listVal, diags +} + +func flattenDestinationModel(ctx context.Context, firehoseApiObject *awstypes.FirehoseStream, s3ApiObject *awstypes.S3Bucket, destinationType string) (types.List, diag.Diagnostics) { + var diags diag.Diagnostics + var elemType types.ObjectType + var obj map[string]attr.Value + var objVal basetypes.ObjectValue + + if destinationType == "firehose" { + var d diag.Diagnostics + elemType = types.ObjectType{AttrTypes: firehoseStreamModelAttrTypes} + obj = map[string]attr.Value{ + "stream_name": fwflex.StringToFramework(ctx, firehoseApiObject.StreamName), + } + objVal, d = types.ObjectValue(firehoseStreamModelAttrTypes, obj) + diags.Append(d...) + } else if destinationType == "s3" { + var d diag.Diagnostics + elemType = types.ObjectType{AttrTypes: s3BucketModelAttrTypes} + obj = map[string]attr.Value{ + "bucket_name": fwflex.StringToFramework(ctx, s3ApiObject.BucketName), + "prefix": fwflex.StringToFramework(ctx, s3ApiObject.Prefix), + } + objVal, d = types.ObjectValue(s3BucketModelAttrTypes, obj) + diags.Append(d...) + } + + listVal, d := types.ListValue(elemType, []attr.Value{objVal}) + diags.Append(d...) + + return listVal, diags +} + +var ( + destinationConfigurationModelAttrTypes = map[string]attr.Type{ + "audit_log": types.ListType{ElemType: types.ObjectType{AttrTypes: destinationConfigurationMemberAuditLogModelAttrTypes}}, + } + + destinationConfigurationMemberAuditLogModelAttrTypes = map[string]attr.Type{ + "destination": types.ListType{ElemType: types.ObjectType{AttrTypes: destinationModelAttrTypes}}, + } + + destinationModelAttrTypes = map[string]attr.Type{ + "firehose_stream": types.ListType{ElemType: firehoseStreamAttrTypes}, + "s3_bucket": types.ListType{ElemType: s3BucketAttrTypes}, + } + + firehoseStreamAttrTypes = types.ObjectType{AttrTypes: firehoseStreamModelAttrTypes} + s3BucketAttrTypes = types.ObjectType{AttrTypes: s3BucketModelAttrTypes} + + firehoseStreamModelAttrTypes = map[string]attr.Type{ + "stream_name": types.StringType, + } + + s3BucketModelAttrTypes = map[string]attr.Type{ + "bucket_name": types.StringType, + "prefix": types.StringType, + } + + processingConfigurationModelAttrTypes = map[string]attr.Type{ + "audit_log": types.ListType{ElemType: types.ObjectType{AttrTypes: processingConfigurationMemberAuditLogModelAttrTypes}}, + } + + processingConfigurationMemberAuditLogModelAttrTypes = map[string]attr.Type{ + "format": types.StringType, + "schema": types.StringType, + } +) + +const ( + ingestionDestinationResourceIDPartCount = 3 +) + +func (m *resourceIngestionDestinationModel) InitFromID() error { + parts, err := flex.ExpandResourceId(m.ID.ValueString(), ingestionDestinationResourceIDPartCount, false) + if err != nil { + return err + } + + m.IngestionDestinationArn = types.StringValue(parts[0]) + m.AppBundleIdentifier = types.StringValue(parts[1]) + m.IngestionIdentifier = types.StringValue(parts[2]) + + return nil +} + +func (m *resourceIngestionDestinationModel) setID() { + m.ID = types.StringValue(errs.Must(flex.FlattenResourceId([]string{m.IngestionDestinationArn.ValueString(), m.AppBundleIdentifier.ValueString(), m.IngestionIdentifier.ValueString()}, ingestionDestinationResourceIDPartCount, false))) +} + +type resourceIngestionDestinationModel struct { + AppBundleIdentifier types.String `tfsdk:"app_bundle_identifier"` + DestinationConfiguration types.List `tfsdk:"destination_configuration"` + ID types.String `tfsdk:"id"` + IngestionArn types.String `tfsdk:"ingestion_arn"` + IngestionDestinationArn types.String `tfsdk:"arn"` + IngestionIdentifier types.String `tfsdk:"ingestion_identifier"` + ProcessingConfiguration fwtypes.ListNestedObjectValueOf[processingConfigurationModel] `tfsdk:"processing_configuration"` + Timeouts timeouts.Value `tfsdk:"timeouts"` +} + +type destinationConfigurationModel struct { + DestinationConfigurationAuditLog fwtypes.ListNestedObjectValueOf[destinationConfigurationAuditLogModel] `tfsdk:"audit_log"` +} + +type destinationConfigurationAuditLogModel struct { + Destination fwtypes.ListNestedObjectValueOf[destinationModel] `tfsdk:"destination"` +} + +type destinationModel struct { + FirehoseStream fwtypes.ListNestedObjectValueOf[firehoseStreamModel] `tfsdk:"firehose_stream"` + S3Bucket fwtypes.ListNestedObjectValueOf[s3BucketModel] `tfsdk:"s3_bucket"` +} + +type firehoseStreamModel struct { + StreamName types.String `tfsdk:"stream_name"` +} + +type s3BucketModel struct { + BucketName types.String `tfsdk:"bucket_name"` + Prefix types.String `tfsdk:"prefix"` +} + +type processingConfigurationModel struct { + ProcessingConfigurationAuditLog fwtypes.ListNestedObjectValueOf[processingConfigurationAuditLogModel] `tfsdk:"audit_log"` +} + +type processingConfigurationAuditLogModel struct { + Format fwtypes.StringEnum[awstypes.Format] `tfsdk:"format"` + Schema fwtypes.StringEnum[awstypes.Schema] `tfsdk:"schema"` +} diff --git a/internal/service/appfabric/ingestion_destination_test.go b/internal/service/appfabric/ingestion_destination_test.go new file mode 100644 index 00000000000..eb6d483edbe --- /dev/null +++ b/internal/service/appfabric/ingestion_destination_test.go @@ -0,0 +1,308 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package appfabric_test + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/appfabric/types" + "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" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" + + tfappfabric "github.com/hashicorp/terraform-provider-aws/internal/service/appfabric" +) + +func TestAccAppFabricIngestionDestination_basic(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_appfabric_ingestion_destination.test" + var ingestiondestination types.IngestionDestination + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.AppFabricServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckIngestionDestinationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccIngestionDestinationConfig_basic(), + Check: resource.ComposeTestCheckFunc( + testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.%", "2"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.%", "2"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", "appfabric-test-audit-logs-658465413021"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.prefix", "AuditLog"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"processing_configuration"}, + }, + }, + }) +} + +func TestAccAppFabricIngestionDestination_firehose(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_appfabric_ingestion_destination.test" + var ingestiondestination types.IngestionDestination + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.AppFabricServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckIngestionDestinationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccIngestionDestinationConfig_firehose(), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.%", "2"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.stream_name", "KDS-HTP-uMu4o"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"processing_configuration"}, + }, + }, + }) +} + +func TestAccAppFabricIngestionDestination_destinationUpdate(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_appfabric_ingestion_destination.test" + var ingestiondestination types.IngestionDestination + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.AppFabricServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckIngestionDestinationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccIngestionDestinationConfig_basic(), + Check: resource.ComposeTestCheckFunc( + testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.%", "2"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.%", "2"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", "appfabric-test-audit-logs-658465413021"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.prefix", "AuditLog"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"processing_configuration"}, + }, + { + Config: testAccIngestionDestinationConfig_destinationUpdate(), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.%", "2"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.%", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.stream_name", "KDS-HTP-uMu4o"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"processing_configuration"}, + }, + }, + }) +} + +func TestAccAppFabricIngestionDestination_disappears(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var ingestiondestination types.IngestionDestination + resourceName := "aws_appfabric_ingestion_destination.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.AppFabricServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckIngestionDestinationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccIngestionDestinationConfig_basic(), + Check: resource.ComposeTestCheckFunc( + testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfappfabric.ResourceIngestionDestination, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckIngestionDestinationDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).AppFabricClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_appfabric_ingestion_destination" { + continue + } + + _, err := tfappfabric.FindIngestionDestinationByID(ctx, conn, rs.Primary.Attributes[names.AttrARN], rs.Primary.Attributes["app_bundle_identifier"], rs.Primary.Attributes["ingestion_identifier"]) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("Ingestion Destination %s still exists", rs.Primary.ID) + } + + return nil + } +} + +func testAccCheckIngestionDestinationExists(ctx context.Context, name string, ingestiondestination *types.IngestionDestination) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.AppFabric, create.ErrActionCheckingExistence, tfappfabric.ResNameIngestionDestination, name, errors.New("not found")) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).AppFabricClient(ctx) + + output, err := tfappfabric.FindIngestionDestinationByID(ctx, conn, rs.Primary.Attributes[names.AttrARN], rs.Primary.Attributes["app_bundle_identifier"], rs.Primary.Attributes["ingestion_identifier"]) + + if err != nil { + return err + } + + *ingestiondestination = *output + + return nil + } +} + +func testAccIngestionDestinationConfig_basic() string { + return ` +resource "aws_appfabric_ingestion_destination" "test" { + app_bundle_identifier = "arn:aws:appfabric:us-east-1:825402724285:appbundle/04a7c9f5-14e1-4697-8b43-551186ff0a6e" + ingestion_identifier = "arn:aws:appfabric:us-east-1:825402724285:appbundle/04a7c9f5-14e1-4697-8b43-551186ff0a6e/ingestion/0379c487-af21-4419-80c2-92968007de1c" + processing_configuration { + audit_log { + format = "json" + schema = "raw" + } + } + destination_configuration { + audit_log { + destination { + s3_bucket { + bucket_name = "appfabric-test-audit-logs-658465413021" + prefix = "AuditLog" + } + } + } + } +} +` +} + +func testAccIngestionDestinationConfig_firehose() string { + return ` +resource "aws_appfabric_ingestion_destination" "test" { + app_bundle_identifier = "arn:aws:appfabric:us-east-1:825402724285:appbundle/04a7c9f5-14e1-4697-8b43-551186ff0a6e" + ingestion_identifier = "arn:aws:appfabric:us-east-1:825402724285:appbundle/04a7c9f5-14e1-4697-8b43-551186ff0a6e/ingestion/63a391c6-2165-453d-94ba-d8da0c59c8be" + processing_configuration { + audit_log { + format = "json" + schema = "ocsf" + } + } + destination_configuration { + audit_log { + destination { + firehose_stream { + stream_name = "KDS-HTP-uMu4o" + } + } + } + } +} +` +} + +func testAccIngestionDestinationConfig_destinationUpdate() string { + return ` +resource "aws_appfabric_ingestion_destination" "test" { + app_bundle_identifier = "arn:aws:appfabric:us-east-1:825402724285:appbundle/04a7c9f5-14e1-4697-8b43-551186ff0a6e" + ingestion_identifier = "arn:aws:appfabric:us-east-1:825402724285:appbundle/04a7c9f5-14e1-4697-8b43-551186ff0a6e/ingestion/0379c487-af21-4419-80c2-92968007de1c" + processing_configuration { + audit_log { + format = "json" + schema = "ocsf" + } + } + destination_configuration { + audit_log { + destination { + firehose_stream { + stream_name = "KDS-HTP-uMu4o" + } + } + } + } +} +` +} diff --git a/internal/service/appfabric/service_package_gen.go b/internal/service/appfabric/service_package_gen.go index 1066d45117d..36da8ea1ad1 100644 --- a/internal/service/appfabric/service_package_gen.go +++ b/internal/service/appfabric/service_package_gen.go @@ -19,7 +19,12 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv } func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { - return []*types.ServicePackageFrameworkResource{} + return []*types.ServicePackageFrameworkResource{ + { + Factory: newResourceIngestionDestination, + Name: "Ingestion Destination", + }, + } } func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePackageSDKDataSource { @@ -39,7 +44,7 @@ func (p *servicePackage) NewClient(ctx context.Context, config map[string]any) ( cfg := *(config["aws_sdkv2_config"].(*aws_sdkv2.Config)) return appfabric_sdkv2.NewFromConfig(cfg, func(o *appfabric_sdkv2.Options) { - if endpoint := config[names.AttrEndpoint].(string); endpoint != "" { + if endpoint := config["endpoint"].(string); endpoint != "" { o.BaseEndpoint = aws_sdkv2.String(endpoint) } }), nil From dd72854a65919b1312dfbea4fc2de18d94d44e16 Mon Sep 17 00:00:00 2001 From: Ian Lodge Date: Tue, 28 May 2024 09:24:24 -0400 Subject: [PATCH 02/12] added changelog and html docs --- .changelog/37627.txt | 3 + ...fabric_ingestion_destination.html.markdown | 95 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 .changelog/37627.txt create mode 100644 website/docs/r/appfabric_ingestion_destination.html.markdown diff --git a/.changelog/37627.txt b/.changelog/37627.txt new file mode 100644 index 00000000000..375cbe93938 --- /dev/null +++ b/.changelog/37627.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_appfabric_ingestion_destination +``` \ No newline at end of file diff --git a/website/docs/r/appfabric_ingestion_destination.html.markdown b/website/docs/r/appfabric_ingestion_destination.html.markdown new file mode 100644 index 00000000000..2d3023546e2 --- /dev/null +++ b/website/docs/r/appfabric_ingestion_destination.html.markdown @@ -0,0 +1,95 @@ +--- +subcategory: "AppFabric" +layout: "aws" +page_title: "AWS: aws_appfabric_ingestion_destination" +description: |- + Terraform resource for managing an AWS AppFabric Ingestion Destination. +--- + +# Resource: aws_appfabric_ingestion_destination + +Terraform resource for managing an AWS AppFabric Ingestion Destination. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_appfabric_ingestion_destination" "example" { + app_bundle_identifier = "aws_appfabric_app_bundle.arn" + ingestion_identifier = "aws_appfabric_ingestion.arn" + processing_configuration { + audit_log { + format = "json" + schema = "raw" + } + } + destination_configuration { + audit_log { + destination { + s3_bucket { + bucket_name = "examplebucketname" + prefix = "AuditLog" + } + } + } + } +} +``` + + +## Argument Reference + +The following arguments are required: + +* `app_bundle_identifier` - (Required) The Amazon Resource Name (ARN) or Universal Unique Identifier (UUID) of the app bundle to use for the request. +* `ingestion_identifier` - (Required) The Amazon Resource Name (ARN) or Universal Unique Identifier (UUID) of the ingestion to use for the request. +* `destination_configuration` - (Required) Contains information about the destination of ingested data. +* `processing_configuration` - (Required) Contains information about how ingested data is processed. + +Destination Configuration support the following: + +* `audit_log` - (Optional) Contains information about an audit log destination configuration. + +Audit Log Destination Configuration support the following: + +* `destination` - (Required) Contains information about an audit log destination. Only one destination (Firehose Stream) or (S3 Bucket) can be specified. + +Destination support the following: + +* `firehose_stream` - (Optional) Contains information about an Amazon Data Firehose delivery stream. +* `s3_bucket` - (Optional) Contains information about an Amazon S3 bucket. + +Firehose Stream support the following: + +* `streamName` - (Required) The name of the Amazon Data Firehose delivery stream. + +S3 Bucket support the following: + +* `bucketName` - (Required) The name of the Amazon S3 bucket. +* `prefix` - (Optional) The object key to use. + +Processing Configuration support the following: + +* `audit_log` - (optional) Contains information about an audit log processing configuration. + +Audit Log Processing Configuration support the following: + +* `format` - (Required) The format in which the audit logs need to be formatted. Valid values: json | parquet +* `schema` - (Required) The event schema in which the audit logs need to be formatted. Valid values: ocsf | raw + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the Ingestion Destination. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. +* `app_bundle_arn` - The Amazon Resource Name (ARN) of the app bundle for the ingestion destination. +* `ingestion_arn` - The Amazon Resource Name (ARN) of the ingestion for the ingestion destination. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `5m`) +* `update` - (Default `5m`) +* `delete` - (Default `5m`) \ No newline at end of file From 791e566dd88f1664befb33001f82dd2207e46280 Mon Sep 17 00:00:00 2001 From: Dave Lemons Date: Mon, 3 Jun 2024 12:18:57 -0400 Subject: [PATCH 03/12] adding tag support --- internal/service/appfabric/generate.go | 1 + .../appfabric/ingestion_destination.go | 12 ++ .../appfabric/ingestion_destination_test.go | 37 +++-- .../service/appfabric/service_package_gen.go | 5 +- internal/service/appfabric/tags_gen.go | 146 ++++++++++++++++++ 5 files changed, 186 insertions(+), 15 deletions(-) create mode 100644 internal/service/appfabric/tags_gen.go diff --git a/internal/service/appfabric/generate.go b/internal/service/appfabric/generate.go index ad9bb798e23..0973d63e475 100644 --- a/internal/service/appfabric/generate.go +++ b/internal/service/appfabric/generate.go @@ -1,6 +1,7 @@ // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 +//go:generate go run ../../generate/tags/main.go -ServiceTagsSlice -AWSSDKVersion=2 -ListTags -UpdateTags //go:generate go run ../../generate/servicepackage/main.go // ONLY generate directives and package declaration! Do not add anything else to this file. diff --git a/internal/service/appfabric/ingestion_destination.go b/internal/service/appfabric/ingestion_destination.go index 047ce6eae7f..1a4e814261a 100644 --- a/internal/service/appfabric/ingestion_destination.go +++ b/internal/service/appfabric/ingestion_destination.go @@ -36,12 +36,14 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/framework" fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) // Function annotations are used for resource registration to the Provider. DO NOT EDIT. // @FrameworkResource(name="Ingestion Destination") +// @Tags(identifierAttribute="arn") func newResourceIngestionDestination(context.Context) (resource.ResourceWithConfigure, error) { r := &ingestionDestinationResource{} @@ -83,6 +85,8 @@ func (r *ingestionDestinationResource) Schema(ctx context.Context, req resource. stringplanmodifier.UseStateForUnknown(), }, }, + names.AttrTags: tftags.TagsAttribute(), + names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), }, Blocks: map[string]schema.Block{ "destination_configuration": schema.ListNestedBlock{ @@ -227,6 +231,8 @@ func (r *ingestionDestinationResource) Create(ctx context.Context, req resource. in.DestinationConfiguration = destinationConfiguration in.ProcessingConfiguration = processingConfiguration + in.Tags = getTagsIn(ctx) + out, err := conn.CreateIngestionDestination(ctx, in) if err != nil { resp.Diagnostics.AddError( @@ -386,6 +392,10 @@ func (r *ingestionDestinationResource) Delete(ctx context.Context, req resource. } +func (r *ingestionDestinationResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + r.SetTagsAll(ctx, req, resp) +} + func (r *ingestionDestinationResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { resource.ImportStatePassthroughID(ctx, path.Root(names.AttrID), req, resp) } @@ -781,6 +791,8 @@ type resourceIngestionDestinationModel struct { IngestionDestinationArn types.String `tfsdk:"arn"` IngestionIdentifier types.String `tfsdk:"ingestion_identifier"` ProcessingConfiguration fwtypes.ListNestedObjectValueOf[processingConfigurationModel] `tfsdk:"processing_configuration"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` Timeouts timeouts.Value `tfsdk:"timeouts"` } diff --git a/internal/service/appfabric/ingestion_destination_test.go b/internal/service/appfabric/ingestion_destination_test.go index eb6d483edbe..3ee61c33d1a 100644 --- a/internal/service/appfabric/ingestion_destination_test.go +++ b/internal/service/appfabric/ingestion_destination_test.go @@ -46,7 +46,7 @@ func TestAccAppFabricIngestionDestination_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.%", "2"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", "1"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.%", "2"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", "appfabric-test-audit-logs-658465413021"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", "s3-bucket-name"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.prefix", "AuditLog"), ), }, @@ -85,7 +85,7 @@ func TestAccAppFabricIngestionDestination_firehose(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.%", "2"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "1"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.%", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.stream_name", "KDS-HTP-uMu4o"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.stream_name", "OpenSearchStack-FirehoseStream-bL4BiszVyNNC"), ), }, { @@ -123,7 +123,7 @@ func TestAccAppFabricIngestionDestination_destinationUpdate(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.%", "2"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", "1"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.%", "2"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", "appfabric-test-audit-logs-658465413021"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", "s3-bucket-name"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.prefix", "AuditLog"), ), }, @@ -145,7 +145,7 @@ func TestAccAppFabricIngestionDestination_destinationUpdate(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.%", "2"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "1"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.%", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.stream_name", "KDS-HTP-uMu4o"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.stream_name", "OpenSearchStack-FirehoseStream-bL4BiszVyNNC"), ), }, { @@ -237,8 +237,8 @@ func testAccCheckIngestionDestinationExists(ctx context.Context, name string, in func testAccIngestionDestinationConfig_basic() string { return ` resource "aws_appfabric_ingestion_destination" "test" { - app_bundle_identifier = "arn:aws:appfabric:us-east-1:825402724285:appbundle/04a7c9f5-14e1-4697-8b43-551186ff0a6e" - ingestion_identifier = "arn:aws:appfabric:us-east-1:825402724285:appbundle/04a7c9f5-14e1-4697-8b43-551186ff0a6e/ingestion/0379c487-af21-4419-80c2-92968007de1c" + app_bundle_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633" + ingestion_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633/ingestion/8b7895cf-171a-494c-9abb-7170eaed13b5" processing_configuration { audit_log { format = "json" @@ -249,12 +249,15 @@ resource "aws_appfabric_ingestion_destination" "test" { audit_log { destination { s3_bucket { - bucket_name = "appfabric-test-audit-logs-658465413021" + bucket_name = "s3-bucket-name" prefix = "AuditLog" } } } } + tags = { + environment = "test" + } } ` } @@ -262,8 +265,8 @@ resource "aws_appfabric_ingestion_destination" "test" { func testAccIngestionDestinationConfig_firehose() string { return ` resource "aws_appfabric_ingestion_destination" "test" { - app_bundle_identifier = "arn:aws:appfabric:us-east-1:825402724285:appbundle/04a7c9f5-14e1-4697-8b43-551186ff0a6e" - ingestion_identifier = "arn:aws:appfabric:us-east-1:825402724285:appbundle/04a7c9f5-14e1-4697-8b43-551186ff0a6e/ingestion/63a391c6-2165-453d-94ba-d8da0c59c8be" + app_bundle_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633" + ingestion_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633/ingestion/8b7895cf-171a-494c-9abb-7170eaed13b5" processing_configuration { audit_log { format = "json" @@ -274,11 +277,14 @@ resource "aws_appfabric_ingestion_destination" "test" { audit_log { destination { firehose_stream { - stream_name = "KDS-HTP-uMu4o" + stream_name = "OpenSearchStack-FirehoseStream-bL4BiszVyNNC" } } } } + tags = { + environment = "test" + } } ` } @@ -286,8 +292,8 @@ resource "aws_appfabric_ingestion_destination" "test" { func testAccIngestionDestinationConfig_destinationUpdate() string { return ` resource "aws_appfabric_ingestion_destination" "test" { - app_bundle_identifier = "arn:aws:appfabric:us-east-1:825402724285:appbundle/04a7c9f5-14e1-4697-8b43-551186ff0a6e" - ingestion_identifier = "arn:aws:appfabric:us-east-1:825402724285:appbundle/04a7c9f5-14e1-4697-8b43-551186ff0a6e/ingestion/0379c487-af21-4419-80c2-92968007de1c" + app_bundle_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633" + ingestion_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633/ingestion/8b7895cf-171a-494c-9abb-7170eaed13b5" processing_configuration { audit_log { format = "json" @@ -298,11 +304,14 @@ resource "aws_appfabric_ingestion_destination" "test" { audit_log { destination { firehose_stream { - stream_name = "KDS-HTP-uMu4o" + stream_name = "OpenSearchStack-FirehoseStream-bL4BiszVyNNC" } } } - } + } + tags = { + environment = "test" + } } ` } diff --git a/internal/service/appfabric/service_package_gen.go b/internal/service/appfabric/service_package_gen.go index 36da8ea1ad1..67a20c3819e 100644 --- a/internal/service/appfabric/service_package_gen.go +++ b/internal/service/appfabric/service_package_gen.go @@ -23,6 +23,9 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic { Factory: newResourceIngestionDestination, Name: "Ingestion Destination", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: names.AttrARN, + }, }, } } @@ -44,7 +47,7 @@ func (p *servicePackage) NewClient(ctx context.Context, config map[string]any) ( cfg := *(config["aws_sdkv2_config"].(*aws_sdkv2.Config)) return appfabric_sdkv2.NewFromConfig(cfg, func(o *appfabric_sdkv2.Options) { - if endpoint := config["endpoint"].(string); endpoint != "" { + if endpoint := config[names.AttrEndpoint].(string); endpoint != "" { o.BaseEndpoint = aws_sdkv2.String(endpoint) } }), nil diff --git a/internal/service/appfabric/tags_gen.go b/internal/service/appfabric/tags_gen.go new file mode 100644 index 00000000000..ae462eda9dc --- /dev/null +++ b/internal/service/appfabric/tags_gen.go @@ -0,0 +1,146 @@ +// Code generated by internal/generate/tags/main.go; DO NOT EDIT. +package appfabric + +import ( + "context" + "fmt" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/appfabric" + awstypes "github.com/aws/aws-sdk-go-v2/service/appfabric/types" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/logging" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/types/option" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// listTags lists appfabric service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func listTags(ctx context.Context, conn *appfabric.Client, identifier string, optFns ...func(*appfabric.Options)) (tftags.KeyValueTags, error) { + input := &appfabric.ListTagsForResourceInput{ + ResourceArn: aws.String(identifier), + } + + output, err := conn.ListTagsForResource(ctx, input, optFns...) + + if err != nil { + return tftags.New(ctx, nil), err + } + + return KeyValueTags(ctx, output.Tags), nil +} + +// ListTags lists appfabric service tags and set them in Context. +// It is called from outside this package. +func (p *servicePackage) ListTags(ctx context.Context, meta any, identifier string) error { + tags, err := listTags(ctx, meta.(*conns.AWSClient).AppFabricClient(ctx), identifier) + + if err != nil { + return err + } + + if inContext, ok := tftags.FromContext(ctx); ok { + inContext.TagsOut = option.Some(tags) + } + + return nil +} + +// []*SERVICE.Tag handling + +// Tags returns appfabric service tags. +func Tags(tags tftags.KeyValueTags) []awstypes.Tag { + result := make([]awstypes.Tag, 0, len(tags)) + + for k, v := range tags.Map() { + tag := awstypes.Tag{ + Key: aws.String(k), + Value: aws.String(v), + } + + result = append(result, tag) + } + + return result +} + +// KeyValueTags creates tftags.KeyValueTags from appfabric service tags. +func KeyValueTags(ctx context.Context, tags []awstypes.Tag) tftags.KeyValueTags { + m := make(map[string]*string, len(tags)) + + for _, tag := range tags { + m[aws.ToString(tag.Key)] = tag.Value + } + + return tftags.New(ctx, m) +} + +// getTagsIn returns appfabric service tags from Context. +// nil is returned if there are no input tags. +func getTagsIn(ctx context.Context) []awstypes.Tag { + if inContext, ok := tftags.FromContext(ctx); ok { + if tags := Tags(inContext.TagsIn.UnwrapOrDefault()); len(tags) > 0 { + return tags + } + } + + return nil +} + +// setTagsOut sets appfabric service tags in Context. +func setTagsOut(ctx context.Context, tags []awstypes.Tag) { + if inContext, ok := tftags.FromContext(ctx); ok { + inContext.TagsOut = option.Some(KeyValueTags(ctx, tags)) + } +} + +// updateTags updates appfabric service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func updateTags(ctx context.Context, conn *appfabric.Client, identifier string, oldTagsMap, newTagsMap any, optFns ...func(*appfabric.Options)) error { + oldTags := tftags.New(ctx, oldTagsMap) + newTags := tftags.New(ctx, newTagsMap) + + ctx = tflog.SetField(ctx, logging.KeyResourceId, identifier) + + removedTags := oldTags.Removed(newTags) + removedTags = removedTags.IgnoreSystem(names.AppFabric) + if len(removedTags) > 0 { + input := &appfabric.UntagResourceInput{ + ResourceArn: aws.String(identifier), + TagKeys: removedTags.Keys(), + } + + _, err := conn.UntagResource(ctx, input, optFns...) + + if err != nil { + return fmt.Errorf("untagging resource (%s): %w", identifier, err) + } + } + + updatedTags := oldTags.Updated(newTags) + updatedTags = updatedTags.IgnoreSystem(names.AppFabric) + if len(updatedTags) > 0 { + input := &appfabric.TagResourceInput{ + ResourceArn: aws.String(identifier), + Tags: Tags(updatedTags), + } + + _, err := conn.TagResource(ctx, input, optFns...) + + if err != nil { + return fmt.Errorf("tagging resource (%s): %w", identifier, err) + } + } + + return nil +} + +// UpdateTags updates appfabric service tags. +// It is called from outside this package. +func (p *servicePackage) UpdateTags(ctx context.Context, meta any, identifier string, oldTags, newTags any) error { + return updateTags(ctx, meta.(*conns.AWSClient).AppFabricClient(ctx), identifier, oldTags, newTags) +} From 7581ceb6bcb77fea7ef12b6c1ea1ab1709ee3593 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 25 Jun 2024 12:35:10 -0400 Subject: [PATCH 04/12] r/aws_appfabric_ingestion_destination: Tidy up. --- internal/service/appfabric/exports_test.go | 4 +- .../appfabric/ingestion_destination.go | 888 ++++++++---------- .../appfabric/ingestion_destination_test.go | 54 +- .../service/appfabric/service_package_gen.go | 8 +- 4 files changed, 433 insertions(+), 521 deletions(-) diff --git a/internal/service/appfabric/exports_test.go b/internal/service/appfabric/exports_test.go index f25708b2a80..bc02e253a39 100644 --- a/internal/service/appfabric/exports_test.go +++ b/internal/service/appfabric/exports_test.go @@ -9,11 +9,11 @@ var ( ResourceAppAuthorizationConnection = newAppAuthorizationConnectionResource ResourceAppBundle = newAppBundleResource ResourceIngestion = newIngestionResource - ResourceIngestionDestination = newResourceIngestionDestination + ResourceIngestionDestination = newIngestionDestinationResource FindAppAuthorizationByTwoPartKey = findAppAuthorizationByTwoPartKey FindAppAuthorizationConnectionByTwoPartKey = findAppAuthorizationConnectionByTwoPartKey FindAppBundleByID = findAppBundleByID FindIngestionByTwoPartKey = findIngestionByTwoPartKey - FindIngestionDestinationByID = findIngestionDestinationByID + FindIngestionDestinationByThreePartKey = findIngestionDestinationByThreePartKey ) diff --git a/internal/service/appfabric/ingestion_destination.go b/internal/service/appfabric/ingestion_destination.go index 1a4e814261a..7705ab6a0e0 100644 --- a/internal/service/appfabric/ingestion_destination.go +++ b/internal/service/appfabric/ingestion_destination.go @@ -5,20 +5,18 @@ package appfabric import ( "context" + "errors" "fmt" - "log" "time" - "github.com/YakDriver/regexache" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/appfabric" awstypes "github.com/aws/aws-sdk-go-v2/service/appfabric/types" + uuid "github.com/hashicorp/go-uuid" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" - "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/resource/schema/listplanmodifier" @@ -26,9 +24,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "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/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" @@ -41,10 +37,9 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// Function annotations are used for resource registration to the Provider. DO NOT EDIT. // @FrameworkResource(name="Ingestion Destination") // @Tags(identifierAttribute="arn") -func newResourceIngestionDestination(context.Context) (resource.ResourceWithConfigure, error) { +func newIngestionDestinationResource(context.Context) (resource.ResourceWithConfigure, error) { r := &ingestionDestinationResource{} r.SetDefaultCreateTimeout(5 * time.Minute) @@ -54,35 +49,33 @@ func newResourceIngestionDestination(context.Context) (resource.ResourceWithConf return r, nil } -const ( - ResNameIngestionDestination = "Ingestion Destination" -) - type ingestionDestinationResource struct { framework.ResourceWithConfigure + framework.WithImportByID framework.WithTimeouts } -func (r *ingestionDestinationResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { - resp.TypeName = "aws_appfabric_ingestion_destination" +func (*ingestionDestinationResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "aws_appfabric_ingestion_destination" } -func (r *ingestionDestinationResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = schema.Schema{ +func (r *ingestionDestinationResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { + response.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - "app_bundle_identifier": schema.StringAttribute{ - Required: true, + "app_bundle_arn": schema.StringAttribute{ + CustomType: fwtypes.ARNType, + Required: true, PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), + stringplanmodifier.RequiresReplace(), }, }, - names.AttrARN: framework.IDAttribute(), - names.AttrID: framework.IDAttribute(), - "ingestion_arn": framework.ARNAttributeComputedOnly(), - "ingestion_identifier": schema.StringAttribute{ - Required: true, + names.AttrARN: framework.ARNAttributeComputedOnly(), + names.AttrID: framework.IDAttribute(), + "ingestion_arn": schema.StringAttribute{ + CustomType: fwtypes.ARNType, + Required: true, PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), + stringplanmodifier.RequiresReplace(), }, }, names.AttrTags: tftags.TagsAttribute(), @@ -90,6 +83,7 @@ func (r *ingestionDestinationResource) Schema(ctx context.Context, req resource. }, Blocks: map[string]schema.Block{ "destination_configuration": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[destinationConfigurationModel](ctx), Validators: []validator.List{ listvalidator.IsRequired(), listvalidator.SizeAtMost(1), @@ -97,7 +91,7 @@ func (r *ingestionDestinationResource) Schema(ctx context.Context, req resource. NestedObject: schema.NestedBlockObject{ Blocks: map[string]schema.Block{ "audit_log": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[destinationConfigurationAuditLogModel](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[auditLogDestinationConfigurationModel](ctx), Validators: []validator.List{ listvalidator.IsRequired(), listvalidator.SizeAtMost(1), @@ -105,35 +99,49 @@ func (r *ingestionDestinationResource) Schema(ctx context.Context, req resource. NestedObject: schema.NestedBlockObject{ Blocks: map[string]schema.Block{ "destination": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[destinationModel](ctx), Validators: []validator.List{ listvalidator.IsRequired(), listvalidator.SizeAtMost(1), }, NestedObject: schema.NestedBlockObject{ - Attributes: map[string]schema.Attribute{ - "firehose_stream": schema.ListAttribute{ - Optional: true, + Blocks: map[string]schema.Block{ + "firehose_stream": schema.ListNestedBlock{ CustomType: fwtypes.NewListNestedObjectTypeOf[firehoseStreamModel](ctx), - ElementType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "stream_name": types.StringType, - }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), }, - PlanModifiers: []planmodifier.List{ - listplanmodifier.UseStateForUnknown(), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "stream_name": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(3, 64), + }, + }, + }, }, }, - "s3_bucket": schema.ListAttribute{ - Optional: true, + "s3_bucket": schema.ListNestedBlock{ CustomType: fwtypes.NewListNestedObjectTypeOf[s3BucketModel](ctx), - ElementType: types.ObjectType{ - AttrTypes: map[string]attr.Type{ - "bucket_name": types.StringType, - "prefix": types.StringType, - }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), }, - PlanModifiers: []planmodifier.List{ - listplanmodifier.UseStateForUnknown(), + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "bucket_name": schema.StringAttribute{ + Required: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(3, 63), + }, + }, + "prefix": schema.StringAttribute{ + Optional: true, + Validators: []validator.String{ + stringvalidator.LengthBetween(1, 120), + }, + }, + }, }, }, }, @@ -151,33 +159,34 @@ func (r *ingestionDestinationResource) Schema(ctx context.Context, req resource. listvalidator.IsRequired(), listvalidator.SizeAtMost(1), }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, NestedObject: schema.NestedBlockObject{ Blocks: map[string]schema.Block{ "audit_log": schema.ListNestedBlock{ - CustomType: fwtypes.NewListNestedObjectTypeOf[processingConfigurationAuditLogModel](ctx), + CustomType: fwtypes.NewListNestedObjectTypeOf[auditLogProcessingConfigurationModel](ctx), Validators: []validator.List{ + listvalidator.IsRequired(), listvalidator.SizeAtMost(1), }, + PlanModifiers: []planmodifier.List{ + listplanmodifier.RequiresReplace(), + }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ "format": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - stringvalidator.LengthBetween(1, 255), - stringvalidator.RegexMatches( - regexache.MustCompile(`^json$|^parquet$`), - "Valid values one of JSON | PARQUET", - ), + CustomType: fwtypes.StringEnumType[awstypes.Format](), + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), }, }, "schema": schema.StringAttribute{ - Required: true, - Validators: []validator.String{ - stringvalidator.LengthBetween(1, 255), - stringvalidator.RegexMatches( - regexache.MustCompile(`^ocsf$|^raw$`), - "Valid values one of OCSF | RAW", - ), + CustomType: fwtypes.StringEnumType[awstypes.Schema](), + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), }, }, }, @@ -195,266 +204,261 @@ func (r *ingestionDestinationResource) Schema(ctx context.Context, req resource. } } -func (r *ingestionDestinationResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - var plan resourceIngestionDestinationModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - if resp.Diagnostics.HasError() { +func (r *ingestionDestinationResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var data ingestionDestinationResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } conn := r.Meta().AppFabricClient(ctx) - var destinationConfigurationData []destinationConfigurationModel - resp.Diagnostics.Append(plan.DestinationConfiguration.ElementsAs(ctx, &destinationConfigurationData, false)...) - if resp.Diagnostics.HasError() { + input := &appfabric.CreateIngestionDestinationInput{} + response.Diagnostics.Append(fwflex.Expand(ctx, data, input)...) + if response.Diagnostics.HasError() { return } - destinationConfiguration, d := expandDestinationConfiguration(ctx, destinationConfigurationData) - resp.Diagnostics.Append(d...) + // AutoFlEx doesn't yet handle union types. + if !data.DestinationConfiguration.IsNull() { + destinationConfigurationData, diags := data.DestinationConfiguration.ToPtr(ctx) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } - var processingConfigurationData []processingConfigurationModel - resp.Diagnostics.Append(plan.ProcessingConfiguration.ElementsAs(ctx, &processingConfigurationData, false)...) - if resp.Diagnostics.HasError() { - return + destinationConfiguration, diags := expandDestinationConfiguration(ctx, destinationConfigurationData) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + input.DestinationConfiguration = destinationConfiguration } - processingConfiguration, d := expandProcessingConfiguration(ctx, processingConfigurationData) - resp.Diagnostics.Append(d...) + if !data.ProcessingConfiguration.IsNull() { + processingConfigurationData, diags := data.ProcessingConfiguration.ToPtr(ctx) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } - in := &appfabric.CreateIngestionDestinationInput{} - resp.Diagnostics.Append(fwflex.Expand(ctx, plan, in)...) - if resp.Diagnostics.HasError() { - return + processingConfiguration, diags := expandProcessingConfiguration(ctx, processingConfigurationData) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + input.ProcessingConfiguration = processingConfiguration } - in.DestinationConfiguration = destinationConfiguration - in.ProcessingConfiguration = processingConfiguration + // Additional fields. + input.ClientToken = aws.String(errs.Must(uuid.GenerateUUID())) + input.Tags = getTagsIn(ctx) - in.Tags = getTagsIn(ctx) + output, err := conn.CreateIngestionDestination(ctx, input) - out, err := conn.CreateIngestionDestination(ctx, in) if err != nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.AppFabric, create.ErrActionWaitingForCreation, ResNameIngestionDestination, plan.ID.String(), err), - err.Error(), - ) + response.Diagnostics.AddError("creating AppFabric Ingestion Destination", err.Error()) + return } // Set values for unknowns. - ingestionDestination := out.IngestionDestination - plan.IngestionDestinationArn = fwflex.StringToFramework(ctx, ingestionDestination.Arn) - plan.setID() + data.ARN = fwflex.StringToFramework(ctx, output.IngestionDestination.Arn) + data.setID() - iDest, err := waitIngestionDestinationCreated(ctx, conn, plan.IngestionDestinationArn.ValueString(), plan.AppBundleIdentifier.ValueString(), plan.IngestionIdentifier.ValueString(), r.CreateTimeout(ctx, plan.Timeouts)) - if err != nil { - resp.Diagnostics.AddError(fmt.Sprintf("Waiting for Ingestion Destination (%s) to be created", plan.IngestionDestinationArn.ValueString()), err.Error()) + if _, err := waitIngestionDestinationActive(ctx, conn, data.AppBundleARN.ValueString(), data.IngestionARN.ValueString(), data.ARN.ValueString(), r.CreateTimeout(ctx, data.Timeouts)); err != nil { + response.Diagnostics.AddError(fmt.Sprintf("waiting for AppFabric Ingestion Destination (%s) create", data.ID.ValueString()), err.Error()) return } - // Set values for unknowns after creation is complete. - plan.IngestionDestinationArn = fwflex.StringToFramework(ctx, iDest.Arn) - plan.IngestionArn = fwflex.StringToFramework(ctx, iDest.IngestionArn) - - resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) + response.Diagnostics.Append(response.State.Set(ctx, data)...) } -func (r *ingestionDestinationResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - var state resourceIngestionDestinationModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { +func (r *ingestionDestinationResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var data ingestionDestinationResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } - conn := r.Meta().AppFabricClient(ctx) - if err := state.InitFromID(); err != nil { - resp.Diagnostics.AddError("parsing resource ID", err.Error()) + if err := data.InitFromID(); err != nil { + response.Diagnostics.AddError("parsing resource ID", err.Error()) return } - out, err := findIngestionDestinationByID(ctx, conn, state.IngestionDestinationArn.ValueString(), state.AppBundleIdentifier.ValueString(), state.IngestionIdentifier.ValueString()) + conn := r.Meta().AppFabricClient(ctx) + + output, err := findIngestionDestinationByThreePartKey(ctx, conn, data.AppBundleARN.ValueString(), data.IngestionARN.ValueString(), data.ARN.ValueString()) if tfresource.NotFound(err) { - resp.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) - resp.State.RemoveResource(ctx) + response.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) + response.State.RemoveResource(ctx) return } if err != nil { - resp.Diagnostics.AddError(fmt.Sprintf("reading Ingestion Destination ID (%s)", state.IngestionDestinationArn.ValueString()), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("reading AppFabric Ingestion Destination (%s)", data.ID.ValueString()), err.Error()) return } - resp.Diagnostics.Append(fwflex.Flatten(ctx, out, &state)...) - if resp.Diagnostics.HasError() { + // Set attributes for import. + response.Diagnostics.Append(fwflex.Flatten(ctx, output, &data)...) + if response.Diagnostics.HasError() { return } - destinationConfigurationOutput, d := flattenDestinationConfiguration(ctx, out.DestinationConfiguration) - resp.Diagnostics.Append(d...) - state.DestinationConfiguration = destinationConfigurationOutput + // AutoFlEx doesn't yet handle union types. + if output.DestinationConfiguration != nil { + destinationConfigurationData, diags := flattenDestinationConfiguration(ctx, output.DestinationConfiguration) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + data.DestinationConfiguration = fwtypes.NewListNestedObjectValueOfPtrMust(ctx, destinationConfigurationData) + } - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + if output.ProcessingConfiguration != nil { + processingConfigurationData, diags := flattenProcessingConfiguration(ctx, output.ProcessingConfiguration) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + data.ProcessingConfiguration = fwtypes.NewListNestedObjectValueOfPtrMust(ctx, processingConfigurationData) + } + + response.Diagnostics.Append(response.State.Set(ctx, &data)...) } -func (r *ingestionDestinationResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var old, new resourceIngestionDestinationModel - resp.Diagnostics.Append(req.Plan.Get(ctx, &new)...) - if resp.Diagnostics.HasError() { +func (r *ingestionDestinationResource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + var old, new ingestionDestinationResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &new)...) + if response.Diagnostics.HasError() { return } - resp.Diagnostics.Append(req.State.Get(ctx, &old)...) - if resp.Diagnostics.HasError() { + response.Diagnostics.Append(request.State.Get(ctx, &old)...) + if response.Diagnostics.HasError() { return } conn := r.Meta().AppFabricClient(ctx) - // Check if updates are necessary based on the changed attributes if !old.DestinationConfiguration.Equal(new.DestinationConfiguration) { - var destinationConfigurationData []destinationConfigurationModel - resp.Diagnostics.Append(new.DestinationConfiguration.ElementsAs(ctx, &destinationConfigurationData, false)...) - if resp.Diagnostics.HasError() { + input := &appfabric.UpdateIngestionDestinationInput{} + response.Diagnostics.Append(fwflex.Expand(ctx, new, input)...) + if response.Diagnostics.HasError() { return } - destinationConfiguration, diags := expandDestinationConfiguration(ctx, destinationConfigurationData) - resp.Diagnostics.Append(diags...) - if diags.HasError() { - return - } + // AutoFlEx doesn't yet handle union types. + if !new.DestinationConfiguration.IsNull() { + destinationConfigurationData, diags := new.DestinationConfiguration.ToPtr(ctx) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } - input := &appfabric.UpdateIngestionDestinationInput{ - IngestionDestinationIdentifier: aws.String(new.IngestionDestinationArn.ValueString()), - } - resp.Diagnostics.Append(fwflex.Expand(ctx, new, input)...) - if resp.Diagnostics.HasError() { - return - } + destinationConfiguration, diags := expandDestinationConfiguration(ctx, destinationConfigurationData) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } - input.DestinationConfiguration = destinationConfiguration + input.DestinationConfiguration = destinationConfiguration + } _, err := conn.UpdateIngestionDestination(ctx, input) + if err != nil { - resp.Diagnostics.AddError( - "Failed to update Ingestion Destination", - fmt.Sprintf("Error updating Ingestion Destination with ID %s: %s", new.IngestionDestinationArn.String(), err.Error()), - ) + response.Diagnostics.AddError(fmt.Sprintf("updating AppFabric Ingestion Destination (%s)", new.ID.ValueString()), err.Error()) + return } - if _, err = waitIngestionDestinationUpdated(ctx, conn, new.IngestionDestinationArn.ValueString(), new.AppBundleIdentifier.ValueString(), new.IngestionIdentifier.ValueString(), r.CreateTimeout(ctx, new.Timeouts)); err != nil { - resp.Diagnostics.AddError(fmt.Sprintf("Waiting for Ingestion Destination (%s) to be updated", new.IngestionDestinationArn.ValueString()), err.Error()) + if _, err := waitIngestionDestinationActive(ctx, conn, new.AppBundleARN.ValueString(), new.IngestionARN.ValueString(), new.ARN.ValueString(), r.UpdateTimeout(ctx, new.Timeouts)); err != nil { + response.Diagnostics.AddError(fmt.Sprintf("waiting for AppFabric Ingestion Destination (%s) update", new.ID.ValueString()), err.Error()) return } } - resp.Diagnostics.Append(resp.State.Set(ctx, &new)...) + response.Diagnostics.Append(response.State.Set(ctx, &new)...) } -func (r *ingestionDestinationResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state resourceIngestionDestinationModel - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) - if resp.Diagnostics.HasError() { +func (r *ingestionDestinationResource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + var data ingestionDestinationResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { return } conn := r.Meta().AppFabricClient(ctx) _, err := conn.DeleteIngestionDestination(ctx, &appfabric.DeleteIngestionDestinationInput{ - IngestionDestinationIdentifier: aws.String(state.IngestionDestinationArn.ValueString()), - AppBundleIdentifier: aws.String(state.AppBundleIdentifier.ValueString()), - IngestionIdentifier: aws.String(state.IngestionIdentifier.ValueString()), + AppBundleIdentifier: aws.String(data.AppBundleARN.ValueString()), + IngestionDestinationIdentifier: aws.String(data.ARN.ValueString()), + IngestionIdentifier: aws.String(data.IngestionARN.ValueString()), }) - if err != nil { - if errs.IsA[*awstypes.ResourceNotFoundException](err) { - return - } - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.AppFabric, create.ErrActionDeleting, ResNameIngestionDestination, state.ID.String(), err), - err.Error(), - ) + if errs.IsA[*awstypes.ResourceNotFoundException](err) { return } - if _, err = waitIngestionDestinationDeleted(ctx, conn, state.IngestionDestinationArn.ValueString(), state.AppBundleIdentifier.ValueString(), state.IngestionIdentifier.ValueString(), r.CreateTimeout(ctx, state.Timeouts)); err != nil { - resp.Diagnostics.AddError(fmt.Sprintf("Waiting for Ingestion Destination (%s) to be deleted", state.IngestionDestinationArn.ValueString()), err.Error()) + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("deleting AppFabric Ingestion Destination (%s)", data.ID.ValueString()), err.Error()) return } -} + if _, err = waitIngestionDestinationDeleted(ctx, conn, data.AppBundleARN.ValueString(), data.IngestionARN.ValueString(), data.ARN.ValueString(), r.DeleteTimeout(ctx, data.Timeouts)); err != nil { + response.Diagnostics.AddError(fmt.Sprintf("waiting for AppFabric Ingestion Destination (%s) delete", data.ID.ValueString()), err.Error()) -func (r *ingestionDestinationResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - r.SetTagsAll(ctx, req, resp) + return + } } -func (r *ingestionDestinationResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root(names.AttrID), req, resp) +func (r *ingestionDestinationResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { + r.SetTagsAll(ctx, request, response) } -func waitIngestionDestinationCreated(ctx context.Context, conn *appfabric.Client, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier string, timeout time.Duration) (*awstypes.IngestionDestination, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{}, - Target: enum.Slice(awstypes.IngestionDestinationStatusActive, awstypes.IngestionDestinationStatusActive), - Refresh: statusIngestionDestination(ctx, conn, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier), - Timeout: timeout, - NotFoundChecks: 20, - ContinuousTargetOccurence: 2, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*awstypes.IngestionDestination); ok { - return out, err +func findIngestionDestinationByThreePartKey(ctx context.Context, conn *appfabric.Client, appBundleARN, ingestionARN, arn string) (*awstypes.IngestionDestination, error) { + in := &appfabric.GetIngestionDestinationInput{ + AppBundleIdentifier: aws.String(appBundleARN), + IngestionDestinationIdentifier: aws.String(arn), + IngestionIdentifier: aws.String(ingestionARN), } - return nil, err -} + output, err := conn.GetIngestionDestination(ctx, in) -func waitIngestionDestinationUpdated(ctx context.Context, conn *appfabric.Client, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier string, timeout time.Duration) (*awstypes.IngestionDestination, error) { - stateConf := &retry.StateChangeConf{ - Pending: []string{}, - Target: enum.Slice(awstypes.IngestionDestinationStatusActive, awstypes.IngestionDestinationStatusActive), - Refresh: statusIngestionDestination(ctx, conn, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier), - Timeout: timeout, - NotFoundChecks: 20, - ContinuousTargetOccurence: 2, - } - - outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*awstypes.IngestionDestination); ok { - return out, err + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, + } } - return nil, err -} - -func waitIngestionDestinationDeleted(ctx context.Context, conn *appfabric.Client, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier string, timeout time.Duration) (*awstypes.IngestionDestination, error) { - stateConf := &retry.StateChangeConf{ - Pending: enum.Slice(awstypes.IngestionDestinationStatusActive, awstypes.IngestionDestinationStatusActive), - Target: []string{}, - Refresh: statusIngestionDestination(ctx, conn, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier), - Timeout: timeout, + if err != nil { + return nil, err } - outputRaw, err := stateConf.WaitForStateContext(ctx) - if out, ok := outputRaw.(*awstypes.IngestionDestination); ok { - return out, err + if output == nil || output.IngestionDestination == nil { + return nil, tfresource.NewEmptyResultError(in) } - return nil, err + return output.IngestionDestination, nil } -func statusIngestionDestination(ctx context.Context, conn *appfabric.Client, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier string) retry.StateRefreshFunc { +func statusIngestionDestination(ctx context.Context, conn *appfabric.Client, appBundleARN, ingestionARN, arn string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - out, err := findIngestionDestinationByID(ctx, conn, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier) + out, err := findIngestionDestinationByThreePartKey(ctx, conn, appBundleARN, ingestionARN, arn) + if tfresource.NotFound(err) { return nil, "", nil } @@ -467,362 +471,274 @@ func statusIngestionDestination(ctx context.Context, conn *appfabric.Client, ing } } -func findIngestionDestinationByID(ctx context.Context, conn *appfabric.Client, ingestionDestinationArn, appBundleIdentifier, ingestionIdentifier string) (*awstypes.IngestionDestination, error) { - in := &appfabric.GetIngestionDestinationInput{ - IngestionDestinationIdentifier: aws.String(ingestionDestinationArn), - AppBundleIdentifier: aws.String(appBundleIdentifier), - IngestionIdentifier: aws.String(ingestionIdentifier), +func waitIngestionDestinationActive(ctx context.Context, conn *appfabric.Client, appBundleARN, ingestionARN, arn string, timeout time.Duration) (*awstypes.IngestionDestination, error) { + stateConf := &retry.StateChangeConf{ + Pending: []string{}, + Target: enum.Slice(awstypes.IngestionDestinationStatusActive), + Refresh: statusIngestionDestination(ctx, conn, appBundleARN, ingestionARN, arn), + Timeout: timeout, } - out, err := conn.GetIngestionDestination(ctx, in) - if err != nil { - if errs.IsA[*awstypes.ResourceNotFoundException](err) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: in, - } - } + outputRaw, err := stateConf.WaitForStateContext(ctx) - return nil, err - } + if output, ok := outputRaw.(*awstypes.IngestionDestination); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.StatusReason))) - if out == nil || out.IngestionDestination == nil { - return nil, tfresource.NewEmptyResultError(in) + return output, err } - return out.IngestionDestination, nil -} - -func expandDestinationConfiguration(ctx context.Context, tfList []destinationConfigurationModel) (awstypes.DestinationConfiguration, diag.Diagnostics) { - auditLog := []awstypes.DestinationConfiguration{} - var diags diag.Diagnostics - - tfObj := tfList[0] - - if !tfObj.DestinationConfigurationAuditLog.IsNull() { - var destinationConfigurationAuditLog []destinationConfigurationAuditLogModel - diags.Append(tfObj.DestinationConfigurationAuditLog.ElementsAs(ctx, &destinationConfigurationAuditLog, false)...) - apiObject := expandDestinationConfigurationAuditLog(ctx, destinationConfigurationAuditLog) - auditLog = append(auditLog, apiObject) - } - return auditLog[0], diags + return nil, err } -func expandDestinationConfigurationAuditLog(ctx context.Context, tfList []destinationConfigurationAuditLogModel) *awstypes.DestinationConfigurationMemberAuditLog { - var diags diag.Diagnostics - - if len(tfList) == 0 { - return nil +func waitIngestionDestinationDeleted(ctx context.Context, conn *appfabric.Client, appBundleARN, ingestionARN, arn string, timeout time.Duration) (*awstypes.IngestionDestination, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.IngestionDestinationStatusActive), + Target: []string{}, + Refresh: statusIngestionDestination(ctx, conn, appBundleARN, ingestionARN, arn), + Timeout: timeout, } - tfObj := tfList[0] + outputRaw, err := stateConf.WaitForStateContext(ctx) - var destinationData []destinationModel - diags.Append(tfObj.Destination.ElementsAs(ctx, &destinationData, false)...) + if output, ok := outputRaw.(*awstypes.IngestionDestination); ok { + tfresource.SetLastError(err, errors.New(aws.ToString(output.StatusReason))) - destination := &awstypes.DestinationConfigurationMemberAuditLog{ - Value: awstypes.AuditLogDestinationConfiguration{ - Destination: expandDestination(ctx, destinationData), - }, + return output, err } - return destination + return nil, err } -func expandDestination(ctx context.Context, tfList []destinationModel) awstypes.Destination { - destination := []awstypes.Destination{} - var diags diag.Diagnostics - - if len(tfList) == 0 { - return nil - } +type ingestionDestinationResourceModel struct { + AppBundleARN fwtypes.ARN `tfsdk:"app_bundle_arn"` + ARN types.String `tfsdk:"arn"` + DestinationConfiguration fwtypes.ListNestedObjectValueOf[destinationConfigurationModel] `tfsdk:"destination_configuration"` + ID types.String `tfsdk:"id"` + IngestionARN fwtypes.ARN `tfsdk:"ingestion_arn"` + ProcessingConfiguration fwtypes.ListNestedObjectValueOf[processingConfigurationModel] `tfsdk:"processing_configuration"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` + Timeouts timeouts.Value `tfsdk:"timeouts"` +} - tfObj := tfList[0] +const ( + ingestionDestinationResourceIDPartCount = 3 +) - for _, item := range tfList { - if !item.FirehoseStream.IsNull() && (len(item.FirehoseStream.Elements()) > 0) { - var firehoseStream []firehoseStreamModel - diags.Append(tfObj.FirehoseStream.ElementsAs(ctx, &firehoseStream, false)...) - streamName := expandFirehoseStream(ctx, firehoseStream) - destination = append(destination, streamName) - } - if !item.S3Bucket.IsNull() && (len(item.S3Bucket.Elements()) > 0) { - var s3Bucket []s3BucketModel - diags.Append(tfObj.S3Bucket.ElementsAs(ctx, &s3Bucket, false)...) - bucketName := expandS3Bucket(ctx, s3Bucket) - destination = append(destination, bucketName) - } +func (m *ingestionDestinationResourceModel) InitFromID() error { + parts, err := flex.ExpandResourceId(m.ID.ValueString(), ingestionDestinationResourceIDPartCount, false) + if err != nil { + return err } - return destination[0] -} + m.AppBundleARN = fwtypes.ARNValue(parts[0]) + m.IngestionARN = fwtypes.ARNValue(parts[1]) + m.ARN = types.StringValue(parts[2]) -func expandFirehoseStream(ctx context.Context, tfList []firehoseStreamModel) *awstypes.DestinationMemberFirehoseStream { - if len(tfList) == 0 { - return nil - } + return nil +} - return &awstypes.DestinationMemberFirehoseStream{ - Value: awstypes.FirehoseStream{ - StreamName: fwflex.StringFromFramework(ctx, tfList[0].StreamName), - }, - } +func (m *ingestionDestinationResourceModel) setID() { + m.ID = types.StringValue(errs.Must(flex.FlattenResourceId([]string{m.AppBundleARN.ValueString(), m.IngestionARN.ValueString(), m.ARN.ValueString()}, ingestionDestinationResourceIDPartCount, false))) } -func expandS3Bucket(ctx context.Context, tfList []s3BucketModel) *awstypes.DestinationMemberS3Bucket { - if len(tfList) == 0 { - return nil - } +type destinationConfigurationModel struct { + AuditLog fwtypes.ListNestedObjectValueOf[auditLogDestinationConfigurationModel] `tfsdk:"audit_log"` +} - return &awstypes.DestinationMemberS3Bucket{ - Value: awstypes.S3Bucket{ - BucketName: fwflex.StringFromFramework(ctx, tfList[0].BucketName), - Prefix: fwflex.StringFromFramework(ctx, tfList[0].Prefix), - }, - } +type auditLogDestinationConfigurationModel struct { + Destination fwtypes.ListNestedObjectValueOf[destinationModel] `tfsdk:"destination"` } -func expandProcessingConfiguration(ctx context.Context, tfList []processingConfigurationModel) (awstypes.ProcessingConfiguration, diag.Diagnostics) { - auditLog := []awstypes.ProcessingConfiguration{} - var diags diag.Diagnostics +type destinationModel struct { + FirehoseStream fwtypes.ListNestedObjectValueOf[firehoseStreamModel] `tfsdk:"firehose_stream"` + S3Bucket fwtypes.ListNestedObjectValueOf[s3BucketModel] `tfsdk:"s3_bucket"` +} - tfObj := tfList[0] +type firehoseStreamModel struct { + StreamName types.String `tfsdk:"stream_name"` +} - if !tfObj.ProcessingConfigurationAuditLog.IsNull() { - var processingConfigurationAuditLog []processingConfigurationAuditLogModel - diags.Append(tfObj.ProcessingConfigurationAuditLog.ElementsAs(ctx, &processingConfigurationAuditLog, false)...) - apiObject := expandProcessingConfigurationAuditLog(processingConfigurationAuditLog) - auditLog = append(auditLog, apiObject) - } - return auditLog[0], diags +type s3BucketModel struct { + BucketName types.String `tfsdk:"bucket_name"` + Prefix types.String `tfsdk:"prefix"` } -func expandProcessingConfigurationAuditLog(auditLog []processingConfigurationAuditLogModel) *awstypes.ProcessingConfigurationMemberAuditLog { - if len(auditLog) == 0 { - return nil - } +type processingConfigurationModel struct { + AuditLog fwtypes.ListNestedObjectValueOf[auditLogProcessingConfigurationModel] `tfsdk:"audit_log"` +} - return &awstypes.ProcessingConfigurationMemberAuditLog{ - Value: awstypes.AuditLogProcessingConfiguration{ - Format: auditLog[0].Format.ValueEnum(), - Schema: auditLog[0].Schema.ValueEnum(), - }, - } +type auditLogProcessingConfigurationModel struct { + Format fwtypes.StringEnum[awstypes.Format] `tfsdk:"format"` + Schema fwtypes.StringEnum[awstypes.Schema] `tfsdk:"schema"` } -func flattenDestinationConfiguration(ctx context.Context, apiObject awstypes.DestinationConfiguration) (types.List, diag.Diagnostics) { +func expandDestinationConfiguration(ctx context.Context, destinationConfigurationData *destinationConfigurationModel) (awstypes.DestinationConfiguration, diag.Diagnostics) { var diags diag.Diagnostics - elemType := types.ObjectType{AttrTypes: destinationConfigurationModelAttrTypes} - if apiObject == nil { - return types.ListNull(elemType), diags - } + if !destinationConfigurationData.AuditLog.IsNull() { + auditLogDestinationConfigurationData, d := destinationConfigurationData.AuditLog.ToPtr(ctx) + diags.Append(d...) + if diags.HasError() { + return nil, diags + } - obj := map[string]attr.Value{} + destinationData, d := auditLogDestinationConfigurationData.Destination.ToPtr(ctx) + diags.Append(d...) + if diags.HasError() { + return nil, diags + } - switch v := apiObject.(type) { - case *awstypes.DestinationConfigurationMemberAuditLog: - auditLog, d := flattenDestinationConfigurationAuditLog(ctx, &v.Value) + destination, d := expandDestination(ctx, destinationData) diags.Append(d...) - obj = map[string]attr.Value{ - "audit_log": auditLog, + if diags.HasError() { + return nil, diags } - default: - log.Println("union is nil or unknown type") - } - objVal, d := types.ObjectValue(destinationConfigurationModelAttrTypes, obj) - diags.Append(d...) + apiObject := &awstypes.DestinationConfigurationMemberAuditLog{ + Value: awstypes.AuditLogDestinationConfiguration{ + Destination: destination, + }, + } - listVal, d := types.ListValue(elemType, []attr.Value{objVal}) - diags.Append(d...) + return apiObject, diags + } - return listVal, diags + return nil, diags } -func flattenDestinationConfigurationAuditLog(ctx context.Context, apiObject *awstypes.AuditLogDestinationConfiguration) (types.List, diag.Diagnostics) { +func expandDestination(ctx context.Context, destinationData *destinationModel) (awstypes.Destination, diag.Diagnostics) { var diags diag.Diagnostics - elemType := types.ObjectType{AttrTypes: destinationConfigurationMemberAuditLogModelAttrTypes} - if apiObject == nil { - return types.ListNull(elemType), diags - } + if !destinationData.FirehoseStream.IsNull() { + firehoseStreamData, d := destinationData.FirehoseStream.ToPtr(ctx) + diags.Append(d...) + if diags.HasError() { + return nil, diags + } - obj := map[string]attr.Value{} + apiObject := &awstypes.DestinationMemberFirehoseStream{} + diags.Append(fwflex.Expand(ctx, firehoseStreamData, &apiObject.Value)...) + if diags.HasError() { + return nil, diags + } - destination, d := flattenDestination(ctx, apiObject.Destination) - diags.Append(d...) - obj["destination"] = destination + return apiObject, diags + } + if !destinationData.S3Bucket.IsNull() { + s3BucketData, d := destinationData.S3Bucket.ToPtr(ctx) + diags.Append(d...) + if diags.HasError() { + return nil, diags + } - objVal, d := types.ObjectValue(destinationConfigurationMemberAuditLogModelAttrTypes, obj) - diags.Append(d...) + apiObject := &awstypes.DestinationMemberS3Bucket{} + diags.Append(fwflex.Expand(ctx, s3BucketData, &apiObject.Value)...) + if diags.HasError() { + return nil, diags + } - listVal, d := types.ListValue(elemType, []attr.Value{objVal}) - diags.Append(d...) + return apiObject, diags + } - return listVal, diags + return nil, diags } -func flattenDestination(ctx context.Context, apiObject awstypes.Destination) (types.List, diag.Diagnostics) { +func expandProcessingConfiguration(ctx context.Context, processingConfigurationData *processingConfigurationModel) (awstypes.ProcessingConfiguration, diag.Diagnostics) { var diags diag.Diagnostics - elemType := types.ObjectType{AttrTypes: destinationModelAttrTypes} - - obj := map[string]attr.Value{} - switch v := apiObject.(type) { - case *awstypes.DestinationMemberFirehoseStream: - destination, d := flattenDestinationModel(ctx, &v.Value, nil, "firehose") + if !processingConfigurationData.AuditLog.IsNull() { + auditLogProcessingConfigurationData, d := processingConfigurationData.AuditLog.ToPtr(ctx) diags.Append(d...) - obj = map[string]attr.Value{ - "firehose_stream": destination, - "s3_bucket": types.ListNull(s3BucketAttrTypes), - } - case *awstypes.DestinationMemberS3Bucket: - destination, d := flattenDestinationModel(ctx, nil, &v.Value, "s3") - diags.Append(d...) - obj = map[string]attr.Value{ - "firehose_stream": types.ListNull(firehoseStreamAttrTypes), - "s3_bucket": destination, + if diags.HasError() { + return nil, diags } - } - objVal, d := types.ObjectValue(destinationModelAttrTypes, obj) - diags.Append(d...) + apiObject := &awstypes.ProcessingConfigurationMemberAuditLog{} + diags.Append(fwflex.Expand(ctx, auditLogProcessingConfigurationData, &apiObject.Value)...) + if diags.HasError() { + return nil, diags + } - listVal, d := types.ListValue(elemType, []attr.Value{objVal}) - diags.Append(d...) + return apiObject, diags + } - return listVal, diags + return nil, diags } -func flattenDestinationModel(ctx context.Context, firehoseApiObject *awstypes.FirehoseStream, s3ApiObject *awstypes.S3Bucket, destinationType string) (types.List, diag.Diagnostics) { +func flattenDestinationConfiguration(ctx context.Context, apiObject awstypes.DestinationConfiguration) (*destinationConfigurationModel, diag.Diagnostics) { var diags diag.Diagnostics - var elemType types.ObjectType - var obj map[string]attr.Value - var objVal basetypes.ObjectValue + var destinationConfigurationData *destinationConfigurationModel - if destinationType == "firehose" { - var d diag.Diagnostics - elemType = types.ObjectType{AttrTypes: firehoseStreamModelAttrTypes} - obj = map[string]attr.Value{ - "stream_name": fwflex.StringToFramework(ctx, firehoseApiObject.StreamName), - } - objVal, d = types.ObjectValue(firehoseStreamModelAttrTypes, obj) - diags.Append(d...) - } else if destinationType == "s3" { - var d diag.Diagnostics - elemType = types.ObjectType{AttrTypes: s3BucketModelAttrTypes} - obj = map[string]attr.Value{ - "bucket_name": fwflex.StringToFramework(ctx, s3ApiObject.BucketName), - "prefix": fwflex.StringToFramework(ctx, s3ApiObject.Prefix), - } - objVal, d = types.ObjectValue(s3BucketModelAttrTypes, obj) + switch v := apiObject.(type) { + case *awstypes.DestinationConfigurationMemberAuditLog: + destinationData, d := flattenDestination(ctx, v.Value.Destination) diags.Append(d...) - } - - listVal, d := types.ListValue(elemType, []attr.Value{objVal}) - diags.Append(d...) - - return listVal, diags -} - -var ( - destinationConfigurationModelAttrTypes = map[string]attr.Type{ - "audit_log": types.ListType{ElemType: types.ObjectType{AttrTypes: destinationConfigurationMemberAuditLogModelAttrTypes}}, - } - - destinationConfigurationMemberAuditLogModelAttrTypes = map[string]attr.Type{ - "destination": types.ListType{ElemType: types.ObjectType{AttrTypes: destinationModelAttrTypes}}, - } + if diags.HasError() { + return nil, diags + } - destinationModelAttrTypes = map[string]attr.Type{ - "firehose_stream": types.ListType{ElemType: firehoseStreamAttrTypes}, - "s3_bucket": types.ListType{ElemType: s3BucketAttrTypes}, + auditLogDestinationConfigurationData := &auditLogDestinationConfigurationModel{ + Destination: fwtypes.NewListNestedObjectValueOfPtrMust(ctx, destinationData), + } + destinationConfigurationData = &destinationConfigurationModel{ + AuditLog: fwtypes.NewListNestedObjectValueOfPtrMust(ctx, auditLogDestinationConfigurationData), + } } - firehoseStreamAttrTypes = types.ObjectType{AttrTypes: firehoseStreamModelAttrTypes} - s3BucketAttrTypes = types.ObjectType{AttrTypes: s3BucketModelAttrTypes} - - firehoseStreamModelAttrTypes = map[string]attr.Type{ - "stream_name": types.StringType, - } + return destinationConfigurationData, diags +} - s3BucketModelAttrTypes = map[string]attr.Type{ - "bucket_name": types.StringType, - "prefix": types.StringType, - } +func flattenDestination(ctx context.Context, apiObject awstypes.Destination) (*destinationModel, diag.Diagnostics) { + var diags diag.Diagnostics + var destinationData *destinationModel - processingConfigurationModelAttrTypes = map[string]attr.Type{ - "audit_log": types.ListType{ElemType: types.ObjectType{AttrTypes: processingConfigurationMemberAuditLogModelAttrTypes}}, - } + switch v := apiObject.(type) { + case *awstypes.DestinationMemberFirehoseStream: + var firehoseStreamData firehoseStreamModel + d := fwflex.Flatten(ctx, v.Value, &firehoseStreamData) + diags.Append(d...) + if diags.HasError() { + return nil, diags + } - processingConfigurationMemberAuditLogModelAttrTypes = map[string]attr.Type{ - "format": types.StringType, - "schema": types.StringType, - } -) + destinationData = &destinationModel{ + FirehoseStream: fwtypes.NewListNestedObjectValueOfPtrMust(ctx, &firehoseStreamData), + } -const ( - ingestionDestinationResourceIDPartCount = 3 -) + case *awstypes.DestinationMemberS3Bucket: + var s3BucketData s3BucketModel + d := fwflex.Flatten(ctx, v.Value, &s3BucketData) + diags.Append(d...) + if diags.HasError() { + return nil, diags + } -func (m *resourceIngestionDestinationModel) InitFromID() error { - parts, err := flex.ExpandResourceId(m.ID.ValueString(), ingestionDestinationResourceIDPartCount, false) - if err != nil { - return err + destinationData = &destinationModel{ + S3Bucket: fwtypes.NewListNestedObjectValueOfPtrMust(ctx, &s3BucketData), + } } - m.IngestionDestinationArn = types.StringValue(parts[0]) - m.AppBundleIdentifier = types.StringValue(parts[1]) - m.IngestionIdentifier = types.StringValue(parts[2]) - - return nil -} - -func (m *resourceIngestionDestinationModel) setID() { - m.ID = types.StringValue(errs.Must(flex.FlattenResourceId([]string{m.IngestionDestinationArn.ValueString(), m.AppBundleIdentifier.ValueString(), m.IngestionIdentifier.ValueString()}, ingestionDestinationResourceIDPartCount, false))) + return destinationData, diags } -type resourceIngestionDestinationModel struct { - AppBundleIdentifier types.String `tfsdk:"app_bundle_identifier"` - DestinationConfiguration types.List `tfsdk:"destination_configuration"` - ID types.String `tfsdk:"id"` - IngestionArn types.String `tfsdk:"ingestion_arn"` - IngestionDestinationArn types.String `tfsdk:"arn"` - IngestionIdentifier types.String `tfsdk:"ingestion_identifier"` - ProcessingConfiguration fwtypes.ListNestedObjectValueOf[processingConfigurationModel] `tfsdk:"processing_configuration"` - Tags types.Map `tfsdk:"tags"` - TagsAll types.Map `tfsdk:"tags_all"` - Timeouts timeouts.Value `tfsdk:"timeouts"` -} - -type destinationConfigurationModel struct { - DestinationConfigurationAuditLog fwtypes.ListNestedObjectValueOf[destinationConfigurationAuditLogModel] `tfsdk:"audit_log"` -} - -type destinationConfigurationAuditLogModel struct { - Destination fwtypes.ListNestedObjectValueOf[destinationModel] `tfsdk:"destination"` -} - -type destinationModel struct { - FirehoseStream fwtypes.ListNestedObjectValueOf[firehoseStreamModel] `tfsdk:"firehose_stream"` - S3Bucket fwtypes.ListNestedObjectValueOf[s3BucketModel] `tfsdk:"s3_bucket"` -} - -type firehoseStreamModel struct { - StreamName types.String `tfsdk:"stream_name"` -} +func flattenProcessingConfiguration(ctx context.Context, apiObject awstypes.ProcessingConfiguration) (*processingConfigurationModel, diag.Diagnostics) { + var diags diag.Diagnostics + var processingConfigurationData *processingConfigurationModel -type s3BucketModel struct { - BucketName types.String `tfsdk:"bucket_name"` - Prefix types.String `tfsdk:"prefix"` -} + switch v := apiObject.(type) { + case *awstypes.ProcessingConfigurationMemberAuditLog: + var auditLogProcessingConfigurationData auditLogProcessingConfigurationModel + d := fwflex.Flatten(ctx, v.Value, &auditLogProcessingConfigurationData) + diags.Append(d...) + if diags.HasError() { + return nil, diags + } -type processingConfigurationModel struct { - ProcessingConfigurationAuditLog fwtypes.ListNestedObjectValueOf[processingConfigurationAuditLogModel] `tfsdk:"audit_log"` -} + processingConfigurationData = &processingConfigurationModel{ + AuditLog: fwtypes.NewListNestedObjectValueOfPtrMust(ctx, &auditLogProcessingConfigurationData), + } + } -type processingConfigurationAuditLogModel struct { - Format fwtypes.StringEnum[awstypes.Format] `tfsdk:"format"` - Schema fwtypes.StringEnum[awstypes.Schema] `tfsdk:"schema"` + return processingConfigurationData, diags } diff --git a/internal/service/appfabric/ingestion_destination_test.go b/internal/service/appfabric/ingestion_destination_test.go index 3ee61c33d1a..8a4df795cf4 100644 --- a/internal/service/appfabric/ingestion_destination_test.go +++ b/internal/service/appfabric/ingestion_destination_test.go @@ -5,26 +5,23 @@ package appfabric_test import ( "context" - "errors" "fmt" "testing" - "github.com/aws/aws-sdk-go-v2/service/appfabric/types" + awstypes "github.com/aws/aws-sdk-go-v2/service/appfabric/types" "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" + tfappfabric "github.com/hashicorp/terraform-provider-aws/internal/service/appfabric" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" - - tfappfabric "github.com/hashicorp/terraform-provider-aws/internal/service/appfabric" ) func TestAccAppFabricIngestionDestination_basic(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_appfabric_ingestion_destination.test" - var ingestiondestination types.IngestionDestination + var ingestiondestination awstypes.IngestionDestination resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { @@ -63,7 +60,7 @@ func TestAccAppFabricIngestionDestination_basic(t *testing.T) { func TestAccAppFabricIngestionDestination_firehose(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_appfabric_ingestion_destination.test" - var ingestiondestination types.IngestionDestination + var ingestiondestination awstypes.IngestionDestination resource.Test(t, resource.TestCase{ PreCheck: func() { @@ -101,7 +98,7 @@ func TestAccAppFabricIngestionDestination_firehose(t *testing.T) { func TestAccAppFabricIngestionDestination_destinationUpdate(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_appfabric_ingestion_destination.test" - var ingestiondestination types.IngestionDestination + var ingestiondestination awstypes.IngestionDestination resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { @@ -164,7 +161,7 @@ func TestAccAppFabricIngestionDestination_disappears(t *testing.T) { t.Skip("skipping long-running test in short mode") } - var ingestiondestination types.IngestionDestination + var ingestiondestination awstypes.IngestionDestination resourceName := "aws_appfabric_ingestion_destination.test" resource.ParallelTest(t, resource.TestCase{ @@ -196,7 +193,7 @@ func testAccCheckIngestionDestinationDestroy(ctx context.Context) resource.TestC continue } - _, err := tfappfabric.FindIngestionDestinationByID(ctx, conn, rs.Primary.Attributes[names.AttrARN], rs.Primary.Attributes["app_bundle_identifier"], rs.Primary.Attributes["ingestion_identifier"]) + _, err := tfappfabric.FindIngestionDestinationByThreePartKey(ctx, conn, rs.Primary.Attributes[names.AttrARN], rs.Primary.Attributes["app_bundle_identifier"], rs.Primary.Attributes["ingestion_identifier"]) if tfresource.NotFound(err) { continue @@ -206,29 +203,29 @@ func testAccCheckIngestionDestinationDestroy(ctx context.Context) resource.TestC return err } - return fmt.Errorf("Ingestion Destination %s still exists", rs.Primary.ID) + return fmt.Errorf("AppFabric Ingestion Destination %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckIngestionDestinationExists(ctx context.Context, name string, ingestiondestination *types.IngestionDestination) resource.TestCheckFunc { +func testAccCheckIngestionDestinationExists(ctx context.Context, n string, v *awstypes.IngestionDestination) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return create.Error(names.AppFabric, create.ErrActionCheckingExistence, tfappfabric.ResNameIngestionDestination, name, errors.New("not found")) + return fmt.Errorf("Not found: %s", n) } conn := acctest.Provider.Meta().(*conns.AWSClient).AppFabricClient(ctx) - output, err := tfappfabric.FindIngestionDestinationByID(ctx, conn, rs.Primary.Attributes[names.AttrARN], rs.Primary.Attributes["app_bundle_identifier"], rs.Primary.Attributes["ingestion_identifier"]) + output, err := tfappfabric.FindIngestionDestinationByThreePartKey(ctx, conn, rs.Primary.Attributes[names.AttrARN], rs.Primary.Attributes["app_bundle_identifier"], rs.Primary.Attributes["ingestion_identifier"]) if err != nil { return err } - *ingestiondestination = *output + *v = *output return nil } @@ -239,25 +236,24 @@ func testAccIngestionDestinationConfig_basic() string { resource "aws_appfabric_ingestion_destination" "test" { app_bundle_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633" ingestion_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633/ingestion/8b7895cf-171a-494c-9abb-7170eaed13b5" + processing_configuration { - audit_log { - format = "json" - schema = "raw" - } + audit_log { + format = "json" + schema = "raw" + } } + destination_configuration { audit_log { - destination { - s3_bucket { - bucket_name = "s3-bucket-name" - prefix = "AuditLog" - } - } + destination { + s3_bucket { + bucket_name = "s3-bucket-name" + prefix = "AuditLog" + } + } } } - tags = { - environment = "test" - } } ` } diff --git a/internal/service/appfabric/service_package_gen.go b/internal/service/appfabric/service_package_gen.go index 236d0cb9137..991cf4f9a11 100644 --- a/internal/service/appfabric/service_package_gen.go +++ b/internal/service/appfabric/service_package_gen.go @@ -39,15 +39,15 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic }, }, { - Factory: newIngestionResource, - Name: "Ingestion", + Factory: newIngestionDestinationResource, + Name: "Ingestion Destination", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: names.AttrARN, }, }, { - Factory: newResourceIngestionDestination, - Name: "Ingestion Destination", + Factory: newIngestionResource, + Name: "Ingestion", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: names.AttrARN, }, From b605cf49c23b4a66a804ba2bc0554e6c4b94c977 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 25 Jun 2024 15:01:12 -0400 Subject: [PATCH 05/12] r/aws_appfabric_ingestion_destination: Fixes after some testing. --- internal/service/appfabric/app_authorization.go | 2 ++ .../appfabric/app_authorization_connection.go | 2 ++ internal/service/appfabric/ingestion_destination.go | 13 ++++++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/internal/service/appfabric/app_authorization.go b/internal/service/appfabric/app_authorization.go index 537aa747c03..07bafd3e846 100644 --- a/internal/service/appfabric/app_authorization.go +++ b/internal/service/appfabric/app_authorization.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "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/resource/schema/planmodifier" @@ -235,6 +236,7 @@ func (r *appAuthorizationResource) Create(ctx context.Context, request resource. appAuthorization, err := waitAppAuthorizationCreated(ctx, conn, data.AppAuthorizationARN.ValueString(), data.AppBundleARN.ValueString(), r.CreateTimeout(ctx, data.Timeouts)) if err != nil { + response.State.SetAttribute(ctx, path.Root(names.AttrID), data.ID) // Set 'id' so as to taint the resource. response.Diagnostics.AddError(fmt.Sprintf("waiting for AppFabric App Authorization (%s) create", data.ID.ValueString()), err.Error()) return diff --git a/internal/service/appfabric/app_authorization_connection.go b/internal/service/appfabric/app_authorization_connection.go index 6f04fcccf65..12864566ae9 100644 --- a/internal/service/appfabric/app_authorization_connection.go +++ b/internal/service/appfabric/app_authorization_connection.go @@ -13,6 +13,7 @@ import ( awstypes "github.com/aws/aws-sdk-go-v2/service/appfabric/types" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "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/resource/schema/planmodifier" @@ -139,6 +140,7 @@ func (r *appAuthorizationConnectionResource) Create(ctx context.Context, request appAuthorization, err := waitConnectAppAuthorizationCreated(ctx, conn, data.AppAuthorizationARN.ValueString(), data.AppBundleARN.ValueString(), r.CreateTimeout(ctx, data.Timeouts)) if err != nil { + response.State.SetAttribute(ctx, path.Root(names.AttrID), data.ID) // Set 'id' so as to taint the resource. response.Diagnostics.AddError(fmt.Sprintf("waiting for AppFabric App Authorization Connection (%s) create", data.ID.ValueString()), err.Error()) return diff --git a/internal/service/appfabric/ingestion_destination.go b/internal/service/appfabric/ingestion_destination.go index 7705ab6a0e0..917602d97a5 100644 --- a/internal/service/appfabric/ingestion_destination.go +++ b/internal/service/appfabric/ingestion_destination.go @@ -17,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "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/resource/schema/listplanmodifier" @@ -253,7 +254,9 @@ func (r *ingestionDestinationResource) Create(ctx context.Context, request resou } // Additional fields. + input.AppBundleIdentifier = aws.String(data.AppBundleARN.ValueString()) input.ClientToken = aws.String(errs.Must(uuid.GenerateUUID())) + input.IngestionIdentifier = aws.String(data.IngestionARN.ValueString()) input.Tags = getTagsIn(ctx) output, err := conn.CreateIngestionDestination(ctx, input) @@ -269,6 +272,7 @@ func (r *ingestionDestinationResource) Create(ctx context.Context, request resou data.setID() if _, err := waitIngestionDestinationActive(ctx, conn, data.AppBundleARN.ValueString(), data.IngestionARN.ValueString(), data.ARN.ValueString(), r.CreateTimeout(ctx, data.Timeouts)); err != nil { + response.State.SetAttribute(ctx, path.Root(names.AttrID), data.ID) // Set 'id' so as to taint the resource. response.Diagnostics.AddError(fmt.Sprintf("waiting for AppFabric Ingestion Destination (%s) create", data.ID.ValueString()), err.Error()) return @@ -374,6 +378,11 @@ func (r *ingestionDestinationResource) Update(ctx context.Context, request resou input.DestinationConfiguration = destinationConfiguration } + // Additional fields. + input.AppBundleIdentifier = aws.String(new.AppBundleARN.ValueString()) + input.IngestionDestinationIdentifier = aws.String(new.ARN.ValueString()) + input.IngestionIdentifier = aws.String(new.IngestionARN.ValueString()) + _, err := conn.UpdateIngestionDestination(ctx, input) if err != nil { @@ -704,6 +713,7 @@ func flattenDestination(ctx context.Context, apiObject awstypes.Destination) (*d destinationData = &destinationModel{ FirehoseStream: fwtypes.NewListNestedObjectValueOfPtrMust(ctx, &firehoseStreamData), + S3Bucket: fwtypes.NewListNestedObjectValueOfNull[s3BucketModel](ctx), } case *awstypes.DestinationMemberS3Bucket: @@ -715,7 +725,8 @@ func flattenDestination(ctx context.Context, apiObject awstypes.Destination) (*d } destinationData = &destinationModel{ - S3Bucket: fwtypes.NewListNestedObjectValueOfPtrMust(ctx, &s3BucketData), + FirehoseStream: fwtypes.NewListNestedObjectValueOfNull[firehoseStreamModel](ctx), + S3Bucket: fwtypes.NewListNestedObjectValueOfPtrMust(ctx, &s3BucketData), } } From b10b92c0af6613bd3174ee4ac74ea6ec7165d51e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 25 Jun 2024 16:06:39 -0400 Subject: [PATCH 06/12] r/aws_appfabric_ingestion_destination: Acceptance tests. --- internal/service/appfabric/appfabric_test.go | 7 + .../appfabric/ingestion_destination.go | 10 + .../appfabric/ingestion_destination_test.go | 472 +++++++++++++----- 3 files changed, 375 insertions(+), 114 deletions(-) diff --git a/internal/service/appfabric/appfabric_test.go b/internal/service/appfabric/appfabric_test.go index 45b08a3ffce..710d7eeca2e 100644 --- a/internal/service/appfabric/appfabric_test.go +++ b/internal/service/appfabric/appfabric_test.go @@ -42,6 +42,13 @@ func TestAccAppFabric_serial(t *testing.T) { acctest.CtDisappears: testAccIngestion_disappears, "tags": testAccIngestion_tags, }, + "IngestionDestination": { + acctest.CtBasic: testAccIngestionDestination_basic, + acctest.CtDisappears: testAccIngestionDestination_disappears, + "tags": testAccIngestionDestination_tags, + "update": testAccIngestionDestination_update, + "firehose": testAccIngestionDestination_firehose, + }, } acctest.RunSerialTests2Levels(t, testCases, serializeDelay) diff --git a/internal/service/appfabric/ingestion_destination.go b/internal/service/appfabric/ingestion_destination.go index 917602d97a5..d1c9a457cf2 100644 --- a/internal/service/appfabric/ingestion_destination.go +++ b/internal/service/appfabric/ingestion_destination.go @@ -15,6 +15,7 @@ import ( uuid "github.com/hashicorp/go-uuid" "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" @@ -433,6 +434,15 @@ func (r *ingestionDestinationResource) Delete(ctx context.Context, request resou } } +func (r *ingestionDestinationResource) ConfigValidators(context.Context) []resource.ConfigValidator { + return []resource.ConfigValidator{ + resourcevalidator.AtLeastOneOf( + path.MatchRoot("destination_configuration").AtListIndex(0).AtName("audit_log").AtListIndex(0).AtName("destination").AtListIndex(0).AtName("firehose_stream"), + path.MatchRoot("destination_configuration").AtListIndex(0).AtName("audit_log").AtListIndex(0).AtName("destination").AtListIndex(0).AtName("s3_bucket"), + ), + } +} + func (r *ingestionDestinationResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { r.SetTagsAll(ctx, request, response) } diff --git a/internal/service/appfabric/ingestion_destination_test.go b/internal/service/appfabric/ingestion_destination_test.go index 8a4df795cf4..2eed288c135 100644 --- a/internal/service/appfabric/ingestion_destination_test.go +++ b/internal/service/appfabric/ingestion_destination_test.go @@ -9,6 +9,7 @@ import ( "testing" awstypes "github.com/aws/aws-sdk-go-v2/service/appfabric/types" + 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" @@ -18,167 +19,228 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -func TestAccAppFabricIngestionDestination_basic(t *testing.T) { +func testAccIngestionDestination_basic(t *testing.T) { ctx := acctest.Context(t) - resourceName := "aws_appfabric_ingestion_destination.test" var ingestiondestination awstypes.IngestionDestination + resourceName := "aws_appfabric_ingestion_destination.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + // See https://docs.aws.amazon.com/appfabric/latest/adminguide/terraform.html#terraform-appfabric-connecting. + tenantID := acctest.SkipIfEnvVarNotSet(t, "AWS_APPFABRIC_TERRAFORMCLOUD_TENANT_ID") + serviceAccountToken := acctest.SkipIfEnvVarNotSet(t, "AWS_APPFABRIC_TERRAFORMCLOUD_SERVICE_ACCOUNT_TOKEN") - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) + acctest.PreCheckRegion(t, names.USEast1RegionID, names.APNortheast1RegionID, names.EUWest1RegionID) + testAccPreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.AppFabricServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIngestionDestinationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIngestionDestinationConfig_basic(), - Check: resource.ComposeTestCheckFunc( + Config: testAccIngestionDestinationConfig_basic(rName, tenantID, serviceAccountToken), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), + resource.TestCheckResourceAttrSet(resourceName, "app_bundle_arn"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.%", "1"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.%", "1"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.%", "2"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "0"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.%", "2"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", "s3-bucket-name"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.prefix", "AuditLog"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", rName), + resource.TestCheckNoResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.prefix"), + resource.TestCheckResourceAttrSet(resourceName, "ingestion_arn"), + resource.TestCheckResourceAttr(resourceName, "processing_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "processing_configuration.0.audit_log.#", "1"), + resource.TestCheckResourceAttr(resourceName, "processing_configuration.0.audit_log.0.format", "json"), + resource.TestCheckResourceAttr(resourceName, "processing_configuration.0.audit_log.0.schema", "raw"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"processing_configuration"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) } -func TestAccAppFabricIngestionDestination_firehose(t *testing.T) { +func testAccIngestionDestination_disappears(t *testing.T) { ctx := acctest.Context(t) + var ingestiondestination awstypes.IngestionDestination resourceName := "aws_appfabric_ingestion_destination.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + // See https://docs.aws.amazon.com/appfabric/latest/adminguide/terraform.html#terraform-appfabric-connecting. + tenantID := acctest.SkipIfEnvVarNotSet(t, "AWS_APPFABRIC_TERRAFORMCLOUD_TENANT_ID") + serviceAccountToken := acctest.SkipIfEnvVarNotSet(t, "AWS_APPFABRIC_TERRAFORMCLOUD_SERVICE_ACCOUNT_TOKEN") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckRegion(t, names.USEast1RegionID, names.APNortheast1RegionID, names.EUWest1RegionID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.AppFabricServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckIngestionDestinationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccIngestionDestinationConfig_basic(rName, tenantID, serviceAccountToken), + Check: resource.ComposeTestCheckFunc( + testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfappfabric.ResourceIngestionDestination, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccIngestionDestination_tags(t *testing.T) { + ctx := acctest.Context(t) var ingestiondestination awstypes.IngestionDestination + resourceName := "aws_appfabric_ingestion_destination.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + // See https://docs.aws.amazon.com/appfabric/latest/adminguide/terraform.html#terraform-appfabric-connecting. + tenantID := acctest.SkipIfEnvVarNotSet(t, "AWS_APPFABRIC_TERRAFORMCLOUD_TENANT_ID") + serviceAccountToken := acctest.SkipIfEnvVarNotSet(t, "AWS_APPFABRIC_TERRAFORMCLOUD_SERVICE_ACCOUNT_TOKEN") resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) + acctest.PreCheckRegion(t, names.USEast1RegionID, names.APNortheast1RegionID, names.EUWest1RegionID) + testAccPreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.AppFabricServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIngestionDestinationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIngestionDestinationConfig_firehose(), - Check: resource.ComposeAggregateTestCheckFunc( + Config: testAccIngestionDestinationConfig_tags1(rName, tenantID, serviceAccountToken, acctest.CtKey1, acctest.CtValue1), + Check: resource.ComposeTestCheckFunc( testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.%", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.%", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.%", "2"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.%", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.stream_name", "OpenSearchStack-FirehoseStream-bL4BiszVyNNC"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"processing_configuration"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccIngestionDestinationConfig_tags2(rName, tenantID, serviceAccountToken, acctest.CtKey1, acctest.CtValue1Updated, acctest.CtKey2, acctest.CtValue2), + Check: resource.ComposeTestCheckFunc( + testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct2), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey1, acctest.CtValue1Updated), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), + ), + }, + { + Config: testAccIngestionDestinationConfig_tags1(rName, tenantID, serviceAccountToken, acctest.CtKey2, acctest.CtValue2), + Check: resource.ComposeTestCheckFunc( + testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsKey2, acctest.CtValue2), + ), }, }, }) } -func TestAccAppFabricIngestionDestination_destinationUpdate(t *testing.T) { +func testAccIngestionDestination_update(t *testing.T) { ctx := acctest.Context(t) - resourceName := "aws_appfabric_ingestion_destination.test" var ingestiondestination awstypes.IngestionDestination + resourceName := "aws_appfabric_ingestion_destination.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + // See https://docs.aws.amazon.com/appfabric/latest/adminguide/terraform.html#terraform-appfabric-connecting. + tenantID := acctest.SkipIfEnvVarNotSet(t, "AWS_APPFABRIC_TERRAFORMCLOUD_TENANT_ID") + serviceAccountToken := acctest.SkipIfEnvVarNotSet(t, "AWS_APPFABRIC_TERRAFORMCLOUD_SERVICE_ACCOUNT_TOKEN") - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) + acctest.PreCheckRegion(t, names.USEast1RegionID, names.APNortheast1RegionID, names.EUWest1RegionID) + testAccPreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.AppFabricServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIngestionDestinationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIngestionDestinationConfig_basic(), + Config: testAccIngestionDestinationConfig_basic(rName, tenantID, serviceAccountToken), Check: resource.ComposeTestCheckFunc( testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.%", "1"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.%", "1"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.%", "2"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "0"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.%", "2"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", "s3-bucket-name"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.prefix", "AuditLog"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", rName), + resource.TestCheckNoResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.prefix"), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"processing_configuration"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, { - Config: testAccIngestionDestinationConfig_destinationUpdate(), - Check: resource.ComposeAggregateTestCheckFunc( + Config: testAccIngestionDestinationConfig_s3Prefix(rName, tenantID, serviceAccountToken, "testing"), + Check: resource.ComposeTestCheckFunc( testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.%", "1"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.%", "1"), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.%", "2"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.%", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.stream_name", "OpenSearchStack-FirehoseStream-bL4BiszVyNNC"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "0"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", rName), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.prefix", "testing"), ), }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"processing_configuration"}, - }, }, }) } -func TestAccAppFabricIngestionDestination_disappears(t *testing.T) { +func testAccIngestionDestination_firehose(t *testing.T) { ctx := acctest.Context(t) - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } - var ingestiondestination awstypes.IngestionDestination resourceName := "aws_appfabric_ingestion_destination.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + // See https://docs.aws.amazon.com/appfabric/latest/adminguide/terraform.html#terraform-appfabric-connecting. + tenantID := acctest.SkipIfEnvVarNotSet(t, "AWS_APPFABRIC_TERRAFORMCLOUD_TENANT_ID") + serviceAccountToken := acctest.SkipIfEnvVarNotSet(t, "AWS_APPFABRIC_TERRAFORMCLOUD_SERVICE_ACCOUNT_TOKEN") - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) + acctest.PreCheckRegion(t, names.USEast1RegionID, names.APNortheast1RegionID, names.EUWest1RegionID) + testAccPreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.AppFabricServiceID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckIngestionDestinationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccIngestionDestinationConfig_basic(), - Check: resource.ComposeTestCheckFunc( + Config: testAccIngestionDestinationConfig_firehose(rName, tenantID, serviceAccountToken), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), - acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfappfabric.ResourceIngestionDestination, resourceName), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.stream_name"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", "0"), ), - ExpectNonEmptyPlan: true, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -193,7 +255,7 @@ func testAccCheckIngestionDestinationDestroy(ctx context.Context) resource.TestC continue } - _, err := tfappfabric.FindIngestionDestinationByThreePartKey(ctx, conn, rs.Primary.Attributes[names.AttrARN], rs.Primary.Attributes["app_bundle_identifier"], rs.Primary.Attributes["ingestion_identifier"]) + _, err := tfappfabric.FindIngestionDestinationByThreePartKey(ctx, conn, rs.Primary.Attributes["app_bundle_arn"], rs.Primary.Attributes["ingestion_arn"], rs.Primary.Attributes[names.AttrARN]) if tfresource.NotFound(err) { continue @@ -219,7 +281,7 @@ func testAccCheckIngestionDestinationExists(ctx context.Context, n string, v *aw conn := acctest.Provider.Meta().(*conns.AWSClient).AppFabricClient(ctx) - output, err := tfappfabric.FindIngestionDestinationByThreePartKey(ctx, conn, rs.Primary.Attributes[names.AttrARN], rs.Primary.Attributes["app_bundle_identifier"], rs.Primary.Attributes["ingestion_identifier"]) + output, err := tfappfabric.FindIngestionDestinationByThreePartKey(ctx, conn, rs.Primary.Attributes["app_bundle_arn"], rs.Primary.Attributes["ingestion_arn"], rs.Primary.Attributes[names.AttrARN]) if err != nil { return err @@ -231,16 +293,35 @@ func testAccCheckIngestionDestinationExists(ctx context.Context, n string, v *aw } } -func testAccIngestionDestinationConfig_basic() string { - return ` +func testAccIngestionDestinationConfig_base(rName, tenantID, serviceAccountToken string) string { + return acctest.ConfigCompose(testAccIngestionConfig_base(rName, tenantID, serviceAccountToken), fmt.Sprintf(` +resource "aws_appfabric_ingestion" "test" { + app = aws_appfabric_app_authorization_connection.test.app + app_bundle_arn = aws_appfabric_app_bundle.test.arn + tenant_id = %[2]q + ingestion_type = "auditLog" + + tags = { + Name = %[1]q + } +} +`, rName, tenantID)) +} + +func testAccIngestionDestinationConfig_basic(rName, tenantID, serviceAccountToken string) string { + return acctest.ConfigCompose(testAccIngestionDestinationConfig_base(rName, tenantID, serviceAccountToken), fmt.Sprintf(` +resource "aws_s3_bucket" "test" { + bucket = %[1]q +} + resource "aws_appfabric_ingestion_destination" "test" { - app_bundle_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633" - ingestion_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633/ingestion/8b7895cf-171a-494c-9abb-7170eaed13b5" + app_bundle_arn = aws_appfabric_app_bundle.test.arn + ingestion_arn = aws_appfabric_ingestion.test.arn processing_configuration { audit_log { format = "json" - schema = "raw" + schema = "raw" } } @@ -248,66 +329,229 @@ resource "aws_appfabric_ingestion_destination" "test" { audit_log { destination { s3_bucket { - bucket_name = "s3-bucket-name" - prefix = "AuditLog" + bucket_name = aws_s3_bucket.test.bucket } } } } } -` +`, rName)) +} + +func testAccIngestionDestinationConfig_s3Prefix(rName, tenantID, serviceAccountToken, prefix string) string { + return acctest.ConfigCompose(testAccIngestionDestinationConfig_base(rName, tenantID, serviceAccountToken), fmt.Sprintf(` +resource "aws_s3_bucket" "test" { + bucket = %[1]q } -func testAccIngestionDestinationConfig_firehose() string { - return ` resource "aws_appfabric_ingestion_destination" "test" { - app_bundle_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633" - ingestion_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633/ingestion/8b7895cf-171a-494c-9abb-7170eaed13b5" + app_bundle_arn = aws_appfabric_app_bundle.test.arn + ingestion_arn = aws_appfabric_ingestion.test.arn + processing_configuration { - audit_log { - format = "json" - schema = "ocsf" - } + audit_log { + format = "json" + schema = "raw" + } } + destination_configuration { audit_log { - destination { - firehose_stream { - stream_name = "OpenSearchStack-FirehoseStream-bL4BiszVyNNC" - } - } + destination { + s3_bucket { + bucket_name = aws_s3_bucket.test.bucket + prefix = %[2]q + } + } + } + } +} +`, rName, prefix)) +} + +func testAccIngestionDestinationConfig_tags1(rName, tenantID, serviceAccountToken, tagKey1, tagValue1 string) string { + return acctest.ConfigCompose(testAccIngestionDestinationConfig_base(rName, tenantID, serviceAccountToken), fmt.Sprintf(` +resource "aws_s3_bucket" "test" { + bucket = %[1]q +} + +resource "aws_appfabric_ingestion_destination" "test" { + app_bundle_arn = aws_appfabric_app_bundle.test.arn + ingestion_arn = aws_appfabric_ingestion.test.arn + + processing_configuration { + audit_log { + format = "json" + schema = "raw" + } + } + + destination_configuration { + audit_log { + destination { + s3_bucket { + bucket_name = aws_s3_bucket.test.bucket + } + } } } + tags = { - environment = "test" + %[2]q = %[3]q } } -` +`, rName, tagKey1, tagValue1)) +} + +func testAccIngestionDestinationConfig_tags2(rName, tenantID, serviceAccountToken, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return acctest.ConfigCompose(testAccIngestionDestinationConfig_base(rName, tenantID, serviceAccountToken), fmt.Sprintf(` +resource "aws_s3_bucket" "test" { + bucket = %[1]q } -func testAccIngestionDestinationConfig_destinationUpdate() string { - return ` resource "aws_appfabric_ingestion_destination" "test" { - app_bundle_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633" - ingestion_identifier = "arn:aws:appfabric:us-east-1:637423205184:appbundle/a9b91477-8831-43c0-970c-95bdc3b06633/ingestion/8b7895cf-171a-494c-9abb-7170eaed13b5" - processing_configuration { - audit_log { - format = "json" - schema = "ocsf" - } - } - destination_configuration { - audit_log { - destination { - firehose_stream { - stream_name = "OpenSearchStack-FirehoseStream-bL4BiszVyNNC" - } - } - } + app_bundle_arn = aws_appfabric_app_bundle.test.arn + ingestion_arn = aws_appfabric_ingestion.test.arn + + processing_configuration { + audit_log { + format = "json" + schema = "raw" + } + } + + destination_configuration { + audit_log { + destination { + s3_bucket { + bucket_name = aws_s3_bucket.test.bucket + } + } + } } + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) +} + +func testAccIngestionDestinationConfig_baseFirehose(rName string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = < Date: Tue, 25 Jun 2024 16:10:12 -0400 Subject: [PATCH 07/12] r/aws_appfabric_ingestion_destination: Documentation. --- ...fabric_ingestion_destination.html.markdown | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/website/docs/r/appfabric_ingestion_destination.html.markdown b/website/docs/r/appfabric_ingestion_destination.html.markdown index 2d3023546e2..b88e49fb0ae 100644 --- a/website/docs/r/appfabric_ingestion_destination.html.markdown +++ b/website/docs/r/appfabric_ingestion_destination.html.markdown @@ -16,40 +16,41 @@ Terraform resource for managing an AWS AppFabric Ingestion Destination. ```terraform resource "aws_appfabric_ingestion_destination" "example" { - app_bundle_identifier = "aws_appfabric_app_bundle.arn" - ingestion_identifier = "aws_appfabric_ingestion.arn" + app_bundle_arn = aws_appfabric_app_bundle.example.arn + ingestion_arn = aws_appfabric_ingestion.example.arn + processing_configuration { - audit_log { - format = "json" - schema = "raw" - } + audit_log { + format = "json" + schema = "raw" + } } + destination_configuration { audit_log { - destination { - s3_bucket { - bucket_name = "examplebucketname" - prefix = "AuditLog" - } - } + destination { + s3_bucket { + bucket_name = aws_s3_bucket.example.bucket + } + } } } } ``` - ## Argument Reference The following arguments are required: -* `app_bundle_identifier` - (Required) The Amazon Resource Name (ARN) or Universal Unique Identifier (UUID) of the app bundle to use for the request. -* `ingestion_identifier` - (Required) The Amazon Resource Name (ARN) or Universal Unique Identifier (UUID) of the ingestion to use for the request. +* `app_bundle_arn` - (Required) The Amazon Resource Name (ARN) of the app bundle to use for the request. +* `ingestion_arn` - (Required) The Amazon Resource Name (ARN) of the ingestion to use for the request. * `destination_configuration` - (Required) Contains information about the destination of ingested data. * `processing_configuration` - (Required) Contains information about how ingested data is processed. +* `tags` - (Optional) Map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. Destination Configuration support the following: -* `audit_log` - (Optional) Contains information about an audit log destination configuration. +* `audit_log` - (Required) Contains information about an audit log destination configuration. Audit Log Destination Configuration support the following: @@ -71,20 +72,19 @@ S3 Bucket support the following: Processing Configuration support the following: -* `audit_log` - (optional) Contains information about an audit log processing configuration. +* `audit_log` - (Required) Contains information about an audit log processing configuration. Audit Log Processing Configuration support the following: -* `format` - (Required) The format in which the audit logs need to be formatted. Valid values: json | parquet -* `schema` - (Required) The event schema in which the audit logs need to be formatted. Valid values: ocsf | raw +* `format` - (Required) The format in which the audit logs need to be formatted. Valid values: `json`, `parquet`. +* `schema` - (Required) The event schema in which the audit logs need to be formatted. Valid values: `ocsf`, `raw`. ## Attribute Reference This resource exports the following attributes in addition to the arguments above: -* `arn` - ARN of the Ingestion Destination. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. -* `app_bundle_arn` - The Amazon Resource Name (ARN) of the app bundle for the ingestion destination. -* `ingestion_arn` - The Amazon Resource Name (ARN) of the ingestion for the ingestion destination. +* `arn` - ARN of the Ingestion Destination. +* `tags_all` - Map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). ## Timeouts From 795d75e34ec31e69ee83e76a5c034fe22a45a3e8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 25 Jun 2024 16:11:06 -0400 Subject: [PATCH 08/12] Run 'make fix-constants PKG=appfabric'. --- .../appfabric/ingestion_destination.go | 16 +++---- .../appfabric/ingestion_destination_test.go | 48 +++++++++---------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/internal/service/appfabric/ingestion_destination.go b/internal/service/appfabric/ingestion_destination.go index d1c9a457cf2..28637d3484b 100644 --- a/internal/service/appfabric/ingestion_destination.go +++ b/internal/service/appfabric/ingestion_destination.go @@ -100,7 +100,7 @@ func (r *ingestionDestinationResource) Schema(ctx context.Context, request resou }, NestedObject: schema.NestedBlockObject{ Blocks: map[string]schema.Block{ - "destination": schema.ListNestedBlock{ + names.AttrDestination: schema.ListNestedBlock{ CustomType: fwtypes.NewListNestedObjectTypeOf[destinationModel](ctx), Validators: []validator.List{ listvalidator.IsRequired(), @@ -124,20 +124,20 @@ func (r *ingestionDestinationResource) Schema(ctx context.Context, request resou }, }, }, - "s3_bucket": schema.ListNestedBlock{ + names.AttrS3Bucket: schema.ListNestedBlock{ CustomType: fwtypes.NewListNestedObjectTypeOf[s3BucketModel](ctx), Validators: []validator.List{ listvalidator.SizeAtMost(1), }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ - "bucket_name": schema.StringAttribute{ + names.AttrBucketName: schema.StringAttribute{ Required: true, Validators: []validator.String{ stringvalidator.LengthBetween(3, 63), }, }, - "prefix": schema.StringAttribute{ + names.AttrPrefix: schema.StringAttribute{ Optional: true, Validators: []validator.String{ stringvalidator.LengthBetween(1, 120), @@ -177,14 +177,14 @@ func (r *ingestionDestinationResource) Schema(ctx context.Context, request resou }, NestedObject: schema.NestedBlockObject{ Attributes: map[string]schema.Attribute{ - "format": schema.StringAttribute{ + names.AttrFormat: schema.StringAttribute{ CustomType: fwtypes.StringEnumType[awstypes.Format](), Required: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }, }, - "schema": schema.StringAttribute{ + names.AttrSchema: schema.StringAttribute{ CustomType: fwtypes.StringEnumType[awstypes.Schema](), Required: true, PlanModifiers: []planmodifier.String{ @@ -437,8 +437,8 @@ func (r *ingestionDestinationResource) Delete(ctx context.Context, request resou func (r *ingestionDestinationResource) ConfigValidators(context.Context) []resource.ConfigValidator { return []resource.ConfigValidator{ resourcevalidator.AtLeastOneOf( - path.MatchRoot("destination_configuration").AtListIndex(0).AtName("audit_log").AtListIndex(0).AtName("destination").AtListIndex(0).AtName("firehose_stream"), - path.MatchRoot("destination_configuration").AtListIndex(0).AtName("audit_log").AtListIndex(0).AtName("destination").AtListIndex(0).AtName("s3_bucket"), + path.MatchRoot("destination_configuration").AtListIndex(0).AtName("audit_log").AtListIndex(0).AtName(names.AttrDestination).AtListIndex(0).AtName("firehose_stream"), + path.MatchRoot("destination_configuration").AtListIndex(0).AtName("audit_log").AtListIndex(0).AtName(names.AttrDestination).AtListIndex(0).AtName(names.AttrS3Bucket), ), } } diff --git a/internal/service/appfabric/ingestion_destination_test.go b/internal/service/appfabric/ingestion_destination_test.go index 2eed288c135..202a24f9de2 100644 --- a/internal/service/appfabric/ingestion_destination_test.go +++ b/internal/service/appfabric/ingestion_destination_test.go @@ -44,19 +44,19 @@ func testAccIngestionDestination_basic(t *testing.T) { testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), resource.TestCheckResourceAttrSet(resourceName, "app_bundle_arn"), resource.TestCheckResourceAttrSet(resourceName, names.AttrARN), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "0"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", rName), resource.TestCheckNoResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.prefix"), resource.TestCheckResourceAttrSet(resourceName, "ingestion_arn"), - resource.TestCheckResourceAttr(resourceName, "processing_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "processing_configuration.0.audit_log.#", "1"), - resource.TestCheckResourceAttr(resourceName, "processing_configuration.0.audit_log.0.format", "json"), + resource.TestCheckResourceAttr(resourceName, "processing_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "processing_configuration.0.audit_log.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "processing_configuration.0.audit_log.0.format", names.AttrJSON), resource.TestCheckResourceAttr(resourceName, "processing_configuration.0.audit_log.0.schema", "raw"), - resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, acctest.CtTagsPercent, acctest.Ct0), ), }, { @@ -175,11 +175,11 @@ func testAccIngestionDestination_update(t *testing.T) { Config: testAccIngestionDestinationConfig_basic(rName, tenantID, serviceAccountToken), Check: resource.ComposeTestCheckFunc( testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "0"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", rName), resource.TestCheckNoResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.prefix"), ), @@ -193,11 +193,11 @@ func testAccIngestionDestination_update(t *testing.T) { Config: testAccIngestionDestinationConfig_s3Prefix(rName, tenantID, serviceAccountToken, "testing"), Check: resource.ComposeTestCheckFunc( testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "0"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", acctest.Ct1), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.bucket_name", rName), resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.0.prefix", "testing"), ), @@ -229,12 +229,12 @@ func testAccIngestionDestination_firehose(t *testing.T) { Config: testAccIngestionDestinationConfig_firehose(rName, tenantID, serviceAccountToken), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckIngestionDestinationExists(ctx, resourceName, &ingestiondestination), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", "1"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", "1"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.#", acctest.Ct1), resource.TestCheckResourceAttrSet(resourceName, "destination_configuration.0.audit_log.0.destination.0.firehose_stream.0.stream_name"), - resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", "0"), + resource.TestCheckResourceAttr(resourceName, "destination_configuration.0.audit_log.0.destination.0.s3_bucket.#", acctest.Ct0), ), }, { From f6f69211f8c01313967437dd4e65c5be62ffadfd Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 25 Jun 2024 16:13:26 -0400 Subject: [PATCH 09/12] Fix golangci-lint 'unparam'. --- internal/service/appfabric/ingestion_destination.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/appfabric/ingestion_destination.go b/internal/service/appfabric/ingestion_destination.go index 28637d3484b..a2b6c366c88 100644 --- a/internal/service/appfabric/ingestion_destination.go +++ b/internal/service/appfabric/ingestion_destination.go @@ -490,7 +490,7 @@ func statusIngestionDestination(ctx context.Context, conn *appfabric.Client, app } } -func waitIngestionDestinationActive(ctx context.Context, conn *appfabric.Client, appBundleARN, ingestionARN, arn string, timeout time.Duration) (*awstypes.IngestionDestination, error) { +func waitIngestionDestinationActive(ctx context.Context, conn *appfabric.Client, appBundleARN, ingestionARN, arn string, timeout time.Duration) (*awstypes.IngestionDestination, error) { //nolint:unparam stateConf := &retry.StateChangeConf{ Pending: []string{}, Target: enum.Slice(awstypes.IngestionDestinationStatusActive), From 0f01df73f6f8a150facb6abe2c918fafdf715861 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 25 Jun 2024 16:15:13 -0400 Subject: [PATCH 10/12] Fix markdownlint 'MD009/no-trailing-spaces Trailing spaces [Expected: 0 or 2; Actual: 1]'. --- website/docs/r/appfabric_ingestion_destination.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/appfabric_ingestion_destination.html.markdown b/website/docs/r/appfabric_ingestion_destination.html.markdown index b88e49fb0ae..e8fc5057a60 100644 --- a/website/docs/r/appfabric_ingestion_destination.html.markdown +++ b/website/docs/r/appfabric_ingestion_destination.html.markdown @@ -54,7 +54,7 @@ Destination Configuration support the following: Audit Log Destination Configuration support the following: -* `destination` - (Required) Contains information about an audit log destination. Only one destination (Firehose Stream) or (S3 Bucket) can be specified. +* `destination` - (Required) Contains information about an audit log destination. Only one destination (Firehose Stream) or (S3 Bucket) can be specified. Destination support the following: From 804bb91ed9ef9aa0528e3d2b511f7989f8065140 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 25 Jun 2024 16:15:49 -0400 Subject: [PATCH 11/12] Fix markdownlint 'MD047/single-trailing-newline Files should end with a single newline character'. --- website/docs/r/appfabric_ingestion_destination.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/appfabric_ingestion_destination.html.markdown b/website/docs/r/appfabric_ingestion_destination.html.markdown index e8fc5057a60..43cbc88c3bc 100644 --- a/website/docs/r/appfabric_ingestion_destination.html.markdown +++ b/website/docs/r/appfabric_ingestion_destination.html.markdown @@ -92,4 +92,4 @@ This resource exports the following attributes in addition to the arguments abov * `create` - (Default `5m`) * `update` - (Default `5m`) -* `delete` - (Default `5m`) \ No newline at end of file +* `delete` - (Default `5m`) From e05f2437d8473eb0d6a07ffec69d74cbc31d3d66 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 25 Jun 2024 16:29:18 -0400 Subject: [PATCH 12/12] Fix 'testAccIngestionDestination_firehose'. --- .../service/appfabric/ingestion_destination_test.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/internal/service/appfabric/ingestion_destination_test.go b/internal/service/appfabric/ingestion_destination_test.go index 202a24f9de2..bcaa7c114d3 100644 --- a/internal/service/appfabric/ingestion_destination_test.go +++ b/internal/service/appfabric/ingestion_destination_test.go @@ -522,6 +522,17 @@ resource "aws_kinesis_firehose_delivery_stream" "test" { role_arn = aws_iam_role.test.arn bucket_arn = aws_s3_bucket.test.arn } + + tags = { + AWSAppFabricManaged = "placeholder" + } + + lifecycle { + ignore_changes = [ + # Ignore changes to AWSAppFabricManaged tag as API adds this tag when ingestion destination is created + tags["AWSAppFabricManaged"], + ] + } } `, rName) }