diff --git a/examples/enterprise_settings/README.md b/examples/enterprise_settings/README.md new file mode 100644 index 0000000000..71a7d3b0e3 --- /dev/null +++ b/examples/enterprise_settings/README.md @@ -0,0 +1,140 @@ +# GitHub Enterprise Settings Example + +This example demonstrates how to configure GitHub Enterprise settings using the Terraform GitHub provider. + +## Overview + +The `github_enterprise_settings` resource allows you to manage various enterprise-level settings for a GitHub Enterprise account, including: + +- Actions permissions (which organizations can run GitHub Actions) +- Allowed actions policies (which actions are allowed to run) +- Workflow permissions (default permissions for GITHUB_TOKEN) +- Pull request review approval settings + +## Requirements + +- GitHub Enterprise account +- Personal access token with enterprise admin permissions +- Terraform >= 0.14 + +## Usage + +1. Set your environment variables: + +```bash +export TF_VAR_github_token="your_github_token" +export TF_VAR_enterprise_slug="your-enterprise-slug" +``` + +2. Initialize and apply: + +```bash +terraform init +terraform plan +terraform apply +``` + +## Examples + +### Basic Configuration + +```terraform +resource "github_enterprise_settings" "basic" { + enterprise_slug = "my-enterprise" + + actions_enabled_organizations = "all" + actions_allowed_actions = "all" + + default_workflow_permissions = "read" + can_approve_pull_request_reviews = false +} +``` + +### Advanced Configuration with Selective Permissions + +```terraform +resource "github_enterprise_settings" "advanced" { + enterprise_slug = "my-enterprise" + + # Only selected organizations can run actions + actions_enabled_organizations = "selected" + + # Only allow specific actions + actions_allowed_actions = "selected" + actions_github_owned_allowed = true + actions_verified_allowed = true + actions_patterns_allowed = [ + "actions/cache@*", + "actions/checkout@*", + "my-org/custom-action@v1" + ] + + # Workflow permissions + default_workflow_permissions = "write" + can_approve_pull_request_reviews = true +} +``` + +## Configuration Reference + +### Actions Settings + +- **`actions_enabled_organizations`**: Controls which organizations can run GitHub Actions + - `"all"` - All organizations in the enterprise + - `"none"` - No organizations + - `"selected"` - Only specified organizations (requires additional configuration) + +- **`actions_allowed_actions`**: Controls which actions can be run + - `"all"` - All actions and reusable workflows + - `"local_only"` - Only actions and workflows in the same repository/organization + - `"selected"` - Only specified actions (requires additional configuration) + +When `actions_allowed_actions` is set to `"selected"`, you can specify: + +- **`actions_github_owned_allowed`**: Allow GitHub-owned actions (e.g., `actions/checkout`) +- **`actions_verified_allowed`**: Allow verified Marketplace actions +- **`actions_patterns_allowed`**: List of specific action patterns to allow + +### Workflow Settings + +- **`default_workflow_permissions`**: Default permissions for the GITHUB_TOKEN + - `"read"` - Read-only permissions (recommended for security) + - `"write"` - Read and write permissions + +- **`can_approve_pull_request_reviews`**: Whether GitHub Actions can approve pull request reviews + - `true` - Actions can approve PR reviews + - `false` - Actions cannot approve PR reviews (recommended for security) + +## Security Considerations + +1. **Workflow Permissions**: Use `"read"` permissions by default and grant `"write"` only when necessary +2. **PR Approvals**: Disable `can_approve_pull_request_reviews` to prevent automated approval bypasses +3. **Action Restrictions**: Use `"selected"` for `actions_allowed_actions` to limit which actions can run +4. **Token Security**: Store your GitHub token securely and use environment variables + +## Limitations + +This resource currently supports a subset of enterprise settings available through the GitHub API. Additional settings like fork PR workflows, artifact retention, and self-hosted runner permissions are not yet supported by the go-github version used in this provider and will be added in future versions. + +## Import + +You can import existing enterprise settings: + +```bash +terraform import github_enterprise_settings.example my-enterprise +``` + +## Troubleshooting + +### Common Issues + +1. **Authentication**: Ensure your token has enterprise admin permissions +2. **Enterprise Access**: Verify you have access to the specified enterprise +3. **API Limits**: GitHub API has rate limits; consider adding delays for large configurations + +### Verification + +After applying, verify settings in the GitHub Enterprise dashboard: +1. Go to your enterprise settings +2. Navigate to "Policies" > "Actions" +3. Check that the configured settings match your Terraform configuration \ No newline at end of file diff --git a/examples/enterprise_settings/main.tf b/examples/enterprise_settings/main.tf new file mode 100644 index 0000000000..be2d77523e --- /dev/null +++ b/examples/enterprise_settings/main.tf @@ -0,0 +1,91 @@ +terraform { + required_providers { + github = { + source = "integrations/github" + version = "~> 6.0" + } + } +} + +provider "github" { + token = var.github_token +} + +variable "github_token" { + description = "GitHub personal access token with enterprise admin permissions" + type = string + sensitive = true +} + +variable "enterprise_slug" { + description = "The GitHub Enterprise slug" + type = string +} + +# Basic Enterprise Settings with minimal configuration +resource "github_enterprise_settings" "basic" { + enterprise_slug = var.enterprise_slug + + # Allow all actions for all organizations + actions_enabled_organizations = "all" + actions_allowed_actions = "all" + + # Use restrictive workflow permissions + default_workflow_permissions = "read" + can_approve_pull_request_reviews = false +} + +# Advanced Enterprise Settings with selective permissions +resource "github_enterprise_settings" "advanced" { + enterprise_slug = var.enterprise_slug + + # Enable actions for selected organizations only + actions_enabled_organizations = "selected" + + # Allow only selected actions + actions_allowed_actions = "selected" + + # Only allow GitHub-owned and verified actions + actions_github_owned_allowed = true + actions_verified_allowed = true + + # Allow specific action patterns + actions_patterns_allowed = [ + "actions/cache@*", + "actions/checkout@*", + "actions/setup-node@*", + "actions/setup-python@*", + "actions/upload-artifact@*", + "actions/download-artifact@*", + "my-org/custom-action@v1" + ] + + # Grant write permissions to workflows + default_workflow_permissions = "write" + can_approve_pull_request_reviews = true +} + +output "basic_enterprise_settings" { + description = "Basic enterprise settings configuration" + value = { + enterprise_slug = github_enterprise_settings.basic.enterprise_slug + actions_enabled_organizations = github_enterprise_settings.basic.actions_enabled_organizations + actions_allowed_actions = github_enterprise_settings.basic.actions_allowed_actions + default_workflow_permissions = github_enterprise_settings.basic.default_workflow_permissions + can_approve_pull_request_reviews = github_enterprise_settings.basic.can_approve_pull_request_reviews + } +} + +output "advanced_enterprise_settings" { + description = "Advanced enterprise settings configuration" + value = { + enterprise_slug = github_enterprise_settings.advanced.enterprise_slug + actions_enabled_organizations = github_enterprise_settings.advanced.actions_enabled_organizations + actions_allowed_actions = github_enterprise_settings.advanced.actions_allowed_actions + actions_github_owned_allowed = github_enterprise_settings.advanced.actions_github_owned_allowed + actions_verified_allowed = github_enterprise_settings.advanced.actions_verified_allowed + actions_patterns_allowed = github_enterprise_settings.advanced.actions_patterns_allowed + default_workflow_permissions = github_enterprise_settings.advanced.default_workflow_permissions + can_approve_pull_request_reviews = github_enterprise_settings.advanced.can_approve_pull_request_reviews + } +} \ No newline at end of file diff --git a/github/provider.go b/github/provider.go index e29859dda2..7c4bf2ff99 100644 --- a/github/provider.go +++ b/github/provider.go @@ -202,6 +202,7 @@ func Provider() *schema.Provider { "github_user_ssh_key": resourceGithubUserSshKey(), "github_enterprise_organization": resourceGithubEnterpriseOrganization(), "github_enterprise_actions_runner_group": resourceGithubActionsEnterpriseRunnerGroup(), + "github_enterprise_settings": resourceGithubEnterpriseSettings(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/github/resource_github_enterprise_settings.go b/github/resource_github_enterprise_settings.go new file mode 100644 index 0000000000..bf97ea8dd2 --- /dev/null +++ b/github/resource_github_enterprise_settings.go @@ -0,0 +1,230 @@ +package github + +import ( + "context" + "log" + + "github.com/google/go-github/v67/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceGithubEnterpriseSettings() *schema.Resource { + return &schema.Resource{ + Description: ` +GitHub Enterprise Settings management. + +Provides a resource to manage various settings for a GitHub Enterprise account. + +~> **Note:** The managing account must have enterprise admin permissions. +~> **Note:** This resource requires a GitHub Enterprise account. + +`, + Create: resourceGithubEnterpriseSettingsCreateOrUpdate, + Read: resourceGithubEnterpriseSettingsRead, + Update: resourceGithubEnterpriseSettingsCreateOrUpdate, + Delete: resourceGithubEnterpriseSettingsDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "enterprise_slug": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The slug of the enterprise.", + }, + + // Actions Permissions - Available in current go-github + "actions_enabled_organizations": { + Type: schema.TypeString, + Optional: true, + Description: "The policy that controls the organizations in the enterprise that are allowed to run GitHub Actions. Can be 'all', 'none', or 'selected'.", + }, + "actions_allowed_actions": { + Type: schema.TypeString, + Optional: true, + Description: "The permissions policy that controls the actions and reusable workflows that are allowed to run. Can be 'all', 'local_only', or 'selected'.", + }, + "actions_github_owned_allowed": { + Type: schema.TypeBool, + Optional: true, + Description: "Whether GitHub-owned actions are allowed.", + }, + "actions_verified_allowed": { + Type: schema.TypeBool, + Optional: true, + Description: "Whether verified Marketplace actions are allowed.", + }, + "actions_patterns_allowed": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Specifies a list of string-matching patterns to allow specific action(s) and reusable workflow(s).", + }, + "default_workflow_permissions": { + Type: schema.TypeString, + Optional: true, + Description: "The default workflow permissions granted to the GITHUB_TOKEN when running workflows. Can be 'read' or 'write'.", + }, + "can_approve_pull_request_reviews": { + Type: schema.TypeBool, + Optional: true, + Description: "Whether GitHub Actions can approve pull request reviews.", + }, + }, + } +} + +func resourceGithubEnterpriseSettingsCreateOrUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + + enterpriseSlug := d.Get("enterprise_slug").(string) + d.SetId(enterpriseSlug) + + hasActionsChange := d.HasChange("actions_enabled_organizations") || + d.HasChange("actions_allowed_actions") + + if d.IsNewResource() || hasActionsChange { + actionsPerms := github.ActionsPermissionsEnterprise{} + + if v, ok := d.GetOk("actions_enabled_organizations"); ok { + actionsPerms.EnabledOrganizations = github.String(v.(string)) + } + + if v, ok := d.GetOk("actions_allowed_actions"); ok { + actionsPerms.AllowedActions = github.String(v.(string)) + } + + _, _, err := client.Actions.EditActionsPermissionsInEnterprise(ctx, enterpriseSlug, actionsPerms) + if err != nil { + return err + } + } + + // Handle allowed actions (only when actions_allowed_actions is "selected") + hasAllowedActionsChange := d.HasChange("actions_github_owned_allowed") || + d.HasChange("actions_verified_allowed") || + d.HasChange("actions_patterns_allowed") + + if (d.IsNewResource() || hasAllowedActionsChange) && d.Get("actions_allowed_actions").(string) == "selected" { + allowedActions := github.ActionsAllowed{} + + if v, ok := d.GetOk("actions_github_owned_allowed"); ok { + allowedActions.GithubOwnedAllowed = github.Bool(v.(bool)) + } + + if v, ok := d.GetOk("actions_verified_allowed"); ok { + allowedActions.VerifiedAllowed = github.Bool(v.(bool)) + } + + if v, ok := d.GetOk("actions_patterns_allowed"); ok { + patterns := expandStringSet(v.(*schema.Set)) + if len(patterns) > 0 { + allowedActions.PatternsAllowed = patterns + } + } + + _, _, err := client.Actions.EditActionsAllowedInEnterprise(ctx, enterpriseSlug, allowedActions) + if err != nil { + return err + } + } + + hasWorkflowChange := d.HasChange("default_workflow_permissions") || + d.HasChange("can_approve_pull_request_reviews") + + if d.IsNewResource() || hasWorkflowChange { + workflowPerms := github.DefaultWorkflowPermissionEnterprise{} + + if v, ok := d.GetOk("default_workflow_permissions"); ok { + workflowPerms.DefaultWorkflowPermissions = github.String(v.(string)) + } + + if v, ok := d.GetOk("can_approve_pull_request_reviews"); ok { + workflowPerms.CanApprovePullRequestReviews = github.Bool(v.(bool)) + } + + _, _, err := client.Actions.EditDefaultWorkflowPermissionsInEnterprise(ctx, enterpriseSlug, workflowPerms) + if err != nil { + return err + } + } + + return resourceGithubEnterpriseSettingsRead(d, meta) +} + +func resourceGithubEnterpriseSettingsRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + + enterpriseSlug := d.Id() + log.Printf("[DEBUG] Reading enterprise settings: %s", enterpriseSlug) + + actionsPerms, _, err := client.Actions.GetActionsPermissionsInEnterprise(ctx, enterpriseSlug) + if err != nil { + return err + } + + if err := d.Set("enterprise_slug", enterpriseSlug); err != nil { + return err + } + if err := d.Set("actions_enabled_organizations", actionsPerms.EnabledOrganizations); err != nil { + return err + } + if err := d.Set("actions_allowed_actions", actionsPerms.AllowedActions); err != nil { + return err + } + + // Read allowed actions if policy is "selected" + if actionsPerms.AllowedActions != nil && *actionsPerms.AllowedActions == "selected" { + allowedActions, _, err := client.Actions.GetActionsAllowedInEnterprise(ctx, enterpriseSlug) + if err != nil { + return err + } + + if err := d.Set("actions_github_owned_allowed", allowedActions.GithubOwnedAllowed); err != nil { + return err + } + if err := d.Set("actions_verified_allowed", allowedActions.VerifiedAllowed); err != nil { + return err + } + if len(allowedActions.PatternsAllowed) > 0 { + if err := d.Set("actions_patterns_allowed", allowedActions.PatternsAllowed); err != nil { + return err + } + } + } + + // Read workflow permissions + workflowPerms, _, err := client.Actions.GetDefaultWorkflowPermissionsInEnterprise(ctx, enterpriseSlug) + if err != nil { + return err + } + + if err := d.Set("default_workflow_permissions", workflowPerms.DefaultWorkflowPermissions); err != nil { + return err + } + if err := d.Set("can_approve_pull_request_reviews", workflowPerms.CanApprovePullRequestReviews); err != nil { + return err + } + + return nil +} + +func resourceGithubEnterpriseSettingsDelete(d *schema.ResourceData, meta interface{}) error { + // Enterprise settings don't get "deleted", they revert to defaults - just remove from state + log.Printf("[DEBUG] Removing enterprise settings from Terraform state: %s", d.Id()) + return nil +} + +// expandStringSet converts a schema.Set to a slice of strings +// TODO: might be useful in other places, consider moving to utility +func expandStringSet(set *schema.Set) []string { + result := make([]string, set.Len()) + for i, v := range set.List() { + result[i] = v.(string) + } + return result +} diff --git a/github/resource_github_enterprise_settings_test.go b/github/resource_github_enterprise_settings_test.go new file mode 100644 index 0000000000..89e924ba05 --- /dev/null +++ b/github/resource_github_enterprise_settings_test.go @@ -0,0 +1,308 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccGithubEnterpriseSettings(t *testing.T) { + + t.Run("creates basic enterprise settings without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_enterprise_settings" "test" { + enterprise_slug = "%s" + + actions_enabled_organizations = "all" + actions_allowed_actions = "all" + + default_workflow_permissions = "read" + can_approve_pull_request_reviews = false + } + `, testEnterprise) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_settings.test", "enterprise_slug", testEnterprise), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_enabled_organizations", "all"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_allowed_actions", "all"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "default_workflow_permissions", "read"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "can_approve_pull_request_reviews", "false"), + ) + + 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 enterprise account", func(t *testing.T) { + if isEnterprise != "true" { + t.Skip("Skipping because `ENTERPRISE_ACCOUNT` is not set or set to false") + } + if testEnterprise == "" { + t.Skip("Skipping because `ENTERPRISE_SLUG` is not set") + } + testCase(t, enterprise) + }) + }) + + t.Run("creates enterprise settings with selected actions without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_enterprise_settings" "test" { + enterprise_slug = "%s" + + actions_enabled_organizations = "all" + actions_allowed_actions = "selected" + actions_github_owned_allowed = true + actions_verified_allowed = true + actions_patterns_allowed = [ + "actions/cache@*", + "actions/checkout@*" + ] + + default_workflow_permissions = "write" + can_approve_pull_request_reviews = true + } + `, testEnterprise) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_settings.test", "enterprise_slug", testEnterprise), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_enabled_organizations", "all"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_allowed_actions", "selected"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_github_owned_allowed", "true"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_verified_allowed", "true"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_patterns_allowed.#", "2"), + resource.TestCheckTypeSetElemAttr("github_enterprise_settings.test", "actions_patterns_allowed.*", "actions/cache@*"), + resource.TestCheckTypeSetElemAttr("github_enterprise_settings.test", "actions_patterns_allowed.*", "actions/checkout@*"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "default_workflow_permissions", "write"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "can_approve_pull_request_reviews", "true"), + ) + + 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 enterprise account", func(t *testing.T) { + if isEnterprise != "true" { + t.Skip("Skipping because `ENTERPRISE_ACCOUNT` is not set or set to false") + } + if testEnterprise == "" { + t.Skip("Skipping because `ENTERPRISE_SLUG` is not set") + } + testCase(t, enterprise) + }) + }) + + t.Run("updates enterprise settings without error", func(t *testing.T) { + + configs := map[string]string{ + "before": fmt.Sprintf(` + resource "github_enterprise_settings" "test" { + enterprise_slug = "%s" + + actions_enabled_organizations = "all" + actions_allowed_actions = "all" + + default_workflow_permissions = "read" + can_approve_pull_request_reviews = false + } + `, testEnterprise), + + "after": fmt.Sprintf(` + resource "github_enterprise_settings" "test" { + enterprise_slug = "%s" + + actions_enabled_organizations = "all" + actions_allowed_actions = "local_only" + + default_workflow_permissions = "write" + can_approve_pull_request_reviews = true + } + `, testEnterprise), + } + + checks := map[string]resource.TestCheckFunc{ + "before": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_allowed_actions", "all"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "default_workflow_permissions", "read"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "can_approve_pull_request_reviews", "false"), + ), + "after": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_allowed_actions", "local_only"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "default_workflow_permissions", "write"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "can_approve_pull_request_reviews", "true"), + ), + } + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: configs["before"], + Check: checks["before"], + }, + { + Config: configs["after"], + Check: checks["after"], + }, + }, + }) + } + + t.Run("with an enterprise account", func(t *testing.T) { + if isEnterprise != "true" { + t.Skip("Skipping because `ENTERPRISE_ACCOUNT` is not set or set to false") + } + if testEnterprise == "" { + t.Skip("Skipping because `ENTERPRISE_SLUG` is not set") + } + testCase(t, enterprise) + }) + }) + + t.Run("updates from all to selected actions policy", func(t *testing.T) { + + configs := map[string]string{ + "before": fmt.Sprintf(` + resource "github_enterprise_settings" "test" { + enterprise_slug = "%s" + + actions_enabled_organizations = "all" + actions_allowed_actions = "all" + + default_workflow_permissions = "read" + can_approve_pull_request_reviews = false + } + `, testEnterprise), + + "after": fmt.Sprintf(` + resource "github_enterprise_settings" "test" { + enterprise_slug = "%s" + + actions_enabled_organizations = "all" + actions_allowed_actions = "selected" + actions_github_owned_allowed = true + actions_verified_allowed = false + actions_patterns_allowed = [ + "my-org/custom-action@v1" + ] + + default_workflow_permissions = "read" + can_approve_pull_request_reviews = false + } + `, testEnterprise), + } + + checks := map[string]resource.TestCheckFunc{ + "before": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_allowed_actions", "all"), + ), + "after": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_allowed_actions", "selected"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_github_owned_allowed", "true"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_verified_allowed", "false"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_patterns_allowed.#", "1"), + resource.TestCheckTypeSetElemAttr("github_enterprise_settings.test", "actions_patterns_allowed.*", "my-org/custom-action@v1"), + ), + } + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: configs["before"], + Check: checks["before"], + }, + { + Config: configs["after"], + Check: checks["after"], + }, + }, + }) + } + + t.Run("with an enterprise account", func(t *testing.T) { + if isEnterprise != "true" { + t.Skip("Skipping because `ENTERPRISE_ACCOUNT` is not set or set to false") + } + if testEnterprise == "" { + t.Skip("Skipping because `ENTERPRISE_SLUG` is not set") + } + testCase(t, enterprise) + }) + }) + + t.Run("imports enterprise settings without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_enterprise_settings" "test" { + enterprise_slug = "%s" + + actions_enabled_organizations = "all" + actions_allowed_actions = "all" + + default_workflow_permissions = "read" + can_approve_pull_request_reviews = false + } + `, testEnterprise) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_settings.test", "enterprise_slug", testEnterprise), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_enabled_organizations", "all"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "actions_allowed_actions", "all"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "default_workflow_permissions", "read"), + resource.TestCheckResourceAttr("github_enterprise_settings.test", "can_approve_pull_request_reviews", "false"), + ) + + 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_enterprise_settings.test", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) + } + + t.Run("with an enterprise account", func(t *testing.T) { + if isEnterprise != "true" { + t.Skip("Skipping because `ENTERPRISE_ACCOUNT` is not set or set to false") + } + if testEnterprise == "" { + t.Skip("Skipping because `ENTERPRISE_SLUG` is not set") + } + testCase(t, enterprise) + }) + }) +} diff --git a/website/docs/r/enterprise_settings.html.markdown b/website/docs/r/enterprise_settings.html.markdown new file mode 100644 index 0000000000..0a1f150a1c --- /dev/null +++ b/website/docs/r/enterprise_settings.html.markdown @@ -0,0 +1,101 @@ +--- +layout: "github" +page_title: "GitHub: github_enterprise_settings" +description: |- + Creates and manages settings for a GitHub Enterprise. +--- + +# github_enterprise_settings + +This resource allows you to create and manage settings for a GitHub Enterprise, including Actions permissions, workflow permissions, and security policies. +You must have admin access to an enterprise to use this resource. + +## Example Usage + +### Basic Configuration + +```hcl +resource "github_enterprise_settings" "example" { + enterprise_slug = "my-enterprise" + + actions_enabled_organizations = "all" + actions_allowed_actions = "all" + + default_workflow_permissions = "read" + can_approve_pull_request_reviews = false +} +``` + +### Advanced Configuration with Selective Permissions + +```hcl +resource "github_enterprise_settings" "advanced" { + enterprise_slug = "my-enterprise" + + # Only selected organizations can run actions + actions_enabled_organizations = "selected" + + # Only allow specific actions + actions_allowed_actions = "selected" + actions_github_owned_allowed = true + actions_verified_allowed = true + actions_patterns_allowed = [ + "actions/cache@*", + "actions/checkout@*", + "my-org/custom-action@v1" + ] + + # Workflow permissions + default_workflow_permissions = "write" + can_approve_pull_request_reviews = true +} +``` + +## Argument Reference + +The following arguments are supported: + +* `enterprise_slug` - (Required) The slug of the enterprise. +* `actions_enabled_organizations` - (Optional) The policy that controls which organizations in the enterprise are allowed to run GitHub Actions. Can be one of: `all`, `none`, or `selected`. Defaults to `all`. +* `actions_allowed_actions` - (Optional) The permissions policy that controls the actions and reusable workflows that are allowed to run. Can be one of: `all`, `local_only`, or `selected`. Defaults to `all`. +* `actions_github_owned_allowed` - (Optional) Whether GitHub-owned actions are allowed. Only used when `actions_allowed_actions` is set to `selected`. Defaults to `false`. +* `actions_verified_allowed` - (Optional) Whether actions from verified creators are allowed. Only used when `actions_allowed_actions` is set to `selected`. Defaults to `false`. +* `actions_patterns_allowed` - (Optional) Specifies a list of string-matching patterns to allow specific action(s) and reusable workflow(s). Wildcards, tags, and SHAs are allowed. For example, `monalisa/octocat@*`, `monalisa/octocat@v2`, `monalisa/*`. Only used when `actions_allowed_actions` is set to `selected`. +* `default_workflow_permissions` - (Optional) The default permissions granted to the GITHUB_TOKEN when running workflows. Can be `read` (recommended) or `write`. Defaults to `read`. +* `can_approve_pull_request_reviews` - (Optional) Whether GitHub Actions can approve pull request reviews. Defaults to `false`. + +## Attributes Reference + +The following additional attributes are exported: + +* `id` - The ID of the enterprise settings. + +## Import + +Enterprise settings can be imported using the enterprise slug: + +``` +$ terraform import github_enterprise_settings.example my-enterprise +``` + +## Notes + +### Actions Policies + +When `actions_allowed_actions` is set to `selected`, you can control which actions are allowed to run by configuring: + +- `actions_github_owned_allowed`: Allow all GitHub-owned actions (like `actions/checkout`, `actions/upload-artifact`, etc.) +- `actions_verified_allowed`: Allow actions from verified creators in the GitHub Marketplace +- `actions_patterns_allowed`: Specify exact action patterns using wildcards and version constraints + +### Security Considerations + +For maximum security, consider: +- Setting `default_workflow_permissions` to `read` to limit GITHUB_TOKEN permissions +- Setting `can_approve_pull_request_reviews` to `false` to prevent automated approval bypasses +- Using `selected` for `actions_allowed_actions` to restrict which actions can run +- Regularly reviewing and updating `actions_patterns_allowed` patterns + +### API Limitations + +Some newer enterprise settings like fork pull request workflows from outside collaborators, artifact retention policies, and self-hosted runner permissions are not yet supported and will be added in future versions when the go-github dependency is updated. \ No newline at end of file diff --git a/website/github.erb b/website/github.erb index 844433840b..75581f2519 100644 --- a/website/github.erb +++ b/website/github.erb @@ -223,6 +223,9 @@