diff --git a/docs/data-sources/account.md b/docs/data-sources/account.md new file mode 100644 index 00000000..7a16621a --- /dev/null +++ b/docs/data-sources/account.md @@ -0,0 +1,47 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "boundary_account Data Source - terraform-provider-boundary" +subcategory: "" +description: |- + The boundary_account data source allows you to find a Boundary account. +--- + +# boundary_account (Data Source) + +The boundary_account data source allows you to find a Boundary account. + +## Example Usage + +```terraform +# Retrieve the ID of a Boundary account +data "boundary_account" "admin" { + name = "admin" + auth_method_id = "ampw_1234567890" +} +``` + + +## Schema + +### Required + +- `auth_method_id` (String) The auth method ID that will be queried for the account. +- `name` (String) The name of the account to retrieve. + +### Read-Only + +- `description` (String) The description of the retrieved account. +- `id` (String) The ID of the retrieved account. +- `scope` (List of Object) (see [below for nested schema](#nestedatt--scope)) +- `type` (String) The type of the account + + +### Nested Schema for `scope` + +Read-Only: + +- `description` (String) +- `id` (String) +- `name` (String) +- `parent_scope_id` (String) +- `type` (String) diff --git a/examples/data-sources/boundary_account/data-source.tf b/examples/data-sources/boundary_account/data-source.tf new file mode 100644 index 00000000..d9214418 --- /dev/null +++ b/examples/data-sources/boundary_account/data-source.tf @@ -0,0 +1,5 @@ +# Retrieve the ID of a Boundary account +data "boundary_account" "admin" { + name = "admin" + auth_method_id = "ampw_1234567890" +} diff --git a/internal/provider/data_source_account.go b/internal/provider/data_source_account.go new file mode 100644 index 00000000..9a24d788 --- /dev/null +++ b/internal/provider/data_source_account.go @@ -0,0 +1,139 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "context" + "net/http" + + "github.com/hashicorp/boundary/api" + "github.com/hashicorp/boundary/api/accounts" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func dataSourceAccount() *schema.Resource { + return &schema.Resource{ + Description: "The boundary_account data source allows you to find a Boundary account.", + ReadContext: dataSourceAccountRead, + + Schema: map[string]*schema.Schema{ + NameKey: { + Description: "The name of the account to retrieve.", + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + AuthMethodIdKey: { + Description: "The auth method ID that will be queried for the account.", + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + IDKey: { + Description: "The ID of the retrieved account.", + Type: schema.TypeString, + Computed: true, + }, + DescriptionKey: { + Description: "The description of the retrieved account.", + Type: schema.TypeString, + Computed: true, + }, + TypeKey: { + Description: "The type of the account", + Type: schema.TypeString, + Computed: true, + }, + ScopeKey: { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + IDKey: { + Type: schema.TypeString, + Computed: true, + }, + NameKey: { + Type: schema.TypeString, + Computed: true, + }, + TypeKey: { + Type: schema.TypeString, + Computed: true, + }, + DescriptionKey: { + Type: schema.TypeString, + Computed: true, + }, + ParentScopeIdKey: { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceAccountRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + md := meta.(*metaData) + + name := d.Get(NameKey).(string) + authMethodId := d.Get(AuthMethodIdKey).(string) + + acl := accounts.NewClient(md.client) + accountsList, err := acl.List(ctx, authMethodId, + accounts.WithFilter(FilterWithItemNameMatches(name)), + ) + if err != nil { + return diag.Errorf("error calling list account: %v", err) + } + accounts := accountsList.GetItems() + if accounts == nil { + return diag.Errorf("no accounts found") + } + if len(accounts) == 0 { + return diag.Errorf("no matching account found") + } + if len(accounts) > 1 { + return diag.Errorf("error found more than 1 account") + } + + arr, err := acl.Read(ctx, accounts[0].Id) + if err != nil { + if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound { + d.SetId("") + return nil + } + return diag.Errorf("error calling read account: %v", err) + } + if arr == nil { + return diag.Errorf("account nil after read") + } + + if err := setFromAccountRead(d, *arr.Item); err != nil { + return diag.FromErr(err) + } + + return nil +} + +func setFromAccountRead(d *schema.ResourceData, account accounts.Account) error { + if err := d.Set(NameKey, account.Name); err != nil { + return err + } + if err := d.Set(DescriptionKey, account.Description); err != nil { + return err + } + if err := d.Set(TypeKey, account.Type); err != nil { + return err + } + + d.Set(ScopeKey, flattenScopeInfo(account.Scope)) + d.SetId(account.Id) + return nil +} diff --git a/internal/provider/data_source_account_test.go b/internal/provider/data_source_account_test.go new file mode 100644 index 00000000..a4ff19e4 --- /dev/null +++ b/internal/provider/data_source_account_test.go @@ -0,0 +1,121 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/boundary/testing/controller" + "github.com/hashicorp/cap/oidc" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var accountPasswordRead = ` +data "boundary_account" "acc_password" { + depends_on = [ boundary_account_password.foo ] + name = "test" + auth_method_id = boundary_auth_method.foo.id +}` + +var accountLdapRead = fmt.Sprintf(` +data "boundary_account" "acc_ldap" { + depends_on = [ boundary_account_ldap.foo ] + name = "%s" + auth_method_id = boundary_auth_method_ldap.foo.id +}`, testAccountLdapName) + +var accountOidcRead = ` +data "boundary_account" "acc_oidc" { + depends_on = [ boundary_account_oidc.foo ] + name = "test" + auth_method_id = boundary_auth_method_oidc.foo.id +}` + +func TestAccAccountReadPassword(t *testing.T) { + tc := controller.NewTestController(t, tcConfig...) + defer tc.Shutdown() + url := tc.ApiAddrs()[0] + + var provider *schema.Provider + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories(&provider), + Steps: []resource.TestStep{ + { + Config: testConfig(url, fooOrg, fooAccountPassword, accountPasswordRead), + Check: resource.ComposeTestCheckFunc( + testAccCheckAccountResourceExists(provider, "boundary_account_password.foo"), + resource.TestCheckResourceAttrSet("data.boundary_account.acc_password", IDKey), + resource.TestCheckResourceAttrSet("data.boundary_account.acc_password", AuthMethodIdKey), + resource.TestCheckResourceAttrSet("data.boundary_account.acc_password", NameKey), + resource.TestCheckResourceAttr("data.boundary_account.acc_password", DescriptionKey, fooAccountPasswordDesc), + resource.TestCheckResourceAttr("data.boundary_account.acc_password", TypeKey, "password"), + resource.TestCheckResourceAttrSet("data.boundary_account.acc_password", "scope.0.id"), + resource.TestCheckResourceAttr("data.boundary_account.acc_password", "scope.0.name", "org1"), + resource.TestCheckResourceAttr("data.boundary_account.acc_password", "scope.0.type", "org"), + ), + }, + }, + }) +} + +func TestAccAccountReadLdap(t *testing.T) { + tc := controller.NewTestController(t, tcConfig...) + defer tc.Shutdown() + url := tc.ApiAddrs()[0] + + var provider *schema.Provider + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories(&provider), + Steps: []resource.TestStep{ + { + Config: testConfig(url, fooOrg, testAccountLdap, accountLdapRead), + Check: resource.ComposeTestCheckFunc( + testAccCheckAccountResourceExists(provider, "boundary_account_ldap.foo"), + resource.TestCheckResourceAttrSet("data.boundary_account.acc_ldap", IDKey), + resource.TestCheckResourceAttrSet("data.boundary_account.acc_ldap", AuthMethodIdKey), + resource.TestCheckResourceAttrSet("data.boundary_account.acc_ldap", NameKey), + resource.TestCheckResourceAttr("data.boundary_account.acc_ldap", DescriptionKey, testAccountLdapDesc), + resource.TestCheckResourceAttr("data.boundary_account.acc_ldap", TypeKey, "ldap"), + resource.TestCheckResourceAttrSet("data.boundary_account.acc_ldap", "scope.0.id"), + resource.TestCheckResourceAttr("data.boundary_account.acc_ldap", "scope.0.name", "org1"), + resource.TestCheckResourceAttr("data.boundary_account.acc_ldap", "scope.0.type", "org"), + ), + }, + }, + }) +} + +func TestAccAccountReadOidc(t *testing.T) { + tp := oidc.StartTestProvider(t) + tc := controller.NewTestController(t, tcConfig...) + defer tc.Shutdown() + url := tc.ApiAddrs()[0] + + var provider *schema.Provider + tpCert := strings.TrimSpace(tp.CACert()) + createConfig := fmt.Sprintf(fooAccountOidc, tp.Addr(), tpCert, fooAccountOidcDesc, tp.ExpectedSubject(), tp.Addr()) + + resource.Test(t, resource.TestCase{ + ProviderFactories: providerFactories(&provider), + Steps: []resource.TestStep{ + { + Config: testConfig(url, fooOrg, createConfig, accountOidcRead), + Check: resource.ComposeTestCheckFunc( + testAccCheckAccountResourceExists(provider, "boundary_account_oidc.foo"), + resource.TestCheckResourceAttrSet("data.boundary_account.acc_oidc", IDKey), + resource.TestCheckResourceAttrSet("data.boundary_account.acc_oidc", AuthMethodIdKey), + resource.TestCheckResourceAttrSet("data.boundary_account.acc_oidc", NameKey), + resource.TestCheckResourceAttr("data.boundary_account.acc_oidc", DescriptionKey, fooAccountOidcDesc), + resource.TestCheckResourceAttr("data.boundary_account.acc_oidc", TypeKey, "oidc"), + resource.TestCheckResourceAttrSet("data.boundary_account.acc_oidc", "scope.0.id"), + resource.TestCheckResourceAttr("data.boundary_account.acc_oidc", "scope.0.name", "org1"), + resource.TestCheckResourceAttr("data.boundary_account.acc_oidc", "scope.0.type", "org"), + ), + }, + }, + }) +} diff --git a/internal/provider/data_source_user.go b/internal/provider/data_source_user.go index e59e0d61..0a6c49cc 100644 --- a/internal/provider/data_source_user.go +++ b/internal/provider/data_source_user.go @@ -6,7 +6,6 @@ package provider import ( "context" - "github.com/hashicorp/boundary/api/scopes" "github.com/hashicorp/boundary/api/users" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -160,29 +159,3 @@ func setFromUserItem(d *schema.ResourceData, user users.User) error { d.SetId(user.Id) return nil } - -func flattenScopeInfo(scope *scopes.ScopeInfo) []interface{} { - if scope == nil { - return []interface{}{} - } - - m := make(map[string]interface{}) - - if v := scope.Id; v != "" { - m[IDKey] = v - } - if v := scope.Type; v != "" { - m[TypeKey] = v - } - if v := scope.Description; v != "" { - m[DescriptionKey] = v - } - if v := scope.ParentScopeId; v != "" { - m[ParentScopeIdKey] = v - } - if v := scope.Name; v != "" { - m[NameKey] = v - } - - return []interface{}{m} -} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 0ab7fca7..83cf8551 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -128,8 +128,9 @@ func New() *schema.Provider { "boundary_worker": resourceWorker(), }, DataSourcesMap: map[string]*schema.Resource{ - "boundary_scope": dataSourceScope(), - "boundary_user": dataSourceUser(), + "boundary_account": dataSourceAccount(), + "boundary_scope": dataSourceScope(), + "boundary_user": dataSourceUser(), }, } diff --git a/internal/provider/scope.go b/internal/provider/scope.go new file mode 100644 index 00000000..da1461d6 --- /dev/null +++ b/internal/provider/scope.go @@ -0,0 +1,32 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import "github.com/hashicorp/boundary/api/scopes" + +func flattenScopeInfo(scope *scopes.ScopeInfo) []interface{} { + if scope == nil { + return []interface{}{} + } + + m := make(map[string]interface{}) + + if v := scope.Id; v != "" { + m[IDKey] = v + } + if v := scope.Type; v != "" { + m[TypeKey] = v + } + if v := scope.Description; v != "" { + m[DescriptionKey] = v + } + if v := scope.ParentScopeId; v != "" { + m[ParentScopeIdKey] = v + } + if v := scope.Name; v != "" { + m[NameKey] = v + } + + return []interface{}{m} +}