diff --git a/RESOURCES.md b/RESOURCES.md
index 31da8864d9..f509264d9d 100644
--- a/RESOURCES.md
+++ b/RESOURCES.md
@@ -69,7 +69,7 @@ The overall status of each resource or data source is captured in this document
| `github_dependabot_public_key` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ |
| `github_dependabot_secrets` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ |
| `github_enterprise` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ |
-| `github_external_groups` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ |
+| `github_external_groups` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| `github_ip_ranges` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ |
| `github_issue_labels` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ |
| `github_membership` | ⚠️ | ✅ | ❓ | ❓ | ❓ | ❓ |
diff --git a/docs/data-sources/external_groups.md b/docs/data-sources/external_groups.md
index 7823355c6f..60a74c5d16 100644
--- a/docs/data-sources/external_groups.md
+++ b/docs/data-sources/external_groups.md
@@ -1,37 +1,58 @@
---
page_title: "github_external_groups (Data Source) - GitHub"
+subcategory: ""
description: |-
- Retrieve external groups belonging to an organization.
+ Data source to list all external groups in an organization.
---
# github_external_groups (Data Source)
-Use this data source to retrieve external groups belonging to an organization.
+Data source to list all external groups in an organization.
## Example Usage
```terraform
-data "github_external_groups" "example_external_groups" {}
+data "github_external_groups" "example" {}
+```
-locals {
- local_groups = data.github_external_groups.example_external_groups
-}
+
+
+## Schema
+
+### Optional
+
+- `display_name_filter` (String) Filter external groups by display name.
+
+### Read-Only
+
+- `external_groups` (List of Object) List of external groups in the organization. (see [below for nested schema](#nestedatt--external_groups))
+- `id` (String) The ID of this resource.
+
+
+### Nested Schema for `external_groups`
+
+Read-Only:
-- `group_id` - the ID of the group.
-- `group_name` - the name of the group.
-- `updated_at` - the date the group was last updated.
+- `group_id` (Number) ID of the external group.
+- `group_name` (String) Name of the external group.
+- `updated_at` (String) Timestamp of the last update to the external group.
diff --git a/examples/data-sources/external_groups/example_1.tf b/examples/data-sources/external_groups/example_1.tf
deleted file mode 100644
index 6c0f3976c1..0000000000
--- a/examples/data-sources/external_groups/example_1.tf
+++ /dev/null
@@ -1,9 +0,0 @@
-data "github_external_groups" "example_external_groups" {}
-
-locals {
- local_groups = data.github_external_groups.example_external_groups
-}
-
-output "groups" {
- value = local.local_groups
-}
diff --git a/examples/data-sources/github_external_groups/data-source_1.tf b/examples/data-sources/github_external_groups/data-source_1.tf
new file mode 100644
index 0000000000..a4be2f083e
--- /dev/null
+++ b/examples/data-sources/github_external_groups/data-source_1.tf
@@ -0,0 +1 @@
+data "github_external_groups" "example" {}
diff --git a/github/acc_test.go b/github/acc_test.go
index 191d5b9e97..d623abeec1 100644
--- a/github/acc_test.go
+++ b/github/acc_test.go
@@ -73,7 +73,10 @@ type testAccConfig struct {
testExternalUser2 string
// Enterprise test configuration
- testEnterpriseEMUGroupId int
+ testEnterpriseEMUGroupId int
+ testExternalGroup1ID int
+ testExternalGroup1DisplayName string
+ testExternalGroup2ID int
// Test options
testAdvancedSecurity bool
diff --git a/github/data_source_github_external_groups.go b/github/data_source_github_external_groups.go
index da3be6b2eb..b57cf336f4 100644
--- a/github/data_source_github_external_groups.go
+++ b/github/data_source_github_external_groups.go
@@ -2,8 +2,6 @@ package github
import (
"context"
- "encoding/json"
- "fmt"
"github.com/google/go-github/v89/github"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
@@ -13,23 +11,33 @@ import (
func dataSourceGithubExternalGroups() *schema.Resource {
return &schema.Resource{
ReadContext: dataSourceGithubExternalGroupsRead,
+ Description: "Data source to list all external groups in an organization.",
Schema: map[string]*schema.Schema{
+ "display_name_filter": {
+ Description: "Filter external groups by display name.",
+ Type: schema.TypeString,
+ Optional: true,
+ },
"external_groups": {
- Type: schema.TypeList,
- Computed: true,
+ Description: "List of external groups in the organization.",
+ Type: schema.TypeList,
+ Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"group_id": {
- Type: schema.TypeInt,
- Computed: true,
+ Description: "ID of the external group.",
+ Type: schema.TypeInt,
+ Computed: true,
},
"group_name": {
- Type: schema.TypeString,
- Computed: true,
+ Description: "Name of the external group.",
+ Type: schema.TypeString,
+ Computed: true,
},
"updated_at": {
- Type: schema.TypeString,
- Computed: true,
+ Description: "Timestamp of the last update to the external group.",
+ Type: schema.TypeString,
+ Computed: true,
},
},
},
@@ -38,48 +46,50 @@ func dataSourceGithubExternalGroups() *schema.Resource {
}
}
-func dataSourceGithubExternalGroupsRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
- err := checkOrganization(meta)
- if err != nil {
+func dataSourceGithubExternalGroupsRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
+ meta, _ := m.(*Owner)
+
+ if err := checkOrganization(meta); err != nil {
return diag.FromErr(err)
}
- client := meta.(*Owner).v3client
- orgName := meta.(*Owner).name
- opts := &github.ListExternalGroupsOptions{}
-
- externalGroups := new(github.ExternalGroupList)
+ var filter *string
+ if v, ok := d.GetOk("display_name_filter"); ok {
+ s, _ := v.(string)
+ filter = &s
+ }
- for {
- groups, resp, err := client.Teams.ListExternalGroups(ctx, orgName, opts)
+ groups := make([]map[string]any, 0)
+ for group, err := range meta.v3client.Teams.ListExternalGroupsIter(ctx, meta.name, &github.ListExternalGroupsOptions{DisplayName: filter, ListOptions: github.ListOptions{PerPage: maxPerPage}}) {
if err != nil {
return diag.FromErr(err)
}
- externalGroups.Groups = append(externalGroups.Groups, groups.Groups...)
-
- if resp.NextPage == 0 {
- break
+ g := map[string]any{
+ "group_id": group.GetGroupID(),
+ "group_name": group.GetGroupName(),
+ "updated_at": group.GetUpdatedAt().String(),
}
- opts.Page = resp.NextPage
- }
- // convert to JSON in order to martial to format we can return
- jsonGroups, err := json.Marshal(externalGroups.Groups)
- if err != nil {
- return diag.FromErr(err)
+ groups = append(groups, g)
}
- groupsState := make([]map[string]any, 0)
- err = json.Unmarshal(jsonGroups, &groupsState)
- if err != nil {
- return diag.FromErr(err)
+ var id string
+ if filter != nil {
+ s, err := buildID(meta.name, *filter)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ id = s
+ } else {
+ id = meta.name
}
- if err := d.Set("external_groups", groupsState); err != nil {
- return diag.FromErr(err)
+ d.SetId(id)
+
+ if err := d.Set("external_groups", groups); err != nil {
+ return diag.Errorf("error setting external_groups: %v", err)
}
- d.SetId(fmt.Sprintf("/orgs/%v/external-groups", orgName))
return nil
}
diff --git a/github/data_source_github_external_groups_test.go b/github/data_source_github_external_groups_test.go
new file mode 100644
index 0000000000..009e729fed
--- /dev/null
+++ b/github/data_source_github_external_groups_test.go
@@ -0,0 +1,82 @@
+package github
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/knownvalue"
+ "github.com/hashicorp/terraform-plugin-testing/statecheck"
+ "github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
+)
+
+func TestAccDataSourceGithubExternalGroups(t *testing.T) {
+ t.Parallel()
+
+ if testAccConf.authMode != enterprise && !testAccConf.enterpriseIsEMU {
+ t.Skip("Skipping as test mode is not enterprise using EMU")
+ }
+
+ t.Run("queries_all_external_groups", func(t *testing.T) {
+ t.Parallel()
+
+ if testAccConf.testExternalGroup1ID == 0 {
+ t.Skip("Skipping as no external groups are configured for the test organization")
+ }
+
+ groupID := int32(testAccConf.testExternalGroup1ID)
+
+ config := `
+data "github_external_groups" "example" {}
+`
+
+ resource.Test(t, resource.TestCase{
+ ProviderFactories: providerFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ ConfigStateChecks: []statecheck.StateCheck{
+ statecheck.ExpectKnownValue("data.github_external_groups.example", tfjsonpath.New("groups"), knownvalue.SetPartial([]knownvalue.Check{
+ knownvalue.MapPartial(map[string]knownvalue.Check{
+ "group_id": knownvalue.Int32Exact(groupID),
+ }),
+ })),
+ },
+ },
+ },
+ })
+ })
+
+ t.Run("queries_external_groups_with_filter", func(t *testing.T) {
+ t.Parallel()
+
+ if testAccConf.testExternalGroup1ID == 0 || testAccConf.testExternalGroup1DisplayName == "" || testAccConf.testExternalGroup2ID == 0 {
+ t.Skip("Skipping as no external groups are configured for the test organization")
+ }
+
+ groupID := int32(testAccConf.testExternalGroup1ID)
+
+ config := fmt.Sprintf(`
+data "github_external_groups" "example" {
+ display_name_filter = "%s"
+}
+`, testAccConf.testExternalGroup1DisplayName)
+
+ // TODO: Use new not in set matcher when available from #3537
+ resource.Test(t, resource.TestCase{
+ ProviderFactories: providerFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ ConfigStateChecks: []statecheck.StateCheck{
+ statecheck.ExpectKnownValue("data.github_external_groups.example", tfjsonpath.New("groups"), knownvalue.ListExact([]knownvalue.Check{
+ knownvalue.MapPartial(map[string]knownvalue.Check{
+ "group_id": knownvalue.Int32Exact(groupID),
+ }),
+ })),
+ },
+ },
+ },
+ })
+ })
+}
diff --git a/templates/data-sources/external_groups.md.tmpl b/templates/data-sources/external_groups.md.tmpl
index 206555129b..3260662c94 100644
--- a/templates/data-sources/external_groups.md.tmpl
+++ b/templates/data-sources/external_groups.md.tmpl
@@ -1,27 +1,48 @@
---
page_title: "{{.Name}} ({{.Type}}) - {{.RenderedProviderName}}"
+subcategory: ""
description: |-
- Retrieve external groups belonging to an organization.
+{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
---
# {{.Name}} ({{.Type}})
-Use this data source to retrieve external groups belonging to an organization.
+{{ .Description | trimspace }}
+{{ if .HasExamples -}}
## Example Usage
-{{ tffile "examples/data-sources/external_groups/example_1.tf" }}
+{{- range .ExampleFiles }}
-## Argument Reference
+{{ tffile . }}
+{{- end }}
+{{- end }}
-N/A. This resource will retrieve all the external groups belonging to an organization.
+" }}
+{{ $line }}
+{{- end }}
+{{- end }}
+-->
-## Attributes Reference
+## Schema
-- `external_groups` - an array of external groups belonging to the organization. Each group consists of the fields documented below.
+### Optional
----
+- `display_name_filter` (String) Filter external groups by display name.
+
+### Read-Only
+
+- `external_groups` (List of Object) List of external groups in the organization. (see [below for nested schema](#nestedatt--external_groups))
+- `id` (String) The ID of this resource.
+
+
+### Nested Schema for `external_groups`
+
+Read-Only:
-- `group_id` - the ID of the group.
-- `group_name` - the name of the group.
-- `updated_at` - the date the group was last updated.
+- `group_id` (Number) ID of the external group.
+- `group_name` (String) Name of the external group.
+- `updated_at` (String) Timestamp of the last update to the external group.