Skip to content

Add aws_organizations_delegated_administrator and aws_organizations_delegated_services_for_account tables (#2421) (#2477) #2496

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 20, 2025
Merged
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
2 changes: 2 additions & 0 deletions aws/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,8 @@ func Plugin(ctx context.Context) *plugin.Plugin {
"aws_organizations_policy": tableAwsOrganizationsPolicy(ctx),
"aws_organizations_policy_target": tableAwsOrganizationsPolicyTarget(ctx),
"aws_organizations_root": tableAwsOrganizationsRoot(ctx),
"aws_organizations_delegated_administrator": tableAwsOrganizationsDelegatedAdministrator(ctx),
"aws_organizations_delegated_services_for_account": tableAwsOrganizationsDelegatedServicesForAccount(ctx),
"aws_pinpoint_app": tableAwsPinpointApp(ctx),
"aws_pipes_pipe": tableAwsPipes(ctx),
"aws_pricing_product": tableAwsPricingProduct(ctx),
Expand Down
129 changes: 129 additions & 0 deletions aws/table_aws_organizations_delegated_administrator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package aws

import (
"context"

"github.com/aws/aws-sdk-go-v2/service/organizations"

"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"
)

func tableAwsOrganizationsDelegatedAdministrator(_ context.Context) *plugin.Table {
return &plugin.Table{
Name: "aws_organizations_delegated_administrator",
Description: "AWS Organizations Delegated Administrator",
List: &plugin.ListConfig{
Hydrate: listOrganizationsDelegatedAdmins,
Tags: map[string]string{"service": "organizations", "action": "ListDelegatedAdministrators"},
},
Columns: awsGlobalRegionColumns([]*plugin.Column{
{
Name: "id",
Description: "The unique identifier (account ID) of the delegated administrator.",
Type: proto.ColumnType_STRING,
},
{
Name: "arn",
Description: "The Amazon Resource Name (ARN) of the delegated administrator.",
Type: proto.ColumnType_STRING,
},
{
Name: "email",
Description: "The email address associated with the delegated administrator account.",
Type: proto.ColumnType_STRING,
},
{
Name: "name",
Description: "The friendly name of the delegated administrator account.",
Type: proto.ColumnType_STRING,
},
{
Name: "status",
Description: "The status of the delegated administrator.",
Type: proto.ColumnType_STRING,
},
{
Name: "joined_method",
Description: "The method by which the account joined the organization.",
Type: proto.ColumnType_STRING,
},
{
Name: "joined_timestamp",
Description: "The date the account became a part of the organization.",
Type: proto.ColumnType_TIMESTAMP,
},
{
Name: "delegation_enabled_date",
Description: "The date when the delegation was enabled.",
Type: proto.ColumnType_TIMESTAMP,
},

// Standard columns for all tables
{
Name: "title",
Description: resourceInterfaceDescription("title"),
Type: proto.ColumnType_STRING,
Transform: transform.FromField("Name"),
},
{
Name: "akas",
Description: resourceInterfaceDescription("akas"),
Type: proto.ColumnType_JSON,
Transform: transform.FromField("Arn").Transform(transform.EnsureStringArray),
},
}),
}
}

func listOrganizationsDelegatedAdmins(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) {

// Get Client
svc, err := OrganizationClient(ctx, d)
if err != nil {
plugin.Logger(ctx).Error("aws_organizations_delegated_administrator.ListDelegatedAdministrators", "client_error", err)
return nil, err
}

// Limiting the result
maxItems := int32(20)

// Reduce the basic request limit down if the user has only requested a small number of rows
if d.QueryContext.Limit != nil {
limit := int32(*d.QueryContext.Limit)
if limit < maxItems {
maxItems = int32(limit)
}
}

params := &organizations.ListDelegatedAdministratorsInput{
MaxResults: &maxItems,
}

paginator := organizations.NewListDelegatedAdministratorsPaginator(svc, params, func(o *organizations.ListDelegatedAdministratorsPaginatorOptions) {
o.Limit = maxItems
o.StopOnDuplicateToken = true
})

for paginator.HasMorePages() {
// apply rate limiting
output, err := paginator.NextPage(ctx)
if err != nil {
plugin.Logger(ctx).Error("aws_organizations_delegated_administrator.ListDelegatedAdministrators", "api_error", err)
return nil, err
}

for _, admin := range output.DelegatedAdministrators {
d.StreamListItem(ctx, admin)

// Context may get cancelled due to manual cancellation or if the limit has been reached
if d.RowsRemaining(ctx) == 0 {
return nil, nil
}
}
}

return nil, nil

}
123 changes: 123 additions & 0 deletions aws/table_aws_organizations_delegated_services_for_account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package aws

import (
"context"

"github.com/aws/aws-sdk-go-v2/service/organizations"
"github.com/aws/aws-sdk-go-v2/service/organizations/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"
)

func tableAwsOrganizationsDelegatedServicesForAccount(_ context.Context) *plugin.Table {

return &plugin.Table{
Name: "aws_organizations_delegated_services_for_account",
Description: "AWS Organizations Delegated Services For Account",
List: &plugin.ListConfig{
ParentHydrate: listOrganizationsDelegatedAdmins, // Use Delegated Administrator as parent per recommendation. Referenced table_aws_cloudwatch_log_stream. This function can be found in table_aws_organizations_delegated_administrator
Hydrate: listDelegatedServices,
Tags: map[string]string{"service": "organizations", "action": "ListDelegatedServicesForAccount"},
KeyColumns: []*plugin.KeyColumn{ // Make delegated_account_id optional, user can still query `where` using this column.
{Name: "delegated_account_id", Require: plugin.Optional},
},
},
Columns: awsGlobalRegionColumns([]*plugin.Column{
{
Name: "delegated_account_id",
Description: "The unique identifier (account ID) of the delegated administrator account for which services are listed.",
Type: proto.ColumnType_STRING,
},
{
Name: "service_principal",
Description: "The service principal delegated to the administrator account.",
Type: proto.ColumnType_STRING,
},
{
Name: "delegation_enabled_date",
Description: "The date when the delegation was enabled.",
Type: proto.ColumnType_TIMESTAMP,
},

// Standard columns for all tables
{
Name: "title",
Description: resourceInterfaceDescription("title"),
Type: proto.ColumnType_STRING,
Transform: transform.FromField("ServicePrincipal"),
},
}),
}
}

// Define a struct to hold the DelegatedService and the AccountId used to fetch it.\
type delegatedServiceInfo struct {
types.DelegatedService
DelegatedAccountId string
}


func listDelegatedServices(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
delegatedAccount := h.Item.(types.DelegatedAdministrator) // Get delegated administrator details

delegatedAccountId := d.EqualsQualString("delegated_account_id") // Get delegated_account_id from where statement

// Minimize API calls
if delegatedAccountId != "" {
if delegatedAccountId != *delegatedAccount.Id {
return nil, nil
}
}

// Get Client
svc, err := OrganizationClient(ctx, d)
if err != nil {
plugin.Logger(ctx).Error("aws_organizations_delegated_services_for_account.ListDelegatedServicesForAccount", "client_error", err)
return nil, err
}

// Limiting the result
maxItems := int32(20)

// Reduce the page size if a smaller limit is provided
if d.QueryContext.Limit != nil {
limit := int32(*d.QueryContext.Limit)
if limit < maxItems {
maxItems = limit
}
}
params := &organizations.ListDelegatedServicesForAccountInput{
AccountId: delegatedAccount.Id,
MaxResults: &maxItems,
}

paginator := organizations.NewListDelegatedServicesForAccountPaginator(svc, params, func(o *organizations.ListDelegatedServicesForAccountPaginatorOptions) {
o.Limit = maxItems
o.StopOnDuplicateToken = true
})

for paginator.HasMorePages() {
output, err := paginator.NextPage(ctx)
if err != nil {
plugin.Logger(ctx).Error("aws_organizations_delegated_services_for_account.ListDelegatedServicesForAccount", "api_error", err)
return nil, err
}

for _, service := range output.DelegatedServices {
// Stream a new struct that includes the AccountId used for the API call
d.StreamListItem(ctx, delegatedServiceInfo{
DelegatedService: service,
DelegatedAccountId: *delegatedAccount.Id,
})

// Context may get cancelled due to manual cancellation or if the limit has been reached
if d.RowsRemaining(ctx) == 0 {
return nil, nil
}
}
}

return nil, nil
}
85 changes: 85 additions & 0 deletions docs/tables/aws_organizations_delegated_administrator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
title: "Steampipe Table: aws_organizations_delegated_administrator - Query AWS Organizations Delegated Administrators using SQL"
description: "Allows users to query AWS Organizations Delegated Administrators and provides information about each account delegated administrative privileges within an AWS Organization."
folder: "Organizations"
---

# Table: aws_organizations_delegated_administrator - Query AWS Organizations Delegated Administrators using SQL

The AWS Organizations Delegated Administrator is a resource within AWS Organizations service that allows you to query details about your delegated administrator accounts, including their status, the date delegation was enabled, and associated metadata. This is useful for managing and auditing delegated administration within your organization.

## Table Usage Guide

The `aws_organizations_delegated_administrator` table in Steampipe lets you query details of accounts designated as delegated administrators in AWS Organizations. This table allows you, as a DevOps engineer, to identify accounts with specific statuses, view the date delegation was enabled, and gather other relevant account information. The schema outlines various attributes of each delegated administrator account.

**Important Notes:**
* This table returns details about *delegated administrator* accounts, **not** the management account executing the API call.
* The `account_id` column shows the ID of the account that made the API request (typically the management account). To retrieve the ID of the delegated administrator (member account), refer to the `id` column.

## Examples

### Basic info
Retrieve basic information about all delegated administrators in your organization.

```sql+postgres
select
id,
arn,
email,
joined_method,
joined_timestamp,
name,
status,
delegation_enabled_date
from
aws_organizations_delegated_administrator;
```

```sql+sqlite
select
id,
arn,
email,
joined_method,
joined_timestamp,
name,
status,
delegation_enabled_date
from
aws_organizations_delegated_administrator;
```

### List delegated administrators with a specific status
Identify delegated administrators with a particular status (e.g., ACTIVE, SUSPENDED, PENDING_CLOSURE).

```sql+postgres
select
id,
name,
arn,
email,
joined_method,
joined_timestamp,
status,
delegation_enabled_date
from
aws_organizations_delegated_administrator
where
status = 'ACTIVE';
```

```sql+sqlite
select
id,
name,
arn,
email,
joined_method,
joined_timestamp,
status,
delegation_enabled_date
from
aws_organizations_delegated_administrator
where
status = 'ACTIVE';
```
Loading
Loading