diff --git a/github/provider.go b/github/provider.go index 8f44c9509..1ef21441f 100644 --- a/github/provider.go +++ b/github/provider.go @@ -195,6 +195,7 @@ func Provider() *schema.Provider { "github_user_ssh_key": resourceGithubUserSshKey(), "github_enterprise_organization": resourceGithubEnterpriseOrganization(), "github_enterprise_actions_runner_group": resourceGithubActionsEnterpriseRunnerGroup(), + "github_workflow_repository_permissions": resourceGithubWorkflowRepositoryPermissions(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/github/resource_github_workflow_repository_permissions.go b/github/resource_github_workflow_repository_permissions.go new file mode 100644 index 000000000..eb2294648 --- /dev/null +++ b/github/resource_github_workflow_repository_permissions.go @@ -0,0 +1,123 @@ +package github + +import ( + "context" + + "github.com/google/go-github/v62/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceGithubWorkflowRepositoryPermissions() *schema.Resource { + return &schema.Resource{ + Create: resourceGithubWorkflowRepositoryPermissionsCreateOrUpdate, + Read: resourceGithubWorkflowRepositoryPermissionsRead, + Update: resourceGithubWorkflowRepositoryPermissionsCreateOrUpdate, + Delete: resourceGithubWorkflowRepositoryPermissionsDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "default_workflow_permissions": { + Type: schema.TypeString, + Optional: true, + Description: "The default workflow permissions granted to the GITHUB_TOKEN when running workflows.", + ValidateDiagFunc: toDiagFunc(validation.StringInSlice([]string{"read", "write"}, false), "default_workflow_permissions"), + }, + "can_approve_pull_request_reviews": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether GitHub Actions can approve pull requests. Enabling this can be a security risk.", + }, + "repository": { + Type: schema.TypeString, + Required: true, + Description: "The GitHub repository.", + ValidateDiagFunc: toDiagFunc(validation.StringLenBetween(1, 100), "repository"), + }, + }, + } +} + +func resourceGithubWorkflowRepositoryPermissionsCreateOrUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + + owner := meta.(*Owner).name + repoName := d.Get("repository").(string) + ctx := context.Background() + if !d.IsNewResource() { + ctx = context.WithValue(ctx, ctxId, d.Id()) + } + + defaultWorkflowPermissions := d.Get("default_workflow_permissions").(string) + canApprovePullRequestReviews := d.Get("can_approve_pull_request_reviews").(bool) + + repoWorkflowPermissions := github.DefaultWorkflowPermissionRepository{ + DefaultWorkflowPermissions: &defaultWorkflowPermissions, + CanApprovePullRequestReviews: &canApprovePullRequestReviews, + } + + _, _, err := client.Repositories.EditDefaultWorkflowPermissions(ctx, + owner, + repoName, + repoWorkflowPermissions, + ) + if err != nil { + return err + } + + d.SetId(repoName) + return resourceGithubWorkflowRepositoryPermissionsRead(d, meta) +} + +func resourceGithubWorkflowRepositoryPermissionsRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + + owner := meta.(*Owner).name + repoName := d.Id() + ctx := context.WithValue(context.Background(), ctxId, d.Id()) + + workflowsPermissions, _, err := client.Repositories.GetDefaultWorkflowPermissions(ctx, owner, repoName) + if err != nil { + return err + } + + if err = d.Set("default_workflow_permissions", workflowsPermissions.GetDefaultWorkflowPermissions()); err != nil { + return err + } + if err = d.Set("can_approve_pull_request_reviews", workflowsPermissions.GetCanApprovePullRequestReviews()); err != nil { + return err + } + if err = d.Set("repository", repoName); err != nil { + return err + } + + return nil +} + +func resourceGithubWorkflowRepositoryPermissionsDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + owner := meta.(*Owner).name + repoName := d.Id() + + ctx := context.WithValue(context.Background(), ctxId, d.Id()) + + // Reset the repo to "default" settings + repoWorkflowPermissions := github.DefaultWorkflowPermissionRepository{ + DefaultWorkflowPermissions: github.String("read"), + CanApprovePullRequestReviews: github.Bool(false), + } + + _, _, err := client.Repositories.EditDefaultWorkflowPermissions(ctx, + owner, + repoName, + repoWorkflowPermissions, + ) + if err != nil { + return err + } + + return nil +} diff --git a/github/resource_github_workflow_repository_permissions_test.go b/github/resource_github_workflow_repository_permissions_test.go new file mode 100644 index 000000000..ac5427f5e --- /dev/null +++ b/github/resource_github_workflow_repository_permissions_test.go @@ -0,0 +1,125 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccGithubWorkflowRepositoryPermissions(t *testing.T) { + + t.Run("test setting of basic workflow repository permissions", func(t *testing.T) { + + defaultWorkflowPermissions := "read" + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-topic-%[1]s" + description = "Terraform acceptance tests %[1]s" + topics = ["terraform", "testing"] + } + + resource "github_workflow_repository_permissions" "test" { + default_workflow_permissions = "%s" + repository = github_repository.test.name + } + `, randomID, defaultWorkflowPermissions) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "github_workflow_repository_permissions.test", "default_workflow_permissions", defaultWorkflowPermissions, + ), + ) + + 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) { + testCase(t, individual) + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + + }) + + t.Run("imports entire set of github workflow repository permissions without error", func(t *testing.T) { + + defaultWorkflowPermissions := "read" + canApprovePullRequestReviews := "true" + + randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) + + config := fmt.Sprintf(` + resource "github_repository" "test" { + name = "tf-acc-test-topic-%[1]s" + description = "Terraform acceptance tests %[1]s" + topics = ["terraform", "testing"] + } + + resource "github_workflow_repository_permissions" "test" { + default_workflow_permissions = "%s" + can_approve_pull_request_reviews = %s + repository = github_repository.test.name + } + `, randomID, defaultWorkflowPermissions, canApprovePullRequestReviews) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr( + "github_workflow_repository_permissions.test", "default_workflow_permissions", defaultWorkflowPermissions, + ), + resource.TestCheckResourceAttr( + "github_workflow_repository_permissions.test", "can_approve_pull_request_reviews", canApprovePullRequestReviews, + ), + ) + + 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, + }, + { + ResourceName: "github_workflow_repository_permissions.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } + + 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) { + testCase(t, individual) + }) + + t.Run("with an organization account", func(t *testing.T) { + testCase(t, organization) + }) + + }) +} diff --git a/website/docs/r/workflow_repository_permissions.html.markdown b/website/docs/r/workflow_repository_permissions.html.markdown new file mode 100644 index 000000000..9ea5d9521 --- /dev/null +++ b/website/docs/r/workflow_repository_permissions.html.markdown @@ -0,0 +1,41 @@ +--- +layout: "github" +page_title: "GitHub: github_workflow_repository_permissions" +description: |- + Enables and manages Workflow permissions for a GitHub repository +--- + +# github_workflow_repository_permissions + +This resource allows you to manage GitHub Workflow permissions for a given repository. +You must have admin access to a repository to use this resource. + +## Example Usage + +```hcl +resource "github_repository" "example" { + name = "my-repository" +} + +resource "github_workflow_repository_permissions" "test" { + default_workflow_permissions = "read" + can_approve_pull_request_reviews = true + repository = github_repository.example.name +} +``` + +## Argument Reference + +The following arguments are supported: + +* `repository` - (Required) The GitHub repository +* `default_workflow_permissions` - (Optional) The default workflow permissions granted to the GITHUB_TOKEN when running workflows. Can be one of: `read` or `write`. +* `can_approve_pull_request_reviews` - (Optional) Whether GitHub Actions can approve pull requests. Enabling this can be a security risk. + +## Import + +This resource can be imported using the name of the GitHub repository: + +``` +$ terraform import github_workflow_repository_permissions.test my-repository +```