Skip to content

Commit eebea06

Browse files
committed
feat: add organization network configuration resource
1 parent 1af72d4 commit eebea06

File tree

4 files changed

+351
-0
lines changed

4 files changed

+351
-0
lines changed

github/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ func Provider() *schema.Provider {
173173
"github_organization_block": resourceOrganizationBlock(),
174174
"github_organization_custom_role": resourceGithubOrganizationCustomRole(),
175175
"github_organization_custom_properties": resourceGithubOrganizationCustomProperties(),
176+
"github_organization_network_configuration": resourceGithubOrganizationNetworkConfiguration(),
176177
"github_organization_project": resourceGithubOrganizationProject(),
177178
"github_organization_repository_role": resourceGithubOrganizationRepositoryRole(),
178179
"github_organization_role": resourceGithubOrganizationRole(),
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package github
2+
3+
import (
4+
"context"
5+
"log"
6+
"net/http"
7+
"regexp"
8+
9+
"github.com/google/go-github/v83/github"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
12+
)
13+
14+
func resourceGithubOrganizationNetworkConfiguration() *schema.Resource {
15+
return &schema.Resource{
16+
Create: resourceGithubOrganizationNetworkConfigurationCreate,
17+
Read: resourceGithubOrganizationNetworkConfigurationRead,
18+
Update: resourceGithubOrganizationNetworkConfigurationUpdate,
19+
Delete: resourceGithubOrganizationNetworkConfigurationDelete,
20+
Importer: &schema.ResourceImporter{
21+
StateContext: schema.ImportStatePassthroughContext,
22+
},
23+
24+
Schema: map[string]*schema.Schema{
25+
"name": {
26+
Type: schema.TypeString,
27+
Required: true,
28+
ValidateDiagFunc: validation.ToDiagFunc(validation.All(
29+
validation.StringLenBetween(1, 100),
30+
validation.StringMatch(
31+
regexp.MustCompile(`^[a-zA-Z0-9._-]+$`),
32+
"name may only contain upper and lowercase letters a-z, numbers 0-9, '.', '-', and '_'",
33+
),
34+
)),
35+
Description: "Name of the network configuration. Must be between 1 and 100 characters and may only contain upper and lowercase letters a-z, numbers 0-9, '.', '-', and '_'.",
36+
},
37+
"compute_service": {
38+
Type: schema.TypeString,
39+
Optional: true,
40+
Default: "none",
41+
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{"none", "actions"}, false)),
42+
Description: "The hosted compute service to use for the network configuration. Can be one of: 'none', 'actions'. Defaults to 'none'.",
43+
},
44+
"network_settings_ids": {
45+
Type: schema.TypeList,
46+
Required: true,
47+
MinItems: 1,
48+
MaxItems: 1,
49+
Elem: &schema.Schema{
50+
Type: schema.TypeString,
51+
},
52+
Description: "An array containing exactly one network settings ID. A network settings resource can only be associated with one network configuration at a time.",
53+
},
54+
"created_on": {
55+
Type: schema.TypeString,
56+
Computed: true,
57+
Description: "Timestamp when the network configuration was created.",
58+
},
59+
},
60+
}
61+
}
62+
63+
func resourceGithubOrganizationNetworkConfigurationCreate(d *schema.ResourceData, meta any) error {
64+
if err := checkOrganization(meta); err != nil {
65+
return err
66+
}
67+
68+
client := meta.(*Owner).v3client
69+
orgName := meta.(*Owner).name
70+
ctx := context.Background()
71+
72+
configuration, _, err := client.Organizations.CreateNetworkConfiguration(ctx, orgName, github.NetworkConfigurationRequest{
73+
Name: github.Ptr(d.Get("name").(string)),
74+
ComputeService: expandOrganizationNetworkConfigurationComputeService(d.Get("compute_service").(string)),
75+
NetworkSettingsIDs: expandOrganizationNetworkSettingsIDs(d.Get("network_settings_ids").([]any)),
76+
})
77+
if err != nil {
78+
return err
79+
}
80+
81+
d.SetId(configuration.GetID())
82+
83+
return resourceGithubOrganizationNetworkConfigurationRead(d, meta)
84+
}
85+
86+
func resourceGithubOrganizationNetworkConfigurationRead(d *schema.ResourceData, meta any) error {
87+
if err := checkOrganization(meta); err != nil {
88+
return err
89+
}
90+
91+
client := meta.(*Owner).v3client
92+
orgName := meta.(*Owner).name
93+
networkConfigurationID := d.Id()
94+
ctx := context.WithValue(context.Background(), ctxId, networkConfigurationID)
95+
96+
configuration, resp, err := client.Organizations.GetNetworkConfiguration(ctx, orgName, networkConfigurationID)
97+
if err != nil {
98+
if ghErr, ok := err.(*github.ErrorResponse); ok && ghErr.Response.StatusCode == http.StatusNotFound {
99+
log.Printf("[WARN] Removing organization network configuration %s from state because it no longer exists in GitHub", networkConfigurationID)
100+
d.SetId("")
101+
return nil
102+
}
103+
104+
return err
105+
}
106+
107+
if resp != nil && resp.StatusCode == http.StatusNotModified {
108+
return nil
109+
}
110+
111+
_ = d.Set("name", configuration.GetName())
112+
if configuration.ComputeService != nil {
113+
_ = d.Set("compute_service", string(*configuration.ComputeService))
114+
}
115+
_ = d.Set("network_settings_ids", configuration.NetworkSettingsIDs)
116+
if configuration.CreatedOn != nil {
117+
_ = d.Set("created_on", configuration.CreatedOn.Format("2006-01-02T15:04:05Z07:00"))
118+
}
119+
120+
return nil
121+
}
122+
123+
func resourceGithubOrganizationNetworkConfigurationUpdate(d *schema.ResourceData, meta any) error {
124+
if err := checkOrganization(meta); err != nil {
125+
return err
126+
}
127+
128+
client := meta.(*Owner).v3client
129+
orgName := meta.(*Owner).name
130+
networkConfigurationID := d.Id()
131+
ctx := context.WithValue(context.Background(), ctxId, networkConfigurationID)
132+
133+
_, _, err := client.Organizations.UpdateNetworkConfiguration(ctx, orgName, networkConfigurationID, github.NetworkConfigurationRequest{
134+
Name: github.Ptr(d.Get("name").(string)),
135+
ComputeService: expandOrganizationNetworkConfigurationComputeService(d.Get("compute_service").(string)),
136+
NetworkSettingsIDs: expandOrganizationNetworkSettingsIDs(d.Get("network_settings_ids").([]any)),
137+
})
138+
if err != nil {
139+
return err
140+
}
141+
142+
return resourceGithubOrganizationNetworkConfigurationRead(d, meta)
143+
}
144+
145+
func resourceGithubOrganizationNetworkConfigurationDelete(d *schema.ResourceData, meta any) error {
146+
if err := checkOrganization(meta); err != nil {
147+
return err
148+
}
149+
150+
client := meta.(*Owner).v3client
151+
orgName := meta.(*Owner).name
152+
ctx := context.Background()
153+
154+
_, err := client.Organizations.DeleteNetworkConfigurations(ctx, orgName, d.Id())
155+
if err != nil {
156+
if ghErr, ok := err.(*github.ErrorResponse); ok && ghErr.Response.StatusCode == http.StatusNotFound {
157+
return nil
158+
}
159+
160+
return err
161+
}
162+
163+
return nil
164+
}
165+
166+
func expandOrganizationNetworkConfigurationComputeService(computeService string) *github.ComputeService {
167+
service := github.ComputeService(computeService)
168+
return &service
169+
}
170+
171+
func expandOrganizationNetworkSettingsIDs(networkSettingsIDs []any) []string {
172+
ids := make([]string, 0, len(networkSettingsIDs))
173+
for _, networkSettingsID := range networkSettingsIDs {
174+
ids = append(ids, networkSettingsID.(string))
175+
}
176+
177+
return ids
178+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package github
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"testing"
7+
8+
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
9+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
10+
)
11+
12+
func TestAccGithubOrganizationNetworkConfiguration(t *testing.T) {
13+
t.Run("create", func(t *testing.T) {
14+
networkSettingsID := os.Getenv("GITHUB_TEST_NETWORK_SETTINGS_ID")
15+
if networkSettingsID == "" {
16+
t.Skip("GITHUB_TEST_NETWORK_SETTINGS_ID not set")
17+
}
18+
19+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
20+
resourceName := "github_organization_network_configuration.test"
21+
configurationName := fmt.Sprintf("%snetwork-config-%s", testResourcePrefix, randomID)
22+
23+
config := fmt.Sprintf(`
24+
resource "github_organization_network_configuration" "test" {
25+
name = %q
26+
compute_service = "actions"
27+
network_settings_ids = [%q]
28+
}
29+
`, configurationName, networkSettingsID)
30+
31+
resource.Test(t, resource.TestCase{
32+
PreCheck: func() { skipUnlessMode(t, organization) },
33+
ProviderFactories: providerFactories,
34+
Steps: []resource.TestStep{
35+
{
36+
Config: config,
37+
Check: resource.ComposeTestCheckFunc(
38+
resource.TestCheckResourceAttr(resourceName, "name", configurationName),
39+
resource.TestCheckResourceAttr(resourceName, "compute_service", "actions"),
40+
resource.TestCheckResourceAttr(resourceName, "network_settings_ids.0", networkSettingsID),
41+
resource.TestCheckResourceAttrSet(resourceName, "id"),
42+
resource.TestCheckResourceAttrSet(resourceName, "created_on"),
43+
),
44+
},
45+
},
46+
})
47+
})
48+
49+
t.Run("update", func(t *testing.T) {
50+
networkSettingsID := os.Getenv("GITHUB_TEST_NETWORK_SETTINGS_ID")
51+
if networkSettingsID == "" {
52+
t.Skip("GITHUB_TEST_NETWORK_SETTINGS_ID not set")
53+
}
54+
55+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
56+
resourceName := "github_organization_network_configuration.test"
57+
beforeName := fmt.Sprintf("%snetwork-config-%s-a", testResourcePrefix, randomID)
58+
afterName := fmt.Sprintf("%snetwork-config-%s-b", testResourcePrefix, randomID)
59+
60+
config := `
61+
resource "github_organization_network_configuration" "test" {
62+
name = %q
63+
compute_service = %q
64+
network_settings_ids = [%q]
65+
}
66+
`
67+
68+
resource.Test(t, resource.TestCase{
69+
PreCheck: func() { skipUnlessMode(t, organization) },
70+
ProviderFactories: providerFactories,
71+
Steps: []resource.TestStep{
72+
{
73+
Config: fmt.Sprintf(config, beforeName, "actions", networkSettingsID),
74+
Check: resource.ComposeTestCheckFunc(
75+
resource.TestCheckResourceAttr(resourceName, "name", beforeName),
76+
resource.TestCheckResourceAttr(resourceName, "compute_service", "actions"),
77+
resource.TestCheckResourceAttr(resourceName, "network_settings_ids.0", networkSettingsID),
78+
),
79+
},
80+
{
81+
Config: fmt.Sprintf(config, afterName, "none", networkSettingsID),
82+
Check: resource.ComposeTestCheckFunc(
83+
resource.TestCheckResourceAttr(resourceName, "name", afterName),
84+
resource.TestCheckResourceAttr(resourceName, "compute_service", "none"),
85+
resource.TestCheckResourceAttr(resourceName, "network_settings_ids.0", networkSettingsID),
86+
),
87+
},
88+
},
89+
})
90+
})
91+
92+
t.Run("import", func(t *testing.T) {
93+
networkSettingsID := os.Getenv("GITHUB_TEST_NETWORK_SETTINGS_ID")
94+
if networkSettingsID == "" {
95+
t.Skip("GITHUB_TEST_NETWORK_SETTINGS_ID not set")
96+
}
97+
98+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
99+
configurationName := fmt.Sprintf("%snetwork-config-%s", testResourcePrefix, randomID)
100+
101+
config := fmt.Sprintf(`
102+
resource "github_organization_network_configuration" "test" {
103+
name = %q
104+
compute_service = "actions"
105+
network_settings_ids = [%q]
106+
}
107+
`, configurationName, networkSettingsID)
108+
109+
resource.Test(t, resource.TestCase{
110+
PreCheck: func() { skipUnlessMode(t, organization) },
111+
ProviderFactories: providerFactories,
112+
Steps: []resource.TestStep{
113+
{
114+
Config: config,
115+
},
116+
{
117+
ResourceName: "github_organization_network_configuration.test",
118+
ImportState: true,
119+
ImportStateVerify: true,
120+
},
121+
},
122+
})
123+
})
124+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
---
2+
layout: "github"
3+
page_title: "GitHub: github_organization_network_configuration"
4+
description: |-
5+
Creates and manages network configurations for GitHub Actions hosted runners in an organization.
6+
---
7+
8+
# github_organization_network_configuration
9+
10+
This resource allows you to create and manage network configurations for GitHub Actions hosted runners in a GitHub organization. Network configurations enable you to configure networking settings for hosted compute services.
11+
12+
~> **Note:** This resource is only available for GitHub Enterprise Cloud organizations. See the [GitHub documentation](https://docs.github.com/en/enterprise-cloud@latest/rest/orgs/network-configurations) for more information.
13+
14+
## Example Usage
15+
16+
```hcl
17+
resource "github_organization_network_configuration" "example" {
18+
name = "my-network-config"
19+
compute_service = "actions"
20+
network_settings_ids = ["23456789ABCDEF1"]
21+
}
22+
```
23+
24+
## Argument Reference
25+
26+
The following arguments are supported:
27+
28+
* `name` - (Required) The name of the network configuration. Must be between 1 and 100 characters and may only contain upper and lowercase letters a-z, numbers 0-9, `.`, `-`, and `_`.
29+
30+
* `compute_service` - (Optional) The hosted compute service to use for the network configuration. Can be one of `none` or `actions`. Defaults to `none`.
31+
32+
* `network_settings_ids` - (Required) An array containing exactly one network settings ID. Network settings resources are configured separately through your cloud provider. A network settings resource can only be associated with one network configuration at a time.
33+
34+
## Attributes Reference
35+
36+
The following additional attributes are exported:
37+
38+
* `id` - The ID of the network configuration.
39+
40+
* `created_on` - The timestamp when the network configuration was created.
41+
42+
## Import
43+
44+
Organization network configurations can be imported using the network configuration ID:
45+
46+
```shell
47+
terraform import github_organization_network_configuration.example 1234567890ABCDEF
48+
```

0 commit comments

Comments
 (0)