Skip to content
Open
Show file tree
Hide file tree
Changes from all 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 @@ -138,6 +138,7 @@ func Provider() *schema.Provider {
"github_actions_environment_variable": resourceGithubActionsEnvironmentVariable(),
"github_actions_organization_oidc_subject_claim_customization_template": resourceGithubActionsOrganizationOIDCSubjectClaimCustomizationTemplate(),
"github_actions_organization_permissions": resourceGithubActionsOrganizationPermissions(),
"github_actions_organization_self_hosted_runners": resourceGithubActionsOrganizationSelfHostedRunners(),
"github_actions_organization_secret": resourceGithubActionsOrganizationSecret(),
"github_actions_organization_secret_repositories": resourceGithubActionsOrganizationSecretRepositories(),
"github_actions_organization_secret_repository": resourceGithubActionsOrganizationSecretRepository(),
Expand Down
190 changes: 190 additions & 0 deletions github/resource_github_actions_organization_self_hosted_runners.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package github

import (
"context"
"errors"

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

func resourceGithubActionsOrganizationSelfHostedRunners() *schema.Resource {
return &schema.Resource{
Create: resourceGithubActionsOrganizationSelfHostedRunnersCreateOrUpdate,
Read: resourceGithubActionsOrganizationSelfHostedRunnersRead,
Update: resourceGithubActionsOrganizationSelfHostedRunnersCreateOrUpdate,
Delete: resourceGithubActionsOrganizationSelfHostedRunnersDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"enabled_repositories": {
Type: schema.TypeString,
Required: true,
Description: "The policy that controls which repositories in the organization can create self-hosted runners. Can be one of: 'all', 'selected', or 'none'.",
ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice([]string{"all", "selected", "none"}, false)),
},
"enabled_repositories_config": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Description: "Sets the list of selected repositories that are allowed to create self-hosted runners. Only available when 'enabled_repositories' = 'selected'.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"repository_ids": {
Type: schema.TypeSet,
Required: true,
Description: "List of repository IDs allowed to create self-hosted runners.",
Elem: &schema.Schema{Type: schema.TypeInt},
},
},
},
},
},
}
}

func resourceGithubActionsOrganizationSelfHostedRunnersEnabledRepos(d *schema.ResourceData) ([]int64, error) {
var repoIDs []int64

config := d.Get("enabled_repositories_config").([]any)
if len(config) > 0 {
data := config[0].(map[string]any)
switch x := data["repository_ids"].(type) {
case *schema.Set:
for _, value := range x.List() {
repoIDs = append(repoIDs, int64(value.(int)))
}
}
} else {
return nil, errors.New("the enabled_repositories_config {} block must be specified if enabled_repositories == 'selected'")
}
return repoIDs, nil
}

func resourceGithubActionsOrganizationSelfHostedRunnersCreateOrUpdate(d *schema.ResourceData, meta any) error {
client := meta.(*Owner).v3client
orgName := meta.(*Owner).name
ctx := context.Background()
if !d.IsNewResource() {
ctx = context.WithValue(ctx, ctxId, d.Id())
}

err := checkOrganization(meta)
if err != nil {
return err
}

enabledRepositories := d.Get("enabled_repositories").(string)

_, err = client.Actions.UpdateSelfHostedRunnersSettingsInOrganization(ctx,
orgName,
github.SelfHostedRunnersSettingsOrganizationOpt{
EnabledRepositories: &enabledRepositories,
})
if err != nil {
return err
}

if enabledRepositories == "selected" {
repoIDs, err := resourceGithubActionsOrganizationSelfHostedRunnersEnabledRepos(d)
if err != nil {
return err
}
_, err = client.Actions.SetRepositoriesSelfHostedRunnersAllowedInOrganization(ctx,
orgName,
repoIDs)
if err != nil {
return err
}
}

d.SetId(orgName)
return resourceGithubActionsOrganizationSelfHostedRunnersRead(d, meta)
}

func resourceGithubActionsOrganizationSelfHostedRunnersRead(d *schema.ResourceData, meta any) error {
client := meta.(*Owner).v3client
ctx := context.Background()

err := checkOrganization(meta)
if err != nil {
return err
}

settings, _, err := client.Actions.GetSelfHostedRunnersSettingsInOrganization(ctx, d.Id())
if err != nil {
return err
}

if err = d.Set("enabled_repositories", settings.GetEnabledRepositories()); err != nil {
return err
}

if settings.GetEnabledRepositories() == "selected" {
opts := github.ListOptions{PerPage: 10, Page: 1}
var repoIDs []int64
var allRepos []*github.Repository

for {
result, resp, err := client.Actions.ListRepositoriesSelfHostedRunnersAllowedInOrganization(ctx, d.Id(), &opts)
if err != nil {
return err
}
allRepos = append(allRepos, result.Repositories...)

opts.Page = resp.NextPage

if resp.NextPage == 0 {
break
}
}
for index := range allRepos {
repoIDs = append(repoIDs, *allRepos[index].ID)
}
if allRepos != nil {
if err = d.Set("enabled_repositories_config", []any{
map[string]any{
"repository_ids": repoIDs,
},
}); err != nil {
return err
}
} else {
if err = d.Set("enabled_repositories_config", []any{}); err != nil {
return err
}
}
} else {
if err = d.Set("enabled_repositories_config", []any{}); err != nil {
return err
}
}

return nil
}

