From fd8793619ff621dbf6920c10cda2f3c76c78d087 Mon Sep 17 00:00:00 2001 From: Nick Floyd Date: Fri, 31 Oct 2025 12:08:12 -0500 Subject: [PATCH 01/10] Adds enterprise settings resources to the provider --- github/resource_github_enterprise_settings.go | 230 ++++++++++++++++++ ...esource_github_enterprise_settings_test.go | 47 ++++ 2 files changed, 277 insertions(+) create mode 100644 github/resource_github_enterprise_settings.go create mode 100644 github/resource_github_enterprise_settings_test.go 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..a1d59acc27 --- /dev/null +++ b/github/resource_github_enterprise_settings_test.go @@ -0,0 +1,47 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccGithubEnterpriseSettings(t *testing.T) { + t.Skip("This test requires enterprise access and should only be run manually") + + if testEnterprise == "" { + t.Skip("Skipping because `GITHUB_TEST_ENTERPRISE` is not set") + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccGithubEnterpriseSettingsConfig(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"), + ), + }, + }, + }) +} + +func testAccGithubEnterpriseSettingsConfig(enterprise string) string { + return 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 +} +`, enterprise) +} From 0462f1abe70183d7c3e2eabdd4359d43d4c117a8 Mon Sep 17 00:00:00 2001 From: Nick Floyd Date: Fri, 31 Oct 2025 12:08:29 -0500 Subject: [PATCH 02/10] Adds example --- examples/enterprise_settings/main.tf | 91 ++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 examples/enterprise_settings/main.tf 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 From b2363c72e6a1fefb899c930636504ce1146f2783 Mon Sep 17 00:00:00 2001 From: Nick Floyd Date: Fri, 31 Oct 2025 12:10:15 -0500 Subject: [PATCH 03/10] Updates tests for better coverage --- ...esource_github_enterprise_settings_test.go | 329 ++++++++++++++++-- 1 file changed, 295 insertions(+), 34 deletions(-) diff --git a/github/resource_github_enterprise_settings_test.go b/github/resource_github_enterprise_settings_test.go index a1d59acc27..e2013fe0d7 100644 --- a/github/resource_github_enterprise_settings_test.go +++ b/github/resource_github_enterprise_settings_test.go @@ -8,40 +8,301 @@ import ( ) func TestAccGithubEnterpriseSettings(t *testing.T) { - t.Skip("This test requires enterprise access and should only be run manually") - - if testEnterprise == "" { - t.Skip("Skipping because `GITHUB_TEST_ENTERPRISE` is not set") - } - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: testAccGithubEnterpriseSettingsConfig(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"), - ), - }, - }, + + 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) + }) }) -} -func testAccGithubEnterpriseSettingsConfig(enterprise string) string { - return 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 -} -`, 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) + }) + }) } From 0c588fa4d66d8183ef978b00463510b84855f7c3 Mon Sep 17 00:00:00 2001 From: Nick Floyd Date: Fri, 31 Oct 2025 12:15:35 -0500 Subject: [PATCH 04/10] Register the resource --- github/provider.go | 1 + 1 file changed, 1 insertion(+) 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{ From 473e07d500b32dcc4c625eb54e2ae026b7cadb4b Mon Sep 17 00:00:00 2001 From: Nick Floyd Date: Fri, 31 Oct 2025 12:17:58 -0500 Subject: [PATCH 05/10] go fmt --- github/resource_github_enterprise_settings_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/github/resource_github_enterprise_settings_test.go b/github/resource_github_enterprise_settings_test.go index e2013fe0d7..89e924ba05 100644 --- a/github/resource_github_enterprise_settings_test.go +++ b/github/resource_github_enterprise_settings_test.go @@ -8,9 +8,9 @@ import ( ) 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" @@ -56,7 +56,7 @@ func TestAccGithubEnterpriseSettings(t *testing.T) { }) 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" @@ -113,7 +113,7 @@ func TestAccGithubEnterpriseSettings(t *testing.T) { }) t.Run("updates enterprise settings without error", func(t *testing.T) { - + configs := map[string]string{ "before": fmt.Sprintf(` resource "github_enterprise_settings" "test" { @@ -182,7 +182,7 @@ func TestAccGithubEnterpriseSettings(t *testing.T) { }) 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" { @@ -256,7 +256,7 @@ func TestAccGithubEnterpriseSettings(t *testing.T) { }) t.Run("imports enterprise settings without error", func(t *testing.T) { - + config := fmt.Sprintf(` resource "github_enterprise_settings" "test" { enterprise_slug = "%s" From 5f0684612294adfbb2844cb549649be5bd41c686 Mon Sep 17 00:00:00 2001 From: Nick Floyd Date: Fri, 31 Oct 2025 16:25:18 -0500 Subject: [PATCH 06/10] adds example readme and docs --- examples/enterprise_settings/README.md | 140 ++++++++++++++++++ .../docs/r/enterprise_settings.html.markdown | 101 +++++++++++++ website/github.erb | 3 + 3 files changed, 244 insertions(+) create mode 100644 examples/enterprise_settings/README.md create mode 100644 website/docs/r/enterprise_settings.html.markdown 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/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 @@
  • github_enterprise_actions_permissions
  • +
  • + github_enterprise_settings +
  • github_actions_organization_secret
  • From 322cacc86a65231c7b5976cde78fc00296ffc8e5 Mon Sep 17 00:00:00 2001 From: Nick Floyd Date: Mon, 10 Nov 2025 15:33:19 -0600 Subject: [PATCH 07/10] breaks up the resources following a 1 to 1 pattern that more closely aligns with the API resource structure --- examples/enterprise_settings/README.md | 86 +++-- examples/enterprise_settings/main.tf | 119 ++++--- github/provider.go | 3 +- github/resource_github_enterprise_settings.go | 230 ------------- ...esource_github_enterprise_settings_test.go | 308 ------------------ .../docs/r/enterprise_settings.html.markdown | 101 ------ 6 files changed, 136 insertions(+), 711 deletions(-) delete mode 100644 github/resource_github_enterprise_settings.go delete mode 100644 github/resource_github_enterprise_settings_test.go delete mode 100644 website/docs/r/enterprise_settings.html.markdown diff --git a/examples/enterprise_settings/README.md b/examples/enterprise_settings/README.md index 71a7d3b0e3..3a6bd9ae57 100644 --- a/examples/enterprise_settings/README.md +++ b/examples/enterprise_settings/README.md @@ -4,12 +4,10 @@ This example demonstrates how to configure GitHub Enterprise settings using the ## Overview -The `github_enterprise_settings` resource allows you to manage various enterprise-level settings for a GitHub Enterprise account, including: +Manage enterprise-level GitHub Actions settings with focused, composable resources: -- 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 +- **Actions Permissions**: Control which organizations can use GitHub Actions and what actions are allowed +- **Workflow Permissions**: Manage default GITHUB_TOKEN permissions and pull request review settings ## Requirements @@ -34,47 +32,81 @@ terraform plan terraform apply ``` -## Examples +## Configuration Examples -### Basic Configuration +### Basic Configuration - Allow All Actions ```terraform -resource "github_enterprise_settings" "basic" { +# Allow all actions for all organizations +resource "github_enterprise_actions_permissions" "basic" { enterprise_slug = "my-enterprise" + + enabled_organizations = "all" + allowed_actions = "all" +} - actions_enabled_organizations = "all" - actions_allowed_actions = "all" +# Use restrictive workflow permissions +resource "github_enterprise_actions_workflow_permissions" "basic" { + enterprise_slug = "my-enterprise" - default_workflow_permissions = "read" + default_workflow_permissions = "read" can_approve_pull_request_reviews = false } ``` -### Advanced Configuration with Selective Permissions +### Advanced Configuration - Selective Permissions ```terraform -resource "github_enterprise_settings" "advanced" { +# Selective actions and organizations +resource "github_enterprise_actions_permissions" "advanced" { enterprise_slug = "my-enterprise" + + enabled_organizations = "selected" + allowed_actions = "selected" + + allowed_actions_config { + github_owned_allowed = true + verified_allowed = true + patterns_allowed = [ + "actions/cache@*", + "actions/checkout@*", + "my-org/custom-action@v1" + ] + } + + enabled_organizations_config { + organization_ids = [123456, 789012] # Replace with actual org IDs + } +} - # Only selected organizations can run actions - actions_enabled_organizations = "selected" +# More permissive workflow settings +resource "github_enterprise_actions_workflow_permissions" "advanced" { + enterprise_slug = "my-enterprise" - # 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" + default_workflow_permissions = "write" can_approve_pull_request_reviews = true } ``` +## Available Enterprise Resources + +### Actions & Workflow Management +- **`github_enterprise_actions_permissions`** - Controls which organizations can use GitHub Actions and which actions are allowed to run +- **`github_enterprise_actions_workflow_permissions`** - Manages default GITHUB_TOKEN permissions and whether GitHub Actions can approve pull requests + +### Security & Analysis +- **`github_enterprise_security_analysis_settings`** - Manages Advanced Security, secret scanning, and code analysis features for new repositories + +### Additional Resources (Available) +- **`github_enterprise_actions_runner_group`** - Manages enterprise-level runner groups for GitHub Actions + +## Security Recommendations + +1. Use `"read"` workflow permissions by default +2. Disable pull request review approvals for security +3. Use `"selected"` actions policy to limit which actions can run +4. Store tokens securely using environment variables + ## Configuration Reference ### Actions Settings diff --git a/examples/enterprise_settings/main.tf b/examples/enterprise_settings/main.tf index be2d77523e..c73cf29c65 100644 --- a/examples/enterprise_settings/main.tf +++ b/examples/enterprise_settings/main.tf @@ -22,70 +22,101 @@ variable "enterprise_slug" { type = string } -# Basic Enterprise Settings with minimal configuration -resource "github_enterprise_settings" "basic" { +# Basic Enterprise Actions Permissions - Allow all actions for all organizations +resource "github_enterprise_actions_permissions" "basic" { enterprise_slug = var.enterprise_slug + + enabled_organizations = "all" + allowed_actions = "all" +} - # Allow all actions for all organizations - actions_enabled_organizations = "all" - actions_allowed_actions = "all" +# Basic Enterprise Workflow Permissions - Restrictive settings +resource "github_enterprise_actions_workflow_permissions" "basic" { + enterprise_slug = var.enterprise_slug - # 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" { +# Advanced Enterprise Actions Permissions - Selective configuration +resource "github_enterprise_actions_permissions" "advanced" { enterprise_slug = var.enterprise_slug - - # Enable actions for selected organizations only - actions_enabled_organizations = "selected" - # Allow only selected actions - actions_allowed_actions = "selected" + enabled_organizations = "selected" + allowed_actions = "selected" - # Only allow GitHub-owned and verified actions - actions_github_owned_allowed = true - actions_verified_allowed = true + # Configure allowed actions when "selected" policy is used + allowed_actions_config { + github_owned_allowed = true + verified_allowed = true + patterns_allowed = [ + "actions/cache@*", + "actions/checkout@*", + "actions/setup-node@*", + "actions/setup-python@*", + "actions/upload-artifact@*", + "actions/download-artifact@*", + "my-org/custom-action@v1" + ] + } - # 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" - ] + # Configure enabled organizations when "selected" policy is used + enabled_organizations_config { + organization_ids = [123456, 789012] # Replace with actual org IDs + } +} + +# Advanced Enterprise Workflow Permissions - Permissive settings +resource "github_enterprise_actions_workflow_permissions" "advanced" { + enterprise_slug = var.enterprise_slug - # Grant write permissions to workflows default_workflow_permissions = "write" can_approve_pull_request_reviews = true } -output "basic_enterprise_settings" { - description = "Basic enterprise settings configuration" +# Security Analysis Settings - Enable security features for new repositories +resource "github_enterprise_security_analysis_settings" "example" { + enterprise_slug = var.enterprise_slug + + advanced_security_enabled_for_new_repositories = true + secret_scanning_enabled_for_new_repositories = true + secret_scanning_push_protection_enabled_for_new_repositories = true + secret_scanning_validity_checks_enabled = true + secret_scanning_push_protection_custom_link = "https://octokit.com/security-help" +} + +output "basic_enterprise_actions" { + description = "Basic enterprise actions permissions configuration" + value = { + enterprise_slug = github_enterprise_actions_permissions.basic.enterprise_slug + enabled_organizations = github_enterprise_actions_permissions.basic.enabled_organizations + allowed_actions = github_enterprise_actions_permissions.basic.allowed_actions + } +} + +output "basic_enterprise_workflow" { + description = "Basic enterprise workflow permissions configuration" + value = { + enterprise_slug = github_enterprise_actions_workflow_permissions.basic.enterprise_slug + default_workflow_permissions = github_enterprise_actions_workflow_permissions.basic.default_workflow_permissions + can_approve_pull_request_reviews = github_enterprise_actions_workflow_permissions.basic.can_approve_pull_request_reviews + } +} + +output "advanced_enterprise_actions" { + description = "Advanced enterprise actions permissions 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 + enterprise_slug = github_enterprise_actions_permissions.advanced.enterprise_slug + enabled_organizations = github_enterprise_actions_permissions.advanced.enabled_organizations + allowed_actions = github_enterprise_actions_permissions.advanced.allowed_actions } } -output "advanced_enterprise_settings" { - description = "Advanced enterprise settings configuration" +output "advanced_enterprise_workflow" { + description = "Advanced enterprise workflow permissions 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 + enterprise_slug = github_enterprise_actions_workflow_permissions.advanced.enterprise_slug + default_workflow_permissions = github_enterprise_actions_workflow_permissions.advanced.default_workflow_permissions + can_approve_pull_request_reviews = github_enterprise_actions_workflow_permissions.advanced.can_approve_pull_request_reviews } } \ No newline at end of file diff --git a/github/provider.go b/github/provider.go index 7c4bf2ff99..cd1f46b902 100644 --- a/github/provider.go +++ b/github/provider.go @@ -202,7 +202,8 @@ func Provider() *schema.Provider { "github_user_ssh_key": resourceGithubUserSshKey(), "github_enterprise_organization": resourceGithubEnterpriseOrganization(), "github_enterprise_actions_runner_group": resourceGithubActionsEnterpriseRunnerGroup(), - "github_enterprise_settings": resourceGithubEnterpriseSettings(), + "github_enterprise_actions_workflow_permissions": resourceGithubEnterpriseActionsWorkflowPermissions(), + "github_enterprise_security_analysis_settings": resourceGithubEnterpriseSecurityAnalysisSettings(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/github/resource_github_enterprise_settings.go b/github/resource_github_enterprise_settings.go deleted file mode 100644 index bf97ea8dd2..0000000000 --- a/github/resource_github_enterprise_settings.go +++ /dev/null @@ -1,230 +0,0 @@ -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 deleted file mode 100644 index 89e924ba05..0000000000 --- a/github/resource_github_enterprise_settings_test.go +++ /dev/null @@ -1,308 +0,0 @@ -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 deleted file mode 100644 index 0a1f150a1c..0000000000 --- a/website/docs/r/enterprise_settings.html.markdown +++ /dev/null @@ -1,101 +0,0 @@ ---- -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 From 80cf7c50284a4dd5b64007b7acd5ef11bbc84128 Mon Sep 17 00:00:00 2001 From: Nick Floyd Date: Mon, 10 Nov 2025 15:34:07 -0600 Subject: [PATCH 08/10] breaks up the resources following a 1 to 1 pattern that more closely aligns with the API resource structure --- ...enterprise_actions_workflow_permissions.go | 117 +++++++++++++ ...prise_actions_workflow_permissions_test.go | 159 ++++++++++++++++++ ...b_enterprise_security_analysis_settings.go | 156 +++++++++++++++++ ...actions_workflow_permissions.html.markdown | 64 +++++++ ...e_security_analysis_settings.html.markdown | 83 +++++++++ 5 files changed, 579 insertions(+) create mode 100644 github/resource_github_enterprise_actions_workflow_permissions.go create mode 100644 github/resource_github_enterprise_actions_workflow_permissions_test.go create mode 100644 github/resource_github_enterprise_security_analysis_settings.go create mode 100644 website/docs/r/enterprise_actions_workflow_permissions.html.markdown create mode 100644 website/docs/r/enterprise_security_analysis_settings.html.markdown diff --git a/github/resource_github_enterprise_actions_workflow_permissions.go b/github/resource_github_enterprise_actions_workflow_permissions.go new file mode 100644 index 0000000000..a8a925d753 --- /dev/null +++ b/github/resource_github_enterprise_actions_workflow_permissions.go @@ -0,0 +1,117 @@ +package github + +import ( + "context" + "log" + + "github.com/google/go-github/v67/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceGithubEnterpriseActionsWorkflowPermissions() *schema.Resource { + return &schema.Resource{ + Description: "GitHub Enterprise Actions Workflow Permissions management.", + Create: resourceGithubEnterpriseActionsWorkflowPermissionsCreateOrUpdate, + Read: resourceGithubEnterpriseActionsWorkflowPermissionsRead, + Update: resourceGithubEnterpriseActionsWorkflowPermissionsCreateOrUpdate, + Delete: resourceGithubEnterpriseActionsWorkflowPermissionsDelete, + 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.", + }, + "default_workflow_permissions": { + Type: schema.TypeString, + Optional: true, + Default: "read", + Description: "The default workflow permissions granted to the GITHUB_TOKEN when running workflows. Can be 'read' or 'write'.", + ValidateFunc: validation.StringInSlice([]string{"read", "write"}, false), + }, + "can_approve_pull_request_reviews": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether GitHub Actions can approve pull request reviews.", + }, + }, + } +} + +func resourceGithubEnterpriseActionsWorkflowPermissionsCreateOrUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + + enterpriseSlug := d.Get("enterprise_slug").(string) + d.SetId(enterpriseSlug) + + 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)) + } + + log.Printf("[DEBUG] Updating workflow permissions for enterprise: %s", enterpriseSlug) + _, _, err := client.Actions.EditDefaultWorkflowPermissionsInEnterprise(ctx, enterpriseSlug, workflowPerms) + if err != nil { + return err + } + + return resourceGithubEnterpriseActionsWorkflowPermissionsRead(d, meta) +} + +func resourceGithubEnterpriseActionsWorkflowPermissionsRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + + enterpriseSlug := d.Id() + log.Printf("[DEBUG] Reading workflow permissions for enterprise: %s", enterpriseSlug) + + workflowPerms, _, err := client.Actions.GetDefaultWorkflowPermissionsInEnterprise(ctx, enterpriseSlug) + if err != nil { + return err + } + + if err := d.Set("enterprise_slug", enterpriseSlug); 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 resourceGithubEnterpriseActionsWorkflowPermissionsDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + + enterpriseSlug := d.Id() + log.Printf("[DEBUG] Resetting workflow permissions to defaults for enterprise: %s", enterpriseSlug) + + // Reset to safe defaults + workflowPerms := github.DefaultWorkflowPermissionEnterprise{ + DefaultWorkflowPermissions: github.String("read"), + CanApprovePullRequestReviews: github.Bool(false), + } + + _, _, err := client.Actions.EditDefaultWorkflowPermissionsInEnterprise(ctx, enterpriseSlug, workflowPerms) + if err != nil { + return err + } + + return nil +} diff --git a/github/resource_github_enterprise_actions_workflow_permissions_test.go b/github/resource_github_enterprise_actions_workflow_permissions_test.go new file mode 100644 index 0000000000..fe84172a60 --- /dev/null +++ b/github/resource_github_enterprise_actions_workflow_permissions_test.go @@ -0,0 +1,159 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccGithubEnterpriseActionsWorkflowPermissions(t *testing.T) { + + t.Run("creates enterprise workflow permissions without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_enterprise_actions_workflow_permissions" "test" { + enterprise_slug = "%s" + + default_workflow_permissions = "read" + can_approve_pull_request_reviews = false + } + `, testEnterprise) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_actions_workflow_permissions.test", "enterprise_slug", testEnterprise), + resource.TestCheckResourceAttr("github_enterprise_actions_workflow_permissions.test", "default_workflow_permissions", "read"), + resource.TestCheckResourceAttr("github_enterprise_actions_workflow_permissions.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("updates enterprise workflow permissions without error", func(t *testing.T) { + + configs := map[string]string{ + "before": fmt.Sprintf(` + resource "github_enterprise_actions_workflow_permissions" "test" { + enterprise_slug = "%s" + + default_workflow_permissions = "read" + can_approve_pull_request_reviews = false + } + `, testEnterprise), + + "after": fmt.Sprintf(` + resource "github_enterprise_actions_workflow_permissions" "test" { + enterprise_slug = "%s" + + default_workflow_permissions = "write" + can_approve_pull_request_reviews = true + } + `, testEnterprise), + } + + checks := map[string]resource.TestCheckFunc{ + "before": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_actions_workflow_permissions.test", "default_workflow_permissions", "read"), + resource.TestCheckResourceAttr("github_enterprise_actions_workflow_permissions.test", "can_approve_pull_request_reviews", "false"), + ), + "after": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_actions_workflow_permissions.test", "default_workflow_permissions", "write"), + resource.TestCheckResourceAttr("github_enterprise_actions_workflow_permissions.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("imports enterprise workflow permissions without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_enterprise_actions_workflow_permissions" "test" { + enterprise_slug = "%s" + + default_workflow_permissions = "read" + can_approve_pull_request_reviews = false + } + `, testEnterprise) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_actions_workflow_permissions.test", "enterprise_slug", testEnterprise), + resource.TestCheckResourceAttr("github_enterprise_actions_workflow_permissions.test", "default_workflow_permissions", "read"), + resource.TestCheckResourceAttr("github_enterprise_actions_workflow_permissions.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_actions_workflow_permissions.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/github/resource_github_enterprise_security_analysis_settings.go b/github/resource_github_enterprise_security_analysis_settings.go new file mode 100644 index 0000000000..8426315064 --- /dev/null +++ b/github/resource_github_enterprise_security_analysis_settings.go @@ -0,0 +1,156 @@ +package github + +import ( + "context" + "log" + + "github.com/google/go-github/v67/github" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceGithubEnterpriseSecurityAnalysisSettings() *schema.Resource { + return &schema.Resource{ + Description: "GitHub Enterprise Security Analysis Settings management.", + Create: resourceGithubEnterpriseSecurityAnalysisSettingsCreateOrUpdate, + Read: resourceGithubEnterpriseSecurityAnalysisSettingsRead, + Update: resourceGithubEnterpriseSecurityAnalysisSettingsCreateOrUpdate, + Delete: resourceGithubEnterpriseSecurityAnalysisSettingsDelete, + 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.", + }, + "advanced_security_enabled_for_new_repositories": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether GitHub Advanced Security is automatically enabled for new repositories.", + }, + "secret_scanning_enabled_for_new_repositories": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether secret scanning is automatically enabled for new repositories.", + }, + "secret_scanning_push_protection_enabled_for_new_repositories": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether secret scanning push protection is automatically enabled for new repositories.", + }, + "secret_scanning_push_protection_custom_link": { + Type: schema.TypeString, + Optional: true, + Description: "Custom URL for secret scanning push protection bypass instructions.", + }, + "secret_scanning_validity_checks_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + Description: "Whether secret scanning validity checks are enabled.", + }, + }, + } +} + +func resourceGithubEnterpriseSecurityAnalysisSettingsCreateOrUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + + enterpriseSlug := d.Get("enterprise_slug").(string) + d.SetId(enterpriseSlug) + + settings := &github.EnterpriseSecurityAnalysisSettings{} + + if v, ok := d.GetOk("advanced_security_enabled_for_new_repositories"); ok { + settings.AdvancedSecurityEnabledForNewRepositories = github.Bool(v.(bool)) + } + + if v, ok := d.GetOk("secret_scanning_enabled_for_new_repositories"); ok { + settings.SecretScanningEnabledForNewRepositories = github.Bool(v.(bool)) + } + + if v, ok := d.GetOk("secret_scanning_push_protection_enabled_for_new_repositories"); ok { + settings.SecretScanningPushProtectionEnabledForNewRepositories = github.Bool(v.(bool)) + } + + if v, ok := d.GetOk("secret_scanning_push_protection_custom_link"); ok { + settings.SecretScanningPushProtectionCustomLink = github.String(v.(string)) + } + + if v, ok := d.GetOk("secret_scanning_validity_checks_enabled"); ok { + settings.SecretScanningValidityChecksEnabled = github.Bool(v.(bool)) + } + + log.Printf("[DEBUG] Updating security analysis settings for enterprise: %s", enterpriseSlug) + _, err := client.Enterprise.UpdateCodeSecurityAndAnalysis(ctx, enterpriseSlug, settings) + if err != nil { + return err + } + + return resourceGithubEnterpriseSecurityAnalysisSettingsRead(d, meta) +} + +func resourceGithubEnterpriseSecurityAnalysisSettingsRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + + enterpriseSlug := d.Id() + log.Printf("[DEBUG] Reading security analysis settings for enterprise: %s", enterpriseSlug) + + settings, _, err := client.Enterprise.GetCodeSecurityAndAnalysis(ctx, enterpriseSlug) + if err != nil { + return err + } + + if err := d.Set("enterprise_slug", enterpriseSlug); err != nil { + return err + } + if err := d.Set("advanced_security_enabled_for_new_repositories", settings.AdvancedSecurityEnabledForNewRepositories); err != nil { + return err + } + if err := d.Set("secret_scanning_enabled_for_new_repositories", settings.SecretScanningEnabledForNewRepositories); err != nil { + return err + } + if err := d.Set("secret_scanning_push_protection_enabled_for_new_repositories", settings.SecretScanningPushProtectionEnabledForNewRepositories); err != nil { + return err + } + if err := d.Set("secret_scanning_push_protection_custom_link", settings.SecretScanningPushProtectionCustomLink); err != nil { + return err + } + if err := d.Set("secret_scanning_validity_checks_enabled", settings.SecretScanningValidityChecksEnabled); err != nil { + return err + } + + return nil +} + +func resourceGithubEnterpriseSecurityAnalysisSettingsDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*Owner).v3client + ctx := context.Background() + + enterpriseSlug := d.Id() + log.Printf("[DEBUG] Resetting security analysis settings to defaults for enterprise: %s", enterpriseSlug) + + // Reset to safe defaults (all disabled) + settings := &github.EnterpriseSecurityAnalysisSettings{ + AdvancedSecurityEnabledForNewRepositories: github.Bool(false), + SecretScanningEnabledForNewRepositories: github.Bool(false), + SecretScanningPushProtectionEnabledForNewRepositories: github.Bool(false), + SecretScanningPushProtectionCustomLink: github.String(""), + SecretScanningValidityChecksEnabled: github.Bool(false), + } + + _, err := client.Enterprise.UpdateCodeSecurityAndAnalysis(ctx, enterpriseSlug, settings) + if err != nil { + return err + } + + return nil +} diff --git a/website/docs/r/enterprise_actions_workflow_permissions.html.markdown b/website/docs/r/enterprise_actions_workflow_permissions.html.markdown new file mode 100644 index 0000000000..d4a179e64d --- /dev/null +++ b/website/docs/r/enterprise_actions_workflow_permissions.html.markdown @@ -0,0 +1,64 @@ +--- +layout: "github" +page_title: "GitHub: github_enterprise_actions_workflow_permissions" +description: |- + Manages GitHub Actions workflow permissions for a GitHub Enterprise. +--- + +# github_enterprise_actions_workflow_permissions + +This resource allows you to manage GitHub Actions workflow permissions for a GitHub Enterprise account. This controls the default permissions granted to the GITHUB_TOKEN when running workflows and whether GitHub Actions can approve pull request reviews. + +You must have enterprise admin access to use this resource. + +## Example Usage + +```hcl +# Basic workflow permissions configuration +resource "github_enterprise_actions_workflow_permissions" "example" { + enterprise_slug = "my-enterprise" + + default_workflow_permissions = "read" + can_approve_pull_request_reviews = false +} + +# Allow write permissions and PR approvals +resource "github_enterprise_actions_workflow_permissions" "permissive" { + enterprise_slug = "my-enterprise" + + 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. + +* `default_workflow_permissions` - (Optional) The default workflow permissions granted to the GITHUB_TOKEN when running workflows. Can be `read` or `write`. Defaults to `read`. + +* `can_approve_pull_request_reviews` - (Optional) Whether GitHub Actions can approve pull request reviews. Defaults to `false`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The enterprise slug. + +## Import + +Enterprise Actions workflow permissions can be imported using the enterprise slug: + +``` +terraform import github_enterprise_actions_workflow_permissions.example my-enterprise +``` + +## Notes + +~> **Note:** This resource requires a GitHub Enterprise account and enterprise admin permissions. + +When this resource is destroyed, the workflow permissions will be reset to safe defaults: +- `default_workflow_permissions` = `read` +- `can_approve_pull_request_reviews` = `false` \ No newline at end of file diff --git a/website/docs/r/enterprise_security_analysis_settings.html.markdown b/website/docs/r/enterprise_security_analysis_settings.html.markdown new file mode 100644 index 0000000000..cfc949cdd6 --- /dev/null +++ b/website/docs/r/enterprise_security_analysis_settings.html.markdown @@ -0,0 +1,83 @@ +--- +layout: "github" +page_title: "GitHub: github_enterprise_security_analysis_settings" +description: |- + Manages GitHub Enterprise security analysis settings. +--- + +# github_enterprise_security_analysis_settings + +This resource allows you to manage code security and analysis settings for a GitHub Enterprise account. This controls Advanced Security, Secret Scanning, and related security features that are automatically enabled for new repositories in the enterprise. + +You must have enterprise admin access to use this resource. + +## Example Usage + +```hcl +# Basic security settings - enable secret scanning only +resource "github_enterprise_security_analysis_settings" "basic" { + enterprise_slug = "my-enterprise" + + secret_scanning_enabled_for_new_repositories = true +} + +# Full security configuration with all features enabled +resource "github_enterprise_security_analysis_settings" "comprehensive" { + enterprise_slug = "my-enterprise" + + advanced_security_enabled_for_new_repositories = true + secret_scanning_enabled_for_new_repositories = true + secret_scanning_push_protection_enabled_for_new_repositories = true + secret_scanning_validity_checks_enabled = true + secret_scanning_push_protection_custom_link = "https://octokit.com/security-guidelines" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `enterprise_slug` - (Required) The slug of the enterprise. + +* `advanced_security_enabled_for_new_repositories` - (Optional) Whether GitHub Advanced Security is automatically enabled for new repositories. Defaults to `false`. Requires Advanced Security license. + +* `secret_scanning_enabled_for_new_repositories` - (Optional) Whether secret scanning is automatically enabled for new repositories. Defaults to `false`. + +* `secret_scanning_push_protection_enabled_for_new_repositories` - (Optional) Whether secret scanning push protection is automatically enabled for new repositories. Defaults to `false`. + +* `secret_scanning_push_protection_custom_link` - (Optional) Custom URL for secret scanning push protection bypass instructions. + +* `secret_scanning_validity_checks_enabled` - (Optional) Whether secret scanning validity checks are enabled. Defaults to `false`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The enterprise slug. + +## Import + +Enterprise security analysis settings can be imported using the enterprise slug: + +``` +terraform import github_enterprise_security_analysis_settings.example my-enterprise +``` + +## Notes + +~> **Note:** This resource requires a GitHub Enterprise account and enterprise admin permissions. + +~> **Note:** Advanced Security features require a GitHub Advanced Security license. + +When this resource is destroyed, all security analysis settings will be reset to disabled defaults for security reasons. + +## Dependencies + +This resource manages the following security features: + +- **Advanced Security**: Code scanning, secret scanning, and dependency review +- **Secret Scanning**: Automatic detection of secrets in code +- **Push Protection**: Prevents secrets from being committed to repositories +- **Validity Checks**: Verifies that detected secrets are actually valid + +These settings only apply to **new repositories** created after the settings are enabled. Existing repositories are not affected and must be configured individually. \ No newline at end of file From 60cf8a961a7509380ce3ca1665181397c334072b Mon Sep 17 00:00:00 2001 From: Nick Floyd Date: Wed, 12 Nov 2025 16:06:26 -0600 Subject: [PATCH 09/10] Adds test coverage for ent securtiy settings --- ...erprise_security_analysis_settings_test.go | 324 ++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 github/resource_github_enterprise_security_analysis_settings_test.go diff --git a/github/resource_github_enterprise_security_analysis_settings_test.go b/github/resource_github_enterprise_security_analysis_settings_test.go new file mode 100644 index 0000000000..05493967f3 --- /dev/null +++ b/github/resource_github_enterprise_security_analysis_settings_test.go @@ -0,0 +1,324 @@ +package github + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccGithubEnterpriseSecurityAnalysisSettings(t *testing.T) { + + t.Run("creates enterprise security analysis settings without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_enterprise_security_analysis_settings" "test" { + enterprise_slug = "%s" + + advanced_security_enabled_for_new_repositories = true + secret_scanning_enabled_for_new_repositories = true + secret_scanning_push_protection_enabled_for_new_repositories = false + secret_scanning_validity_checks_enabled = true + } + `, testEnterprise) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "enterprise_slug", testEnterprise), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "advanced_security_enabled_for_new_repositories", "true"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_enabled_for_new_repositories", "true"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_push_protection_enabled_for_new_repositories", "false"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_validity_checks_enabled", "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("creates enterprise security analysis settings with custom link", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_enterprise_security_analysis_settings" "test" { + enterprise_slug = "%s" + + advanced_security_enabled_for_new_repositories = true + secret_scanning_enabled_for_new_repositories = true + secret_scanning_push_protection_enabled_for_new_repositories = true + secret_scanning_push_protection_custom_link = "https://example.com/security-help" + secret_scanning_validity_checks_enabled = true + } + `, testEnterprise) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "enterprise_slug", testEnterprise), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "advanced_security_enabled_for_new_repositories", "true"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_enabled_for_new_repositories", "true"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_push_protection_enabled_for_new_repositories", "true"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_push_protection_custom_link", "https://example.com/security-help"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_validity_checks_enabled", "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 security analysis settings without error", func(t *testing.T) { + + configs := map[string]string{ + "before": fmt.Sprintf(` + resource "github_enterprise_security_analysis_settings" "test" { + enterprise_slug = "%s" + + advanced_security_enabled_for_new_repositories = false + secret_scanning_enabled_for_new_repositories = false + secret_scanning_push_protection_enabled_for_new_repositories = false + secret_scanning_validity_checks_enabled = false + } + `, testEnterprise), + + "after": fmt.Sprintf(` + resource "github_enterprise_security_analysis_settings" "test" { + enterprise_slug = "%s" + + advanced_security_enabled_for_new_repositories = true + secret_scanning_enabled_for_new_repositories = true + secret_scanning_push_protection_enabled_for_new_repositories = true + secret_scanning_push_protection_custom_link = "https://updated.example.com/security" + secret_scanning_validity_checks_enabled = true + } + `, testEnterprise), + } + + checks := map[string]resource.TestCheckFunc{ + "before": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "advanced_security_enabled_for_new_repositories", "false"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_enabled_for_new_repositories", "false"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_push_protection_enabled_for_new_repositories", "false"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_validity_checks_enabled", "false"), + ), + "after": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "advanced_security_enabled_for_new_repositories", "true"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_enabled_for_new_repositories", "true"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_push_protection_enabled_for_new_repositories", "true"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_push_protection_custom_link", "https://updated.example.com/security"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_validity_checks_enabled", "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("creates minimal enterprise security analysis settings", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_enterprise_security_analysis_settings" "test" { + enterprise_slug = "%s" + } + `, testEnterprise) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "enterprise_slug", testEnterprise), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "advanced_security_enabled_for_new_repositories", "false"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_enabled_for_new_repositories", "false"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_push_protection_enabled_for_new_repositories", "false"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_validity_checks_enabled", "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("imports enterprise security analysis settings without error", func(t *testing.T) { + + config := fmt.Sprintf(` + resource "github_enterprise_security_analysis_settings" "test" { + enterprise_slug = "%s" + + advanced_security_enabled_for_new_repositories = true + secret_scanning_enabled_for_new_repositories = true + secret_scanning_push_protection_enabled_for_new_repositories = false + secret_scanning_validity_checks_enabled = true + } + `, testEnterprise) + + check := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "enterprise_slug", testEnterprise), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "advanced_security_enabled_for_new_repositories", "true"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_enabled_for_new_repositories", "true"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_push_protection_enabled_for_new_repositories", "false"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_validity_checks_enabled", "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, + }, + { + ResourceName: "github_enterprise_security_analysis_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) + }) + }) + + t.Run("handles custom link removal", func(t *testing.T) { + + configs := map[string]string{ + "with_link": fmt.Sprintf(` + resource "github_enterprise_security_analysis_settings" "test" { + enterprise_slug = "%s" + + secret_scanning_push_protection_enabled_for_new_repositories = true + secret_scanning_push_protection_custom_link = "https://example.com/help" + } + `, testEnterprise), + + "without_link": fmt.Sprintf(` + resource "github_enterprise_security_analysis_settings" "test" { + enterprise_slug = "%s" + + secret_scanning_push_protection_enabled_for_new_repositories = true + } + `, testEnterprise), + } + + checks := map[string]resource.TestCheckFunc{ + "with_link": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_push_protection_enabled_for_new_repositories", "true"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_push_protection_custom_link", "https://example.com/help"), + ), + "without_link": resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_push_protection_enabled_for_new_repositories", "true"), + resource.TestCheckResourceAttr("github_enterprise_security_analysis_settings.test", "secret_scanning_push_protection_custom_link", ""), + ), + } + + testCase := func(t *testing.T, mode string) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { skipUnlessMode(t, mode) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: configs["with_link"], + Check: checks["with_link"], + }, + { + Config: configs["without_link"], + Check: checks["without_link"], + }, + }, + }) + } + + 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) + }) + }) +} From 9a7ae06282acdea0c0fd8810467157fcc4634efd Mon Sep 17 00:00:00 2001 From: Nick Floyd <139819+nickfloyd@users.noreply.github.com> Date: Thu, 13 Nov 2025 09:50:19 -0600 Subject: [PATCH 10/10] Apply suggestion from @nickfloyd --- examples/enterprise_settings/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/enterprise_settings/main.tf b/examples/enterprise_settings/main.tf index c73cf29c65..695b47a35d 100644 --- a/examples/enterprise_settings/main.tf +++ b/examples/enterprise_settings/main.tf @@ -119,4 +119,4 @@ output "advanced_enterprise_workflow" { default_workflow_permissions = github_enterprise_actions_workflow_permissions.advanced.default_workflow_permissions can_approve_pull_request_reviews = github_enterprise_actions_workflow_permissions.advanced.can_approve_pull_request_reviews } -} \ No newline at end of file +}