Skip to content
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

Add organizations ou child and descendant accounts data source #1

Conversation

ahublersos
Copy link
Owner

@ahublersos ahublersos commented Apr 18, 2022

This PR adds two data sources that allow the retrieval of a list of account IDs based on an AWS organizations Organizational Unit ID.

The aws_organizations_organizational_unit_child_accounts data source provides the immediate child accounts while the aws_organizations_organizational_unit_descendant_accounts provides all generations of child account IDs under the given OU.

Community Note

  • Please vote on this pull request by adding a 👍 reaction to the original pull request comment to help the community and maintainers prioritize this request
  • Please do not leave "+1" or other comments that do not add relevant new information or questions, they generate extra noise for pull request followers and do not help prioritize the request

Output from acceptance testing:
I'm still having trouble getting acceptance tests to run. I'm sure it's something simple I'm missing with the test framework. (Open to any suggestions!)
Cleanup from testing is tricky since all the accounts need to be manually deleted.

❯ make testacc TESTS=testAccOrganizationalUnitChildAccountsDataSource_basic PKG=organizations
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./internal/service/organizations/... -v -count 1 -parallel 20 -run='testAccOrganizationalUnitChildAccountsDataSource_basic'  -timeout 180m
testing: warning: no tests to run
PASS
ok  	github.com/hashicorp/terraform-provider-aws/internal/service/organizations	3.254s [no tests to run]
❯ make testacc TESTS=testAccOrganizationalUnitDescendantAccountsDataSource_basic PKG=organizations
==> Checking that code complies with gofmt requirements...
TF_ACC=1 go test ./internal/service/organizations/... -v -count 1 -parallel 20 -run='testAccOrganizationalUnitDescendantAccountsDataSource_basic'  -timeout 180m
testing: warning: no tests to run
PASS
ok  	github.com/hashicorp/terraform-provider-aws/internal/service/organizations	3.162s [no tests to run]

My test org for manual testing looks like this:

  • Root OU
    • Account "test0" (283961987728)
    • Account "ahubler-provider-test-mgmt" (831556657578)
    • OU "TestA"
    • OU "Test0"
      • Account "test1" (574221610224)
      • OU "Test1"
        • Account "test2" (137602874675)

Output from some manual testing:

root_children = {
  "accounts" = tolist([
    {
      "arn" = "arn:aws:organizations::831556657578:account/o-ux49ku5z71/831556657578"
      "email" = "ahubler@rapidsos.com"
      "id" = "831556657578"
      "joined_method" = "INVITED"
      "joined_timestamp" = "2022-04-20 14:46:44.35 +0000 UTC"
      "name" = "ahubler-provider-test-mgmt"
      "status" = "ACTIVE"
    },
    {
      "arn" = "arn:aws:organizations::831556657578:account/o-ux49ku5z71/283961987728"
      "email" = "ahubler+test0@rapidsos.com"
      "id" = "283961987728"
      "joined_method" = "CREATED"
      "joined_timestamp" = "2022-04-20 17:23:24.888 +0000 UTC"
      "name" = "test0"
      "status" = "ACTIVE"
    },
  ])
  "id" = "r-eca0"
  "parent_id" = "r-eca0"
}
root_descendant_ids = tolist([
  "831556657578",
  "283961987728",
  "574221610224",
  "137602874675",
])
test0_children = {
  "accounts" = tolist([
    {
      "arn" = "arn:aws:organizations::831556657578:account/o-ux49ku5z71/574221610224"
      "email" = "ahubler+test1@rapidsos.com"
      "id" = "574221610224"
      "joined_method" = "CREATED"
      "joined_timestamp" = "2022-04-20 17:22:56.755 +0000 UTC"
      "name" = "test1"
      "status" = "ACTIVE"
    },
  ])
  "id" = "ou-eca0-cno43299"
  "parent_id" = "ou-eca0-cno43299"
}
test0_descendants = {
  "accounts" = tolist([
    {
      "arn" = "arn:aws:organizations::831556657578:account/o-ux49ku5z71/574221610224"
      "email" = "ahubler+test1@rapidsos.com"
      "id" = "574221610224"
      "joined_method" = "CREATED"
      "joined_timestamp" = "2022-04-20 17:22:56.755 +0000 UTC"
      "name" = "test1"
      "status" = "ACTIVE"
    },
    {
      "arn" = "arn:aws:organizations::831556657578:account/o-ux49ku5z71/137602874675"
      "email" = "ahubler+test2@rapidsos.com"
      "id" = "137602874675"
      "joined_method" = "CREATED"
      "joined_timestamp" = "2022-04-20 17:26:55.225 +0000 UTC"
      "name" = "test2"
      "status" = "ACTIVE"
    },
  ])
  "id" = "ou-eca0-cno43299"
  "parent_id" = "ou-eca0-cno43299"
}
testA_children = {
  "accounts" = tolist([])
  "id" = "ou-eca0-f8gxkt9v"
  "parent_id" = "ou-eca0-f8gxkt9v"
}
testA_descendants = {
  "accounts" = tolist([])
  "id" = "ou-eca0-f8gxkt9v"
  "parent_id" = "ou-eca0-f8gxkt9v"
}

