Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
943df18
Adding github_enterprise_ip_allow_list_entry resource
ErikElkins May 2, 2025
eb02154
Merge branch 'main' into feat/enterprise-ip-allow-list
ErikElkins Jul 11, 2025
935e689
Merge branch 'main' into feat/enterprise-ip-allow-list
nickfloyd Jan 13, 2026
83a0055
Update github/resource_github_enterprise_ip_allow_list_entry.go
ErikElkins Jan 13, 2026
c3735ac
Update github/resource_github_enterprise_ip_allow_list_entry.go
ErikElkins Jan 13, 2026
e1b06a2
Merge branch 'main' into feat/enterprise-ip-allow-list
ErikElkins Jan 13, 2026
833f3f1
Update github/resource_github_enterprise_ip_allow_list_entry.go
ErikElkins Jan 13, 2026
60c686b
Update github/resource_github_enterprise_ip_allow_list_entry_test.go
ErikElkins Jan 13, 2026
f667dcc
Update github/resource_github_enterprise_ip_allow_list_entry_test.go
ErikElkins Jan 13, 2026
06b2e43
Code review fixes
ErikElkins Jan 13, 2026
f8d8807
Merge branch 'main' into feat/enterprise-ip-allow-list
ErikElkins Jan 24, 2026
aed945b
Fixes from code review
ErikElkins Jan 24, 2026
a032d9d
Merge branch 'main' into feat/enterprise-ip-allow-list
ErikElkins Feb 10, 2026
b076b2a
Fixing code review comments
ErikElkins Feb 10, 2026
d5a1915
Update resource_github_enterprise_ip_allow_list_entry.go
ErikElkins Feb 14, 2026
2511b0b
Update resource_github_enterprise_ip_allow_list_entry.go
ErikElkins Feb 14, 2026
4c9ac44
Merge branch 'main' into feat/enterprise-ip-allow-list
ErikElkins Feb 20, 2026
d593d56
Update github/resource_github_enterprise_ip_allow_list_entry_test.go
ErikElkins Feb 20, 2026
10de6a7
Update github/resource_github_enterprise_ip_allow_list_entry.go
ErikElkins Feb 20, 2026
4474a4f
Code review changes
ErikElkins Feb 20, 2026
01d61fc
Fixing config in test
ErikElkins Feb 20, 2026
03d79ef
Flattening update test
ErikElkins Feb 20, 2026
4e50668
Adding error handling
ErikElkins Feb 20, 2026
252cfe4
Simplifying import function
ErikElkins Feb 20, 2026
0369b10
Fix docs
ErikElkins Feb 20, 2026
8918bb8
Merge branch 'main' into feat/enterprise-ip-allow-list
ErikElkins Feb 20, 2026
2eb508d
Fixing code review changes
ErikElkins Feb 23, 2026
85b1956
Merge branch 'main' into feat/enterprise-ip-allow-list
ErikElkins Feb 23, 2026
5dd8ef6
Fixing lint
ErikElkins Feb 23, 2026
08aa432
Merge branch 'main' into feat/enterprise-ip-allow-list
ErikElkins Feb 23, 2026
c5e2e12
Adding error handling for missing global ID
ErikElkins Feb 24, 2026
43a86af
Merge branch 'main' into feat/enterprise-ip-allow-list
ErikElkins Mar 24, 2026
1c87cc4
Removing two old functions
ErikElkins Mar 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,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(),
"github_enterprise_actions_workflow_permissions": resourceGithubEnterpriseActionsWorkflowPermissions(),
"github_actions_organization_workflow_permissions": resourceGithubActionsOrganizationWorkflowPermissions(),
"github_enterprise_security_analysis_settings": resourceGithubEnterpriseSecurityAnalysisSettings(),
Expand Down
283 changes: 283 additions & 0 deletions github/resource_github_enterprise_ip_allow_list_entry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
package github

import (
"context"
"strings"

"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/shurcooL/githubv4"
)

func resourceGithubEnterpriseIpAllowListEntry() *schema.Resource {
return &schema.Resource{
Description: "Manage a GitHub Enterprise IP Allow List Entry.",
CreateContext: resourceGithubEnterpriseIpAllowListEntryCreate,
ReadContext: resourceGithubEnterpriseIpAllowListEntryRead,
UpdateContext: resourceGithubEnterpriseIpAllowListEntryUpdate,
DeleteContext: resourceGithubEnterpriseIpAllowListEntryDelete,
Importer: &schema.ResourceImporter{
StateContext: resourceGithubEnterpriseIpAllowListEntryImport,
},

Schema: map[string]*schema.Schema{
"enterprise_slug": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The slug of the enterprise to apply the IP allow list entry to.",
},
"ip": {
Type: schema.TypeString,
Required: true,
ForceNew: 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.",
},
"created_at": {
Type: schema.TypeString,
Computed: true,
Description: "Timestamp of when the entry was created.",
},
"updated_at": {
Type: schema.TypeString,
Computed: true,
Description: "Timestamp of when the entry was last updated.",
},
},
}
}

func resourceGithubEnterpriseIpAllowListEntryCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*Owner).v4client

// 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 diag.FromErr(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 diag.FromErr(err)
}

d.SetId(string(mutation.CreateIpAllowListEntry.IpAllowListEntry.ID))

