feat: Add boundary_group data source (#503)

* refact: Move groupMemberIdsKey const

* feat: Add `boundary_group` data source

* CR
This commit is contained in:
Michael Li authored and GitHub committed 2023-11-02 14:37:02 -04:00
1 parent 13f6a2497d
commit dc3adfa96a
9 files changed
+348 -18

No files matched your search

+60
View File
@@ -0,0 +1,60 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_group Data Source - terraform-provider-boundary"
subcategory: ""
description: |-
The boundary_group data source allows you to find a Boundary group.
---
# boundary_group (Data Source)
The boundary_group data source allows you to find a Boundary group.
## Example Usage
```terraform
# Retrieve a user from the global scope
data "boundary_group" "global_group" {
name = "admin"
}
# User from an org scope
data "boundary_scope" "org" {
name = "org"
scope_id = "global"
}
data "boundary_group" "org_group" {
name = "username"
scope_id = data.boundary_scope.org.id
}
```
<!-- schema generated by tfplugindocs -->
## Schema
### Required
- `name` (String) The name of the group to retrieve.
### Optional
- `scope_id` (String) The scope ID in which the resource is created. Defaults `global` if unset.
### Read-Only
- `description` (String) The description of the retrieved group.
- `id` (String) The ID of the retrieved group.
- `member_ids` (Set of String) Resource IDs for group members, these are most likely boundary users.
- `scope` (List of Object) (see [below for nested schema](#nestedatt--scope))
<a id="nestedatt--scope"></a>
### Nested Schema for `scope`
Read-Only:
- `description` (String)
- `id` (String)
- `name` (String)
- `parent_scope_id` (String)
- `type` (String)
-2
View File
@@ -13,13 +13,11 @@ The user data source allows you to find a Boundary user.
```terraform
# Retrieve a user from the global scope
data "boundary_user" "global_scope_admin" {
name = "admin"
}
# User from a org scope
data "boundary_user" "org_user" {
name = "username"
scope_id = data.boundary_scope.org.id
@@ -0,0 +1,15 @@
# Retrieve a user from the global scope
data "boundary_group" "global_group" {
name = "admin"
}
# User from an org scope
data "boundary_scope" "org" {
name = "org"
scope_id = "global"
}
data "boundary_group" "org_group" {
name = "username"
scope_id = data.boundary_scope.org.id
}
@@ -1,11 +1,9 @@
# Retrieve a user from the global scope
data "boundary_user" "global_scope_admin" {
name = "admin"
}
# User from a org scope
data "boundary_user" "org_user" {
name = "username"
scope_id = data.boundary_scope.org.id
+9 -4
View File
@@ -46,9 +46,14 @@ const (
internalForceUpdateKey = "internal_force_update"
// workerFilter is used for common "worker_filter" resource attribute
WorkerFilterKey = "worker_filter"
// common User arguments
LoginNameKey = "login_name"
// LoginNameKey is used for common "login_name" resource attribute
LoginNameKey = "login_name"
// PrimaryAccountIdKey is used for common "primary_account_id" resource attribute
PrimaryAccountIdKey = "primary_account_id"
ScopeKey = "scope"
ParentScopeIdKey = "parent_scope_id"
// ScopeKey is used for common "scope" resource attribute
ScopeKey = "scope"
// ParentScopeKey is used for common "parent_scope_id" resource attribute
ParentScopeIdKey = "parent_scope_id"
// GroupMemberIdsKey is used for common "member_ids" resource attribute
GroupMemberIdsKey = "member_ids"
)
+144
View File
@@ -0,0 +1,144 @@
// 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/groups"
"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 dataSourceGroup() *schema.Resource {
return &schema.Resource{
Description: "The boundary_group data source allows you to find a Boundary group.",
ReadContext: dataSourceGroupRead,
Schema: map[string]*schema.Schema{
NameKey: {
Description: "The name of the group to retrieve.",
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringIsNotEmpty,
},
ScopeIdKey: {
Description: "The scope ID in which the resource is created. Defaults `global` if unset.",
Type: schema.TypeString,
Optional: true,
Default: "global",
ValidateFunc: validation.StringIsNotEmpty,
},
IDKey: {
Description: "The ID of the retrieved group.",
Type: schema.TypeString,
Computed: true,
},
DescriptionKey: {
Description: "The description of the retrieved group.",
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,
},
},
},
},
GroupMemberIdsKey: {
Description: "Resource IDs for group members, these are most likely boundary users.",
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
},
},
}
}
func dataSourceGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
name := d.Get(NameKey).(string)
scopeId := d.Get(ScopeIdKey).(string)
gcl := groups.NewClient(md.client)
groupsList, err := gcl.List(ctx, scopeId,
groups.WithFilter(FilterWithItemNameMatches(name)),
)
if err != nil {
return diag.Errorf("error calling list group: %v", err)
}
groups := groupsList.GetItems()
if groups == nil {
return diag.Errorf("no groups found")
}
if len(groups) == 0 {
return diag.Errorf("no matching group found")
}
if len(groups) > 1 {
return diag.Errorf("error found more than 1 group")
}
grr, err := gcl.Read(ctx, groups[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 group: %v", err)
}
if grr == nil {
return diag.Errorf("group nil after read")
}
if err := setFromGroupRead(d, *grr.Item); err != nil {
return diag.FromErr(err)
}
return nil
}
func setFromGroupRead(d *schema.ResourceData, group groups.Group) error {
if err := d.Set(NameKey, group.Name); err != nil {
return err
}
if err := d.Set(DescriptionKey, group.Description); err != nil {
return err
}
if err := d.Set(ScopeIdKey, group.ScopeId); err != nil {
return err
}
if err := d.Set(GroupMemberIdsKey, group.MemberIds); err != nil {
return err
}
d.Set(ScopeKey, flattenScopeInfo(group.Scope))
d.SetId(group.Id)
return nil
}
+113
View File
@@ -0,0 +1,113 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package provider
import (
"fmt"
"testing"
"github.com/hashicorp/boundary/testing/controller"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
testGroupName = "test_group"
)
var groupReadGlobal = fmt.Sprintf(`
resource "boundary_user" "user" {
description = "user"
scope_id = "global"
depends_on = [boundary_role.org1_admin]
}
resource "boundary_group" "group" {
name = "%s"
description = "test"
scope_id = "global"
member_ids = [boundary_user.user.id]
depends_on = [boundary_user.user]
}
data "boundary_group" "group" {
depends_on = [ boundary_group.group ]
name = "%s"
}`, testGroupName, testGroupName)
var groupReadOrg = fmt.Sprintf(`
resource "boundary_user" "user" {
description = "user"
scope_id = boundary_scope.org1.id
depends_on = [boundary_role.org1_admin]
}
resource "boundary_group" "group" {
name = "%s"
description = "test"
scope_id = boundary_scope.org1.id
member_ids = [boundary_user.user.id]
depends_on = [boundary_user.user]
}
data "boundary_group" "group" {
depends_on = [ boundary_group.group ]
name = "%s"
scope_id = boundary_scope.org1.id
}`, testGroupName, testGroupName)
func TestAccGroupReadGlobal(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, groupReadGlobal),
Check: resource.ComposeTestCheckFunc(
testAccCheckGroupResourceExists(provider, "boundary_group.group"),
resource.TestCheckResourceAttrSet("data.boundary_group.group", IDKey),
resource.TestCheckResourceAttrSet("data.boundary_group.group", ScopeIdKey),
resource.TestCheckResourceAttr("data.boundary_group.group", NameKey, testGroupName),
resource.TestCheckResourceAttrSet("data.boundary_group.group", DescriptionKey),
resource.TestCheckResourceAttrSet("data.boundary_group.group", fmt.Sprintf("%s.#", GroupMemberIdsKey)),
resource.TestCheckResourceAttrSet("data.boundary_group.group", "scope.0.id"),
resource.TestCheckResourceAttr("data.boundary_group.group", "scope.0.name", "global"),
resource.TestCheckResourceAttr("data.boundary_group.group", "scope.0.type", "global"),
),
},
},
})
}
func TestAccGroupReadOrg(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, groupReadOrg),
Check: resource.ComposeTestCheckFunc(
testAccCheckGroupResourceExists(provider, "boundary_group.group"),
resource.TestCheckResourceAttrSet("data.boundary_group.group", IDKey),
resource.TestCheckResourceAttrSet("data.boundary_group.group", ScopeIdKey),
resource.TestCheckResourceAttr("data.boundary_group.group", NameKey, testGroupName),
resource.TestCheckResourceAttrSet("data.boundary_group.group", DescriptionKey),
resource.TestCheckResourceAttrSet("data.boundary_group.group", fmt.Sprintf("%s.#", GroupMemberIdsKey)),
resource.TestCheckResourceAttrSet("data.boundary_group.group", "scope.0.id"),
resource.TestCheckResourceAttr("data.boundary_group.group", "scope.0.name", "org1"),
resource.TestCheckResourceAttr("data.boundary_group.group", "scope.0.type", "org"),
),
},
},
})
}
+1
View File
@@ -129,6 +129,7 @@ func New() *schema.Provider {
},
DataSourcesMap: map[string]*schema.Resource{
"boundary_account": dataSourceAccount(),
"boundary_group": dataSourceGroup(),
"boundary_scope": dataSourceScope(),
"boundary_user": dataSourceUser(),
},
+6 -10
View File
@@ -13,10 +13,6 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
const (
groupMemberIdsKey = "member_ids"
)
func resourceGroup() *schema.Resource {
return &schema.Resource{
Description: "The group resource allows you to configure a Boundary group.",
@@ -51,7 +47,7 @@ func resourceGroup() *schema.Resource {
Required: true,
ForceNew: true,
},
groupMemberIdsKey: {
GroupMemberIdsKey: {
Description: "Resource IDs for group members, these are most likely boundary users.",
Type: schema.TypeSet,
Optional: true,
@@ -71,7 +67,7 @@ func setFromGroupResponseMap(d *schema.ResourceData, raw map[string]interface{})
if err := d.Set(ScopeIdKey, raw["scope_id"]); err != nil {
return err
}
if err := d.Set(groupMemberIdsKey, raw["member_ids"]); err != nil {
if err := d.Set(GroupMemberIdsKey, raw["member_ids"]); err != nil {
return err
}
d.SetId(raw["id"].(string))
@@ -118,7 +114,7 @@ func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta inter
}
}()
if val, ok := d.GetOk(groupMemberIdsKey); ok {
if val, ok := d.GetOk(GroupMemberIdsKey); ok {
list := val.(*schema.Set).List()
memberIds := make([]string, 0, len(list))
for _, i := range list {
@@ -209,9 +205,9 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter
// The above call may not actually happen, so we use d.Id() and automatic
// versioning here
if d.HasChange(groupMemberIdsKey) {
if d.HasChange(GroupMemberIdsKey) {
var memberIds []string
if membersVal, ok := d.GetOk(groupMemberIdsKey); ok {
if membersVal, ok := d.GetOk(GroupMemberIdsKey); ok {
members := membersVal.(*schema.Set).List()
for _, member := range members {
memberIds = append(memberIds, member.(string))
@@ -222,7 +218,7 @@ func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta inter
if err != nil {
return diag.Errorf("error updating members in group: %v", err)
}
if err := d.Set(groupMemberIdsKey, memberIds); err != nil {
if err := d.Set(GroupMemberIdsKey, memberIds); err != nil {
return diag.FromErr(err)
}