Skip to content

Commit

Permalink
Create resource to set repository allow list for organisation secret (#…
Browse files Browse the repository at this point in the history
…882)

* Only applicable when secret access is "selected"
* Create/update the resource will override secret's existing repository allowlist
* Destroy the resource will clear secret's existing repository allowlist
* Can be used when secret value is set externally
  • Loading branch information
xun-guo-anzx authored Sep 13, 2021
1 parent 653427e commit d80ff81
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 31 deletions.
63 changes: 32 additions & 31 deletions github/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,37 +75,38 @@ func Provider() terraform.ResourceProvider {
},

ResourcesMap: map[string]*schema.Resource{
"github_actions_environment_secret": resourceGithubActionsEnvironmentSecret(),
"github_actions_organization_secret": resourceGithubActionsOrganizationSecret(),
"github_actions_secret": resourceGithubActionsSecret(),
"github_app_installation_repository": resourceGithubAppInstallationRepository(),
"github_branch": resourceGithubBranch(),
"github_branch_protection": resourceGithubBranchProtection(),
"github_branch_protection_v3": resourceGithubBranchProtectionV3(),
"github_issue_label": resourceGithubIssueLabel(),
"github_membership": resourceGithubMembership(),
"github_organization_block": resourceOrganizationBlock(),
"github_organization_project": resourceGithubOrganizationProject(),
"github_organization_webhook": resourceGithubOrganizationWebhook(),
"github_project_card": resourceGithubProjectCard(),
"github_project_column": resourceGithubProjectColumn(),
"github_repository_collaborator": resourceGithubRepositoryCollaborator(),
"github_repository_deploy_key": resourceGithubRepositoryDeployKey(),
"github_repository_environment": resourceGithubRepositoryEnvironment(),
"github_repository_file": resourceGithubRepositoryFile(),
"github_repository_milestone": resourceGithubRepositoryMilestone(),
"github_repository_project": resourceGithubRepositoryProject(),
"github_repository_pull_request": resourceGithubRepositoryPullRequest(),
"github_repository_webhook": resourceGithubRepositoryWebhook(),
"github_repository": resourceGithubRepository(),
"github_team_membership": resourceGithubTeamMembership(),
"github_team_repository": resourceGithubTeamRepository(),
"github_team_sync_group_mapping": resourceGithubTeamSyncGroupMapping(),
"github_team": resourceGithubTeam(),
"github_user_gpg_key": resourceGithubUserGpgKey(),
"github_user_invitation_accepter": resourceGithubUserInvitationAccepter(),
"github_user_ssh_key": resourceGithubUserSshKey(),
"github_branch_default": resourceGithubBranchDefault(),
"github_actions_environment_secret": resourceGithubActionsEnvironmentSecret(),
"github_actions_organization_secret": resourceGithubActionsOrganizationSecret(),
"github_actions_organization_secret_repositories": resourceGithubActionsOrganizationSecretRepositories(),
"github_actions_secret": resourceGithubActionsSecret(),
"github_app_installation_repository": resourceGithubAppInstallationRepository(),
"github_branch": resourceGithubBranch(),
"github_branch_protection": resourceGithubBranchProtection(),
"github_branch_protection_v3": resourceGithubBranchProtectionV3(),
"github_issue_label": resourceGithubIssueLabel(),
"github_membership": resourceGithubMembership(),
"github_organization_block": resourceOrganizationBlock(),
"github_organization_project": resourceGithubOrganizationProject(),
"github_organization_webhook": resourceGithubOrganizationWebhook(),
"github_project_card": resourceGithubProjectCard(),
"github_project_column": resourceGithubProjectColumn(),
"github_repository_collaborator": resourceGithubRepositoryCollaborator(),
"github_repository_deploy_key": resourceGithubRepositoryDeployKey(),
"github_repository_environment": resourceGithubRepositoryEnvironment(),
"github_repository_file": resourceGithubRepositoryFile(),
"github_repository_milestone": resourceGithubRepositoryMilestone(),
"github_repository_project": resourceGithubRepositoryProject(),
"github_repository_pull_request": resourceGithubRepositoryPullRequest(),
"github_repository_webhook": resourceGithubRepositoryWebhook(),
"github_repository": resourceGithubRepository(),
"github_team_membership": resourceGithubTeamMembership(),
"github_team_repository": resourceGithubTeamRepository(),
"github_team_sync_group_mapping": resourceGithubTeamSyncGroupMapping(),
"github_team": resourceGithubTeam(),
"github_user_gpg_key": resourceGithubUserGpgKey(),
"github_user_invitation_accepter": resourceGithubUserInvitationAccepter(),
"github_user_ssh_key": resourceGithubUserSshKey(),
"github_branch_default": resourceGithubBranchDefault(),
},

