-
Notifications
You must be signed in to change notification settings - Fork 157
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 Data Source: Organizations & Organization #320
Merged
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
b686f96
Add admin orgs
omarismail 54e9272
use string
omarismail cf5fd93
use proper format
omarismail a856468
WIP
omarismail 7fd1d0c
Add org
omarismail 0badad1
Add tests. TODO: cleanup, more tests
omarismail 5b77dd8
Merge branch 'master' into omar/org-data-soruce
omarismail c5c2576
WIP
omarismail 5bf9369
Add tests
omarismail cfda956
use interface
omarismail f569630
Add docs
omarismail 9206219
doc typo
omarismail 47c1f6e
update readme for documentaiton
omarismail 5ad629b
cleanup unused, cleanup docs
omarismail 78bee7d
remove attr
omarismail 32259cd
forgot something
omarismail 57af0fa
doc cleanup
omarismail 0fda0ac
Add changelog
omarismail File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package tfe | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
tfe "github.com/hashicorp/go-tfe" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
func dataSourceTFEOrganization() *schema.Resource { | ||
return &schema.Resource{ | ||
Read: dataSourceTFEOrganizationRead, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
|
||
"external_id": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
|
||
"collaborator_auth_policy": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
|
||
"cost_estimation_enabled": { | ||
Type: schema.TypeBool, | ||
Computed: true, | ||
}, | ||
|
||
"email": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
|
||
"owners_team_saml_role_id": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
|
||
"two_factor_conformant": { | ||
Type: schema.TypeBool, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func dataSourceTFEOrganizationRead(d *schema.ResourceData, meta interface{}) error { | ||
tfeClient := meta.(*tfe.Client) | ||
|
||
name := d.Get("name").(string) | ||
log.Printf("[DEBUG] Read configuration for Organization: %s", name) | ||
org, err := tfeClient.Organizations.Read(ctx, name) | ||
if err != nil { | ||
if err == tfe.ErrResourceNotFound { | ||
return fmt.Errorf("Could not read organization '%s'", name) | ||
} | ||
return fmt.Errorf("Error retrieving organization: %v", err) | ||
} | ||
|
||
log.Printf("[DEBUG] Setting Organization Attributes") | ||
d.SetId(org.ExternalID) | ||
d.Set("name", org.Name) | ||
d.Set("external_id", org.ExternalID) | ||
d.Set("collaborator_auth_policy", org.CollaboratorAuthPolicy) | ||
d.Set("cost_estimation_enabled", org.CostEstimationEnabled) | ||
d.Set("email", org.Email) | ||
d.Set("owners_team_saml_role_id", org.OwnersTeamSAMLRoleID) | ||
d.Set("two_factor_conformant", org.TwoFactorConformant) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package tfe | ||
|
||
import ( | ||
"fmt" | ||
"math/rand" | ||
"testing" | ||
"time" | ||
|
||
tfe "github.com/hashicorp/go-tfe" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
) | ||
|
||
func TestAccTFEOrganizationDataSource_basic(t *testing.T) { | ||
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() | ||
org := &tfe.Organization{} | ||
orgName := fmt.Sprintf("tst-terraform-foo-%d", rInt) | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccTFEOrganizationDataSourceConfig_basic(rInt), | ||
Check: resource.ComposeAggregateTestCheckFunc( | ||
testAccCheckTFEOrganizationExists("tfe_organization.foo", org), | ||
// check resource attrs | ||
resource.TestCheckResourceAttr("tfe_organization.foo", "name", orgName), | ||
resource.TestCheckResourceAttr("tfe_organization.foo", "email", "admin@company.com"), | ||
|
||
// check data attrs | ||
resource.TestCheckResourceAttr("data.tfe_organization.foo", "name", orgName), | ||
resource.TestCheckResourceAttr("data.tfe_organization.foo", "email", "admin@company.com"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccTFEOrganizationDataSourceConfig_basic(rInt int) string { | ||
return fmt.Sprintf(` | ||
resource "tfe_organization" "foo" { | ||
name = "tst-terraform-foo-%d" | ||
email = "admin@company.com" | ||
} | ||
|
||
data "tfe_organization" "foo" { | ||
name = tfe_organization.foo.name | ||
}`, rInt) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package tfe | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
|
||
tfe "github.com/hashicorp/go-tfe" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
func dataSourceTFEOrganizations() *schema.Resource { | ||
return &schema.Resource{ | ||
Read: dataSourceTFEOrganizationList, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"names": { | ||
Type: schema.TypeList, | ||
Computed: true, | ||
Elem: &schema.Schema{Type: schema.TypeString}, | ||
}, | ||
|
||
"ids": { | ||
Type: schema.TypeMap, | ||
Computed: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func dataSourceTFEOrganizationList(d *schema.ResourceData, meta interface{}) error { | ||
tfeClient := meta.(*tfe.Client) | ||
|
||
log.Printf("[DEBUG] Listing all organizations") | ||
orgs, err := tfeClient.Organizations.List(ctx, tfe.OrganizationListOptions{}) | ||
if err != nil { | ||
if err == tfe.ErrResourceNotFound { | ||
return fmt.Errorf("Could not list organizations.") | ||
} | ||
return fmt.Errorf("Error retrieving organizations: %v.", err) | ||
} | ||
|
||
names := []string{} | ||
ids := map[string]string{} | ||
for _, org := range orgs.Items { | ||
ids[org.Name] = org.ExternalID | ||
names = append(names, org.Name) | ||
} | ||
|
||
log.Printf("[DEBUG] Setting Organizations Attributes") | ||
d.SetId("organizations") | ||
d.Set("names", names) | ||
d.Set("ids", ids) | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package tfe | ||
|
||
import ( | ||
"fmt" | ||
"math/rand" | ||
"strconv" | ||
"testing" | ||
"time" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform" | ||
) | ||
|
||
func TestAccTFEOrganizationsDataSource_basic(t *testing.T) { | ||
rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int() | ||
|
||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccTFEOrganizationsDataSourceConfig_basic_resource(rInt), | ||
}, | ||
{ | ||
Config: testAccTFEOrganizationsDataSourceConfig_basic_data(), | ||
Check: resource.ComposeAggregateTestCheckFunc( | ||
testAccCheckTFEOrganizationHasNames("data.tfe_organizations.foobarbaz", []string{ | ||
fmt.Sprintf("tst-terraform-foo-%d", rInt), | ||
fmt.Sprintf("tst-terraform-bar-%d", rInt), | ||
fmt.Sprintf("tst-terraform-baz-%d", rInt), | ||
}), | ||
testAccCheckTFEOrganizationHasIDs("data.tfe_organizations.foobarbaz", []string{ | ||
fmt.Sprintf("tst-terraform-foo-%d", rInt), | ||
fmt.Sprintf("tst-terraform-bar-%d", rInt), | ||
fmt.Sprintf("tst-terraform-baz-%d", rInt), | ||
}), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckTFEOrganizationHasNames(dataOrg string, orgNames []string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
org, ok := s.RootModule().Resources[dataOrg] | ||
if !ok { | ||
return fmt.Errorf("Data organization '%s' not found.", dataOrg) | ||
} | ||
numOrgsStr := org.Primary.Attributes["names.#"] | ||
numOrgs, _ := strconv.Atoi(numOrgsStr) | ||
|
||
if numOrgs < len(orgNames) { | ||
return fmt.Errorf("Expected at least %d organizations, but found %d.", len(orgNames), numOrgs) | ||
} | ||
|
||
allOrgsMap := map[string]struct{}{} | ||
for i := 0; i < numOrgs; i++ { | ||
orgName := org.Primary.Attributes[fmt.Sprintf("names.%d", i)] | ||
allOrgsMap[orgName] = struct{}{} | ||
} | ||
|
||
for _, orgName := range orgNames { | ||
_, ok := allOrgsMap[orgName] | ||
if !ok { | ||
return fmt.Errorf("Expected to find organization name %s, but did not.", orgName) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccCheckTFEOrganizationHasIDs(dataOrg string, orgNames []string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
org, ok := s.RootModule().Resources[dataOrg] | ||
if !ok { | ||
return fmt.Errorf("Organization not found: %s.", dataOrg) | ||
} | ||
|
||
for _, orgName := range orgNames { | ||
id := fmt.Sprintf("ids.%s", orgName) | ||
_, ok := org.Primary.Attributes[id] | ||
if !ok { | ||
return fmt.Errorf("Expected to find organization id %s, but did not.", id) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccTFEOrganizationsDataSourceConfig_basic_resource(rInt int) string { | ||
return fmt.Sprintf(` | ||
resource "tfe_organization" "foo" { | ||
name = "tst-terraform-foo-%d" | ||
email = "admin@company.com" | ||
} | ||
|
||
resource "tfe_organization" "bar" { | ||
name = "tst-terraform-bar-%d" | ||
email = "admin@company.com" | ||
} | ||
|
||
resource "tfe_organization" "baz" { | ||
name = "tst-terraform-baz-%d" | ||
email = "admin@company.com" | ||
}`, rInt, rInt, rInt) | ||
} | ||
|
||
func testAccTFEOrganizationsDataSourceConfig_basic_data() string { | ||
return `data "tfe_organizations" "foobarbaz" {}` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
--- | ||
layout: "tfe" | ||
page_title: "Terraform Enterprise: tfe_organization" | ||
sidebar_current: "docs-datasource-tfe-organization" | ||
description: |- | ||
Get information on an Organization. | ||
--- | ||
|
||
# Data Source: tfe_organization | ||
|
||
Use this data source to get information about an organization. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
data "tfe_organization" "foo" { | ||
name = "organization-name" | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
* `name` - (Required) Name of the organization. | ||
|
||
## Attributes Reference | ||
|
||
In addition to all arguments above, the following attributes are exported: | ||
|
||
* `name` - Name of the organization. | ||
* `email` - Admin email address. | ||
* `external_id` - An identifier for the organization. | ||
* `collaborator_auth_policy` - Authentication policy (`password` | ||
or `two_factor_mandatory`). Defaults to `password`. | ||
* `cost_estimation_enabled` - Whether or not the cost estimation feature is enabled for all workspaces in the organization. Defaults to true. In a Terraform Cloud organization which does not have Teams & Governance features, this value is always false and cannot be changed. In Terraform Enterprise, Cost Estimation must also be enabled in Site Administration. | ||
* `owners_team_saml_role_id` - The name of the "owners" team. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
--- | ||
layout: "tfe" | ||
page_title: "Terraform Enterprise: tfe_organizations" | ||
sidebar_current: "docs-datasource-tfe-organizations" | ||
description: |- | ||
Get information on Organizations. | ||
--- | ||
|
||
# Data Source: tfe_organizations | ||
|
||
Use this data source to get a list of Organizations and a map of their IDs. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
data "tfe_organizations" "foo" { | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
No arguments are required. This retrieves the names and IDs of all the organizations readable by the provided token. | ||
|
||
## Attributes Reference | ||
|
||
The following attributes are exported: | ||
|
||
* `names` - A list of names of every organization. | ||
* `ids` - A map of organization names and their IDs. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this is necessary? For workspaces,
names
is a required argument you must provide; here in organizations, you're just fetching all organizations accessible by the authenticated token.Since
ids
(which matches the equivalent workspaces one) includes both names and IDs, this just feels like duplication, no?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put
names
here as well because we use the organization'sname
as a primary identifier throughout the API, and I figured a common usage pattern is that users would query this data source and iterate through the names. That being said, one could also just iterate throughids
(IDs/Names map) and do an action using thename
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, I was actually wondering the opposite! We use
id
elsewhere in the provider to mean the organization's name, so it seems like it could be confusing to useid
to mean the organization's external id 😬 plus you can't really use an organization's external id for anything right now, though of course that may change in the future.Possibly terrible idea: what if we left
ids
out of this data source completely and hadnames
as the only attribute?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that was my thought
The fact that we don't ever use external IDs for orgs is problematic, and I hope we make some enhancements to allow for either in the future and actually change things like the reference CJ linked, but I can also appreciate that it might be overoptimizing for now. I think
names
as the only attribute is just fine, actually! Maybe we could do that and leave the ID map for when (because we probably will) we migrater/tfe_organization.id
over 🤷♂️There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm in favor keeping
names
. The user is used to using the organization name to execute operations, so now seeing "ids" only (and notname
) will be confusing. But like CJ pointed, we setid
elsewhere as name where its not actually the ID (external_id) but in fact is a name...😢.I'm actually in favor of keeping both.
names
because the user is already used to using that as the primary "id". And keeping theids
map, even though it may not be immediately used, is good because we can put the pieces in place for a future migration to start using external_id as the primary identifier instead of names.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good discussion, sounds good to me!