From 943df18b09da024d5deece11651a9a1acbf08a4c Mon Sep 17 00:00:00 2001 From: ErikElkins Date: Fri, 2 May 2025 09:02:47 -0500 Subject: [PATCH 1/2] Adding github_enterprise_ip_allow_list_entry resource --- github/provider.go | 1 + ...e_github_enterprise_ip_allow_list_entry.go | 218 ++++++++++++++++++ ...hub_enterprise_ip_allow_list_entry_test.go | 101 ++++++++ ...terprise_ip_allow_list_entry.html.markdown | 38 +++ 4 files changed, 358 insertions(+) create mode 100644 github/resource_github_enterprise_ip_allow_list_entry.go create mode 100644 github/resource_github_enterprise_ip_allow_list_entry_test.go create mode 100644 website/docs/r/enterprise_ip_allow_list_entry.html.markdown diff --git a/github/provider.go b/github/provider.go index 8f44c95098..49f0d541eb 100644 --- a/github/provider.go +++ b/github/provider.go @@ -195,6 +195,7 @@ func Provider() *schema.Provider { "github_user_ssh_key": resourceGithubUserSshKey(), "github_enterprise_organization": resourceGithubEnterpriseOrganization(), "github_enterprise_actions_runner_group": resourceGithubActionsEnterpriseRunnerGroup(), + "github_enterprise_ip_allow_list_entry": resourceGithubEnterpriseIpAllowListEntry(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/github/resource_github_enterprise_ip_allow_list_entry.go b/github/resource_github_enterprise_ip_allow_list_entry.go new file mode 100644 index 0000000000..65ebb98373 --- /dev/null +++ b/github/resource_github_enterprise_ip_allow_list_entry.go @@ -0,0 +1,218 @@ +package github + +import ( + "context" + "log" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/shurcooL/githubv4" +) + +func resourceGithubEnterpriseIpAllowListEntry() *schema.Resource { + return &schema.Resource{ + Create: resourceGithubEnterpriseIpAllowListEntryCreate, + Read: resourceGithubEnterpriseIpAllowListEntryRead, + Update: resourceGithubEnterpriseIpAllowListEntryUpdate, + Delete: resourceGithubEnterpriseIpAllowListEntryDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "enterprise_slug": { + Type: schema.TypeString, + Required: true, + Description: "The slug of the enterprise to apply the IP allow list entry to.", + }, + "ip": { + Type: schema.TypeString, + Required: true, + Description: "An IP address or range of IP addresses in CIDR notation.", + }, + "name": { + Type: schema.TypeString, + Optional: true, + Description: "An optional name for the IP allow list entry.", + }, + "is_active": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Whether the entry is currently active.", + }, + }, + } +} + +func resourceGithubEnterpriseIpAllowListEntryCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v4client + ctx := context.WithValue(context.Background(), ctxId, d.Id()) + + // First, get the enterprise ID as we need it for the mutation + enterpriseSlug := d.Get("enterprise_slug").(string) + enterpriseID, err := getEnterpriseID(ctx, client, enterpriseSlug) + if err != nil { + return err + } + + // Then create the IP allow list entry + var mutation struct { + CreateIpAllowListEntry struct { + IpAllowListEntry struct { + ID githubv4.String + AllowListValue githubv4.String + Name githubv4.String + IsActive githubv4.Boolean + CreatedAt githubv4.String + UpdatedAt githubv4.String + } + } `graphql:"createIpAllowListEntry(input: $input)"` + } + + name := d.Get("name").(string) + input := githubv4.CreateIpAllowListEntryInput{ + OwnerID: githubv4.ID(enterpriseID), + AllowListValue: githubv4.String(d.Get("ip").(string)), + IsActive: githubv4.Boolean(d.Get("is_active").(bool)), + } + + if name != "" { + input.Name = githubv4.NewString(githubv4.String(name)) + } + + err = client.Mutate(ctx, &mutation, input, nil) + if err != nil { + return err + } + + d.SetId(string(mutation.CreateIpAllowListEntry.IpAllowListEntry.ID)) + + return resourceGithubEnterpriseIpAllowListEntryRead(d, meta) +} + +func resourceGithubEnterpriseIpAllowListEntryRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v4client + ctx := context.WithValue(context.Background(), ctxId, d.Id()) + + var query struct { + Node struct { + IpAllowListEntry struct { + ID githubv4.String + AllowListValue githubv4.String + Name githubv4.String + IsActive githubv4.Boolean + CreatedAt githubv4.String + UpdatedAt githubv4.String + Owner struct { + Enterprise struct { + Slug githubv4.String + } `graphql:"... on Enterprise"` + } + } `graphql:"... on IpAllowListEntry"` + } `graphql:"node(id: $id)"` + } + + variables := map[string]interface{}{ + "id": githubv4.ID(d.Id()), + } + + err := client.Query(ctx, &query, variables) + if err != nil { + if strings.Contains(err.Error(), "Could not resolve to a node with the global id") { + log.Printf("[INFO] Removing IP allow list entry (%s) from state because it no longer exists in GitHub", d.Id()) + d.SetId("") + return nil + } + return err + } + + entry := query.Node.IpAllowListEntry + + d.Set("ip", entry.AllowListValue) + d.Set("name", entry.Name) + d.Set("is_active", entry.IsActive) + d.Set("created_at", entry.CreatedAt) + d.Set("updated_at", entry.UpdatedAt) + d.Set("enterprise_slug", entry.Owner.Enterprise.Slug) + + return nil +} + +func resourceGithubEnterpriseIpAllowListEntryUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v4client + ctx := context.WithValue(context.Background(), ctxId, d.Id()) + + var mutation struct { + UpdateIpAllowListEntry struct { + IpAllowListEntry struct { + ID githubv4.String + AllowListValue githubv4.String + Name githubv4.String + IsActive githubv4.Boolean + UpdatedAt githubv4.String + } + } `graphql:"updateIpAllowListEntry(input: $input)"` + } + + name := d.Get("name").(string) + input := githubv4.UpdateIpAllowListEntryInput{ + IPAllowListEntryID: githubv4.ID(d.Id()), + AllowListValue: githubv4.String(d.Get("ip").(string)), + IsActive: githubv4.Boolean(d.Get("is_active").(bool)), + } + + if name != "" { + input.Name = githubv4.NewString(githubv4.String(name)) + } + + err := client.Mutate(ctx, &mutation, input, nil) + if err != nil { + return err + } + + return resourceGithubEnterpriseIpAllowListEntryRead(d, meta) +} + +func resourceGithubEnterpriseIpAllowListEntryDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v4client + ctx := context.WithValue(context.Background(), ctxId, d.Id()) + + var mutation struct { + DeleteIpAllowListEntry struct { + ClientMutationID githubv4.String + } `graphql:"deleteIpAllowListEntry(input: $input)"` + } + + input := githubv4.DeleteIpAllowListEntryInput{ + IPAllowListEntryID: githubv4.ID(d.Id()), + } + + err := client.Mutate(ctx, &mutation, input, nil) + if err != nil { + return err + } + + d.SetId("") + return nil +} + +// Helper function to get Enterprise ID from slug +func getEnterpriseID(ctx context.Context, client *githubv4.Client, enterpriseSlug string) (string, error) { + var query struct { + Enterprise struct { + ID githubv4.ID + } `graphql:"enterprise(slug: $slug)"` + } + + variables := map[string]interface{}{ + "slug": githubv4.String(enterpriseSlug), + } + + err := client.Query(ctx, &query, variables) + if err != nil { + return "", err + } + + return query.Enterprise.ID.(string), nil +} diff --git a/github/resource_github_enterprise_ip_allow_list_entry_test.go b/github/resource_github_enterprise_ip_allow_list_entry_test.go new file mode 100644 index 0000000000..59ff1f15db --- /dev/null +++ b/github/resource_github_enterprise_ip_allow_list_entry_test.go @@ -0,0 +1,101 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccGithubEnterpriseIpAllowListEntry_basic(t *testing.T) { + t.Skip("Acceptance test requires a real GitHub Enterprise environment") + + resourceName := "github_enterprise_ip_allow_list_entry.test" + enterpriseSlug := "test-enterprise" + ip := "192.168.1.0/24" + name := "Test Entry" + isActive := true + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckEnterprise(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccGithubEnterpriseIpAllowListEntryConfig(enterpriseSlug, ip, name, isActive), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "enterprise_slug", enterpriseSlug), + resource.TestCheckResourceAttr(resourceName, "ip", ip), + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "is_active", fmt.Sprintf("%t", isActive)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccGithubEnterpriseIpAllowListEntry_update(t *testing.T) { + t.Skip("Acceptance test requires a real GitHub Enterprise environment") + + resourceName := "github_enterprise_ip_allow_list_entry.test" + enterpriseSlug := "test-enterprise" + ip := "192.168.1.0/24" + name := "Test Entry" + isActive := true + + updatedIP := "10.0.0.0/16" + updatedName := "Updated Entry" + updatedIsActive := false + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckEnterprise(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccGithubEnterpriseIpAllowListEntryConfig(enterpriseSlug, ip, name, isActive), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "enterprise_slug", enterpriseSlug), + resource.TestCheckResourceAttr(resourceName, "ip", ip), + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "is_active", fmt.Sprintf("%t", isActive)), + ), + }, + { + Config: testAccGithubEnterpriseIpAllowListEntryConfig(enterpriseSlug, updatedIP, updatedName, updatedIsActive), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "enterprise_slug", enterpriseSlug), + resource.TestCheckResourceAttr(resourceName, "ip", updatedIP), + resource.TestCheckResourceAttr(resourceName, "name", updatedName), + resource.TestCheckResourceAttr(resourceName, "is_active", fmt.Sprintf("%t", updatedIsActive)), + ), + }, + }, + }) +} + +func testAccGithubEnterpriseIpAllowListEntryConfig(enterpriseSlug, ip, name string, isActive bool) string { + return fmt.Sprintf(` +resource "github_enterprise_ip_allow_list_entry" "test" { + enterprise_slug = "%s" + ip = "%s" + name = "%s" + is_active = %t +} +`, enterpriseSlug, ip, name, isActive) +} + +func testAccPreCheckEnterprise(t *testing.T) { + if v := testAccProvider.Meta().(*Owner).name; v == "" { + t.Fatal("The GITHUB_ENTERPRISE_SLUG environment variable must be set for enterprise tests") + } +} diff --git a/website/docs/r/enterprise_ip_allow_list_entry.html.markdown b/website/docs/r/enterprise_ip_allow_list_entry.html.markdown new file mode 100644 index 0000000000..45817dffba --- /dev/null +++ b/website/docs/r/enterprise_ip_allow_list_entry.html.markdown @@ -0,0 +1,38 @@ +--- +layout: "github" +page_title: "GitHub: github_enterprise_ip_allow_list_entry" +description: |- + Creates and manages IP allow list entries within a GitHub Enterprise +--- + +# github_enterprise_ip_allow_list_entry + +This resource allows you to create and manage IP allow list entries for a GitHub Enterprise account. IP allow list entries define IP addresses or ranges that are permitted to access private resources in the enterprise. + +## Example Usage + +```hcl +resource "github_enterprise_ip_allow_list_entry" "test" { + enterprise_slug = "my-enterprise" + ip = "192.168.1.0/20" + name = "My IP Range Name" + is_active = true +} +``` + +## Argument Reference + +The following arguments are supported: + +* `enterprise_slug` - (Required) The slug of the enterprise. +* `ip` - (Required) An IP address or range of IP addresses in CIDR notation. +* `name` - (Optional) A descriptive name for the IP allow list entry. +* `is_active` - (Optional) Whether the entry is currently active. Default: true. + +## Import + +This resource can be imported using the ID of the IP allow list entry: + +```bash +$ terraform import github_enterprise_ip_allow_list_entry.test IALE_kwHOC1234567890a +``` \ No newline at end of file From 8425ca1baf2a9b8751210a8696cf0600b05f203d Mon Sep 17 00:00:00 2001 From: Guilherme Teixeira <4645845+gateixeira@users.noreply.github.com> Date: Tue, 13 Jan 2026 14:58:24 -0300 Subject: [PATCH 2/2] Fix lint issues --- ...e_github_enterprise_ip_allow_list_entry.go | 26 +++++++++---------- ...hub_enterprise_ip_allow_list_entry_test.go | 4 +-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/github/resource_github_enterprise_ip_allow_list_entry.go b/github/resource_github_enterprise_ip_allow_list_entry.go index 65ebb98373..2b5342f9ed 100644 --- a/github/resource_github_enterprise_ip_allow_list_entry.go +++ b/github/resource_github_enterprise_ip_allow_list_entry.go @@ -45,7 +45,7 @@ func resourceGithubEnterpriseIpAllowListEntry() *schema.Resource { } } -func resourceGithubEnterpriseIpAllowListEntryCreate(d *schema.ResourceData, meta interface{}) error { +func resourceGithubEnterpriseIpAllowListEntryCreate(d *schema.ResourceData, meta any) error { client := meta.(*Owner).v4client ctx := context.WithValue(context.Background(), ctxId, d.Id()) @@ -91,7 +91,7 @@ func resourceGithubEnterpriseIpAllowListEntryCreate(d *schema.ResourceData, meta return resourceGithubEnterpriseIpAllowListEntryRead(d, meta) } -func resourceGithubEnterpriseIpAllowListEntryRead(d *schema.ResourceData, meta interface{}) error { +func resourceGithubEnterpriseIpAllowListEntryRead(d *schema.ResourceData, meta any) error { client := meta.(*Owner).v4client ctx := context.WithValue(context.Background(), ctxId, d.Id()) @@ -113,7 +113,7 @@ func resourceGithubEnterpriseIpAllowListEntryRead(d *schema.ResourceData, meta i } `graphql:"node(id: $id)"` } - variables := map[string]interface{}{ + variables := map[string]any{ "id": githubv4.ID(d.Id()), } @@ -129,17 +129,17 @@ func resourceGithubEnterpriseIpAllowListEntryRead(d *schema.ResourceData, meta i entry := query.Node.IpAllowListEntry - d.Set("ip", entry.AllowListValue) - d.Set("name", entry.Name) - d.Set("is_active", entry.IsActive) - d.Set("created_at", entry.CreatedAt) - d.Set("updated_at", entry.UpdatedAt) - d.Set("enterprise_slug", entry.Owner.Enterprise.Slug) + _ = d.Set("ip", entry.AllowListValue) + _ = d.Set("name", entry.Name) + _ = d.Set("is_active", entry.IsActive) + _ = d.Set("created_at", entry.CreatedAt) + _ = d.Set("updated_at", entry.UpdatedAt) + _ = d.Set("enterprise_slug", entry.Owner.Enterprise.Slug) return nil } -func resourceGithubEnterpriseIpAllowListEntryUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceGithubEnterpriseIpAllowListEntryUpdate(d *schema.ResourceData, meta any) error { client := meta.(*Owner).v4client ctx := context.WithValue(context.Background(), ctxId, d.Id()) @@ -174,7 +174,7 @@ func resourceGithubEnterpriseIpAllowListEntryUpdate(d *schema.ResourceData, meta return resourceGithubEnterpriseIpAllowListEntryRead(d, meta) } -func resourceGithubEnterpriseIpAllowListEntryDelete(d *schema.ResourceData, meta interface{}) error { +func resourceGithubEnterpriseIpAllowListEntryDelete(d *schema.ResourceData, meta any) error { client := meta.(*Owner).v4client ctx := context.WithValue(context.Background(), ctxId, d.Id()) @@ -197,7 +197,7 @@ func resourceGithubEnterpriseIpAllowListEntryDelete(d *schema.ResourceData, meta return nil } -// Helper function to get Enterprise ID from slug +// Helper function to get Enterprise ID from slug. func getEnterpriseID(ctx context.Context, client *githubv4.Client, enterpriseSlug string) (string, error) { var query struct { Enterprise struct { @@ -205,7 +205,7 @@ func getEnterpriseID(ctx context.Context, client *githubv4.Client, enterpriseSlu } `graphql:"enterprise(slug: $slug)"` } - variables := map[string]interface{}{ + variables := map[string]any{ "slug": githubv4.String(enterpriseSlug), } diff --git a/github/resource_github_enterprise_ip_allow_list_entry_test.go b/github/resource_github_enterprise_ip_allow_list_entry_test.go index 59ff1f15db..8de72a9653 100644 --- a/github/resource_github_enterprise_ip_allow_list_entry_test.go +++ b/github/resource_github_enterprise_ip_allow_list_entry_test.go @@ -18,7 +18,7 @@ func TestAccGithubEnterpriseIpAllowListEntry_basic(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { - testAccPreCheck(t) + skipUnauthenticated(t) testAccPreCheckEnterprise(t) }, Providers: testAccProviders, @@ -56,7 +56,7 @@ func TestAccGithubEnterpriseIpAllowListEntry_update(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { - testAccPreCheck(t) + skipUnauthenticated(t) testAccPreCheckEnterprise(t) }, Providers: testAccProviders,