DataSourcesMap: map[string]*schema.Resource{
Expand Down
120 changes: 120 additions & 0 deletions github/resource_github_actions_organization_secret_repositories.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package github

import (
"context"

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

func resourceGithubActionsOrganizationSecretRepositories() *schema.Resource {
return &schema.Resource{
Create: resourceGithubActionsOrganizationSecretRepositoriesCreateOrUpdate,
Read: resourceGithubActionsOrganizationSecretRepositoriesRead,
Update: resourceGithubActionsOrganizationSecretRepositoriesCreateOrUpdate,
Delete: resourceGithubActionsOrganizationSecretRepositoriesDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"secret_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateSecretNameFunc,
},
"selected_repository_ids": {
Type: schema.TypeSet,
Elem: &schema.Schema{
Type: schema.TypeInt,
},
Set: schema.HashInt,
Required: true,
},
},
}
}

func resourceGithubActionsOrganizationSecretRepositoriesCreateOrUpdate(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
}

secretName := d.Get("secret_name").(string)
selectedRepositories := d.Get("selected_repository_ids")

selectedRepositoryIDs := []int64{}

ids := selectedRepositories.(*schema.Set).List()
for _, id := range ids {
selectedRepositoryIDs = append(selectedRepositoryIDs, int64(id.(int)))
}

_, err = client.Actions.SetSelectedReposForOrgSecret(ctx, owner, secretName, selectedRepositoryIDs)
if err != nil {
return err
}

d.SetId(secretName)
return resourceGithubActionsOrganizationSecretRepositoriesRead(d, meta)
}

func resourceGithubActionsOrganizationSecretRepositoriesRead(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
}

selectedRepositoryIDs := []int64{}
opt := &github.ListOptions{
PerPage: maxPerPage,
}
for {
results, resp, err := client.Actions.ListSelectedReposForOrgSecret(ctx, owner, d.Id(), opt)
if err != nil {
return err
}

for _, repo := range results.Repositories {
selectedRepositoryIDs = append(selectedRepositoryIDs, repo.GetID())
}

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

d.Set("selected_repository_ids", selectedRepositoryIDs)

return nil
}

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

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

selectedRepositoryIDs := []int64{}
_, err = client.Actions.SetSelectedReposForOrgSecret(ctx, owner, d.Id(), selectedRepositoryIDs)
if err != nil {
return err
}

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

import (
"fmt"
"os"
"testing"

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

func TestAccGithubActionsOrganizationSecretRepositories(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_repository" "test_repo_2" {
name = "tf-acc-test-%s-2"
visibility = "internal"
vulnerability_alerts = "true"
}
resource "github_actions_organization_secret_repositories" "org_secret_repos" {
secret_name = "%s"
selected_repository_ids = [
github_repository.test_repo_1.repo_id,
github_repository.test_repo_2.repo_id
]
}
`, randomID, randomID, secret_name)

check := resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet(
"github_actions_organization_secret_repositories.org_secret_repos", "secret_name",
),
resource.TestCheckResourceAttr(
"github_actions_organization_secret_repositories.org_secret_repos", "selected_repository_ids.#", "2",
),
)

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_repositories"
description: |-
Manages repository allow list for an Action Secret within a GitHub organization
---

# github_actions_organization_secret_repositories

This resource allows you to manage repository allow list for 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_repositories" "org_secret_repos" {
secret_name = "existing_secret_name"
selected_repository_ids = [data.github_repository.repo.repo_id]
}
```

## Argument Reference

The following arguments are supported:

* `secret_name` - (Required) Name of the existing secret
* `selected_repository_ids` - (Required) An array of repository ids 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_repositories.test_secret_repos test_secret_name
```
3 changes: 3 additions & 0 deletions website/github.erb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@
<li>
<a href="/docs/providers/github/r/actions_organization_secret.html">github_actions_organization_secret</a>
</li>
<li>
<a href="/docs/providers/github/r/actions_organization_secret_repositories.html">github_actions_organization_secret_repositories</a>
</li>
<li>
<a href="/docs/providers/github/r/actions_secret.html">github_actions_secret</a>
</li>
Expand Down

0 comments on commit d80ff81

Please sign in to comment.