Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
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 @@ -173,6 +173,7 @@ func Provider() *schema.Provider {
"github_organization_block": resourceOrganizationBlock(),
"github_organization_custom_role": resourceGithubOrganizationCustomRole(),
"github_organization_custom_properties": resourceGithubOrganizationCustomProperties(),
"github_organization_network_configuration": resourceGithubOrganizationNetworkConfiguration(),
"github_organization_project": resourceGithubOrganizationProject(),
"github_organization_repository_role": resourceGithubOrganizationRepositoryRole(),
"github_organization_role": resourceGithubOrganizationRole(),
Expand Down
193 changes: 193 additions & 0 deletions github/resource_github_organization_network_configuration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
package github

import (
"context"
"log"
"net/http"
"regexp"
"time"

"github.com/google/go-github/v83/github"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

var organizationNetworkConfigurationNamePattern = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)

func resourceGithubOrganizationNetworkConfiguration() *schema.Resource {
return &schema.Resource{
Create: resourceGithubOrganizationNetworkConfigurationCreate,
Read: resourceGithubOrganizationNetworkConfigurationRead,
Update: resourceGithubOrganizationNetworkConfigurationUpdate,
Delete: resourceGithubOrganizationNetworkConfigurationDelete,
Comment thread
austenstone marked this conversation as resolved.
Outdated
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
Comment thread
austenstone marked this conversation as resolved.
"name": {
Type: schema.TypeString,
Required: true,
ValidateDiagFunc: validation.ToDiagFunc(validation.All(
validation.StringLenBetween(1, 100),
validation.StringMatch(
organizationNetworkConfigurationNamePattern,
"name may only contain upper and lowercase letters a-z, numbers 0-9, '.', '-', and '_'",
),
)),
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 '_'.",
},
"compute_service": {
Type: schema.TypeString,
Optional: true,
Default: "none",
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{"none", "actions"}, false)),
Description: "The hosted compute service to use for the network configuration. Can be one of: 'none', 'actions'. Defaults to 'none'.",
},
"network_settings_ids": {
Type: schema.TypeList,
Required: true,
MinItems: 1,
MaxItems: 1,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: "An array containing exactly one network settings ID. A network settings resource can only be associated with one network configuration at a time.",
},
"created_on": {
Type: schema.TypeString,
Computed: true,
Description: "Timestamp when the network configuration was created.",
},
},
}
}

func resourceGithubOrganizationNetworkConfigurationCreate(d *schema.ResourceData, meta any) error {
if err := checkOrganization(meta); err != nil {
return err
}

client := meta.(*Owner).v3client
orgName := meta.(*Owner).name
ctx := context.Background()

configuration, _, err := client.Organizations.CreateNetworkConfiguration(ctx, orgName, buildOrganizationNetworkConfigurationRequest(d))
if err != nil {
return err
}

d.SetId(configuration.GetID())

return resourceGithubOrganizationNetworkConfigurationRead(d, meta)
Comment thread
austenstone marked this conversation as resolved.
Outdated
}

func resourceGithubOrganizationNetworkConfigurationRead(d *schema.ResourceData, meta any) error {
if err := checkOrganization(meta); err != nil {
return err
}

client := meta.(*Owner).v3client
orgName := meta.(*Owner).name
networkConfigurationID := d.Id()
ctx := context.WithValue(context.Background(), ctxId, networkConfigurationID)

configuration, resp, err := client.Organizations.GetNetworkConfiguration(ctx, orgName, networkConfigurationID)
if err != nil {
if ghErr, ok := err.(*github.ErrorResponse); ok && ghErr.Response.StatusCode == http.StatusNotFound {
log.Printf("[WARN] Removing organization network configuration %s from state because it no longer exists in GitHub", networkConfigurationID)
Comment thread
austenstone marked this conversation as resolved.
Outdated
d.SetId("")
return nil
}

return err
}

if resp != nil && resp.StatusCode == http.StatusNotModified {
return nil
}

return setOrganizationNetworkConfigurationState(d, configuration)
Comment thread
austenstone marked this conversation as resolved.
Outdated
}

