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 @@ -133,6 +133,7 @@ func Provider() *schema.Provider {
"github_actions_organization_secret": resourceGithubActionsOrganizationSecret(),
"github_actions_organization_variable": resourceGithubActionsOrganizationVariable(),
"github_actions_organization_secret_repositories": resourceGithubActionsOrganizationSecretRepositories(),
"github_actions_organization_secret_repository": resourceGithubActionsOrganizationSecretRepository(),
"github_actions_repository_access_level": resourceGithubActionsRepositoryAccessLevel(),
"github_actions_repository_oidc_subject_claim_customization_template": resourceGithubActionsRepositoryOIDCSubjectClaimCustomizationTemplate(),
"github_actions_repository_permissions": resourceGithubActionsRepositoryPermissions(),
Expand Down
144 changes: 144 additions & 0 deletions github/resource_github_actions_organization_secret_repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package github

import (
"context"
"log"
"strconv"

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

func resourceGithubActionsOrganizationSecretRepository() *schema.Resource {
return &schema.Resource{
Create: resourceGithubActionsOrganizationSecretRepositoryCreate,
Read: resourceGithubActionsOrganizationSecretRepositoryRead,
Delete: resourceGithubActionsOrganizationSecretRepositoryDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"secret_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Name of the existing secret.",
ValidateDiagFunc: validateSecretNameFunc,
},
"repository_id": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
Description: "The repository ID that can access the organization secret.",
},
},
}
}

func resourceGithubActionsOrganizationSecretRepositoryCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*Owner).v3client
owner := meta.(*Owner).name
ctx := context.Background()

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

repositoryID := d.Get("repository_id").(int)
secretName := d.Get("secret_name").(string)

repoIDInt64 := int64(repositoryID)
repository := &github.Repository{
ID: &repoIDInt64,
}

_, err = client.Actions.AddSelectedRepoToOrgSecret(ctx, owner, secretName, repository)

if err != nil {
return err
}

d.SetId(buildTwoPartID(secretName, strconv.Itoa(repositoryID)))
return resourceGithubActionsOrganizationSecretRepositoryRead(d, meta)
}

func resourceGithubActionsOrganizationSecretRepositoryRead(d *schema.ResourceData, meta interface{}) error {
owner := meta.(*Owner).name

err := checkOrganization(meta)
if err != nil {
return err
}
client := meta.(*Owner).v3client

secretName, repositoryIDString, err := parseTwoPartID(d.Id(), "secret_name", "repository_id")
if err != nil {
return err
}

repositoryID, err := strconv.ParseInt(repositoryIDString, 10, 64)
if err != nil {
return unconvertibleIdErr(repositoryIDString, err)
}

ctx := context.WithValue(context.Background(), ctxId, d.Id())

opt := &github.ListOptions{
PerPage: maxPerPage,
}
for {
repos, resp, err := client.Actions.ListSelectedReposForOrgSecret(ctx, owner, secretName, opt)
if err != nil {
return err
}

for _, repo := range repos.Repositories {
if repo.GetID() == repositoryID {
if err = d.Set("secret_name", secretName); err != nil {
return err
}
if err = d.Set("repository_id", repositoryID); err != nil {
return err
}
return nil
}
}

if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}

log.Printf("[INFO] Removing secret repository association %s from state because it no longer exists in GitHub",
d.Id())
d.SetId("")

return nil
}

func resourceGithubActionsOrganizationSecretRepositoryDelete(d *schema.ResourceData, meta interface{}) error {
err := checkOrganization(meta)
if err != nil {
return err
}
client := meta.(*Owner).v3client
owner := meta.(*Owner).name
ctx := context.WithValue(context.Background(), ctxId, d.Id())

secretName := d.Get("secret_name").(string)
repositoryID := d.Get("repository_id").(int)

repoIDInt64 := int64(repositoryID)
repository := &github.Repository{
ID: &repoIDInt64,
}
_, err = client.Actions.RemoveSelectedRepoFromOrgSecret(ctx, owner, secretName, repository)
if err != nil {
return err
}

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

import (
"fmt"
"os"
"testing"

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

func TestAccGithubActionsOrganizationSecretRepository(t *testing.T) {

const ORG_SECRET_NAME = "ORG_SECRET_NAME"
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
secret_name, exists := os.LookupEnv(ORG_SECRET_NAME)

t.Run("set repository allowlist for a organization secret", func(t *testing.T) {
if !exists {
t.Skipf("%s environment variable is missing", ORG_SECRET_NAME)
}

config := fmt.Sprintf(`
resource "github_repository" "test_repo_1" {
name = "tf-acc-test-%s-1"
visibility = "internal"
vulnerability_alerts = "true"
}

resource "github_actions_organization_secret_repository" "org_secret_repo" {
secret_name = "%s"
repository_id = github_repository.test_repo_1.repo_id
}
`, randomID, secret_name)

check := resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet(
"github_actions_organization_secret_repository.org_secret_repo", "secret_name",
),
resource.TestCheckResourceAttr(
"github_actions_organization_secret_repository.org_secret_repo", "repository_id.#", "1",
),
)

testCase := func(t *testing.T, mode string) {
resource.Test(t, resource.TestCase{
PreCheck: func() { skipUnlessMode(t, mode) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: config,
Check: check,
},
},
})
}

t.Run("with an anonymous account", func(t *testing.T) {
t.Skip("anonymous account not supported for this operation")
})

t.Run("with an individual account", func(t *testing.T) {
t.Skip("individual account not supported for this operation")
})

t.Run("with an organization account", func(t *testing.T) {
testCase(t, organization)
})
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
layout: "github"
page_title: "GitHub: github_actions_organization_secret_repository"
description: |-
Adds/remove a repository to an organization secret when the visibility for repository access is set to selected.
---

# github_actions_organization_secret_repository

This resource help you to allow/unallow a repository to use an existing GitHub Actions secrets within your GitHub organization.
You must have write access to an organization secret to use this resource.

This resource is only applicable when `visibility` of the existing organization secret has been set to `selected`.

## Example Usage

```hcl
data "github_repository" "repo" {
full_name = "my-org/repo"
}

resource "github_actions_organization_secret_repository" "org_secret_repos" {
secret_name = "EXAMPLE_SECRET_NAME"
repository_id = github_repository.repo.repo_id
}
```

## Argument Reference

The following arguments are supported:

* `secret_name` - (Required) Name of the existing secret
* `repository_id` - (Required) Repository id that can access the organization secret.

## Import

This resource can be imported using an ID made up of the secret name:

```
$ terraform import github_actions_organization_secret_repository.test_secret_repos test_secret_name:repo_id
```