Skip to content

Instantly share code, notes, and snippets.

@SafeEval
Last active June 11, 2023 00:02
Show Gist options
  • Save SafeEval/0bb39698b8415b1e3626fbf55fff12dd to your computer and use it in GitHub Desktop.
Save SafeEval/0bb39698b8415b1e3626fbf55fff12dd to your computer and use it in GitHub Desktop.
PoC for Terraform Github repo creation with GHAS settings
###############################################################################
# Workaround for Terraform repository creation with Github Advanced Security
# feature configuration in dynamic blocks.
#
# This works, but you have to run Terraform twice for GHAS settings to apply.
# - Can switch between public and private visibility.
# - Can switch between archived and unarchived.
# - Fails when modifying visibility and archived setting simultaneously.
#
# GHAS API settings depend on existing repository state. Simultaneously modifying
# both in the same Terraform resource causes errors.
#
# registry.terraform.io/hashicorp/github @ 5.26.0
###############################################################################
data "github_repository" "existing" {
name = "test-repo-tf-ghas-dynamic"
}
locals {
target_visibility = "public"
initial_visibility = data.github_repository.existing.visibility
visibility_change = local.target_visibility != local.initial_visibility
target_archived = false
initial_archived = data.github_repository.existing.archived
archived_change = local.target_archived != local.initial_archived
has_paid_ghas = false
ghas_settings = [
{
advanced_security = "enabled"
secret_scanning = "enabled"
secret_scanning_push_protection = "enabled"
}
]
}
# Janky workaround because Terraform lacks a clean raise() assertion feature,
# and GHAS settings are tightly coupled with Github repository settings.
# https://github.com/hashicorp/terraform/issues/15469
# https://github.com/hashicorp/terraform/pull/25088
resource "null_resource" "simultaneous_visibility_archive_update" {
count = !(local.visibility_change && local.archived_change) ? 0 : "Cannot change visibility and archive at the same time"
}
resource "github_repository" "creation_ghas_dynamic" {
# Per-repo configuration.
name = "test-repo-tf-ghas-dynamic"
description = "TF test: repo creation with ghas=dynamic"
visibility = local.target_visibility
# Public without paid GHAS: can archive and unarchive.
# Private without paid GHAS: can archive and unarchive.
# Private to public, active to archived: fails with "403 Repository was archived so is read-only."
# Private to public, archived to active: works.
# Public to private, active to archived: fails with "403 Repository was archived so is read-only."
# Public to private, archived to active: works.
archived = local.target_archived
auto_init = true
# These are only available on public and "enterprise" plan repositories.
# Configuring with Terraform dynamic blocks with different conditions.
security_and_analysis {
# Public without paid GHAS: neither "enabled" or "disabled" work.
# Private without paid GHAS: neither "enabled" or "disabled" work.
dynamic "advanced_security" {
iterator = itr
for_each = [
for i in local.ghas_settings: i
if (local.has_paid_ghas == true) && (local.initial_visibility == "private")
]
content {
status = itr.value.advanced_security
}
}
# Public without paid GHAS: both "enabled" and "disabled" work.
# Private without paid GHAS: neither "enabled" or "disabled" work.
dynamic "secret_scanning" {
iterator = itr
for_each = [
for i in local.ghas_settings: i
if (local.has_paid_ghas == true) || ((local.initial_visibility == "public") && (local.target_visibility == "public"))
]
content {
status = itr.value.secret_scanning
}
}
# Public without paid GHAS: both "enabled" and "disabled" work.
# Private without paid GHAS: neither "enabled" or "disabled" work.
dynamic "secret_scanning_push_protection" {
iterator = itr
for_each = [
for i in local.ghas_settings: i
if (local.has_paid_ghas == true) || ((local.initial_visibility == "public") && (local.target_visibility == "public"))
]
content {
status = itr.value.secret_scanning_push_protection
}
}
}
}
output "initial_visibility" {
value = local.initial_visibility
}
output "target_visibility" {
value = local.target_visibility
}
output "initial_archived" {
value = local.initial_archived
}
output "target_archived" {
value = local.target_archived
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment