diff --git a/.changelog/39120.txt b/.changelog/39120.txt new file mode 100644 index 000000000000..84271c54ed65 --- /dev/null +++ b/.changelog/39120.txt @@ -0,0 +1,3 @@ +```release-note:new-data-source +aws_organizations_organizational_unit_descendant_organizational_units +``` \ No newline at end of file diff --git a/internal/service/organizations/organizational_units_data_source.go b/internal/service/organizations/organizational_units_data_source.go index 0ce4ca170b14..e922fa2720a1 100644 --- a/internal/service/organizations/organizational_units_data_source.go +++ b/internal/service/organizations/organizational_units_data_source.go @@ -62,7 +62,7 @@ func dataSourceOrganizationalUnitsRead(ctx context.Context, d *schema.ResourceDa children, err := findOrganizationalUnitsForParentByID(ctx, conn, parentID) if err != nil { - return sdkdiag.AppendErrorf(diags, "listing Organizations Organization Units for parent (%s): %s", parentID, err) + return sdkdiag.AppendErrorf(diags, "listing Organizations Organizational Units for parent (%s): %s", parentID, err) } d.SetId(parentID) diff --git a/internal/service/organizations/organizational_units_descendant_organizational_units_data_source.go b/internal/service/organizations/organizational_units_descendant_organizational_units_data_source.go new file mode 100644 index 000000000000..a9940f316555 --- /dev/null +++ b/internal/service/organizations/organizational_units_descendant_organizational_units_data_source.go @@ -0,0 +1,96 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package organizations + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/organizations" + awstypes "github.com/aws/aws-sdk-go-v2/service/organizations/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @SDKDataSource("aws_organizations_organizational_unit_descendant_organizational_units", name="Organizational Unit Descendant Organization Units") +func dataSourceOrganizationalUnitDescendantOrganizationalUnits() *schema.Resource { + return &schema.Resource{ + ReadWithoutTimeout: dataSourceOrganizationalUnitDescendantOrganizationalUnitsRead, + + Schema: map[string]*schema.Schema{ + "children": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + names.AttrARN: { + Type: schema.TypeString, + Computed: true, + }, + names.AttrID: { + Type: schema.TypeString, + Computed: true, + }, + names.AttrName: { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "parent_id": { + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func dataSourceOrganizationalUnitDescendantOrganizationalUnitsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).OrganizationsClient(ctx) + + parentID := d.Get("parent_id").(string) + organizationUnits, err := findAllOrganizationalUnitsForParentAndBelow(ctx, conn, parentID) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "listing Organizations Organizational Units for parent (%s) and descendants: %s", parentID, err) + } + + d.SetId(parentID) + + if err := d.Set("children", flattenOrganizationalUnits(organizationUnits)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting children: %s", err) + } + + return diags +} + +// findAllOrganizationalUnitsForParentAndBelow recurses down an OU tree, returning all organizational units at the specified parent and below. +func findAllOrganizationalUnitsForParentAndBelow(ctx context.Context, conn *organizations.Client, id string) ([]awstypes.OrganizationalUnit, error) { + var output []awstypes.OrganizationalUnit + + ous, err := findOrganizationalUnitsForParentByID(ctx, conn, id) + + if err != nil { + return nil, err + } + + output = append(output, ous...) + + for _, ou := range ous { + organizationUnits, err := findAllOrganizationalUnitsForParentAndBelow(ctx, conn, aws.ToString(ou.Id)) + + if err != nil { + return nil, err + } + + output = append(output, organizationUnits...) + } + + return output, nil +} diff --git a/internal/service/organizations/organizational_units_descendant_organizational_units_data_source_test.go b/internal/service/organizations/organizational_units_descendant_organizational_units_data_source_test.go new file mode 100644 index 000000000000..5ff2a86693fe --- /dev/null +++ b/internal/service/organizations/organizational_units_descendant_organizational_units_data_source_test.go @@ -0,0 +1,73 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package organizations_test + +import ( + "fmt" + "testing" + + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func testOrganizationalUnitDescendantOUsDataSource_basic(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + topOUDataSourceName := "data.aws_organizations_organizational_unit_descendant_organizational_units.current" + newOU1DataSourceName := "data.aws_organizations_organizational_unit_descendant_organizational_units.test0" + newOU2DataSourceName := "data.aws_organizations_organizational_unit_descendant_organizational_units.test1" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckOrganizationManagementAccount(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.OrganizationsServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testOrganizationalUnitDescendantOusDataSourceConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + acctest.CheckResourceAttrGreaterThanOrEqualValue(topOUDataSourceName, "children.#", 2), + resource.TestCheckResourceAttr(newOU1DataSourceName, "children.#", acctest.Ct1), + resource.TestCheckResourceAttr(newOU2DataSourceName, "children.#", acctest.Ct0), + ), + }, + }, + }) +} + +func testOrganizationalUnitDescendantOusDataSourceConfig_basic(rName string) string { + return fmt.Sprintf(` +data "aws_organizations_organization" "current" {} + +resource "aws_organizations_organizational_unit" "test0" { + name = "%[1]s-0" + parent_id = data.aws_organizations_organization.current.roots[0].id +} + +resource "aws_organizations_organizational_unit" "test1" { + name = "%[1]s-1" + parent_id = aws_organizations_organizational_unit.test0.id +} + +data "aws_organizations_organizational_unit_descendant_organizational_units" "current" { + parent_id = data.aws_organizations_organization.current.roots[0].id + + depends_on = [aws_organizations_organizational_unit.test0, aws_organizations_organizational_unit.test1] +} + +data "aws_organizations_organizational_unit_descendant_organizational_units" "test0" { + parent_id = aws_organizations_organizational_unit.test0.id + + depends_on = [aws_organizations_organizational_unit.test1] +} + +data "aws_organizations_organizational_unit_descendant_organizational_units" "test1" { + parent_id = aws_organizations_organizational_unit.test1.id +} +`, rName) +} diff --git a/internal/service/organizations/organizations_test.go b/internal/service/organizations/organizations_test.go index 9f260ffdff70..61df0c7091a8 100644 --- a/internal/service/organizations/organizations_test.go +++ b/internal/service/organizations/organizations_test.go @@ -50,6 +50,7 @@ func TestAccOrganizations_serial(t *testing.T) { "update": testAccOrganizationalUnit_update, "tags": testAccOrganizationalUnit_tags, "DataSource_basic": testAccOrganizationalUnitDataSource_basic, + "DescendantOUsDataSource_basic": testOrganizationalUnitDescendantOUsDataSource_basic, "ChildAccountsDataSource_basic": testAccOrganizationalUnitChildAccountsDataSource_basic, "DescendantAccountsDataSource_basic": testAccOrganizationalUnitDescendantAccountsDataSource_basic, "PluralDataSource_basic": testAccOrganizationalUnitsDataSource_basic, diff --git a/internal/service/organizations/service_package_gen.go b/internal/service/organizations/service_package_gen.go index 5aba3d9eedf4..8348d7ad9641 100644 --- a/internal/service/organizations/service_package_gen.go +++ b/internal/service/organizations/service_package_gen.go @@ -52,6 +52,11 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac TypeName: "aws_organizations_organizational_unit_descendant_accounts", Name: "Organizational Unit Descendant Accounts", }, + { + Factory: dataSourceOrganizationalUnitDescendantOrganizationalUnits, + TypeName: "aws_organizations_organizational_unit_descendant_organizational_units", + Name: "Organizational Unit Descendant Organization Units", + }, { Factory: dataSourceOrganizationalUnits, TypeName: "aws_organizations_organizational_units", diff --git a/website/docs/d/organizations_organizational_unit_descendant_organizational_units.html.markdown b/website/docs/d/organizations_organizational_unit_descendant_organizational_units.html.markdown new file mode 100644 index 000000000000..07c5a84789dc --- /dev/null +++ b/website/docs/d/organizations_organizational_unit_descendant_organizational_units.html.markdown @@ -0,0 +1,35 @@ +--- +subcategory: "Organizations" +layout: "aws" +page_title: "AWS: aws_organizations_organizational_unit_descendant_organizational_units" +description: |- + Get all direct child organizational units under a parent organizational unit. This provides all children. +--- + +# Data Source: aws_organizations_organizational_unit_descendant_organizational_units + +Get all direct child organizational units under a parent organizational unit. This provides all children. + +## Example Usage + +```terraform +data "aws_organizations_organization" "org" {} + +data "aws_organizations_organizational_unit_descendant_organizational_units" "ous" { + parent_id = data.aws_organizations_organization.org.roots[0].id +} +``` + +## Argument Reference + +* `parent_id` - (Required) Parent ID of the organizational unit. + +## Attribute Reference + +This data source exports the following attributes in addition to the arguments above: + +* `children` - List of child organizational units, which have the following attributes: + * `arn` - ARN of the organizational unit + * `name` - Name of the organizational unit + * `id` - ID of the organizational unit +* `id` - Parent identifier of the organizational units.