func resourceGithubOrganizationNetworkConfigurationUpdate(d *schema.ResourceData, meta any) error {
if err := checkOrganization(meta); err != nil {
return err
}

client := meta.(*Owner).v3client
orgName := meta.(*Owner).name
networkConfigurationID := d.Id()
ctx := context.WithValue(context.Background(), ctxId, networkConfigurationID)

_, _, err := client.Organizations.UpdateNetworkConfiguration(ctx, orgName, networkConfigurationID, buildOrganizationNetworkConfigurationRequest(d))
if err != nil {
return err
}

return resourceGithubOrganizationNetworkConfigurationRead(d, meta)
}

func resourceGithubOrganizationNetworkConfigurationDelete(d *schema.ResourceData, meta any) error {
if err := checkOrganization(meta); err != nil {
return err
}

client := meta.(*Owner).v3client
orgName := meta.(*Owner).name
ctx := context.Background()

_, err := client.Organizations.DeleteNetworkConfigurations(ctx, orgName, d.Id())
if err != nil {
if ghErr, ok := err.(*github.ErrorResponse); ok && ghErr.Response.StatusCode == http.StatusNotFound {
return nil
}

return err
}

return nil
}

func expandOrganizationNetworkConfigurationComputeService(computeService string) *github.ComputeService {
service := github.ComputeService(computeService)
return &service
}

func expandOrganizationNetworkSettingsIDs(networkSettingsIDs []any) []string {
ids := make([]string, 0, len(networkSettingsIDs))
for _, networkSettingsID := range networkSettingsIDs {
ids = append(ids, networkSettingsID.(string))
}

return ids
}

func buildOrganizationNetworkConfigurationRequest(d *schema.ResourceData) github.NetworkConfigurationRequest {
return github.NetworkConfigurationRequest{
Name: github.Ptr(d.Get("name").(string)),
ComputeService: expandOrganizationNetworkConfigurationComputeService(d.Get("compute_service").(string)),
NetworkSettingsIDs: expandOrganizationNetworkSettingsIDs(d.Get("network_settings_ids").([]any)),
}
}

func setOrganizationNetworkConfigurationState(d *schema.ResourceData, configuration *github.NetworkConfiguration) error {
if err := d.Set("name", configuration.GetName()); err != nil {
return err
}
if configuration.ComputeService != nil {
if err := d.Set("compute_service", string(*configuration.ComputeService)); err != nil {
return err
}
}
if err := d.Set("network_settings_ids", configuration.NetworkSettingsIDs); err != nil {
return err
}
if configuration.CreatedOn != nil {
if err := d.Set("created_on", configuration.CreatedOn.Format(time.RFC3339)); err != nil {
return err
}
}

return nil
}
116 changes: 116 additions & 0 deletions github/resource_github_organization_network_configuration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package github

import (
"fmt"
"os"
"testing"

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

func TestAccGithubOrganizationNetworkConfiguration(t *testing.T) {
t.Run("create", func(t *testing.T) {
networkSettingsID := testAccOrganizationNetworkConfigurationID(t)

randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
resourceName := "github_organization_network_configuration.test"
configurationName := fmt.Sprintf("%snetwork-config-%s", testResourcePrefix, randomID)

config := testAccOrganizationNetworkConfigurationConfig(configurationName, "actions", networkSettingsID)

resource.Test(t, resource.TestCase{
PreCheck: func() { skipUnlessHasPaidOrgs(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
Comment thread
austenstone marked this conversation as resolved.
Outdated
resource.TestCheckResourceAttr(resourceName, "name", configurationName),
resource.TestCheckResourceAttr(resourceName, "compute_service", "actions"),
resource.TestCheckResourceAttr(resourceName, "network_settings_ids.0", networkSettingsID),
resource.TestCheckResourceAttrSet(resourceName, "id"),
resource.TestCheckResourceAttrSet(resourceName, "created_on"),
),
},
},
})
})

t.Run("update", func(t *testing.T) {
networkSettingsID := testAccOrganizationNetworkConfigurationID(t)

randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
resourceName := "github_organization_network_configuration.test"
beforeName := fmt.Sprintf("%snetwork-config-%s-a", testResourcePrefix, randomID)
afterName := fmt.Sprintf("%snetwork-config-%s-b", testResourcePrefix, randomID)

resource.Test(t, resource.TestCase{
PreCheck: func() { skipUnlessHasPaidOrgs(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: testAccOrganizationNetworkConfigurationConfig(beforeName, "actions", networkSettingsID),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", beforeName),
resource.TestCheckResourceAttr(resourceName, "compute_service", "actions"),
resource.TestCheckResourceAttr(resourceName, "network_settings_ids.0", networkSettingsID),
),
},
{
Config: testAccOrganizationNetworkConfigurationConfig(afterName, "none", networkSettingsID),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", afterName),
resource.TestCheckResourceAttr(resourceName, "compute_service", "none"),
resource.TestCheckResourceAttr(resourceName, "network_settings_ids.0", networkSettingsID),
),
},
},
})
})

