Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions aws/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
"aws_rds_db_cluster": tableAwsRDSDBCluster(ctx),
"aws_rds_db_engine_version": tableAwsRDSDBEngineVersion(ctx),
"aws_rds_db_event_subscription": tableAwsRDSDBEventSubscription(ctx),
"aws_rds_db_global_cluster": tableAwsRDSGlobalCluster(ctx),
"aws_rds_db_instance_automated_backup": tableAwsRDSDBInstanceAutomatedBackup(ctx),
"aws_rds_db_instance_metric_connections_daily": tableAwsRdsInstanceMetricConnectionsDaily(ctx),
"aws_rds_db_instance_metric_connections_hourly": tableAwsRdsInstanceMetricConnectionsHourly(ctx),
Expand Down
254 changes: 254 additions & 0 deletions aws/table_aws_rds_db_global_cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
package aws

import (
"context"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/aws/aws-sdk-go-v2/service/rds/types"

"github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto"
"github.com/turbot/steampipe-plugin-sdk/v5/plugin"
"github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform"
)

//// TABLE DEFINITION

func tableAwsRDSGlobalCluster(_ context.Context) *plugin.Table {
return &plugin.Table{
Name: "aws_rds_db_global_cluster",
Description: "AWS RDS Global Cluster",
Comment on lines +17 to +20
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table factory function name is inconsistent with the existing RDS DB table naming pattern (e.g., tableAwsRDSDBCluster/tableAwsRDSDBOptionGroup). To match the convention and improve discoverability, rename this to tableAwsRDSDBGlobalCluster and update the reference in aws/plugin.go.

Copilot uses AI. Check for mistakes.
Get: &plugin.GetConfig{
KeyColumns: plugin.SingleColumn("global_cluster_identifier"),
IgnoreConfig: &plugin.IgnoreConfig{
ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"GlobalClusterNotFoundFault"}),
},
Hydrate: getRDSGlobalCluster,
Tags: map[string]string{"service": "rds", "action": "DescribeGlobalClusters"},
},
List: &plugin.ListConfig{
Hydrate: listRDSGlobalClusters,
Tags: map[string]string{"service": "rds", "action": "DescribeGlobalClusters"},
},
HydrateConfig: []plugin.HydrateConfig{
{
Func: getRDSGlobalClusterTags,
Tags: map[string]string{"service": "rds", "action": "ListTagsForResource"},
},
},
GetMatrixItemFunc: SupportedRegionMatrix(AWS_RDS_SERVICE_ID),
Columns: awsRegionalColumns([]*plugin.Column{
{
Name: "global_cluster_identifier",
Description: "Contains a user-supplied global database cluster identifier.",
Type: proto.ColumnType_STRING,
},
{
Name: "global_cluster_arn",
Description: "The Amazon Resource Name (ARN) for the global database cluster.",
Type: proto.ColumnType_STRING,
},
Comment on lines +47 to +50
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most tables in this plugin expose the primary ARN as a column named arn (including other RDS tables). Exposing it here as global_cluster_arn makes cross-table querying less consistent. Consider renaming this column to arn (keeping akas as-is) and updating any docs/examples accordingly before release.

Copilot uses AI. Check for mistakes.
{
Name: "status",
Description: "Specifies the current state of this global database cluster.",
Type: proto.ColumnType_STRING,
},
{
Name: "endpoint",
Description: "The writer endpoint for the primary DB cluster in this global database cluster.",
Type: proto.ColumnType_STRING,
Transform: transform.FromField("Endpoint"),
},
Comment thread
ppapishe marked this conversation as resolved.
{
Name: "global_cluster_resource_id",
Description: "The AWS Region-unique, immutable identifier for the global database cluster.",
Type: proto.ColumnType_STRING,
},
{
Name: "database_name",
Description: "The default database name within the global database cluster.",
Type: proto.ColumnType_STRING,
},
{
Name: "deletion_protection",
Description: "The deletion protection setting for the global database cluster.",
Type: proto.ColumnType_BOOL,
},
{
Name: "engine",
Description: "The Aurora database engine used by the global database cluster.",
Type: proto.ColumnType_STRING,
},
{
Name: "engine_version",
Description: "Indicates the database engine version.",
Type: proto.ColumnType_STRING,
},
{
Name: "storage_encrypted",
Description: "The storage encryption setting for the global database cluster.",
Type: proto.ColumnType_BOOL,
},
{
Name: "failover_state",
Description: "Properties for the current state of an in-process or pending switchover/failover.",
Type: proto.ColumnType_JSON,
},
{
Name: "global_cluster_members",
Description: "The list of primary and secondary clusters within the global database cluster.",
Type: proto.ColumnType_JSON,
},
{
Name: "tags_src",
Description: "A list of tags attached to the global database cluster.",
Hydrate: getRDSGlobalClusterTags,
Type: proto.ColumnType_JSON,
Transform: transform.FromValue(),
},

// Standard columns
{
Name: "title",
Description: resourceInterfaceDescription("title"),
Type: proto.ColumnType_STRING,
Transform: transform.FromField("GlobalClusterIdentifier"),
},
{
Name: "tags",
Description: resourceInterfaceDescription("tags"),
Type: proto.ColumnType_JSON,
Hydrate: getRDSGlobalClusterTags,
Transform: transform.FromValue().Transform(rdsGlobalClusterTagListToTurbotTags),
},
{
Name: "akas",
Description: resourceInterfaceDescription("akas"),
Type: proto.ColumnType_JSON,
Transform: transform.FromField("GlobalClusterArn").Transform(arnToAkas),
},
}),
}
}

//// LIST FUNCTION