if err := d.Set("created_at", mutation.CreateIpAllowListEntry.IpAllowListEntry.CreatedAt); err != nil {
return diag.FromErr(err)
}
if err := d.Set("updated_at", mutation.CreateIpAllowListEntry.IpAllowListEntry.UpdatedAt); err != nil {
return diag.FromErr(err)
}

return nil
}

func resourceGithubEnterpriseIpAllowListEntryRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*Owner).v4client

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]any{
"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") {
tflog.Info(ctx, "Removing IP allow list entry from state because it no longer exists in GitHub", map[string]any{
"id": d.Id(),
})
d.SetId("")
return nil
}
return diag.FromErr(err)
}

entry := query.Node.IpAllowListEntry
if err := d.Set("name", entry.Name); err != nil {
return diag.FromErr(err)
}
if err := d.Set("ip", entry.AllowListValue); err != nil {
return diag.FromErr(err)
}
if err := d.Set("is_active", entry.IsActive); err != nil {
return diag.FromErr(err)
}
if err := d.Set("created_at", entry.CreatedAt); err != nil {
return diag.FromErr(err)
}
if err := d.Set("updated_at", entry.UpdatedAt); err != nil {
return diag.FromErr(err)
}

return nil
}

func resourceGithubEnterpriseIpAllowListEntryUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*Owner).v4client

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 diag.FromErr(err)
}

if err := d.Set("updated_at", mutation.UpdateIpAllowListEntry.IpAllowListEntry.UpdatedAt); err != nil {
return diag.FromErr(err)
}

return nil
}

func resourceGithubEnterpriseIpAllowListEntryDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*Owner).v4client

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)
// GraphQL will return a 200 OK if it couldn't find the global ID
if err != nil && !strings.Contains(err.Error(), "Could not resolve to a node with the global id") {
return diag.FromErr(err)
}

return nil
}

func resourceGithubEnterpriseIpAllowListEntryImport(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
client := meta.(*Owner).v4client

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]any{
"id": githubv4.ID(d.Id()),
}

err := client.Query(ctx, &query, variables)
if err != nil {
return nil, err
}

entry := query.Node.IpAllowListEntry

if err := d.Set("enterprise_slug", string(entry.Owner.Enterprise.Slug)); err != nil {
return nil, err
}
if err := d.Set("ip", string(entry.AllowListValue)); err != nil {
return nil, err
}
if err := d.Set("name", entry.Name); err != nil {
return nil, err
}
if err := d.Set("is_active", entry.IsActive); err != nil {
return nil, err
}
if err := d.Set("created_at", entry.CreatedAt); err != nil {
return nil, err
}
if err := d.Set("updated_at", entry.UpdatedAt); err != nil {
return nil, err
}

return []*schema.ResourceData{d}, nil
}
96 changes: 96 additions & 0 deletions github/resource_github_enterprise_ip_allow_list_entry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package github

import (
"fmt"
"strconv"
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

func TestAccGithubEnterpriseIpAllowListEntry(t *testing.T) {
t.Run("basic", func(t *testing.T) {
resourceName := "github_enterprise_ip_allow_list_entry.test"
ip := "192.168.1.0/24"
name := "Test Entry"
isActive := true

config := `
resource "github_enterprise_ip_allow_list_entry" "test" {
enterprise_slug = "%s"
ip = "%s"
name = "%s"
is_active = %t
}
`

resource.Test(t, resource.TestCase{
PreCheck: func() {
skipUnlessEnterprise(t)
},
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(config, testAccConf.enterpriseSlug, ip, name, isActive),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "enterprise_slug", testAccConf.enterpriseSlug),
resource.TestCheckResourceAttr(resourceName, "ip", ip),
resource.TestCheckResourceAttr(resourceName, "name", name),
resource.TestCheckResourceAttr(resourceName, "is_active", strconv.FormatBool(isActive)),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
})
t.Run("update", func(t *testing.T) {
resourceName := "github_enterprise_ip_allow_list_entry.test"
ip := "192.168.1.0/24"
name := "Test Entry"
isActive := true

updatedIP := "10.0.0.0/16"
updatedName := "Updated Entry"
updatedIsActive := false

config := `
resource "github_enterprise_ip_allow_list_entry" "test" {
enterprise_slug = "%s"
ip = "%s"
name = "%s"
is_active = %t
}
`

resource.Test(t, resource.TestCase{
PreCheck: func() {
skipUnlessEnterprise(t)
},
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: fmt.Sprintf(config, testAccConf.enterpriseSlug, ip, name, isActive),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "enterprise_slug", testAccConf.enterpriseSlug),
resource.TestCheckResourceAttr(resourceName, "ip", ip),
resource.TestCheckResourceAttr(resourceName, "name", name),
resource.TestCheckResourceAttr(resourceName, "is_active", fmt.Sprintf("%t", isActive)),
),
},
{
Config: fmt.Sprintf(config, testAccConf.enterpriseSlug, updatedIP, updatedName, updatedIsActive),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "enterprise_slug", testAccConf.enterpriseSlug),
resource.TestCheckResourceAttr(resourceName, "ip", updatedIP),
resource.TestCheckResourceAttr(resourceName, "name", updatedName),
resource.TestCheckResourceAttr(resourceName, "is_active", fmt.Sprintf("%t", updatedIsActive)),
),
},
},
})
})
}
Loading