diff --git a/.changelog/32740.txt b/.changelog/32740.txt new file mode 100644 index 00000000000..f1c96526b1b --- /dev/null +++ b/.changelog/32740.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +data-source/aws_db_instances: Add ability to filter by `tags` +``` + +```release-note:enhancement +data-source/aws_db_instance: Add ability to filter by `tags` +``` \ No newline at end of file diff --git a/internal/service/rds/instance.go b/internal/service/rds/instance.go index 482e644333b..45afa19e7f0 100644 --- a/internal/service/rds/instance.go +++ b/internal/service/rds/instance.go @@ -30,6 +30,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -2368,9 +2369,10 @@ func parseDBInstanceARN(s string) (dbInstanceARN, error) { // "db-BE6UI2KLPQP3OVDYD74ZEV6NUM" rather than a DB identifier. However, in some cases only // the identifier is available, and can be used. func findDBInstanceByIDSDKv1(ctx context.Context, conn *rds.RDS, id string) (*rds.DBInstance, error) { + idLooksLikeDbiResourceId := regexp.MustCompile(`^db-[a-zA-Z0-9]{2,255}$`).MatchString(id) input := &rds.DescribeDBInstancesInput{} - if regexp.MustCompile(`^db-[a-zA-Z0-9]{2,255}$`).MatchString(id) { + if idLooksLikeDbiResourceId { input.Filters = []*rds.Filter{ { Name: aws.String("dbi-resource-id"), @@ -2381,16 +2383,51 @@ func findDBInstanceByIDSDKv1(ctx context.Context, conn *rds.RDS, id string) (*rd input.DBInstanceIdentifier = aws.String(id) } - output, err := conn.DescribeDBInstancesWithContext(ctx, input) + output, err := findDBInstanceSDKv1(ctx, conn, input) // in case a DB has an *identifier* starting with "db-"" - if regexp.MustCompile(`^db-[a-zA-Z0-9]{2,255}$`).MatchString(id) && (output == nil || len(output.DBInstances) == 0) { - input = &rds.DescribeDBInstancesInput{ + if idLooksLikeDbiResourceId && tfresource.NotFound(err) { + input := &rds.DescribeDBInstancesInput{ DBInstanceIdentifier: aws.String(id), } - output, err = conn.DescribeDBInstancesWithContext(ctx, input) + + output, err = findDBInstanceSDKv1(ctx, conn, input) + } + + if err != nil { + return nil, err + } + + return output, nil +} + +func findDBInstanceSDKv1(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBInstancesInput) (*rds.DBInstance, error) { + output, err := findDBInstancesSDKv1(ctx, conn, input, tfslices.PredicateTrue[*rds.DBInstance]()) + + if err != nil { + return nil, err } + return tfresource.AssertSinglePtrResult(output) +} + +func findDBInstancesSDKv1(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBInstancesInput, filter tfslices.Predicate[*rds.DBInstance]) ([]*rds.DBInstance, error) { + var output []*rds.DBInstance + + err := conn.DescribeDBInstancesPagesWithContext(ctx, input, func(page *rds.DescribeDBInstancesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.DBInstances { + if v != nil && filter(v) { + output = append(output, v) + } + } + + return !lastPage + }) + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBInstanceNotFoundFault) { return nil, &retry.NotFoundError{ LastError: err, @@ -2402,11 +2439,7 @@ func findDBInstanceByIDSDKv1(ctx context.Context, conn *rds.RDS, id string) (*rd return nil, err } - if output == nil { - return nil, tfresource.NewEmptyResultError(input) - } - - return tfresource.AssertSinglePtrResult(output.DBInstances) + return output, nil } // findDBInstanceByIDSDKv2 in general should be called with a DbiResourceId of the form diff --git a/internal/service/rds/instance_data_source.go b/internal/service/rds/instance_data_source.go index 97eeb472ae2..500871a8f4e 100644 --- a/internal/service/rds/instance_data_source.go +++ b/internal/service/rds/instance_data_source.go @@ -8,16 +8,19 @@ import ( "fmt" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/rds" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_db_instance") +// @SDKDataSource("aws_db_instance", name="DB Instance") +// @Tags func DataSourceInstance() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceInstanceRead, @@ -60,9 +63,9 @@ func DataSourceInstance() *schema.Resource { Computed: true, }, "db_instance_identifier": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, + Type: schema.TypeString, + Optional: true, + Computed: true, }, "db_instance_port": { Type: schema.TypeInt, @@ -199,7 +202,7 @@ func DataSourceInstance() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "tags": tftags.TagsSchemaComputed(), + names.AttrTags: tftags.TagsSchemaComputed(), "timezone": { Type: schema.TypeString, Computed: true, @@ -216,74 +219,104 @@ func DataSourceInstance() *schema.Resource { func dataSourceInstanceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).RDSConn(ctx) - ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - v, err := findDBInstanceByIDSDKv1(ctx, conn, d.Get("db_instance_identifier").(string)) - if err != nil { - return diag.FromErr(tfresource.SingularDataSourceFindError("RDS DB Instance", err)) + var instance *rds.DBInstance + + filter := tfslices.PredicateTrue[*rds.DBInstance]() + if tags := getTagsIn(ctx); len(tags) > 0 { + filter = func(v *rds.DBInstance) bool { + return KeyValueTags(ctx, v.TagList).ContainsAll(KeyValueTags(ctx, tags)) + } } - d.SetId(aws.StringValue(v.DBInstanceIdentifier)) - d.Set("allocated_storage", v.AllocatedStorage) - d.Set("auto_minor_version_upgrade", v.AutoMinorVersionUpgrade) - d.Set("availability_zone", v.AvailabilityZone) - d.Set("backup_retention_period", v.BackupRetentionPeriod) - d.Set("ca_cert_identifier", v.CACertificateIdentifier) - d.Set("db_cluster_identifier", v.DBClusterIdentifier) - d.Set("db_instance_arn", v.DBInstanceArn) - d.Set("db_instance_class", v.DBInstanceClass) - d.Set("db_instance_port", v.DbInstancePort) - d.Set("db_name", v.DBName) - var parameterGroupNames []string - for _, v := range v.DBParameterGroups { - parameterGroupNames = append(parameterGroupNames, aws.StringValue(v.DBParameterGroupName)) + if v, ok := d.GetOk("db_instance_identifier"); ok { + id := v.(string) + output, err := findDBInstanceByIDSDKv1(ctx, conn, id) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading RDS DB Instance (%s): %s", id, err) + } + + if !filter(output) { + return sdkdiag.AppendErrorf(diags, "Your query returned no results. Please change your search criteria and try again.") + } + + instance = output + } else { + input := &rds.DescribeDBInstancesInput{} + instances, err := findDBInstancesSDKv1(ctx, conn, input, filter) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading RDS DB Instances: %s", err) + } + + output, err := tfresource.AssertSinglePtrResult(instances) + + if err != nil { + return sdkdiag.AppendFromErr(diags, tfresource.SingularDataSourceFindError("RDS DB Instance", err)) + } + + instance = output } + + d.SetId(aws.StringValue(instance.DBInstanceIdentifier)) + d.Set("allocated_storage", instance.AllocatedStorage) + d.Set("auto_minor_version_upgrade", instance.AutoMinorVersionUpgrade) + d.Set("availability_zone", instance.AvailabilityZone) + d.Set("backup_retention_period", instance.BackupRetentionPeriod) + d.Set("ca_cert_identifier", instance.CACertificateIdentifier) + d.Set("db_cluster_identifier", instance.DBClusterIdentifier) + d.Set("db_instance_arn", instance.DBInstanceArn) + d.Set("db_instance_class", instance.DBInstanceClass) + d.Set("db_instance_port", instance.DbInstancePort) + d.Set("db_name", instance.DBName) + parameterGroupNames := tfslices.ApplyToAll(instance.DBParameterGroups, func(v *rds.DBParameterGroupStatus) string { + return aws.StringValue(v.DBParameterGroupName) + }) d.Set("db_parameter_groups", parameterGroupNames) - if v.DBSubnetGroup != nil { - d.Set("db_subnet_group", v.DBSubnetGroup.DBSubnetGroupName) + if instance.DBSubnetGroup != nil { + d.Set("db_subnet_group", instance.DBSubnetGroup.DBSubnetGroupName) } else { d.Set("db_subnet_group", "") } - d.Set("enabled_cloudwatch_logs_exports", aws.StringValueSlice(v.EnabledCloudwatchLogsExports)) - d.Set("engine", v.Engine) - d.Set("engine_version", v.EngineVersion) - d.Set("iops", v.Iops) - d.Set("kms_key_id", v.KmsKeyId) - d.Set("license_model", v.LicenseModel) - d.Set("master_username", v.MasterUsername) - if v.MasterUserSecret != nil { - if err := d.Set("master_user_secret", []interface{}{flattenManagedMasterUserSecret(v.MasterUserSecret)}); err != nil { + d.Set("enabled_cloudwatch_logs_exports", aws.StringValueSlice(instance.EnabledCloudwatchLogsExports)) + d.Set("engine", instance.Engine) + d.Set("engine_version", instance.EngineVersion) + d.Set("iops", instance.Iops) + d.Set("kms_key_id", instance.KmsKeyId) + d.Set("license_model", instance.LicenseModel) + d.Set("master_username", instance.MasterUsername) + if instance.MasterUserSecret != nil { + if err := d.Set("master_user_secret", []interface{}{flattenManagedMasterUserSecret(instance.MasterUserSecret)}); err != nil { return sdkdiag.AppendErrorf(diags, "setting master_user_secret: %s", err) } } - d.Set("max_allocated_storage", v.MaxAllocatedStorage) - d.Set("monitoring_interval", v.MonitoringInterval) - d.Set("monitoring_role_arn", v.MonitoringRoleArn) - d.Set("multi_az", v.MultiAZ) - d.Set("network_type", v.NetworkType) - var optionGroupNames []string - for _, v := range v.OptionGroupMemberships { - optionGroupNames = append(optionGroupNames, aws.StringValue(v.OptionGroupName)) - } + d.Set("max_allocated_storage", instance.MaxAllocatedStorage) + d.Set("monitoring_interval", instance.MonitoringInterval) + d.Set("monitoring_role_arn", instance.MonitoringRoleArn) + d.Set("multi_az", instance.MultiAZ) + d.Set("network_type", instance.NetworkType) + optionGroupNames := tfslices.ApplyToAll(instance.OptionGroupMemberships, func(v *rds.OptionGroupMembership) string { + return aws.StringValue(v.OptionGroupName) + }) d.Set("option_group_memberships", optionGroupNames) - d.Set("preferred_backup_window", v.PreferredBackupWindow) - d.Set("preferred_maintenance_window", v.PreferredMaintenanceWindow) - d.Set("publicly_accessible", v.PubliclyAccessible) - d.Set("replicate_source_db", v.ReadReplicaSourceDBInstanceIdentifier) - d.Set("resource_id", v.DbiResourceId) - d.Set("storage_encrypted", v.StorageEncrypted) - d.Set("storage_throughput", v.StorageThroughput) - d.Set("storage_type", v.StorageType) - d.Set("timezone", v.Timezone) - var vpcSecurityGroupIDs []string - for _, v := range v.VpcSecurityGroups { - vpcSecurityGroupIDs = append(vpcSecurityGroupIDs, aws.StringValue(v.VpcSecurityGroupId)) - } + d.Set("preferred_backup_window", instance.PreferredBackupWindow) + d.Set("preferred_maintenance_window", instance.PreferredMaintenanceWindow) + d.Set("publicly_accessible", instance.PubliclyAccessible) + d.Set("replicate_source_db", instance.ReadReplicaSourceDBInstanceIdentifier) + d.Set("resource_id", instance.DbiResourceId) + d.Set("storage_encrypted", instance.StorageEncrypted) + d.Set("storage_throughput", instance.StorageThroughput) + d.Set("storage_type", instance.StorageType) + d.Set("timezone", instance.Timezone) + vpcSecurityGroupIDs := tfslices.ApplyToAll(instance.VpcSecurityGroups, func(v *rds.VpcSecurityGroupMembership) string { + return aws.StringValue(v.VpcSecurityGroupId) + }) d.Set("vpc_security_groups", vpcSecurityGroupIDs) // Per AWS SDK Go docs: // The endpoint might not be shown for instances whose status is creating. - if dbEndpoint := v.Endpoint; dbEndpoint != nil { + if dbEndpoint := instance.Endpoint; dbEndpoint != nil { d.Set("address", dbEndpoint.Address) d.Set("endpoint", fmt.Sprintf("%s:%d", aws.StringValue(dbEndpoint.Address), aws.Int64Value(dbEndpoint.Port))) d.Set("hosted_zone_id", dbEndpoint.HostedZoneId) @@ -295,11 +328,7 @@ func dataSourceInstanceRead(ctx context.Context, d *schema.ResourceData, meta in d.Set("port", nil) } - tags := KeyValueTags(ctx, v.TagList) - - if err := d.Set("tags", tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return sdkdiag.AppendErrorf(diags, "setting tags: %s", err) - } + setTagsOut(ctx, instance.TagList) return diags } diff --git a/internal/service/rds/instance_data_source_test.go b/internal/service/rds/instance_data_source_test.go index bada70ef351..c2b217b8153 100644 --- a/internal/service/rds/instance_data_source_test.go +++ b/internal/service/rds/instance_data_source_test.go @@ -104,6 +104,51 @@ func TestAccRDSInstanceDataSource_ManagedMasterPassword_managed(t *testing.T) { }) } +func TestAccRDSInstanceDataSource_tags(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceName := "data.aws_db_instance.test" + resourceName := "aws_db_instance.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccInstanceDataSourceConfig_tags(rName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, "address", resourceName, "address"), + resource.TestCheckResourceAttrPair(dataSourceName, "allocated_storage", resourceName, "allocated_storage"), + resource.TestCheckResourceAttrPair(dataSourceName, "auto_minor_version_upgrade", resourceName, "auto_minor_version_upgrade"), + resource.TestCheckResourceAttrPair(dataSourceName, "db_instance_arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(dataSourceName, "db_instance_class", resourceName, "instance_class"), + resource.TestCheckResourceAttrPair(dataSourceName, "db_name", resourceName, "db_name"), + resource.TestCheckResourceAttrPair(dataSourceName, "db_subnet_group", resourceName, "db_subnet_group_name"), + resource.TestCheckResourceAttrPair(dataSourceName, "enabled_cloudwatch_logs_exports.#", resourceName, "enabled_cloudwatch_logs_exports.#"), + resource.TestCheckResourceAttrPair(dataSourceName, "endpoint", resourceName, "endpoint"), + resource.TestCheckResourceAttrPair(dataSourceName, "engine", resourceName, "engine"), + resource.TestCheckResourceAttrPair(dataSourceName, "hosted_zone_id", resourceName, "hosted_zone_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "iops", resourceName, "iops"), + resource.TestCheckResourceAttrPair(dataSourceName, "master_username", resourceName, "username"), + resource.TestCheckResourceAttrPair(dataSourceName, "max_allocated_storage", resourceName, "max_allocated_storage"), + resource.TestCheckResourceAttrPair(dataSourceName, "multi_az", resourceName, "multi_az"), + resource.TestCheckResourceAttrPair(dataSourceName, "network_type", resourceName, "network_type"), + resource.TestCheckResourceAttrPair(dataSourceName, "port", resourceName, "port"), + resource.TestCheckResourceAttrPair(dataSourceName, "resource_id", resourceName, "resource_id"), + resource.TestCheckResourceAttrPair(dataSourceName, "storage_throughput", resourceName, "storage_throughput"), + resource.TestCheckResourceAttrPair(dataSourceName, "storage_type", resourceName, "storage_type"), + resource.TestCheckResourceAttrPair(dataSourceName, "tags.%", resourceName, "tags.%"), + ), + }, + }, + }) +} + func testAccInstanceDataSourceConfig_basic(rName string) string { return acctest.ConfigCompose( testAccInstanceConfig_orderableClassMariadb(), @@ -167,3 +212,42 @@ data "aws_db_instance" "test" { } `, rName)) } + +func testAccInstanceDataSourceConfig_tags(rName string) string { + return acctest.ConfigCompose( + testAccInstanceConfig_orderableClassMariadb(), + testAccInstanceConfig_baseVPC(rName), + fmt.Sprintf(` +resource "aws_db_instance" "test" { + allocated_storage = 10 + backup_retention_period = 0 + db_subnet_group_name = aws_db_subnet_group.test.name + engine = data.aws_rds_engine_version.default.engine + engine_version = data.aws_rds_engine_version.default.version + identifier = %[1]q + instance_class = data.aws_rds_orderable_db_instance.test.instance_class + db_name = "test" + password = "avoid-plaintext-passwords" + skip_final_snapshot = true + username = "tfacctest" + max_allocated_storage = 100 + + enabled_cloudwatch_logs_exports = [ + "audit", + "error", + ] + + tags = { + Name = %[1]q + } +} + +data "aws_db_instance" "test" { + tags = { + Name = %[1]q + } + + depends_on = [aws_db_instance.test] +} +`, rName)) +} diff --git a/internal/service/rds/instances_data_source.go b/internal/service/rds/instances_data_source.go index facd89ab2fa..96aec2101df 100644 --- a/internal/service/rds/instances_data_source.go +++ b/internal/service/rds/instances_data_source.go @@ -11,9 +11,10 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/generate/namevaluesfilters" - "github.com/hashicorp/terraform-provider-aws/names" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" ) // @SDKDataSource("aws_db_instances") @@ -22,6 +23,7 @@ func DataSourceInstances() *schema.Resource { ReadWithoutTimeout: dataSourceInstancesRead, Schema: map[string]*schema.Schema{ + "filter": namevaluesfilters.Schema(), "instance_arns": { Type: schema.TypeList, Computed: true, @@ -32,16 +34,13 @@ func DataSourceInstances() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "filter": namevaluesfilters.Schema(), + "tags": tftags.TagsSchemaComputed(), }, } } -const ( - DSNameInstances = "Instances Data Source" -) - func dataSourceInstancesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).RDSConn(ctx) input := &rds.DescribeDBInstancesInput{} @@ -50,32 +49,30 @@ func dataSourceInstancesRead(ctx context.Context, d *schema.ResourceData, meta i input.Filters = namevaluesfilters.New(v.(*schema.Set)).RDSFilters() } - var instanceARNS []string - var instanceIdentifiers []string - - err := conn.DescribeDBInstancesPagesWithContext(ctx, input, func(page *rds.DescribeDBInstancesOutput, lastPage bool) bool { - if page == nil { - return !lastPage + filter := tfslices.PredicateTrue[*rds.DBInstance]() + if v, ok := d.GetOk("tags"); ok { + filter = func(x *rds.DBInstance) bool { + return KeyValueTags(ctx, x.TagList).ContainsAll(tftags.New(ctx, v.(map[string]interface{}))) } + } - for _, dbInstance := range page.DBInstances { - if dbInstance == nil { - continue - } - - instanceARNS = append(instanceARNS, aws.StringValue(dbInstance.DBInstanceArn)) - instanceIdentifiers = append(instanceIdentifiers, aws.StringValue(dbInstance.DBInstanceIdentifier)) - } + instances, err := findDBInstancesSDKv1(ctx, conn, input, filter) - return !lastPage - }) if err != nil { - return create.DiagError(names.RDS, create.ErrActionReading, DSNameInstances, "", err) + return sdkdiag.AppendErrorf(diags, "reading RDS DB Instances: %s", err) + } + + var instanceARNS []string + var instanceIdentifiers []string + + for _, instance := range instances { + instanceARNS = append(instanceARNS, aws.StringValue(instance.DBInstanceArn)) + instanceIdentifiers = append(instanceIdentifiers, aws.StringValue(instance.DBInstanceIdentifier)) } d.SetId(meta.(*conns.AWSClient).Region) d.Set("instance_arns", instanceARNS) d.Set("instance_identifiers", instanceIdentifiers) - return nil + return diags } diff --git a/internal/service/rds/instances_data_source_test.go b/internal/service/rds/instances_data_source_test.go index cec0a8faad6..b95684c8f28 100644 --- a/internal/service/rds/instances_data_source_test.go +++ b/internal/service/rds/instances_data_source_test.go @@ -40,6 +40,33 @@ func TestAccRDSInstancesDataSource_filter(t *testing.T) { }) } +func TestAccRDSInstancesDataSource_tags(t *testing.T) { + ctx := acctest.Context(t) + var dbInstance rds.DBInstance + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceName := "data.aws_db_instances.test" + resourceName := "aws_db_instance.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckInstanceDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccInstancesDataSourceConfig_tags(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists(ctx, resourceName, &dbInstance), + resource.TestCheckResourceAttr(dataSourceName, "instance_arns.#", "1"), + resource.TestCheckResourceAttrPair(dataSourceName, "instance_arns.0", resourceName, "arn"), + resource.TestCheckResourceAttr(dataSourceName, "instance_identifiers.#", "1"), + resource.TestCheckResourceAttrPair(dataSourceName, "instance_identifiers.0", resourceName, "identifier"), + ), + }, + }, + }) +} + func testAccInstancesDataSourceConfig_filter(rName string) string { return fmt.Sprintf(` data "aws_rds_engine_version" "default" { @@ -62,7 +89,7 @@ resource "aws_db_instance" "test" { } resource "aws_db_instance" "wrong" { - identifier = "wrong-%[1]s" + identifier = "%[1]s-wrong" allocated_storage = 10 engine = data.aws_rds_engine_version.default.engine engine_version = data.aws_rds_engine_version.default.version @@ -82,6 +109,64 @@ data "aws_db_instances" "test" { name = "db-instance-id" values = [aws_db_instance.test.identifier] } + + depends_on = [aws_db_instance.wrong] +} +`, rName) +} + +func testAccInstancesDataSourceConfig_tags(rName string) string { + return fmt.Sprintf(` +data "aws_rds_engine_version" "default" { + engine = "postgres" +} + +resource "aws_db_instance" "test" { + identifier = %[1]q + allocated_storage = 10 + engine = data.aws_rds_engine_version.default.engine + engine_version = data.aws_rds_engine_version.default.version + instance_class = "db.t4g.micro" + db_name = "test" + password = "avoid-plaintext-passwords" + username = "tfacctest" + parameter_group_name = "default.${data.aws_rds_engine_version.default.parameter_group_family}" + skip_final_snapshot = true + + apply_immediately = true + + tags = { + Name = %[1]q + Test = "true" + } +} + +resource "aws_db_instance" "wrong" { + identifier = "%[1]s-wrong" + allocated_storage = 10 + engine = data.aws_rds_engine_version.default.engine + engine_version = data.aws_rds_engine_version.default.version + instance_class = "db.t4g.micro" + db_name = "test" + password = "avoid-plaintext-passwords" + username = "tfacctest" + parameter_group_name = "default.${data.aws_rds_engine_version.default.parameter_group_family}" + skip_final_snapshot = true + + apply_immediately = true + + tags = { + Name = "%[1]s-wrong" + Test = "true" + } +} + +data "aws_db_instances" "test" { + tags = { + Name = %[1]q + } + + depends_on = [aws_db_instance.test, aws_db_instance.wrong] } `, rName) } diff --git a/internal/service/rds/service_package_gen.go b/internal/service/rds/service_package_gen.go index a8589234c25..e66ec36d2c0 100644 --- a/internal/service/rds/service_package_gen.go +++ b/internal/service/rds/service_package_gen.go @@ -44,6 +44,8 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac { Factory: DataSourceInstance, TypeName: "aws_db_instance", + Name: "DB Instance", + Tags: &types.ServicePackageResourceTags{}, }, { Factory: DataSourceInstances, diff --git a/website/docs/d/db_instance.html.markdown b/website/docs/d/db_instance.html.markdown index 4cf6f9604b7..ade4a86fbb6 100644 --- a/website/docs/d/db_instance.html.markdown +++ b/website/docs/d/db_instance.html.markdown @@ -22,7 +22,8 @@ data "aws_db_instance" "database" { This data source supports the following arguments: -* `db_instance_identifier` - (Required) Name of the RDS instance +* `db_instance_identifier` - (Optional) Name of the RDS instance. +* `tags` - (Optional) Map of tags, each pair of which must exactly match a pair on the desired instance. ## Attribute Reference diff --git a/website/docs/d/db_instances.html.markdown b/website/docs/d/db_instances.html.markdown index cad4522b360..3bd004db4bc 100644 --- a/website/docs/d/db_instances.html.markdown +++ b/website/docs/d/db_instances.html.markdown @@ -23,17 +23,28 @@ data "aws_db_instances" "example" { } ``` +### Using tags + +```terraform +data "aws_db_instances" "example" { + tags = { + Env = "test" + } +} +``` + ## Argument Reference The following arguments are optional: -* `filter` - (Optional) Configuration block(s) for filtering. Detailed below. +* `filter` - (Optional) Configuration block(s) used to filter instances with AWS supported attributes, such as `engine`, `db-cluster-id` or `db-instance-id` for example. Detailed below. +* `tags` - (Optional) Map of tags, each pair of which must exactly match a pair on the desired instances. ### filter Configuration block The `filter` configuration block supports the following arguments: -* `name` - (Required) Name of the filter field. Valid values can be found in the [RDS DescribeDBClusters API Reference](https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DescribeDBClusters.html). +* `name` - (Required) Name of the filter field. Valid values can be found in the [RDS DescribeDBClusters API Reference](https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DescribeDBClusters.html) or [RDS DescribeDBInstances API Reference](https://docs.aws.amazon.com/AmazonRDS/latest/APIReference/API_DescribeDBInstances.html). * `values` - (Required) Set of values that are accepted for the given filter field. Results will be selected if any given value matches. ## Attribute Reference