Skip to content

Commit

Permalink
Add table aws_organizations_organizational_unit back to the table list (
Browse files Browse the repository at this point in the history
#2063)


Co-authored-by: Ved misra <[email protected]>
  • Loading branch information
ParthaI and misraved authored Feb 12, 2024
1 parent 5d5ee85 commit 53aecf3
Show file tree
Hide file tree
Showing 3 changed files with 385 additions and 183 deletions.
2 changes: 1 addition & 1 deletion aws/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
"aws_oam_sink": tableAwsOAMSink(ctx),
"aws_opensearch_domain": tableAwsOpenSearchDomain(ctx),
"aws_organizations_account": tableAwsOrganizationsAccount(ctx),
// "aws_organizations_organizational_unit": tableAwsOrganizationsOrganizationalUnit(ctx),
"aws_organizations_organizational_unit": tableAwsOrganizationsOrganizationalUnit(ctx),
"aws_organizations_policy": tableAwsOrganizationsPolicy(ctx),
"aws_organizations_policy_target": tableAwsOrganizationsPolicyTarget(ctx),
"aws_organizations_root": tableAwsOrganizationsRoot(ctx),
Expand Down
372 changes: 190 additions & 182 deletions aws/table_aws_organizations_organizational_unit.go
Original file line number Diff line number Diff line change
@@ -1,184 +1,192 @@
package aws

// Commenting out the table for the time being because, we need to hove the column type LTREE first then we can add it back.

// The table will return the Organizational Units for the root account if parent_id is not specified in the query parameter.
// If parent_id is specified in the query parameter then it will return the Organizational Units for the given parent.
// func tableAwsOrganizationsOrganizationalUnit(_ context.Context) *plugin.Table {
// return &plugin.Table{
// Name: "aws_organizations_organizational_unit",
// Description: "AWS Organizations Organizational Unit",
// List: &plugin.ListConfig{
// ParentHydrate: listOrganizationsRoots,
// Hydrate: listOrganizationsOrganizationalUnits,
// IgnoreConfig: &plugin.IgnoreConfig{
// ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"ParentNotFoundException", "InvalidInputException"}),
// },
// KeyColumns: plugin.KeyColumnSlice{
// {
// Name: "parent_id",
// Require: plugin.Optional,
// Operators: []string{"="},
// },
// },
// },
// Columns: awsGlobalRegionColumns([]*plugin.Column{
// {
// Name: "name",
// Description: "The friendly name of this OU.",
// Hydrate: getOrganizationsOrganizationalUnit,
// Type: proto.ColumnType_STRING,
// },
// {
// Name: "id",
// Description: "The unique identifier (ID) associated with this OU.",
// Type: proto.ColumnType_STRING,
// },
// {
// Name: "arn",
// Description: "The Amazon Resource Name (ARN) of this OU.",
// Hydrate: getOrganizationsOrganizationalUnit,
// Type: proto.ColumnType_STRING,
// },
// {
// Name: "parent_id",
// Description: "The unique identifier (ID) of the root or OU whose child OUs you want to list.",
// Type: proto.ColumnType_STRING,
// },
// {
// Name: "path",
// Description: "The OU path is a string representation that uniquely identifies the hierarchical location of an Organizational Unit within the AWS Organizations structure.",
// Type: proto.ColumnType_LTREE,
// },

// // Steampipe standard columns
// {
// Name: "title",
// Description: resourceInterfaceDescription("title"),
// Type: proto.ColumnType_STRING,
// Hydrate: getOrganizationsOrganizationalUnit,
// Transform: transform.FromField("Name"),
// },
// {
// Name: "akas",
// Description: resourceInterfaceDescription("akas"),
// Type: proto.ColumnType_JSON,
// Hydrate: getOrganizationsOrganizationalUnit,
// Transform: transform.FromField("Arn").Transform(transform.EnsureStringArray),
// },
// }),
// }
// }

// type OrganizationalUnit struct {
// types.OrganizationalUnit
// Path string
// ParentId string
// }

// //// LIST FUNCTION

// func listOrganizationsOrganizationalUnits(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
// parentId := *h.Item.(types.Root).Id

// // Check if the parentId is provided
// // The unique identifier (ID) of the root or OU whose child OUs you want to list.
// if d.EqualsQualString("parent_id") != "" {
// parentId = d.EqualsQualString("parent_id")
// }

// // empty check
// if parentId == "" {
// return nil, nil
// }

// // Get Client
// svc, err := OrganizationClient(ctx, d)
// if err != nil {
// plugin.Logger(ctx).Error("aws_organizations_organizational_unit.listOrganizationsOrganizationalUnits", "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)
// }
// }

// // Call the recursive function to list all nested OUs
// rootPath := parentId
// err = listAllNestedOUs(ctx, d, svc, parentId, maxItems, rootPath)
// if err != nil {
// plugin.Logger(ctx).Error("aws_organizations_organizational_unit.listOrganizationsOrganizationalUnits", "recursive_call_error", err)
// return nil, err
// }

// return nil, nil
// }

// func listAllNestedOUs(ctx context.Context, d *plugin.QueryData, svc *organizations.Client, parentId string, maxItems int32, currentPath string) error {
// params := &organizations.ListOrganizationalUnitsForParentInput{
// ParentId: aws.String(parentId),
// MaxResults: &maxItems,
// }

// paginator := organizations.NewListOrganizationalUnitsForParentPaginator(svc, params, func(o *organizations.ListOrganizationalUnitsForParentPaginatorOptions) {
// o.Limit = maxItems
// o.StopOnDuplicateToken = true
// })

// for paginator.HasMorePages() {
// // apply rate limiting
// output, err := paginator.NextPage(ctx)
// if err != nil {
// return err
// }

// for _, unit := range output.OrganizationalUnits {
// ouPath := strings.Replace(currentPath, "-", "_", -1) + "." + strings.Replace(*unit.Id, "-", "_", -1)
// d.StreamListItem(ctx, OrganizationalUnit{unit, ouPath, parentId})

// // Recursively list units for this child
// err := listAllNestedOUs(ctx, d, svc, *unit.Id, maxItems, ouPath)
// if err != nil {
// return err
// }

// if d.RowsRemaining(ctx) == 0 {
// return nil
// }
// }
// }

// return nil
// }

// //// HYDRATE FUNCTIONS

// func getOrganizationsOrganizationalUnit(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
// orgUnitId := *h.Item.(OrganizationalUnit).Id

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

// params := &organizations.DescribeOrganizationalUnitInput{
// OrganizationalUnitId: aws.String(orgUnitId),
// }

// op, err := svc.DescribeOrganizationalUnit(ctx, params)
// if err != nil {
// plugin.Logger(ctx).Error("aws_organizations_organizational_unit.getOrganizationsOrganizationalUnit", "api_error", err)
// return nil, err
// }

// return *op.OrganizationalUnit, nil
// }
import (
"context"
"strings"

"github.com/aws/aws-sdk-go-v2/aws"
"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 tableAwsOrganizationsOrganizationalUnit(_ context.Context) *plugin.Table {
return &plugin.Table{
Name: "aws_organizations_organizational_unit",
Description: "AWS Organizations Organizational Unit",
List: &plugin.ListConfig{
ParentHydrate: listOrganizationsRoots,
Hydrate: listOrganizationsOrganizationalUnits,
IgnoreConfig: &plugin.IgnoreConfig{
ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"ParentNotFoundException", "InvalidInputException"}),
},
KeyColumns: plugin.KeyColumnSlice{
{
Name: "parent_id",
Require: plugin.Optional,
Operators: []string{"="},
},
},
},
Columns: awsGlobalRegionColumns([]*plugin.Column{
{
Name: "name",
Description: "The friendly name of this OU.",
Hydrate: getOrganizationsOrganizationalUnit,
Type: proto.ColumnType_STRING,
},
{
Name: "id",
Description: "The unique identifier (ID) associated with this OU.",
Type: proto.ColumnType_STRING,
},
{
Name: "arn",
Description: "The Amazon Resource Name (ARN) of this OU.",
Hydrate: getOrganizationsOrganizationalUnit,
Type: proto.ColumnType_STRING,
},
{
Name: "parent_id",
Description: "The unique identifier (ID) of the root or OU whose child OUs you want to list.",
Type: proto.ColumnType_STRING,
},
{
Name: "path",
Description: "The OU path is a string representation that uniquely identifies the hierarchical location of an Organizational Unit within the AWS Organizations structure.",
Type: proto.ColumnType_LTREE,
},

// Steampipe standard columns
{
Name: "title",
Description: resourceInterfaceDescription("title"),
Type: proto.ColumnType_STRING,
Hydrate: getOrganizationsOrganizationalUnit,
Transform: transform.FromField("Name"),
},
{
Name: "akas",
Description: resourceInterfaceDescription("akas"),
Type: proto.ColumnType_JSON,
Hydrate: getOrganizationsOrganizationalUnit,
Transform: transform.FromField("Arn").Transform(transform.EnsureStringArray),
},
}),
}
}

