feat: Add github_organization_security_configuration and github_enterprise_security_configuration resource#3284
Conversation
|
👋 Hi! Thank you for this contribution! Just to let you know, our GitHub SDK team does a round of issue and PR reviews twice a week, every Monday and Friday! We have a process in place for prioritizing and responding to your input. Because you are a part of this community please feel free to comment, add to, or pick up any issues/PRs that are labeled with |
…prise_security_configuration resources Adds two new resources to manage Code Security Configurations: - github_organization_security_configuration: manages code security configurations at the organization level - github_enterprise_security_configuration: manages code security configurations at the enterprise level Both resources include: - Full CRUD operations using GitHub's Code Security Configurations API - Composite IDs (org/enterprise + config ID) - 404-tolerant delete - tflog structured logging throughout - All optional fields use GetOk to avoid sending unset values - Custom import support - Shared expandCodeSecurityConfigurationCommon helper to avoid duplication - All 4 delegated fields on enterprise: code_scanning_delegated_alert_dismissal, secret_scanning_delegated_bypass, secret_scanning_delegated_bypass_options, secret_scanning_delegated_alert_dismissal - Fix flattenCodeScanningDefaultSetupOptions runner_type empty string drift Acceptance tests (5 per resource): - creates without error (with import verification) - updates without error - creates with nested options (runner, autosubmit) - creates with minimal config (with import verification) - creates with delegated bypass options Documentation added for both resources. Resolves integrations#2412 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ppers - Add setCodeSecurityConfigurationState() to util_security_configuration.go, replacing ~83 identical d.Set() lines duplicated across both Read functions - Remove expandCodeSecurityConfiguration() and expandEnterpriseCodeSecurityConfiguration() one-liner wrappers; callers now call expandCodeSecurityConfigurationCommon() directly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove fmt.Sprintf from all tflog calls; use static messages with structured fields map for dynamic data (28 instances fixed) - Add configuration_id Computed field to both resources so the numeric config ID is stored separately in state - Update/Delete now read enterprise_slug and configuration_id from state via d.Get() instead of parsing the composite ID - Update enterprise docs with configuration_id attribute Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…n_id - Add missing organization_security_configuration documentation - Fix enterprise docs: description is Optional not Required - Add configuration_id assertions to both test files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8b5e69b to
20f4c17
Compare
…view feedback - Upgrade go-github imports from v83 to v84 across all feature files - Remove secret_scanning_delegated_bypass from enterprise resource (org-only API) - Fix reviewer_type enum casing to TEAM/ROLE to match GitHub API - Wire expandSecretScanningDelegatedBypass into org Create/Update - Remove hardcoded "disabled" defaults for code_security/secret_protection - Use GetOk for description field in expand (consistency with other Optional fields) - Add unit tests for all flatten utility functions (deiga requested) - Add missing ImportState steps to acceptance tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@deiga ready for review. Thanks for your patience! I have allowed edits by maintainers. |
deiga
left a comment
There was a problem hiding this comment.
Partial review.
Please take some time and look at lately merged PRs to understand what kind of structures we are looking for.
I don't have the energy to review thousands of lines of code, when you haven't put in the work to adhere to the standards of the repo
| func TestAccGithubOrganizationSecurityConfiguration(t *testing.T) { | ||
| t.Run("creates organization security configuration without error", func(t *testing.T) { | ||
| randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) | ||
| configName := fmt.Sprintf("test-config-%s", randomID) |
There was a problem hiding this comment.
Done — switched all test cases to use testResourcePrefix instead of the hardcoded "test-config-" prefix. Updated across all sub-tests (create, import, update, options, minimal, delegated bypass) at lines 20, 63, 90, 91, 136, 181, and 209 of the test file. The config names now follow the pattern fmt.Sprintf("%s%s", testResourcePrefix, randomID).
| { | ||
| ResourceName: "github_organization_security_configuration.test", | ||
| ImportState: true, | ||
| ImportStateVerify: true, | ||
| }, |
There was a problem hiding this comment.
Create separate test for import
There was a problem hiding this comment.
Done — extracted the import step into its own dedicated sub-test: t.Run("imports organization security configuration without error", ...) at lines 61–86 of the test file. It creates a minimal resource first, then runs the import step with ImportState: true and ImportStateVerify: true in a separate resource.TestCase.
| configBefore := fmt.Sprintf(tmpl, configName, "Test configuration", "disabled") | ||
| configAfter := fmt.Sprintf(tmpl, configNameUpdated, "Test configuration updated", "enabled") |
There was a problem hiding this comment.
Done — removed the shared tmpl variable and inlined the HCL configs directly. configBefore and configAfter are now each defined with their own fmt.Sprintf() call containing the full resource block (lines 93–105 of the test file).
| UpdateContext: resourceGithubOrganizationSecurityConfigurationUpdate, | ||
| DeleteContext: resourceGithubOrganizationSecurityConfigurationDelete, | ||
| Importer: &schema.ResourceImporter{ | ||
| StateContext: schema.ImportStatePassthroughContext, |
There was a problem hiding this comment.
Use a custom Importer function so that Read doesn't have to do ID parsing
There was a problem hiding this comment.
Done — replaced schema.ImportStatePassthroughContext with a custom resourceGithubOrganizationSecurityConfigurationImport function (lines 490–512 of the resource file). The custom importer uses parseID2() to split the composite ID (org:configID), parses the configuration ID, sets it in state via d.Set("configuration_id", int(configID)), and rebuilds the canonical ID with buildID(). This way Read can simply fetch org from meta.(*Owner).name and configuration_id from state (line 359) without any ID parsing.
| "id": configuration.GetID(), | ||
| }) | ||
|
|
||
| return resourceGithubOrganizationSecurityConfigurationRead(ctx, d, meta) |
There was a problem hiding this comment.
Never call CRUD functions directly
| return resourceGithubOrganizationSecurityConfigurationRead(ctx, d, meta) | |
| return nil |
There was a problem hiding this comment.
Done — both Create (lines 333–349) and Update (lines 432–447) now populate state directly from the API response using setCodeSecurityConfigurationState() and return nil, instead of calling Read at the end. The secret scanning delegated bypass fields are also set inline from the response.
| org, idStr, err := parseID2(d.Id()) | ||
| if err != nil { | ||
| return diag.FromErr(err) | ||
| } |
There was a problem hiding this comment.
Fetch these from state instead of parsing from ID
There was a problem hiding this comment.
Done — Read now fetches both values from state/provider config instead of parsing from the composite resource ID:
org := meta.(*Owner).name(line 358)id := int64(d.Get("configuration_id").(int))(line 359)
The ID parsing (parseID2) is now only done in the custom import function, which sets configuration_id in state before Read is called.
| return diag.FromErr(err) | ||
| } | ||
|
|
||
| if diags := setCodeSecurityConfigurationState(d, configuration); diags != nil { |
There was a problem hiding this comment.
I think the check should diags.hasErrors() or is there another reason for this?
There was a problem hiding this comment.
Good catch — changed from diags != nil to diags.HasError(). This is now applied consistently in all three places: Create (line 333), Read (line 387), and Update (line 432). Using HasError() is the correct approach since diags could contain warnings that shouldn't be treated as errors.
…nventions - Add custom importer functions so Read doesn't parse from ID - Read fetches org/enterprise and configuration_id from state - Create/Update return nil instead of calling Read directly - Use diags.HasError() instead of diags != nil - Use testResourcePrefix in all test resource names - Extract import tests into separate t.Run blocks - Inline test HCL templates instead of shared tmpl variables
… Read Create and Update functions now set state directly from the API response via setCodeSecurityConfigurationState, rather than only setting configuration_id. Enterprise Update also captures the API response instead of discarding it.
…escription - Add CheckDestroy functions to all acceptance tests for both org and enterprise security configuration resources - Cast configuration.GetID() to int to match schema.TypeInt - Fix redundant "code security configuration for the code security configuration" description on the code_security field
|
Reviewed and looking at the recent PRs to add in the changes the maintainers are implementing to try and make it consistent. I hope we are much closer this time. |
This commit adds a new resource github_organization_security_configuration & github_enterprise_security_configuration to manage Code Security Configurations at the organization & enterprise level respectively. It includes:
Resolves #2412
Before the change?
After the change?
Pull request checklist
Does this introduce a breaking change?
Please see our docs on breaking changes to help!
Tests