diff --git a/clusters/csvd-lab-dja/main.tf b/clusters/csvd-lab-dja/main.tf new file mode 100644 index 0000000..7972a23 --- /dev/null +++ b/clusters/csvd-lab-dja/main.tf @@ -0,0 +1,60 @@ +provider "aws" { +} + +data "aws_secretsmanager_secret_version" "github_token" { + secret_id = "/eks-cluster-deployment/github_token" +} + +provider "github" { + token = data.aws_secretsmanager_secret_version.github_token.secret_string +} + +module "eks_deployment" { + source = "../../" + repository_mode = "create" + organization = "SCT-Engineering" + + # Repository and cluster configuration - single name for both + name = "csvd-lab-dja" + environment = "lab" + region = "us-gov-east-1" + + # Cluster configuration - simplified interface + cluster_config = { + account_name = "lab-dev-gov" + aws_account_id = "224384469011" + cluster_mailing_list = "matthew.c.morgan@census.gov" + environment_abbr = "lab" + finops_project_name = "csvd_platformbaseline" + finops_project_number = "fs0000000078" + finops_project_role = "csvd_platformbaseline_eks" + vpc_domain_name = "dev.lab.csp2.census.gov" + vpc_name = "vpc3-lab-dev" + tags = { + Owner = "matthew.c.morgan@census.gov" + Environment = "lab" + CostCenter = "fs0000000078" + } + organization = "census:ocio:csvd" + } + template_enabled_modules = { + eks-gatekeeper = false + eks-grafana = false + eks-kiali = false + eks-keycloak = false + eks-loki = false + eks-otel = false + eks-prometheus = false + eks-tempo = false + } +} + +output "repository_url" { + description = "URL of the created GitHub repository" + value = module.eks_deployment.repository_url +} + +output "ssh_clone_url" { + description = "SSH clone URL of the repository" + value = module.eks_deployment.ssh_clone_url +} diff --git a/clusters/csvd-lab-mcm/main.tf b/clusters/csvd-lab-mcm/main.tf index 09ed437..4b16870 100644 --- a/clusters/csvd-lab-mcm/main.tf +++ b/clusters/csvd-lab-mcm/main.tf @@ -11,12 +11,12 @@ provider "github" { module "eks_deployment" { source = "../../" - repository_mode = "create" + repository_mode = "update" organization = "SCT-Engineering" # Repository and cluster configuration - single name for both name = "csvd-lab-mcm" - environment = "dev" + environment = "lab" region = "us-gov-east-1" # Cluster configuration - simplified interface @@ -32,7 +32,7 @@ module "eks_deployment" { vpc_name = "vpc3-lab-dev" tags = { Owner = "matthew.c.morgan@census.gov" - Environment = "development" + Environment = "lab" CostCenter = "fs0000000078" } organization = "census:ocio:csvd" diff --git a/main.tf b/main.tf index 6aab949..c7f8752 100644 --- a/main.tf +++ b/main.tf @@ -1,6 +1,7 @@ locals { - create_repository = var.repository_mode == "create" + create_repository = lower(trimspace(var.repository_mode)) == "create" effective_files_branch = local.create_repository ? "new/${var.name}" : "update/${var.name}" + update_source_branch = var.files_branch_source_branch != null ? var.files_branch_source_branch : (local.create_repository ? null : data.github_repository.existing_repo[0].default_branch) effective_template_enabled_modules = merge(var.template_enabled_modules, { eks = true eks-config = true @@ -57,6 +58,7 @@ locals { eks_ng_max_size = local.eks_defaults.ng_max_size eks_ng_min_size = local.eks_defaults.ng_min_size organization = var.cluster_config.organization + CostAllocation = var.cluster_config.organization finops_project_name = local.organization_defaults.finops_project_name finops_project_number = local.organization_defaults.finops_project_number finops_project_role = local.organization_defaults.finops_project_role @@ -105,6 +107,35 @@ locals { aws_region = var.region }) } + + desired_managed_files_by_path = { + for file in concat([ + for path, content in local.rendered_files : { + path = path + content = content + } + ], + local.template_cluster_sync_files, + local.managed_extra_files + ) : file.path => file.content + } + + existing_managed_file_paths = !local.create_repository && local.update_source_branch != null ? toset([ + for entry in data.github_tree.update_source_branch_tree[0].entries : entry.path + if entry.type == "blob" + ]) : toset([]) + + managed_files_requiring_update = local.create_repository ? local.desired_managed_files_by_path : { + for path, content in local.desired_managed_files_by_path : path => content + if !contains(local.existing_managed_file_paths, path) || data.github_repository_file.existing_managed_files[path].content != content + } + + has_update_changes = length(local.managed_files_requiring_update) > 0 + effective_update_files_branch = local.create_repository || local.has_update_changes ? local.effective_files_branch : null + managed_extra_files_for_module = [for path, content in local.managed_files_requiring_update : { + path = path + content = content + }] } data "github_repository" "existing_repo" { @@ -112,6 +143,30 @@ data "github_repository" "existing_repo" { full_name = "${var.organization}/${var.name}" } +# Used to detect accidental create-mode runs against an already-existing repo. +data "github_repositories" "create_mode_check" { + count = local.create_repository ? 1 : 0 + query = "org:${var.organization} ${var.name} in:name" +} + +locals { + create_mode_repo_exists = local.create_repository && contains( + try(data.github_repositories.create_mode_check[0].names, []), + var.name + ) +} + +resource "terraform_data" "create_mode_guard" { + count = local.create_repository ? 1 : 0 + + lifecycle { + precondition { + condition = !local.create_mode_repo_exists + error_message = "repository_mode=\"create\" is set but repository \"${var.name}\" already exists in ${var.organization}. Switch to repository_mode=\"update\"." + } + } +} + data "github_repository_file" "template_cluster_files" { for_each = toset(var.template_cluster_file_paths) @@ -120,25 +175,42 @@ data "github_repository_file" "template_cluster_files" { file = "environment/region/vpc/cluster/${each.value}" } -resource "github_branch" "files_branch" { - count = !local.create_repository ? 1 : 0 - repository = var.name - branch = local.effective_files_branch - source_branch = var.files_branch_source_branch != null ? var.files_branch_source_branch : data.github_repository.existing_repo[0].default_branch +data "github_tree" "update_source_branch_tree" { + count = !local.create_repository && local.update_source_branch != null ? 1 : 0 + + repository = var.name + tree_sha = local.update_source_branch + recursive = true + + depends_on = [ + data.github_repository.existing_repo, + ] +} + +data "github_repository_file" "existing_managed_files" { + for_each = !local.create_repository ? { + for path, content in local.desired_managed_files_by_path : path => content + if contains(local.existing_managed_file_paths, path) + } : {} + + repository = "${var.organization}/${var.name}" + branch = coalesce(local.update_source_branch, "main") + file = each.key } module "github_repo" { source = "../terraform-github-repo" - name = var.name - repo_org = var.organization - create_repo = local.create_repository - create_codeowners = true - enforce_prs = true - files_branch = local.effective_files_branch - github_repo_description = "EKS Cluster Configuration for ${var.name}" - github_repo_topics = ["eks", "kubernetes", "terraform", "infrastructure"] - force_name = var.force_name + name = var.name + repo_org = var.organization + create_repo = local.create_repository + create_codeowners = true + enforce_prs = true + files_branch = local.effective_update_files_branch + files_branch_source_branch = local.update_source_branch + github_repo_description = "EKS Cluster Configuration for ${var.name}" + github_repo_topics = ["eks", "kubernetes", "terraform", "infrastructure"] + force_name = var.force_name template_repo_org = null template_repo = null @@ -149,15 +221,7 @@ module "github_repo" { github_has_projects = true vulnerability_alerts = null - managed_extra_files = concat([ - for path, content in local.rendered_files : { - path = path - content = content - }], - local.template_cluster_sync_files, - local.managed_extra_files, - var.github_actions_workflows - ) + managed_extra_files = local.managed_extra_files_for_module archive_on_destroy = false github_org_teams = [ @@ -170,9 +234,6 @@ module "github_repo" { } ] - depends_on = [ - github_branch.files_branch - ] } # The EKS deployment logic will go here, and will be skipped if create_repository is true. diff --git a/templates/cluster.hcl.tf.tpl b/templates/cluster.hcl.tf.tpl index 0a30d6c..0d8c524 100644 --- a/templates/cluster.hcl.tf.tpl +++ b/templates/cluster.hcl.tf.tpl @@ -7,6 +7,7 @@ locals { eks_ng_max_size = ${eks_ng_max_size} eks_ng_min_size = ${eks_ng_min_size} organization = "${organization}" + CostAllocation = "${organization}" finops_project_name = "${finops_project_name}" finops_project_number = "${finops_project_number}" finops_project_role = "${finops_project_role}" diff --git a/templates/root.hcl.tf.tpl b/templates/root.hcl.tf.tpl index 981f885..d4f7bf1 100644 --- a/templates/root.hcl.tf.tpl +++ b/templates/root.hcl.tf.tpl @@ -142,7 +142,9 @@ generate "aws-provider" { finops_project_name = "$${local.finops_project_name}" finops_project_number = "$${local.finops_project_number}" finops_project_role = "$${local.finops_project_role}" + CostAllocation = "$${local.organization}" organization = "$${local.organization}" + "boc:created-by" = "terragrunt" } } # Only these AWS Account IDs may be operated on by this template @@ -151,6 +153,19 @@ generate "aws-provider" { EOF } +generate "tags-yml" { + path = "tags.yml" + if_exists = "overwrite" + contents = !local.is_eks_module ? "" : <<-EOF + finops: + number: "$${tonumber(regex("[0-9]+$", local.finops_project_number))}" + name: "$${local.finops_project_name}" + roles: + - eks + - "$${local.finops_project_role}" + EOF +} + # --------------------------------------------------------------------------------------------------------------------- # GLOBAL PARAMETERS # These variables apply to all configurations in this subfolder. These are automatically merged into the child diff --git a/variables.tf b/variables.tf index 48db28b..da70bfd 100644 --- a/variables.tf +++ b/variables.tf @@ -258,7 +258,7 @@ variable "repository_mode" { default = "create" validation { - condition = contains(["create", "update"], var.repository_mode) + condition = contains(["create", "update"], lower(trimspace(var.repository_mode))) error_message = "repository_mode must be either 'create' or 'update'." } }