Comment on lines 92 to 109
func flattenAccounts(accounts []*organizations.Account) []map[string]interface{} {
if len(accounts) == 0 {
return nil
}
var result []map[string]interface{}
for _, account := range accounts {
result = append(result, map[string]interface{}{
"arn": aws.StringValue(account.Arn),
"email": aws.StringValue(account.Email),
"id": aws.StringValue(account.Id),
"joined_method": aws.StringValue(account.JoinedMethod),
"joined_timestamp": aws.TimeValue(account.JoinedTimestamp).String(),
"name": aws.StringValue(account.Name),
"status": aws.StringValue(account.Status),
})
}
return result
}

Choose a reason for hiding this comment

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

What's the point of this function? Isn't the input already in this format? https://docs.aws.amazon.com/sdk-for-go/api/service/organizations/#Account

Copy link
Owner Author

Choose a reason for hiding this comment

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

I believe it's main point is to get the account info into a format that fits the schema defined in DataSourceOrganizationalUnitChildAccounts. organizational_unit_child_accounts.go is based on organizational_units_data_source.go which has a similar function.

Choose a reason for hiding this comment

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

Huh, wonder if there's a typing thing I missed in the AWS SDK. Or maybe go does something weird and requires this, which wouldn't be the first time I've seen it (had to do some odd stuff to get terraform plan to be usable in tests).

}

//Base Case. ParentId has no children.
return nil, nil

Choose a reason for hiding this comment

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

Since you could have some results at this point, could you return those and then ditch the entire internal/service/organizations/organizational_unit_child_accounts.go file?

Copy link
Owner Author

Choose a reason for hiding this comment

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

organizational_unit_child_accounts.go is needed so that we can have two separate data sources. In the case where an OU has no child OUs both data sources should return the same data.

Choose a reason for hiding this comment

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

Right, so if both data sources return the same thing in that case, then we don't need the other one. Right? If you can stick all the logic into a single data source, then consumers don't need to know their organization in order to call the right one.

Copy link
Owner Author

Choose a reason for hiding this comment

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

I had it in my head that there would be a valid use case where I had an OU that contained both child OUs and immediate child accounts, and I wanted just those immediate child accounts and not the other "generations" of children. If there was only one data source you would only be able to get all generations of child accounts.

I think that's still a valid use case, but i guess it's debatable whether it's a common use case.

@@ -0,0 +1,51 @@
package organizations_test

Choose a reason for hiding this comment

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

Can you document how we run these? Is it just go test?

Copy link
Owner Author

Choose a reason for hiding this comment

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

There's some documentation here on provider acceptance testing. Just under that section is instructions for using the provider locally for manual testing. That's how I did all my testing so far. The automated tests are tricky b/c you can't easily destroy the account resources after creating them so in practicality you get one run before you have to change the tests.

"github.com/hashicorp/terraform-provider-aws/internal/acctest"
)

func testAccOrganizationalUnitDescendantAccountsDataSource_basic(t *testing.T) {

Choose a reason for hiding this comment

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

Not sure if the Terraform source tests allow for this behavior, but I assume you want to check all them. If possible, you can try the t.Run method with 0, 1, 2, 3 for that resource.TestCheckResourceAttr(dataSourceName, "accounts.#", "3"), line. https://go.dev/blog/subtests

@ahublersos ahublersos closed this Apr 21, 2022
@ahublersos ahublersos deleted the add_organizations_ou_child_and_descendant_accounts_data_source branch April 21, 2022 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants