Skip to content

Commit 866a673

Browse files
ErikElkinsnickfloydstevehipwellgateixeiradeiga
authored
feat: Adding github_enterprise_ip_allow_list_entry resource (#2649)
* Adding github_enterprise_ip_allow_list_entry resource * Update github/resource_github_enterprise_ip_allow_list_entry.go Co-authored-by: Steve Hipwell <[email protected]> * Update github/resource_github_enterprise_ip_allow_list_entry.go Co-authored-by: Steve Hipwell <[email protected]> * Update github/resource_github_enterprise_ip_allow_list_entry.go Co-authored-by: gateixeira <[email protected]> * Update github/resource_github_enterprise_ip_allow_list_entry_test.go Co-authored-by: gateixeira <[email protected]> * Update github/resource_github_enterprise_ip_allow_list_entry_test.go Co-authored-by: gateixeira <[email protected]> * Code review fixes * Fixes from code review * Fixing code review comments * Update resource_github_enterprise_ip_allow_list_entry.go Co-authored-by: Timo Sand <[email protected]> * Update resource_github_enterprise_ip_allow_list_entry.go Co-authored-by: Timo Sand <[email protected]> * Update github/resource_github_enterprise_ip_allow_list_entry_test.go Co-authored-by: Steve Hipwell <[email protected]> * Update github/resource_github_enterprise_ip_allow_list_entry.go Co-authored-by: Steve Hipwell <[email protected]> * Code review changes * Fixing config in test * Flattening update test * Adding error handling * Simplifying import function * Fix docs * Fixing code review changes * Fixing lint * Adding error handling for missing global ID * Removing two old functions * Fixing lint --------- Co-authored-by: Nick Floyd <[email protected]> Co-authored-by: Steve Hipwell <[email protected]> Co-authored-by: gateixeira <[email protected]> Co-authored-by: Timo Sand <[email protected]>
1 parent 7bcb796 commit 866a673

6 files changed

Lines changed: 442 additions & 15 deletions

github/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ func Provider() *schema.Provider {
214214
"github_user_ssh_key": resourceGithubUserSshKey(),
215215
"github_enterprise_organization": resourceGithubEnterpriseOrganization(),
216216
"github_enterprise_actions_runner_group": resourceGithubActionsEnterpriseRunnerGroup(),
217+
"github_enterprise_ip_allow_list_entry": resourceGithubEnterpriseIpAllowListEntry(),
217218
"github_enterprise_actions_workflow_permissions": resourceGithubEnterpriseActionsWorkflowPermissions(),
218219
"github_actions_organization_workflow_permissions": resourceGithubActionsOrganizationWorkflowPermissions(),
219220
"github_enterprise_security_analysis_settings": resourceGithubEnterpriseSecurityAnalysisSettings(),
Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"strings"
6+
7+
"github.com/hashicorp/terraform-plugin-log/tflog"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10+
"github.com/shurcooL/githubv4"
11+
)
12+
13+
func resourceGithubEnterpriseIpAllowListEntry() *schema.Resource {
14+
return &schema.Resource{
15+
Description: "Manage a GitHub Enterprise IP Allow List Entry.",
16+
CreateContext: resourceGithubEnterpriseIpAllowListEntryCreate,
17+
ReadContext: resourceGithubEnterpriseIpAllowListEntryRead,
18+
UpdateContext: resourceGithubEnterpriseIpAllowListEntryUpdate,
19+
DeleteContext: resourceGithubEnterpriseIpAllowListEntryDelete,
20+
Importer: &schema.ResourceImporter{
21+
StateContext: resourceGithubEnterpriseIpAllowListEntryImport,
22+
},
23+
24+
Schema: map[string]*schema.Schema{
25+
"enterprise_slug": {
26+
Type: schema.TypeString,
27+
Required: true,
28+
ForceNew: true,
29+
Description: "The slug of the enterprise to apply the IP allow list entry to.",
30+
},
31+
"ip": {
32+
Type: schema.TypeString,
33+
Required: true,
34+
ForceNew: true,
35+
Description: "An IP address or range of IP addresses in CIDR notation.",
36+
},
37+
"name": {
38+
Type: schema.TypeString,
39+
Optional: true,
40+
Description: "An optional name for the IP allow list entry.",
41+
},
42+
"is_active": {
43+
Type: schema.TypeBool,
44+
Optional: true,
45+
Default: true,
46+
Description: "Whether the entry is currently active.",
47+
},
48+
"created_at": {
49+
Type: schema.TypeString,
50+
Computed: true,
51+
Description: "Timestamp of when the entry was created.",
52+
},
53+
"updated_at": {
54+
Type: schema.TypeString,
55+
Computed: true,
56+
Description: "Timestamp of when the entry was last updated.",
57+
},
58+
},
59+
}
60+
}
61+
62+
func resourceGithubEnterpriseIpAllowListEntryCreate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
63+
client := meta.(*Owner).v4client
64+
65+
// First, get the enterprise ID as we need it for the mutation
66+
enterpriseSlug := d.Get("enterprise_slug").(string)
67+
enterpriseID, err := getEnterpriseID(ctx, client, enterpriseSlug)
68+
if err != nil {
69+
return diag.FromErr(err)
70+
}
71+
72+
// Then create the IP allow list entry
73+
var mutation struct {
74+
CreateIpAllowListEntry struct {
75+
IpAllowListEntry struct {
76+
ID githubv4.String
77+
AllowListValue githubv4.String
78+
Name githubv4.String
79+
IsActive githubv4.Boolean
80+
CreatedAt githubv4.String
81+
UpdatedAt githubv4.String
82+
}
83+
} `graphql:"createIpAllowListEntry(input: $input)"`
84+
}
85+
86+
name := d.Get("name").(string)
87+
input := githubv4.CreateIpAllowListEntryInput{
88+
OwnerID: githubv4.ID(enterpriseID),
89+
AllowListValue: githubv4.String(d.Get("ip").(string)),
90+
IsActive: githubv4.Boolean(d.Get("is_active").(bool)),
91+
}
92+
93+
if name != "" {
94+
v := githubv4.String(name)
95+
input.Name = &v
96+
}
97+
98+
err = client.Mutate(ctx, &mutation, input, nil)
99+
if err != nil {
100+
return diag.FromErr(err)
101+
}
102+
103+
d.SetId(string(mutation.CreateIpAllowListEntry.IpAllowListEntry.ID))
104+
105+
if err := d.Set("created_at", mutation.CreateIpAllowListEntry.IpAllowListEntry.CreatedAt); err != nil {
106+
return diag.FromErr(err)
107+
}
108+
if err := d.Set("updated_at", mutation.CreateIpAllowListEntry.IpAllowListEntry.UpdatedAt); err != nil {
109+
return diag.FromErr(err)
110+
}
111+
112+
return nil
113+
}
114+
115+
func resourceGithubEnterpriseIpAllowListEntryRead(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
116+
client := meta.(*Owner).v4client
117+
118+
var query struct {
119+
Node struct {
120+
IpAllowListEntry struct {
121+
ID githubv4.String
122+
AllowListValue githubv4.String
123+
Name githubv4.String
124+
IsActive githubv4.Boolean
125+
CreatedAt githubv4.String
126+
UpdatedAt githubv4.String
127+
Owner struct {
128+
Enterprise struct {
129+
Slug githubv4.String
130+
} `graphql:"... on Enterprise"`
131+
}
132+
} `graphql:"... on IpAllowListEntry"`
133+
} `graphql:"node(id: $id)"`
134+
}
135+
136+
variables := map[string]any{
137+
"id": githubv4.ID(d.Id()),
138+
}
139+
140+
err := client.Query(ctx, &query, variables)
141+
if err != nil {
142+
if strings.Contains(err.Error(), "Could not resolve to a node with the global id") {
143+
tflog.Info(ctx, "Removing IP allow list entry from state because it no longer exists in GitHub", map[string]any{
144+
"id": d.Id(),
145+
})
146+
d.SetId("")
147+
return nil
148+
}
149+
return diag.FromErr(err)
150+
}
151+
152+
entry := query.Node.IpAllowListEntry
153+
if err := d.Set("name", entry.Name); err != nil {
154+
return diag.FromErr(err)
155+
}
156+
if err := d.Set("ip", entry.AllowListValue); err != nil {
157+
return diag.FromErr(err)
158+
}
159+
if err := d.Set("is_active", entry.IsActive); err != nil {
160+
return diag.FromErr(err)
161+
}
162+
if err := d.Set("created_at", entry.CreatedAt); err != nil {
163+
return diag.FromErr(err)
164+
}
165+
if err := d.Set("updated_at", entry.UpdatedAt); err != nil {
166+
return diag.FromErr(err)
167+
}
168+
169+
return nil
170+
}
171+
172+
func resourceGithubEnterpriseIpAllowListEntryUpdate(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
173+
client := meta.(*Owner).v4client
174+
175+
var mutation struct {
176+
UpdateIpAllowListEntry struct {
177+
IpAllowListEntry struct {
178+
ID githubv4.String
179+
AllowListValue githubv4.String
180+
Name githubv4.String
181+
IsActive githubv4.Boolean
182+
UpdatedAt githubv4.String
183+
}
184+
} `graphql:"updateIpAllowListEntry(input: $input)"`
185+
}
186+
187+
name := d.Get("name").(string)
188+
input := githubv4.UpdateIpAllowListEntryInput{
189+
IPAllowListEntryID: githubv4.ID(d.Id()),
190+
AllowListValue: githubv4.String(d.Get("ip").(string)),
191+
IsActive: githubv4.Boolean(d.Get("is_active").(bool)),
192+
}
193+
194+
if name != "" {
195+
v := githubv4.String(name)
196+
input.Name = &v
197+
}
198+
199+
err := client.Mutate(ctx, &mutation, input, nil)
200+
if err != nil {
201+
return diag.FromErr(err)
202+
}
203+
204+
if err := d.Set("updated_at", mutation.UpdateIpAllowListEntry.IpAllowListEntry.UpdatedAt); err != nil {
205+
return diag.FromErr(err)
206+
}
207+
208+
return nil
209+
}
210+
211+
func resourceGithubEnterpriseIpAllowListEntryDelete(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
212+
client := meta.(*Owner).v4client
213+
214+
var mutation struct {
215+
DeleteIpAllowListEntry struct {
216+
ClientMutationID githubv4.String
217+
} `graphql:"deleteIpAllowListEntry(input: $input)"`
218+
}
219+
220+
input := githubv4.DeleteIpAllowListEntryInput{
221+
IPAllowListEntryID: githubv4.ID(d.Id()),
222+
}
223+
224+
err := client.Mutate(ctx, &mutation, input, nil)
225+
// GraphQL will return a 200 OK if it couldn't find the global ID
226+
if err != nil && !strings.Contains(err.Error(), "Could not resolve to a node with the global id") {
227+
return diag.FromErr(err)
228+
}
229+
230+
return nil
231+
}
232+
233+
func resourceGithubEnterpriseIpAllowListEntryImport(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
234+
client := meta.(*Owner).v4client
235+
236+
var query struct {
237+
Node struct {
238+
IpAllowListEntry struct {
239+
ID githubv4.String
240+
AllowListValue githubv4.String
241+
Name githubv4.String
242+
IsActive githubv4.Boolean
243+
CreatedAt githubv4.String
244+
UpdatedAt githubv4.String
245+
Owner struct {
246+
Enterprise struct {
247+
Slug githubv4.String
248+
} `graphql:"... on Enterprise"`
249+
}
250+
} `graphql:"... on IpAllowListEntry"`
251+
} `graphql:"node(id: $id)"`
252+
}
253+
254+
variables := map[string]any{
255+
"id": githubv4.ID(d.Id()),
256+
}
257+
258+
err := client.Query(ctx, &query, variables)
259+
if err != nil {
260+
return nil, err
261+
}
262+
263+
entry := query.Node.IpAllowListEntry
264+
265+
if err := d.Set("enterprise_slug", string(entry.Owner.Enterprise.Slug)); err != nil {
266+
return nil, err
267+
}
268+
if err := d.Set("ip", string(entry.AllowListValue)); err != nil {
269+
return nil, err
270+
}
271+
if err := d.Set("name", entry.Name); err != nil {
272+
return nil, err
273+
}
274+
if err := d.Set("is_active", entry.IsActive); err != nil {
275+
return nil, err
276+
}
277+
if err := d.Set("created_at", entry.CreatedAt); err != nil {
278+
return nil, err
279+
}
280+
if err := d.Set("updated_at", entry.UpdatedAt); err != nil {
281+
return nil, err
282+
}
283+
284+
return []*schema.ResourceData{d}, nil
285+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package github
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"testing"
7+
8+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
9+
)
10+
11+
func TestAccGithubEnterpriseIpAllowListEntry(t *testing.T) {
12+
t.Run("basic", func(t *testing.T) {
13+
resourceName := "github_enterprise_ip_allow_list_entry.test"
14+
ip := "192.168.1.0/24"
15+
name := "Test Entry"
16+
isActive := true
17+
18+
config := `
19+
resource "github_enterprise_ip_allow_list_entry" "test" {
20+
enterprise_slug = "%s"
21+
ip = "%s"
22+
name = "%s"
23+
is_active = %t
24+
}
25+
`
26+
27+
resource.Test(t, resource.TestCase{
28+
PreCheck: func() {
29+
skipUnlessEnterprise(t)
30+
},
31+
ProviderFactories: providerFactories,
32+
Steps: []resource.TestStep{
33+
{
34+
Config: fmt.Sprintf(config, testAccConf.enterpriseSlug, ip, name, isActive),
35+
Check: resource.ComposeTestCheckFunc(
36+
resource.TestCheckResourceAttr(resourceName, "enterprise_slug", testAccConf.enterpriseSlug),
37+
resource.TestCheckResourceAttr(resourceName, "ip", ip),
38+
resource.TestCheckResourceAttr(resourceName, "name", name),
39+
resource.TestCheckResourceAttr(resourceName, "is_active", strconv.FormatBool(isActive)),
40+
),
41+
},
42+
{
43+
ResourceName: resourceName,
44+
ImportState: true,
45+
ImportStateVerify: true,
46+
},
47+
},
48+
})
49+
})
50+
t.Run("update", func(t *testing.T) {
51+
resourceName := "github_enterprise_ip_allow_list_entry.test"
52+
ip := "192.168.1.0/24"
53+
name := "Test Entry"
54+
isActive := true
55+
56+
updatedIP := "10.0.0.0/16"
57+
updatedName := "Updated Entry"
58+
updatedIsActive := false
59+
60+
config := `
61+
resource "github_enterprise_ip_allow_list_entry" "test" {
62+
enterprise_slug = "%s"
63+
ip = "%s"
64+
name = "%s"
65+
is_active = %t
66+
}
67+
`
68+
69+
resource.Test(t, resource.TestCase{
70+
PreCheck: func() {
71+
skipUnlessEnterprise(t)
72+
},
73+
ProviderFactories: providerFactories,
74+
Steps: []resource.TestStep{
75+
{
76+
Config: fmt.Sprintf(config, testAccConf.enterpriseSlug, ip, name, isActive),
77+
Check: resource.ComposeTestCheckFunc(
78+
resource.TestCheckResourceAttr(resourceName, "enterprise_slug", testAccConf.enterpriseSlug),
79+
resource.TestCheckResourceAttr(resourceName, "ip", ip),
80+
resource.TestCheckResourceAttr(resourceName, "name", name),
81+
resource.TestCheckResourceAttr(resourceName, "is_active", fmt.Sprintf("%t", isActive)),
82+
),
83+
},
84+
{
85+
Config: fmt.Sprintf(config, testAccConf.enterpriseSlug, updatedIP, updatedName, updatedIsActive),
86+
Check: resource.ComposeTestCheckFunc(
87+
resource.TestCheckResourceAttr(resourceName, "enterprise_slug", testAccConf.enterpriseSlug),
88+
resource.TestCheckResourceAttr(resourceName, "ip", updatedIP),
89+
resource.TestCheckResourceAttr(resourceName, "name", updatedName),
90+
resource.TestCheckResourceAttr(resourceName, "is_active", fmt.Sprintf("%t", updatedIsActive)),
91+
),
92+
},
93+
},
94+
})
95+
})
96+
}

0 commit comments

Comments
 (0)