t.Run("import", func(t *testing.T) {
networkSettingsID := testAccOrganizationNetworkConfigurationID(t)

randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
configurationName := fmt.Sprintf("%snetwork-config-%s", testResourcePrefix, randomID)

config := testAccOrganizationNetworkConfigurationConfig(configurationName, "actions", networkSettingsID)

resource.Test(t, resource.TestCase{
PreCheck: func() { skipUnlessHasPaidOrgs(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: config,
},
{
ResourceName: "github_organization_network_configuration.test",
ImportState: true,
ImportStateVerify: true,
},
},
})
})
}

func testAccOrganizationNetworkConfigurationID(t *testing.T) string {
t.Helper()

networkSettingsID := os.Getenv("GITHUB_TEST_NETWORK_SETTINGS_ID")
if networkSettingsID == "" {
t.Skip("GITHUB_TEST_NETWORK_SETTINGS_ID not set")
}

return networkSettingsID
}

func testAccOrganizationNetworkConfigurationConfig(name, computeService, networkSettingsID string) string {
return fmt.Sprintf(`
resource "github_organization_network_configuration" "test" {
name = %q
compute_service = %q
network_settings_ids = [%q]
}
`, name, computeService, networkSettingsID)
}
56 changes: 56 additions & 0 deletions website/docs/r/organization_network_configuration.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
layout: "github"
page_title: "GitHub: github_organization_network_configuration"
description: |-
Creates and manages hosted compute network configurations for a GitHub organization.
---

# github_organization_network_configuration

This resource allows you to create and manage hosted compute network configurations for a GitHub Organization. Network configurations allow GitHub-hosted compute services, such as Actions hosted runners, to connect to your private network resources.

~> **Note:** This resource is organization-only and is 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.

## Example Usage

```hcl
resource "github_organization_network_configuration" "example" {
name = "my-network-config"
compute_service = "actions"
network_settings_ids = ["23456789ABCDEF1"]
}
```

## Argument Reference

The following arguments are supported:

* `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 `_`.

* `compute_service` - (Optional) The hosted compute service to use for the network configuration. Can be one of `none` or `actions`. Defaults to `none`.

* `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.

## Attributes Reference

In addition to the arguments above, the following attributes are exported:

* `id` - The ID of the network configuration.

* `created_on` - The timestamp when the network configuration was created.

## Notes

* This resource can only be used with organization accounts.
* GitHub currently allows exactly one `network_settings_ids` value per organization network configuration.
* The `network_settings_ids` value must reference an existing hosted compute network settings resource configured outside this provider.

## Import

Organization network configurations can be imported using the network configuration ID:

```shell
terraform import github_organization_network_configuration.example 1234567890ABCDEF
```

The network configuration ID can be found using the [list hosted compute network configurations for an organization](https://docs.github.com/en/enterprise-cloud@latest/rest/orgs/network-configurations#list-hosted-compute-network-configurations-for-an-organization) API.