From 2ab58131ebebd7a41ff8840c45d042d75ac2ec7f Mon Sep 17 00:00:00 2001 From: ErikElkins Date: Fri, 2 May 2025 09:00:09 -0500 Subject: [PATCH] Adding github_organization_ip_allow_list_entry resource --- github/provider.go | 1 + ...github_organization_ip_allow_list_entry.go | 197 ++++++++++++++++++ ...b_organization_ip_allow_list_entry_test.go | 99 +++++++++ ...nization_ip_allow_list_entry.html.markdown | 36 ++++ 4 files changed, 333 insertions(+) create mode 100644 github/resource_github_organization_ip_allow_list_entry.go create mode 100644 github/resource_github_organization_ip_allow_list_entry_test.go create mode 100644 website/docs/r/organization_ip_allow_list_entry.html.markdown diff --git a/github/provider.go b/github/provider.go index 8f44c95098..ea473806f3 100644 --- a/github/provider.go +++ b/github/provider.go @@ -159,6 +159,7 @@ func Provider() *schema.Provider { "github_membership": resourceGithubMembership(), "github_organization_block": resourceOrganizationBlock(), "github_organization_custom_role": resourceGithubOrganizationCustomRole(), + "github_organization_ip_allow_list_entry": resourceGithubOrganizationIpAllowListEntry(), "github_organization_project": resourceGithubOrganizationProject(), "github_organization_security_manager": resourceGithubOrganizationSecurityManager(), "github_organization_ruleset": resourceGithubOrganizationRuleset(), diff --git a/github/resource_github_organization_ip_allow_list_entry.go b/github/resource_github_organization_ip_allow_list_entry.go new file mode 100644 index 0000000000..f4766fc434 --- /dev/null +++ b/github/resource_github_organization_ip_allow_list_entry.go @@ -0,0 +1,197 @@ +package github + +import ( + "context" + "log" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/shurcooL/githubv4" +) + +func resourceGithubOrganizationIpAllowListEntry() *schema.Resource { + return &schema.Resource{ + Create: resourceGithubOrganizationIpAllowListEntryCreate, + Read: resourceGithubOrganizationIpAllowListEntryRead, + Update: resourceGithubOrganizationIpAllowListEntryUpdate, + Delete: resourceGithubOrganizationIpAllowListEntryDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "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 resourceGithubOrganizationIpAllowListEntryCreate(d *schema.ResourceData, meta interface{}) error { + err := checkOrganization(meta) + if err != nil { + return err + } + + client := meta.(*Owner).v4client + orgName := meta.(*Owner).name + ctx := context.WithValue(context.Background(), ctxId, d.Id()) + + // First, get the organization ID as we need it for the mutation + var getOrgQuery struct { + Organization struct { + ID githubv4.ID + } `graphql:"organization(login: $login)"` + } + + variables := map[string]interface{}{ + "login": githubv4.String(orgName), + } + + err = client.Query(ctx, &getOrgQuery, variables) + 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 + } + } `graphql:"createIpAllowListEntry(input: $input)"` + } + + name := d.Get("name").(string) + input := githubv4.CreateIpAllowListEntryInput{ + OwnerID: getOrgQuery.Organization.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 + } + + d.SetId(string(mutation.CreateIpAllowListEntry.IpAllowListEntry.ID)) + + return resourceGithubOrganizationIpAllowListEntryRead(d, meta) +} + +func resourceGithubOrganizationIpAllowListEntryRead(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 + } `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) + + return nil +} + +func resourceGithubOrganizationIpAllowListEntryUpdate(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 + } + } `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 resourceGithubOrganizationIpAllowListEntryRead(d, meta) +} + +func resourceGithubOrganizationIpAllowListEntryDelete(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 +} diff --git a/github/resource_github_organization_ip_allow_list_entry_test.go b/github/resource_github_organization_ip_allow_list_entry_test.go new file mode 100644 index 0000000000..2cdfc95f03 --- /dev/null +++ b/github/resource_github_organization_ip_allow_list_entry_test.go @@ -0,0 +1,99 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccGithubOrganizationIpAllowListEntry_basic(t *testing.T) { + t.Skip("Acceptance test requires a real GitHub organization") + + resourceName := "github_organization_ip_allow_list_entry.test" + orgName := "test-organization" + ip := "192.168.1.0/24" + name := "Test Entry" + isActive := true + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheckOrg(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccGithubOrganizationIpAllowListEntryConfig(orgName, ip, name, isActive), + Check: resource.ComposeTestCheckFunc( + 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 TestAccGithubOrganizationIpAllowListEntry_update(t *testing.T) { + t.Skip("Acceptance test requires a real GitHub organization") + + resourceName := "github_organization_ip_allow_list_entry.test" + orgName := "test-organization" + 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() { + testAccPreCheckOrg(t) + }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccGithubOrganizationIpAllowListEntryConfig(orgName, ip, name, isActive), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "ip", ip), + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "is_active", fmt.Sprintf("%t", isActive)), + ), + }, + { + Config: testAccGithubOrganizationIpAllowListEntryConfig(orgName, updatedIP, updatedName, updatedIsActive), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "ip", updatedIP), + resource.TestCheckResourceAttr(resourceName, "name", updatedName), + resource.TestCheckResourceAttr(resourceName, "is_active", fmt.Sprintf("%t", updatedIsActive)), + ), + }, + }, + }) +} + +func testAccGithubOrganizationIpAllowListEntryConfig(orgName, ip, name string, isActive bool) string { + return fmt.Sprintf(` +provider "github" { + owner = "%s" +} + +resource "github_organization_ip_allow_list_entry" "test" { + ip = "%s" + name = "%s" + is_active = %t +} +`, orgName, ip, name, isActive) +} + +func testAccPreCheckOrg(t *testing.T) { + if v := testAccProvider.Meta().(*Owner).name; v == "" { + t.Fatal("The GITHUB_OWNER environment variable must be set for acceptance tests") + } +} diff --git a/website/docs/r/organization_ip_allow_list_entry.html.markdown b/website/docs/r/organization_ip_allow_list_entry.html.markdown new file mode 100644 index 0000000000..a2550a6edb --- /dev/null +++ b/website/docs/r/organization_ip_allow_list_entry.html.markdown @@ -0,0 +1,36 @@ +--- +layout: "github" +page_title: "GitHub: github_organization_ip_allow_list_entry" +description: |- + Creates and manages IP allow list entries within a GitHub organization +--- + +# github_organization_ip_allow_list_entry + +This resource allows you to create and manage IP allow list entries for a GitHub organization. IP allow list entries define IP addresses or ranges that are permitted to access private resources in the organization. + +## Example Usage + +```hcl +resource "github_organization_ip_allow_list_entry" "test" { + ip = "192.168.1.0/20" + name = "My IP Range Name" + is_active = true +} +``` + +## Argument Reference + +The following arguments are supported: + +* `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_organization_ip_allow_list_entry.test IALE_kwHOC1234567890a +``` \ No newline at end of file