type OrganizationalUnit struct {
types.OrganizationalUnit
Path string
ParentId string
}

//// LIST FUNCTION

func listOrganizationsOrganizationalUnits(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
parentId := *h.Item.(types.Root).Id

// Check if the parentId is provided
// The unique identifier (ID) of the root or OU whose child OUs you want to list.
if d.EqualsQualString("parent_id") != "" {
parentId = d.EqualsQualString("parent_id")
}

// empty check
if parentId == "" {
return nil, nil
}

// Get Client
svc, err := OrganizationClient(ctx, d)
if err != nil {
plugin.Logger(ctx).Error("aws_organizations_organizational_unit.listOrganizationsOrganizationalUnits", "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)
}
}

// Call the recursive function to list all nested OUs
rootPath := parentId
err = listAllNestedOUs(ctx, d, svc, parentId, maxItems, rootPath)
if err != nil {
plugin.Logger(ctx).Error("aws_organizations_organizational_unit.listOrganizationsOrganizationalUnits", "recursive_call_error", err)
return nil, err
}

return nil, nil
}

func listAllNestedOUs(ctx context.Context, d *plugin.QueryData, svc *organizations.Client, parentId string, maxItems int32, currentPath string) error {
params := &organizations.ListOrganizationalUnitsForParentInput{
ParentId: aws.String(parentId),
MaxResults: &maxItems,
}

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

for paginator.HasMorePages() {
// apply rate limiting
output, err := paginator.NextPage(ctx)
if err != nil {
return err
}

for _, unit := range output.OrganizationalUnits {
ouPath := strings.Replace(currentPath, "-", "_", -1) + "." + strings.Replace(*unit.Id, "-", "_", -1)
d.StreamListItem(ctx, OrganizationalUnit{unit, ouPath, parentId})

// Recursively list units for this child
err := listAllNestedOUs(ctx, d, svc, *unit.Id, maxItems, ouPath)
if err != nil {
return err
}

if d.RowsRemaining(ctx) == 0 {
return nil
}
}
}

return nil
}

//// HYDRATE FUNCTIONS

func getOrganizationsOrganizationalUnit(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
orgUnitId := *h.Item.(OrganizationalUnit).Id

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

params := &organizations.DescribeOrganizationalUnitInput{
OrganizationalUnitId: aws.String(orgUnitId),
}

op, err := svc.DescribeOrganizationalUnit(ctx, params)
if err != nil {
plugin.Logger(ctx).Error("aws_organizations_organizational_unit.getOrganizationsOrganizationalUnit", "api_error", err)
return nil, err
}

return *op.OrganizationalUnit, nil
}
Loading

0 comments on commit 53aecf3

Please sign in to comment.