func listRDSGlobalClusters(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) {
svc, err := RDSClient(ctx, d)
if err != nil {
plugin.Logger(ctx).Error("aws_rds_db_global_cluster.listRDSGlobalClusters", "connection_error", err)
return nil, err
}

maxLimit := int32(100)
if d.QueryContext.Limit != nil {
limit := int32(*d.QueryContext.Limit)
if limit < maxLimit {
if limit < 20 {
maxLimit = 20
} else {
maxLimit = limit
}
}
}

input := &rds.DescribeGlobalClustersInput{MaxRecords: aws.Int32(maxLimit)}
paginator := rds.NewDescribeGlobalClustersPaginator(svc, input, func(o *rds.DescribeGlobalClustersPaginatorOptions) {
o.Limit = maxLimit
o.StopOnDuplicateToken = true
})

for paginator.HasMorePages() {
d.WaitForListRateLimit(ctx)

output, err := paginator.NextPage(ctx)
if err != nil {
plugin.Logger(ctx).Error("aws_rds_db_global_cluster.listRDSGlobalClusters", "api_error", err)
return nil, err
}

for _, globalCluster := range output.GlobalClusters {
d.StreamListItem(ctx, globalCluster)
if d.RowsRemaining(ctx) == 0 {
return nil, nil
}
}
}

return nil, nil
}

//// HYDRATE FUNCTIONS

func getRDSGlobalCluster(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) {
globalClusterIdentifier := d.EqualsQuals["global_cluster_identifier"].GetStringValue()
if globalClusterIdentifier == "" {
return nil, nil
}

svc, err := RDSClient(ctx, d)
if err != nil {
plugin.Logger(ctx).Error("aws_rds_db_global_cluster.getRDSGlobalCluster", "connection_error", err)
return nil, err
}

input := &rds.DescribeGlobalClustersInput{
GlobalClusterIdentifier: aws.String(globalClusterIdentifier),
}

op, err := svc.DescribeGlobalClusters(ctx, input)
if err != nil {
plugin.Logger(ctx).Error("aws_rds_db_global_cluster.getRDSGlobalCluster", "api_error", err)
return nil, err
}

if len(op.GlobalClusters) > 0 {
return op.GlobalClusters[0], nil
}

return nil, nil
}

func getRDSGlobalClusterTags(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
svc, err := RDSClient(ctx, d)
if err != nil {
plugin.Logger(ctx).Error("aws_rds_db_global_cluster.getRDSGlobalClusterTags", "connection_error", err)
return nil, err
}

globalCluster := h.Item.(types.GlobalCluster)
if globalCluster.GlobalClusterArn == nil {
return nil, nil
}

input := &rds.ListTagsForResourceInput{ResourceName: globalCluster.GlobalClusterArn}
op, err := svc.ListTagsForResource(ctx, input)
if err != nil {
plugin.Logger(ctx).Error("aws_rds_db_global_cluster.getRDSGlobalClusterTags", "api_error", err)
return nil, err
}

return op.TagList, nil
}

//// TRANSFORM FUNCTION

func rdsGlobalClusterTagListToTurbotTags(_ context.Context, d *transform.TransformData) (interface{}, error) {
tagList, ok := d.Value.([]types.Tag)
if !ok {
return nil, nil
}

if len(tagList) == 0 {
return nil, nil
}

tagsMap := map[string]string{}
for _, tag := range tagList {
if tag.Key != nil && tag.Value != nil {
tagsMap[*tag.Key] = *tag.Value
}
}

return tagsMap, nil
}
85 changes: 85 additions & 0 deletions docs/tables/aws_rds_db_global_cluster.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
title: "Steampipe Table: aws_rds_db_global_cluster - Query AWS RDS Global Clusters using SQL"
description: "Allows users to query AWS RDS Global Clusters and retrieve details about global database topology, status, and member clusters."
folder: "RDS"
---

# Table: aws_rds_db_global_cluster - Query AWS RDS Global Clusters using SQL

An AWS RDS Global Cluster (Aurora Global Database) is an overarching layer that groups regional Aurora DB clusters into a single globally distributed database.

## Table Usage Guide

The `aws_rds_db_global_cluster` table in Steampipe provides information about RDS global clusters in your account. You can use this table to audit global database configurations, check failover readiness, and inspect member clusters and encryption settings.

## Examples

### List RDS global clusters

```sql+postgres
select
global_cluster_identifier,
status,
endpoint,
engine,
engine_version
from
aws_rds_db_global_cluster;
```

```sql+sqlite
select
global_cluster_identifier,
status,
endpoint,
engine,
engine_version
from
aws_rds_db_global_cluster;
```

### Show global cluster members

```sql+postgres
select
global_cluster_identifier,
member ->> 'DBClusterArn' as db_cluster_arn,
member ->> 'IsWriter' as is_writer,
member -> 'Readers' as readers
from
aws_rds_db_global_cluster
cross join jsonb_array_elements(global_cluster_members) as member;
```

```sql+sqlite
select
global_cluster_identifier,
json_extract(member.value, '$.DBClusterArn') as db_cluster_arn,
json_extract(member.value, '$.IsWriter') as is_writer,
json_extract(member.value, '$.Readers') as readers
from
aws_rds_db_global_cluster,
json_each(global_cluster_members) as member;
```

### Find unencrypted global clusters

```sql+postgres
select
global_cluster_identifier,
storage_encrypted
from
aws_rds_db_global_cluster
where
not storage_encrypted;
```

```sql+sqlite
select
global_cluster_identifier,
storage_encrypted
from
aws_rds_db_global_cluster
where
storage_encrypted = 0;
```
Loading