diff --git a/.changelog/37566.txt b/.changelog/37566.txt new file mode 100644 index 000000000000..9b2be6e05f3a --- /dev/null +++ b/.changelog/37566.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_appflow_flow: Add `prefix_hierarchy` attribute to `destination_flow_config.s3.s3_output_format_config` +``` + +```release-note:enhancement +resource/aws_appflow_flow: Add `metadata_catalog_config` attribute +``` \ No newline at end of file diff --git a/internal/service/appflow/flow.go b/internal/service/appflow/flow.go index 61470f265438..1766f8238cba 100644 --- a/internal/service/appflow/flow.go +++ b/internal/service/appflow/flow.go @@ -395,6 +395,15 @@ func resourceFlow() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "prefix_hierarchy": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: enum.Validate[types.PathPrefix](), + }, + }, "prefix_format": { Type: schema.TypeString, Optional: true, @@ -632,6 +641,15 @@ func resourceFlow() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "prefix_hierarchy": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: enum.Validate[types.PathPrefix](), + }, + }, "prefix_format": { Type: schema.TypeString, Optional: true, @@ -1259,6 +1277,38 @@ func resourceFlow() *schema.Resource { }, }, }, + "metadata_catalog_config": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "glue_data_catalog": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + names.AttrDatabaseName: { + Type: schema.TypeString, + Required: true, + }, + names.AttrRoleARN: { + Type: schema.TypeString, + Required: true, + ValidateDiagFunc: validation.ToDiagFunc(verify.ValidARN), + }, + "table_prefix": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + }, + }, }, CustomizeDiff: verify.SetTagsDiff, @@ -1280,6 +1330,10 @@ func resourceFlowCreate(ctx context.Context, d *schema.ResourceData, meta interf TriggerConfig: expandTriggerConfig(d.Get("trigger_config").([]interface{})[0].(map[string]interface{})), } + if v, ok := d.GetOk("metadata_catalog_config"); ok { + input.MetadataCatalogConfig = expandMetadataCatalogConfig(v.([]any)) + } + if v, ok := d.GetOk(names.AttrDescription); ok { input.Description = aws.String(v.(string)) } @@ -1348,6 +1402,14 @@ func resourceFlowRead(ctx context.Context, d *schema.ResourceData, meta interfac d.Set("trigger_config", nil) } + if output.MetadataCatalogConfig != nil { + if err := d.Set("metadata_catalog_config", flattenMetadataCatalogConfig(output.MetadataCatalogConfig)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting metadata_catalog_config: %s", err) + } + } else { + d.Set("metadata_catalog_config", nil) + } + setTagsOut(ctx, output.Tags) return diags @@ -1367,6 +1429,10 @@ func resourceFlowUpdate(ctx context.Context, d *schema.ResourceData, meta interf TriggerConfig: expandTriggerConfig(d.Get("trigger_config").([]interface{})[0].(map[string]interface{})), } + if v, ok := d.GetOk("metadata_catalog_config"); ok { + input.MetadataCatalogConfig = expandMetadataCatalogConfig(v.([]any)) + } + // always send description when updating a task if v, ok := d.GetOk(names.AttrDescription); ok { input.Description = aws.String(v.(string)) @@ -1529,6 +1595,10 @@ func expandPrefixConfig(tfMap map[string]interface{}) *types.PrefixConfig { a.PrefixType = types.PrefixType(v) } + if v, ok := tfMap["prefix_hierarchy"].([]interface{}); ok && len(v) > 0 && v[0] != nil { + a.PathPrefixHierarchy = flex.ExpandStringyValueList[types.PathPrefix](v) + } + return a } @@ -2602,6 +2672,70 @@ func expandScheduledTriggerProperties(tfMap map[string]interface{}) *types.Sched return a } +func expandMetadataCatalogConfig(tfList []any) *types.MetadataCatalogConfig { + if len(tfList) == 0 { + return nil + } + + m := tfList[0].(map[string]any) + + a := &types.MetadataCatalogConfig{} + + if v, ok := m["glue_data_catalog"].([]any); ok && len(v) > 0 && v[0] != nil { + a.GlueDataCatalog = expandGlueDataCatalog(v[0].(map[string]any)) + } + + return a +} + +func expandGlueDataCatalog(tfMap map[string]interface{}) *types.GlueDataCatalogConfig { + if tfMap == nil { + return nil + } + + a := &types.GlueDataCatalogConfig{} + + if v, ok := tfMap[names.AttrDatabaseName].(string); ok && v != "" { + a.DatabaseName = aws.String(v) + } + + if v, ok := tfMap[names.AttrRoleARN].(string); ok && v != "" { + a.RoleArn = aws.String(v) + } + + if v, ok := tfMap["table_prefix"].(string); ok && v != "" { + a.TablePrefix = aws.String(v) + } + + return a +} + +func flattenMetadataCatalogConfig(in *types.MetadataCatalogConfig) []any { + if in == nil { + return nil + } + + m := map[string]any{ + "glue_data_catalog": flattenGlueDataCatalog(in.GlueDataCatalog), + } + + return []any{m} +} + +func flattenGlueDataCatalog(in *types.GlueDataCatalogConfig) []any { + if in == nil { + return nil + } + + m := map[string]any{ + names.AttrDatabaseName: in.DatabaseName, + names.AttrRoleARN: in.RoleArn, + "table_prefix": in.TablePrefix, + } + + return []any{m} +} + func flattenErrorHandlingConfig(errorHandlingConfig *types.ErrorHandlingConfig) map[string]interface{} { if errorHandlingConfig == nil { return nil @@ -2631,6 +2765,7 @@ func flattenPrefixConfig(prefixConfig *types.PrefixConfig) map[string]interface{ m["prefix_format"] = prefixConfig.PrefixFormat m["prefix_type"] = prefixConfig.PrefixType + m["prefix_hierarchy"] = flex.FlattenStringyValueList(prefixConfig.PathPrefixHierarchy) return m } @@ -3496,7 +3631,7 @@ func flattenTask(task types.Task) map[string]interface{} { } if v := task.TaskProperties; v != nil { - m["task_properties"] = v + m["task_properties"] = flex.FlattenStringValueMap(v) } m["task_type"] = task.TaskType diff --git a/internal/service/appflow/flow_test.go b/internal/service/appflow/flow_test.go index c74de7226c19..96887f025e7f 100644 --- a/internal/service/appflow/flow_test.go +++ b/internal/service/appflow/flow_test.go @@ -308,6 +308,36 @@ func TestAccAppFlowFlow_disappears(t *testing.T) { }) } +func TestAccAppFlowFlow_metadataCatalog(t *testing.T) { + ctx := acctest.Context(t) + var flowOutput appflow.DescribeFlowOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_appflow_flow.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.AppFlowServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckFlowDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccFlowConfig_metadata_catalog(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFlowExists(ctx, resourceName, &flowOutput), + resource.TestCheckResourceAttr(resourceName, "metadata_catalog_config.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "destination_flow_config.0.destination_connector_properties.0.s3.0.s3_output_format_config.0.prefix_config.0.prefix_hierarchy.0", "SCHEMA_VERSION"), + resource.TestCheckResourceAttr(resourceName, "destination_flow_config.0.destination_connector_properties.0.s3.0.s3_output_format_config.0.prefix_config.0.prefix_hierarchy.1", "EXECUTION_ID"), + resource.TestCheckResourceAttr(resourceName, "destination_flow_config.0.destination_connector_properties.0.s3.0.s3_output_format_config.0.prefix_config.0.prefix_hierarchy.#", acctest.Ct2), + ), + }, + { + Config: testAccFlowConfig_metadata_catalog(rName), + PlanOnly: true, + }, + }, + }) +} + func testAccFlowConfig_base(rName string) string { return fmt.Sprintf(` data "aws_partition" "current" {} @@ -761,6 +791,90 @@ resource "aws_appflow_flow" "test" { ) } +func testAccFlowConfig_metadata_catalog(rName string) string { + return acctest.ConfigCompose( + testAccFlowConfig_base(rName), + fmt.Sprintf(` +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = <