Skip to content

Commit 609022c

Browse files
authored
Merge pull request #32740 from jeremychauvet/b-2988-rds-filter-instances-by-tags
enhancement: add support of tags to filter RDS instances (#2988)
2 parents 170aa7a + 35302ef commit 609022c

9 files changed

+351
-102
lines changed

.changelog/32740.txt

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
```release-note:enhancement
2+
data-source/aws_db_instances: Add ability to filter by `tags`
3+
```
4+
5+
```release-note:enhancement
6+
data-source/aws_db_instance: Add ability to filter by `tags`
7+
```

internal/service/rds/instance.go

+43-10
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/hashicorp/terraform-provider-aws/internal/errs"
3131
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
3232
"github.com/hashicorp/terraform-provider-aws/internal/flex"
33+
tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices"
3334
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
3435
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
3536
"github.com/hashicorp/terraform-provider-aws/internal/verify"
@@ -2368,9 +2369,10 @@ func parseDBInstanceARN(s string) (dbInstanceARN, error) {
23682369
// "db-BE6UI2KLPQP3OVDYD74ZEV6NUM" rather than a DB identifier. However, in some cases only
23692370
// the identifier is available, and can be used.
23702371
func findDBInstanceByIDSDKv1(ctx context.Context, conn *rds.RDS, id string) (*rds.DBInstance, error) {
2372+
idLooksLikeDbiResourceId := regexp.MustCompile(`^db-[a-zA-Z0-9]{2,255}$`).MatchString(id)
23712373
input := &rds.DescribeDBInstancesInput{}
23722374

2373-
if regexp.MustCompile(`^db-[a-zA-Z0-9]{2,255}$`).MatchString(id) {
2375+
if idLooksLikeDbiResourceId {
23742376
input.Filters = []*rds.Filter{
23752377
{
23762378
Name: aws.String("dbi-resource-id"),
@@ -2381,16 +2383,51 @@ func findDBInstanceByIDSDKv1(ctx context.Context, conn *rds.RDS, id string) (*rd
23812383
input.DBInstanceIdentifier = aws.String(id)
23822384
}
23832385

2384-
output, err := conn.DescribeDBInstancesWithContext(ctx, input)
2386+
output, err := findDBInstanceSDKv1(ctx, conn, input)
23852387

23862388
// in case a DB has an *identifier* starting with "db-""
2387-
if regexp.MustCompile(`^db-[a-zA-Z0-9]{2,255}$`).MatchString(id) && (output == nil || len(output.DBInstances) == 0) {
2388-
input = &rds.DescribeDBInstancesInput{
2389+
if idLooksLikeDbiResourceId && tfresource.NotFound(err) {
2390+
input := &rds.DescribeDBInstancesInput{
23892391
DBInstanceIdentifier: aws.String(id),
23902392
}
2391-
output, err = conn.DescribeDBInstancesWithContext(ctx, input)
2393+
2394+
output, err = findDBInstanceSDKv1(ctx, conn, input)
2395+
}
2396+
2397+
if err != nil {
2398+
return nil, err
2399+
}
2400+
2401+
return output, nil
2402+
}
2403+
2404+
func findDBInstanceSDKv1(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBInstancesInput) (*rds.DBInstance, error) {
2405+
output, err := findDBInstancesSDKv1(ctx, conn, input, tfslices.PredicateTrue[*rds.DBInstance]())
2406+
2407+
if err != nil {
2408+
return nil, err
23922409
}
23932410

2411+
return tfresource.AssertSinglePtrResult(output)
2412+
}
2413+
2414+
func findDBInstancesSDKv1(ctx context.Context, conn *rds.RDS, input *rds.DescribeDBInstancesInput, filter tfslices.Predicate[*rds.DBInstance]) ([]*rds.DBInstance, error) {
2415+
var output []*rds.DBInstance
2416+
2417+
err := conn.DescribeDBInstancesPagesWithContext(ctx, input, func(page *rds.DescribeDBInstancesOutput, lastPage bool) bool {
2418+
if page == nil {
2419+
return !lastPage
2420+
}
2421+
2422+
for _, v := range page.DBInstances {
2423+
if v != nil && filter(v) {
2424+
output = append(output, v)
2425+
}
2426+
}
2427+
2428+
return !lastPage
2429+
})
2430+
23942431
if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBInstanceNotFoundFault) {
23952432
return nil, &retry.NotFoundError{
23962433
LastError: err,
@@ -2402,11 +2439,7 @@ func findDBInstanceByIDSDKv1(ctx context.Context, conn *rds.RDS, id string) (*rd
24022439
return nil, err
24032440
}
24042441

2405-
if output == nil {
2406-
return nil, tfresource.NewEmptyResultError(input)
2407-
}
2408-
2409-
return tfresource.AssertSinglePtrResult(output.DBInstances)
2442+
return output, nil
24102443
}
24112444

24122445
// findDBInstanceByIDSDKv2 in general should be called with a DbiResourceId of the form

internal/service/rds/instance_data_source.go

+92-63
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@ import (
88
"fmt"
99

1010
"github.com/aws/aws-sdk-go/aws"
11+
"github.com/aws/aws-sdk-go/service/rds"
1112
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1213
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
13-
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1414
"github.com/hashicorp/terraform-provider-aws/internal/conns"
1515
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
16+
tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices"
1617
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
1718
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
19+
"github.com/hashicorp/terraform-provider-aws/names"
1820
)
1921

20-
// @SDKDataSource("aws_db_instance")
22+
// @SDKDataSource("aws_db_instance", name="DB Instance")
23+
// @Tags
2124
func DataSourceInstance() *schema.Resource {
2225
return &schema.Resource{
2326
ReadWithoutTimeout: dataSourceInstanceRead,
@@ -60,9 +63,9 @@ func DataSourceInstance() *schema.Resource {
6063
Computed: true,
6164
},
6265
"db_instance_identifier": {
63-
Type: schema.TypeString,
64-
Required: true,
65-
ValidateFunc: validation.StringIsNotEmpty,
66+
Type: schema.TypeString,
67+
Optional: true,
68+
Computed: true,
6669
},
6770
"db_instance_port": {
6871
Type: schema.TypeInt,
@@ -199,7 +202,7 @@ func DataSourceInstance() *schema.Resource {
199202
Type: schema.TypeString,
200203
Computed: true,
201204
},
202-
"tags": tftags.TagsSchemaComputed(),
205+
names.AttrTags: tftags.TagsSchemaComputed(),
203206
"timezone": {
204207
Type: schema.TypeString,
205208
Computed: true,
@@ -216,74 +219,104 @@ func DataSourceInstance() *schema.Resource {
216219
func dataSourceInstanceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
217220
var diags diag.Diagnostics
218221
conn := meta.(*conns.AWSClient).RDSConn(ctx)
219-
ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig
220222

221-
v, err := findDBInstanceByIDSDKv1(ctx, conn, d.Get("db_instance_identifier").(string))
222-
if err != nil {
223-
return diag.FromErr(tfresource.SingularDataSourceFindError("RDS DB Instance", err))
223+
var instance *rds.DBInstance
224+
225+
filter := tfslices.PredicateTrue[*rds.DBInstance]()
226+
if tags := getTagsIn(ctx); len(tags) > 0 {
227+
filter = func(v *rds.DBInstance) bool {
228+
return KeyValueTags(ctx, v.TagList).ContainsAll(KeyValueTags(ctx, tags))
229+
}
224230
}
225231

226-
d.SetId(aws.StringValue(v.DBInstanceIdentifier))
227-
d.Set("allocated_storage", v.AllocatedStorage)
228-
d.Set("auto_minor_version_upgrade", v.AutoMinorVersionUpgrade)
229-
d.Set("availability_zone", v.AvailabilityZone)
230-
d.Set("backup_retention_period", v.BackupRetentionPeriod)
231-
d.Set("ca_cert_identifier", v.CACertificateIdentifier)
232-
d.Set("db_cluster_identifier", v.DBClusterIdentifier)
233-
d.Set("db_instance_arn", v.DBInstanceArn)
234-
d.Set("db_instance_class", v.DBInstanceClass)
235-
d.Set("db_instance_port", v.DbInstancePort)
236-
d.Set("db_name", v.DBName)
237-
var parameterGroupNames []string
238-
for _, v := range v.DBParameterGroups {
239-
parameterGroupNames = append(parameterGroupNames, aws.StringValue(v.DBParameterGroupName))
232+
if v, ok := d.GetOk("db_instance_identifier"); ok {
233+
id := v.(string)
234+
output, err := findDBInstanceByIDSDKv1(ctx, conn, id)
235+
236+
if err != nil {
237+
return sdkdiag.AppendErrorf(diags, "reading RDS DB Instance (%s): %s", id, err)
238+
}
239+
240+
if !filter(output) {
241+
return sdkdiag.AppendErrorf(diags, "Your query returned no results. Please change your search criteria and try again.")
242+
}
243+
244+
instance = output
245+
} else {
246+
input := &rds.DescribeDBInstancesInput{}
247+
instances, err := findDBInstancesSDKv1(ctx, conn, input, filter)
248+
249+
if err != nil {
250+
return sdkdiag.AppendErrorf(diags, "reading RDS DB Instances: %s", err)
251+
}
252+
253+
output, err := tfresource.AssertSinglePtrResult(instances)
254+
255+
if err != nil {
256+
return sdkdiag.AppendFromErr(diags, tfresource.SingularDataSourceFindError("RDS DB Instance", err))
257+
}
258+
259+
instance = output
240260
}
261+
262+
d.SetId(aws.StringValue(instance.DBInstanceIdentifier))
263+
d.Set("allocated_storage", instance.AllocatedStorage)
264+
d.Set("auto_minor_version_upgrade", instance.AutoMinorVersionUpgrade)
265+
d.Set("availability_zone", instance.AvailabilityZone)
266+
d.Set("backup_retention_period", instance.BackupRetentionPeriod)
267+
d.Set("ca_cert_identifier", instance.CACertificateIdentifier)
268+
d.Set("db_cluster_identifier", instance.DBClusterIdentifier)
269+
d.Set("db_instance_arn", instance.DBInstanceArn)
270+
d.Set("db_instance_class", instance.DBInstanceClass)
271+
d.Set("db_instance_port", instance.DbInstancePort)
272+
d.Set("db_name", instance.DBName)
273+
parameterGroupNames := tfslices.ApplyToAll(instance.DBParameterGroups, func(v *rds.DBParameterGroupStatus) string {
274+
return aws.StringValue(v.DBParameterGroupName)
275+
})
241276
d.Set("db_parameter_groups", parameterGroupNames)
242-
if v.DBSubnetGroup != nil {
243-
d.Set("db_subnet_group", v.DBSubnetGroup.DBSubnetGroupName)
277+
if instance.DBSubnetGroup != nil {
278+
d.Set("db_subnet_group", instance.DBSubnetGroup.DBSubnetGroupName)
244279
} else {
245280
d.Set("db_subnet_group", "")
246281
}
247-
d.Set("enabled_cloudwatch_logs_exports", aws.StringValueSlice(v.EnabledCloudwatchLogsExports))
248-
d.Set("engine", v.Engine)
249-
d.Set("engine_version", v.EngineVersion)
250-
d.Set("iops", v.Iops)
251-
d.Set("kms_key_id", v.KmsKeyId)
252-
d.Set("license_model", v.LicenseModel)
253-
d.Set("master_username", v.MasterUsername)
254-
if v.MasterUserSecret != nil {
255-
if err := d.Set("master_user_secret", []interface{}{flattenManagedMasterUserSecret(v.MasterUserSecret)}); err != nil {
282+
d.Set("enabled_cloudwatch_logs_exports", aws.StringValueSlice(instance.EnabledCloudwatchLogsExports))
283+
d.Set("engine", instance.Engine)
284+
d.Set("engine_version", instance.EngineVersion)
285+
d.Set("iops", instance.Iops)
286+
d.Set("kms_key_id", instance.KmsKeyId)
287+
d.Set("license_model", instance.LicenseModel)
288+
d.Set("master_username", instance.MasterUsername)
289+
if instance.MasterUserSecret != nil {
290+
if err := d.Set("master_user_secret", []interface{}{flattenManagedMasterUserSecret(instance.MasterUserSecret)}); err != nil {
256291
return sdkdiag.AppendErrorf(diags, "setting master_user_secret: %s", err)
257292
}
258293
}
259-
d.Set("max_allocated_storage", v.MaxAllocatedStorage)
260-
d.Set("monitoring_interval", v.MonitoringInterval)
261-
d.Set("monitoring_role_arn", v.MonitoringRoleArn)
262-
d.Set("multi_az", v.MultiAZ)
263-
d.Set("network_type", v.NetworkType)
264-
var optionGroupNames []string
265-
for _, v := range v.OptionGroupMemberships {
266-
optionGroupNames = append(optionGroupNames, aws.StringValue(v.OptionGroupName))
267-
}
294+
d.Set("max_allocated_storage", instance.MaxAllocatedStorage)
295+
d.Set("monitoring_interval", instance.MonitoringInterval)
296+
d.Set("monitoring_role_arn", instance.MonitoringRoleArn)
297+
d.Set("multi_az", instance.MultiAZ)
298+
d.Set("network_type", instance.NetworkType)
299+
optionGroupNames := tfslices.ApplyToAll(instance.OptionGroupMemberships, func(v *rds.OptionGroupMembership) string {
300+
return aws.StringValue(v.OptionGroupName)
301+
})
268302
d.Set("option_group_memberships", optionGroupNames)
269-
d.Set("preferred_backup_window", v.PreferredBackupWindow)
270-
d.Set("preferred_maintenance_window", v.PreferredMaintenanceWindow)
271-
d.Set("publicly_accessible", v.PubliclyAccessible)
272-
d.Set("replicate_source_db", v.ReadReplicaSourceDBInstanceIdentifier)
273-
d.Set("resource_id", v.DbiResourceId)
274-
d.Set("storage_encrypted", v.StorageEncrypted)
275-
d.Set("storage_throughput", v.StorageThroughput)
276-
d.Set("storage_type", v.StorageType)
277-
d.Set("timezone", v.Timezone)
278-
var vpcSecurityGroupIDs []string
279-
for _, v := range v.VpcSecurityGroups {
280-
vpcSecurityGroupIDs = append(vpcSecurityGroupIDs, aws.StringValue(v.VpcSecurityGroupId))
281-
}
303+
d.Set("preferred_backup_window", instance.PreferredBackupWindow)
304+
d.Set("preferred_maintenance_window", instance.PreferredMaintenanceWindow)
305+
d.Set("publicly_accessible", instance.PubliclyAccessible)
306+
d.Set("replicate_source_db", instance.ReadReplicaSourceDBInstanceIdentifier)
307+
d.Set("resource_id", instance.DbiResourceId)
308+
d.Set("storage_encrypted", instance.StorageEncrypted)
309+
d.Set("storage_throughput", instance.StorageThroughput)
310+
d.Set("storage_type", instance.StorageType)
311+
d.Set("timezone", instance.Timezone)
312+
vpcSecurityGroupIDs := tfslices.ApplyToAll(instance.VpcSecurityGroups, func(v *rds.VpcSecurityGroupMembership) string {
313+
return aws.StringValue(v.VpcSecurityGroupId)
314+
})
282315
d.Set("vpc_security_groups", vpcSecurityGroupIDs)
283316

284317
// Per AWS SDK Go docs:
285318
// The endpoint might not be shown for instances whose status is creating.
286-
if dbEndpoint := v.Endpoint; dbEndpoint != nil {
319+
if dbEndpoint := instance.Endpoint; dbEndpoint != nil {
287320
d.Set("address", dbEndpoint.Address)
288321
d.Set("endpoint", fmt.Sprintf("%s:%d", aws.StringValue(dbEndpoint.Address), aws.Int64Value(dbEndpoint.Port)))
289322
d.Set("hosted_zone_id", dbEndpoint.HostedZoneId)
@@ -295,11 +328,7 @@ func dataSourceInstanceRead(ctx context.Context, d *schema.ResourceData, meta in
295328
d.Set("port", nil)
296329
}
297330

298-
tags := KeyValueTags(ctx, v.TagList)
299-
300-
if err := d.Set("tags", tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
301-
return sdkdiag.AppendErrorf(diags, "setting tags: %s", err)
302-
}
331+
setTagsOut(ctx, instance.TagList)
303332

304333
return diags
305334
}

0 commit comments

Comments
 (0)