func resourceGithubActionsOrganizationSelfHostedRunnersDelete(d *schema.ResourceData, meta any) error {
client := meta.(*Owner).v3client
orgName := meta.(*Owner).name
ctx := context.WithValue(context.Background(), ctxId, d.Id())

err := checkOrganization(meta)
if err != nil {
return err
}

allPolicy := "all"
_, err = client.Actions.UpdateSelfHostedRunnersSettingsInOrganization(ctx,
orgName,
github.SelfHostedRunnersSettingsOrganizationOpt{
EnabledRepositories: &allPolicy,
})
if err != nil {
return err
}

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package github

import (
"fmt"
"testing"

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

func TestAccGithubActionsOrganizationSelfHostedRunners(t *testing.T) {
t.Run("test setting of basic self-hosted runners policy", func(t *testing.T) {
enabledRepositories := "all"

config := fmt.Sprintf(`
resource "github_actions_organization_self_hosted_runners" "test" {
enabled_repositories = "%s"
}
`, enabledRepositories)

check := resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"github_actions_organization_self_hosted_runners.test", "enabled_repositories", enabledRepositories,
),
)

resource.Test(t, resource.TestCase{
PreCheck: func() { skipUnlessHasOrgs(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: config,
Check: check,
},
},
})
})

t.Run("test setting selected repositories with import", func(t *testing.T) {
enabledRepositories := "selected"
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
repoName := fmt.Sprintf("%srepo-selfhosted-runners-%s", testResourcePrefix, randomID)

config := fmt.Sprintf(`
resource "github_repository" "test" {
name = "%[1]s"
description = "Terraform acceptance tests %[1]s"
topics = ["terraform", "testing"]
}

resource "github_actions_organization_self_hosted_runners" "test" {
enabled_repositories = "%s"
enabled_repositories_config {
repository_ids = [github_repository.test.repo_id]
}
}
`, repoName, enabledRepositories)

check := resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"github_actions_organization_self_hosted_runners.test", "enabled_repositories", enabledRepositories,
),
resource.TestCheckResourceAttr(
"github_actions_organization_self_hosted_runners.test", "enabled_repositories_config.#", "1",
),
)

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

t.Run("test updating from all to selected repositories", func(t *testing.T) {
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
repoName := fmt.Sprintf("%srepo-selfhosted-runners-%s", testResourcePrefix, randomID)

configAll := `
resource "github_actions_organization_self_hosted_runners" "test" {
enabled_repositories = "all"
}
`

configSelected := fmt.Sprintf(`
resource "github_repository" "test" {
name = "%[1]s"
description = "Terraform acceptance tests %[1]s"
topics = ["terraform", "testing"]
}

resource "github_actions_organization_self_hosted_runners" "test" {
enabled_repositories = "selected"
enabled_repositories_config {
repository_ids = [github_repository.test.repo_id]
}
}
`, repoName)

resource.Test(t, resource.TestCase{
PreCheck: func() { skipUnlessHasOrgs(t) },
ProviderFactories: providerFactories,
Steps: []resource.TestStep{
{
Config: configAll,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"github_actions_organization_self_hosted_runners.test", "enabled_repositories", "all",
),
),
},
{
Config: configSelected,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"github_actions_organization_self_hosted_runners.test", "enabled_repositories", "selected",
),
resource.TestCheckResourceAttr(
"github_actions_organization_self_hosted_runners.test", "enabled_repositories_config.#", "1",
),
),
},
},
})
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
layout: "github"
page_title: "GitHub: github_actions_organization_self_hosted_runners"
description: |-
Creates and manages self-hosted runners settings within a GitHub organization
---

# github_actions_organization_self_hosted_runners

This resource allows you to manage self-hosted runners settings within your GitHub organization.
It controls which repositories are allowed to create repository-level self-hosted runners.
You must have admin access to an organization to use this resource.

## Example Usage

```hcl
resource "github_actions_organization_self_hosted_runners" "example" {
enabled_repositories = "all"
}
```

```hcl
resource "github_repository" "example" {
name = "my-repository"
}

resource "github_actions_organization_self_hosted_runners" "example" {
enabled_repositories = "selected"
enabled_repositories_config {
repository_ids = [github_repository.example.repo_id]
}
}
```

## Argument Reference

The following arguments are supported:

* `enabled_repositories` - (Required) The policy that controls which repositories in the organization can create self-hosted runners. Can be one of: `all`, `selected`, or `none`.
* `enabled_repositories_config` - (Optional) Sets the list of selected repositories that are allowed to create self-hosted runners. Only available when `enabled_repositories` = `selected`. See [Enabled Repositories Config](#enabled-repositories-config) below for details.

### Enabled Repositories Config

The `enabled_repositories_config` block supports the following:

* `repository_ids` - (Required) List of repository IDs allowed to create self-hosted runners.

## Import

This resource can be imported using the name of the GitHub organization:

```
$ terraform import github_actions_organization_self_hosted_runners.test github_organization_name
```