diff --git a/aws/plugin.go b/aws/plugin.go index c99709ca8..28aa328d7 100644 --- a/aws/plugin.go +++ b/aws/plugin.go @@ -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), diff --git a/aws/table_aws_organizations_delegated_administrator.go b/aws/table_aws_organizations_delegated_administrator.go new file mode 100644 index 000000000..1d5f17318 --- /dev/null +++ b/aws/table_aws_organizations_delegated_administrator.go @@ -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 + +} diff --git a/aws/table_aws_organizations_delegated_services_for_account.go b/aws/table_aws_organizations_delegated_services_for_account.go new file mode 100644 index 000000000..16ee7e9b2 --- /dev/null +++ b/aws/table_aws_organizations_delegated_services_for_account.go @@ -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 +} \ No newline at end of file diff --git a/docs/tables/aws_organizations_delegated_administrator.md b/docs/tables/aws_organizations_delegated_administrator.md new file mode 100644 index 000000000..eaa8ed0a5 --- /dev/null +++ b/docs/tables/aws_organizations_delegated_administrator.md @@ -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'; +``` \ No newline at end of file diff --git a/docs/tables/aws_organizations_delegated_services_for_account.md b/docs/tables/aws_organizations_delegated_services_for_account.md new file mode 100644 index 000000000..1b0e1acf4 --- /dev/null +++ b/docs/tables/aws_organizations_delegated_services_for_account.md @@ -0,0 +1,65 @@ +--- +title: "Steampipe Table: aws_organizations_delegated_services_for_account - Query AWS Organizations Delegated Services for an Account using SQL" +description: "Allows users to query AWS Organizations delegated services for a specific account, providing details on which services have been granted delegated administrative privileges." +folder: "Organizations" +--- + +# Table: aws_organizations_delegated_services_for_account - Query AWS Organizations Delegated Services for an Account using SQL + +The AWS Organizations Delegated Services for an Account is a resource that allows you to query information about the services that have been delegated administrative privileges for a given AWS account within an AWS Organization service. This allows you to query details about these services, including the service principal and the date the delegation was enabled. This is useful for managing and auditing delegated service administration within your organization. + +## Table Usage Guide + +The `aws_organizations_delegated_administrator` table in Steampipe lets you query the services granted delegated administration access to a specific account. You specify the delegated account ID, and the table returns a list of services and their delegation status. The schema outlines various attributes of each delegated service. + +**Important Notes:** +- This table supports the optional list key column `delegated_account_id`. +- The `delegated_account_id` is the ID of the account which has been granted delegated administration, *not* the management account making the API calls. + +## Examples + +### Basic info +Retrieve basic information about all delegated services. + +```sql+postgres +select + delegated_account_id, + service_principal, + delegation_enabled_date +from + aws_organizations_delegated_services_for_account +``` + +```sql+sqlite +select + delegated_account_id, + service_principal, + delegation_enabled_date +from + aws_organizations_delegated_services_for_account +``` + +### Basic info for a specific account +Retrieve basic information about all delegated services for a specific account. + +```sql+postgres +select + delegated_account_id, + service_principal, + delegation_enabled_date +from + aws_organizations_delegated_services_for_account +where + delegated_account_id = '123456789012'; +``` + +```sql+sqlite +select + delegated_account_id, + service_principal, + delegation_enabled_date +from + aws_organizations_delegated_services_for_account +where + delegated_account_id = '123456789012'; +``` \ No newline at end of file