Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ A [Terraform] module for creating a public or private repository on [Github].
- [Resources](#resources)
- [Inputs](#inputs)
- [Outputs](#outputs)
- [Security And Analysis Configuration](#security-and-analysis-configuration)
- [External Documentation](#external-documentation)
- [Terraform Github Provider Documentation](#terraform-github-provider-documentation)
- [Module Versioning](#module-versioning)
Expand Down Expand Up @@ -87,13 +88,13 @@ See [variables.tf] and [examples/] for details and use-cases.
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.3 |
| <a name="requirement_github"></a> [github](#requirement\_github) | >= 6.2, < 7.0 |
| <a name="requirement_github"></a> [github](#requirement\_github) | >= 6.2, < 6.9 |

### Providers

| Name | Version |
|------|---------|
| <a name="provider_github"></a> [github](#provider\_github) | >= 6.2, < 7.0 |
| <a name="provider_github"></a> [github](#provider\_github) | >= 6.2, < 6.9 |

### Resources

Expand Down Expand Up @@ -178,6 +179,7 @@ See [variables.tf] and [examples/] for details and use-cases.
| <a name="input_push_team_ids"></a> [push\_team\_ids](#input\_push\_team\_ids) | (Optional) A list of teams (by id) to grant push (read-write) permission to. | `list(string)` | `[]` | no |
| <a name="input_push_teams"></a> [push\_teams](#input\_push\_teams) | (Optional) A list of teams (by name/slug) to grant push (read-write) permission to. | `list(string)` | `[]` | no |
| <a name="input_rulesets"></a> [rulesets](#input\_rulesets) | (Optional) A list of branch rulesets to apply to the repository. Default is [].<br/><br/>It is very likely removal of any section will require setting it to an empty list/map.<br/>This is due to limitations in the API whereby components are not destroyed upon removal. | <pre>list(<br/> object({<br/> enforcement = string<br/> name = string<br/> target = string<br/><br/> rules = list(<br/> object({<br/> creation = optional(bool)<br/> deletion = optional(bool)<br/> non_fast_forward = optional(bool)<br/> required_signatures = optional(bool)<br/> required_linear_history = optional(bool)<br/> update = optional(bool)<br/> update_allows_fetch_and_merge = optional(bool)<br/><br/> branch_name_pattern = optional(<br/> object({<br/> operator = string<br/> pattern = string<br/> name = optional(string)<br/> negate = optional(bool)<br/> })<br/> )<br/><br/> commit_author_email_pattern = optional(<br/> object({<br/> operator = string<br/> pattern = string<br/> name = optional(string)<br/> negate = optional(bool)<br/> })<br/> )<br/><br/> commit_message_pattern = optional(<br/> object({<br/> operator = string<br/> pattern = string<br/> name = optional(string)<br/> negate = optional(bool)<br/> })<br/> )<br/><br/> committer_email_pattern = optional(<br/> object({<br/> operator = string<br/> pattern = string<br/> name = optional(string)<br/> negate = optional(bool)<br/> })<br/> )<br/><br/> tag_name_pattern = optional(<br/> object({<br/> operator = string<br/> pattern = string<br/> name = optional(string)<br/> negate = optional(bool)<br/> })<br/> )<br/><br/> required_status_checks = optional(<br/> object({<br/> strict_required_status_checks_policy = optional(bool)<br/> do_not_enforce_on_create = optional(bool)<br/> required_check = list(<br/> object({<br/> context = string<br/> integration_id = optional(number)<br/> })<br/> )<br/> })<br/> )<br/><br/> pull_request = optional(<br/> object({<br/> dismiss_stale_reviews_on_push = optional(bool)<br/> require_code_owner_review = optional(bool)<br/> require_last_push_approval = optional(bool)<br/> required_approving_review_count = optional(number)<br/> required_review_thread_resolution = optional(bool)<br/> })<br/> )<br/><br/> required_workflows = optional(<br/> object({<br/> required_workflow = list(<br/> object({<br/> repository_id = number<br/> ref = string<br/> path = string<br/> })<br/> )<br/> })<br/> )<br/><br/> required_deployments = optional(<br/> object({<br/> required_deployment_environments = list(string)<br/> })<br/> )<br/><br/> required_code_scanning = optional(<br/> object({<br/> required_code_scanning_tool = list(<br/> object({<br/> tool = string<br/> alerts_threshold = string<br/> security_alerts_threshold = string<br/> })<br/> )<br/> })<br/> )<br/><br/> merge_queue = optional(<br/> object({<br/> check_response_timeout_minutes = optional(number)<br/> grouping_strategy = optional(string)<br/> max_entries_to_build = optional(number)<br/> max_entries_to_merge = optional(number)<br/> merge_method = optional(string)<br/> min_entries_to_merge = optional(number)<br/> min_entries_to_merge_wait_minutes = optional(number)<br/> })<br/> )<br/> })<br/> )<br/><br/> bypass_actors = optional(<br/> list(<br/> object({<br/> actor_id = optional(number)<br/> actor_type = string<br/> bypass_mode = optional(string)<br/> })<br/> )<br/> )<br/><br/> conditions = optional(<br/> object({<br/> ref_name = object({<br/> include = list(string)<br/> exclude = list(string)<br/> })<br/> })<br/> )<br/> })<br/> )</pre> | `[]` | no |
| <a name="input_security_and_analysis"></a> [security\_and\_analysis](#input\_security\_and\_analysis) | (Optional) Security and analysis configuration for the repository.<br/><br/>- All fields except org\_advanced\_security are strings: "enabled" or "disabled".<br/>- org\_advanced\_security is a bool indicating whether the org has split licensing for Advanced Security. | <pre>object({<br/> org_advanced_security = optional(bool, false)<br/> advanced_security = optional(string, "disabled")<br/> code_security = optional(string, "disabled")<br/> secret_scanning = optional(string, "disabled")<br/> secret_scanning_push_protection = optional(string, "disabled")<br/> secret_scanning_ai_detection = optional(string, "disabled")<br/> secret_scanning_non_provider_patterns = optional(string, "disabled")<br/> })</pre> | `null` | no |
| <a name="input_squash_merge_commit_message"></a> [squash\_merge\_commit\_message](#input\_squash\_merge\_commit\_message) | (Optional) Can be `PR_BODY`, `COMMIT_MESSAGES`, or `BLANK` for a default squash merge commit message. | `string` | `"COMMIT_MESSAGES"` | no |
| <a name="input_squash_merge_commit_title"></a> [squash\_merge\_commit\_title](#input\_squash\_merge\_commit\_title) | (Optional) Can be `PR_BODY`, `COMMIT_MESSAGES`, or `BLANK` for a default squash merge commit message. | `string` | `"COMMIT_OR_PR_TITLE"` | no |
| <a name="input_template"></a> [template](#input\_template) | (Optional) Template repository to use. (Default: {}) | <pre>object({<br/> owner = string<br/> repository = string<br/> })</pre> | `null` | no |
Expand Down Expand Up @@ -210,6 +212,41 @@ See [variables.tf] and [examples/] for details and use-cases.
| <a name="output_webhooks"></a> [webhooks](#output\_webhooks) | All attributes and arguments as returned by the github\_repository\_webhook resource. |
<!-- END_TF_DOCS -->

### Security And Analysis Configuration

- [**`security_and_analysis`**](#var-security_and_analysis): *(Optional `object(security_and_analysis)`)*<a name="var-security_and_analysis"></a>

(Optional) The repository's [security and analysis](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-security-and-analysis-settings-for-your-repository) configuration.
See [Security and Analysis Configuration](#security-and-analysis-configuration) below for details.

Default is `null`.

The `security_and_analysis` object accepts the following attributes:

- [**`org_advanced_security`**](#attr-security_and_analysis-org_advanced_security: *(**Optional** `bool`)*<a name="attr-security_and_analysis-org_advanced_security"></a>

If your GitHub Organization has split licensing for Advanced Security you can have Security and Analysis on non-public repositories. Otherwise Security and Analysis operates on all public repositories. Currently there is no known way to automatically detect this.

Define `org_advanced_security` as true to give more visibility options.

- [**`advanced_security`**](#attr-security_and_analysis-advanced_security): *(**Optional** `string`)*<a name="attr-security_and_analysis-advanced_security"></a>

The advanced security configuration for the repository. See [Advanced Security Configuration](#advanced-security-configuration) below for details.

Default is `"disabled"`, except if `org_advanced_security` is not true when it is `"enabled"`.

- [**`secret_scanning`**](#attr-security_and_analysis-secret_scanning): *(**Optional** `string`)*<a name="attr-security_and_analysis-secret_scanning"></a>

The secret scanning configuration for the repository. See [Secret Scanning Configuration](#secret-scanning-configuration) below for details.

Default is `"disabled"`.

- [**`secret_scanning_push_protection`**](#attr-security_and_analysis-secret_scanning_push_protection): *(**Optional** `string`)*<a name="attr-security_and_analysis-secret_scanning_push_protection"></a>

The secret scanning push protection configuration for the repository. See [Secret Scanning Push Protection Configuration](#secret-scanning-push-protection-configuration) below for details.

Default is `"disabled"`.

## External Documentation

### Terraform Github Provider Documentation
Expand Down
5 changes: 0 additions & 5 deletions locals.tf

This file was deleted.

50 changes: 50 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,63 @@ resource "github_repository" "repository" {
}
}

dynamic "security_and_analysis" {
for_each = (
var.visibility == "public" || local.security_and_analysis.org_advanced_security
) ? [local.security_and_analysis] : []

content {
dynamic "advanced_security" {
for_each = var.visibility == "public" ? [] : [local.advanced_security_status]
content {
status = advanced_security.value
}
}

secret_scanning {
status = local.security_and_analysis.secret_scanning
}

secret_scanning_push_protection {
status = local.security_and_analysis.secret_scanning_push_protection
}

# code_security, secret_scanning_ai_detection, and secret_scanning_non_provider_patterns require integrations/github >= 6.9
# code_security {
# status = local.security_and_analysis.code_security
# }
# secret_scanning_ai_detection {
# status = local.security_and_analysis.secret_scanning_ai_detection
# }
# secret_scanning_non_provider_patterns {
# status = local.security_and_analysis.secret_scanning_non_provider_patterns
# }
}
}

lifecycle {
ignore_changes = [
auto_init,
license_template,
gitignore_template,
template,
]
precondition {
condition = (
(
var.visibility == "public"
|| local.security_and_analysis.org_advanced_security
|| !local.saa_child_enabled
)
&&
local.push_protection_valid
)
error_message = (
local.push_protection_valid
? "security_and_analysis cannot be used for private/internal repositories unless org_advanced_security is true."
: "secret_scanning_push_protection requires secret_scanning to also be enabled."
)
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions rulesets_resources.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
locals {
rulesets_by_name = {
for rs in var.rulesets : rs.name => rs
}
}

resource "github_repository_ruleset" "ruleset" {
for_each = local.rulesets_by_name

Expand Down
29 changes: 29 additions & 0 deletions securityanalysis_resources.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
locals {
security_and_analysis = var.security_and_analysis != null ? var.security_and_analysis : {
org_advanced_security = false
advanced_security = "disabled"
code_security = "disabled"
secret_scanning = "disabled"
secret_scanning_push_protection = "disabled"
secret_scanning_ai_detection = "disabled"
secret_scanning_non_provider_patterns = "disabled"
}

saa_child_enabled = (
local.security_and_analysis.code_security == "enabled" ||
local.security_and_analysis.secret_scanning == "enabled" ||
local.security_and_analysis.secret_scanning_push_protection == "enabled" ||
local.security_and_analysis.secret_scanning_ai_detection == "enabled" ||
local.security_and_analysis.secret_scanning_non_provider_patterns == "enabled"
)

advanced_security_status = (
local.saa_child_enabled || local.security_and_analysis.advanced_security == "enabled"
) ? "enabled" : "disabled"

push_protection_valid = (
local.security_and_analysis.secret_scanning_push_protection == "enabled"
? local.security_and_analysis.secret_scanning == "enabled"
: true
)
}
31 changes: 31 additions & 0 deletions securityanalysis_variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
variable "security_and_analysis" {
description = <<EOF
(Optional) Security and analysis configuration for the repository.

- All fields except org_advanced_security are strings: "enabled" or "disabled".
- org_advanced_security is a bool indicating whether the org has split licensing for Advanced Security.
EOF
type = object({
org_advanced_security = optional(bool, false)
advanced_security = optional(string, "disabled")
code_security = optional(string, "disabled")
secret_scanning = optional(string, "disabled")
secret_scanning_push_protection = optional(string, "disabled")
secret_scanning_ai_detection = optional(string, "disabled")
secret_scanning_non_provider_patterns = optional(string, "disabled")
})

default = null

validation {
condition = (
alltrue([
for key, value in(
var.security_and_analysis != null ? var.security_and_analysis : {}
) :
key != "org_advanced_security" ? contains(["enabled", "disabled"], value) : true
])
)
error_message = "All security_and_analysis string fields must be 'enabled' or 'disabled'."
}
}
2 changes: 1 addition & 1 deletion versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ terraform {
required_providers {
github = {
source = "integrations/github"
version = ">= 6.2, < 7.0"
version = ">= 6.2, < 6.9"
}
}
}