Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TFE integration for backend and CICD #2611

Merged
merged 13 commits into from
Oct 16, 2024
61 changes: 31 additions & 30 deletions fast/stages/0-bootstrap/README.md

Large diffs are not rendered by default.

13 changes: 9 additions & 4 deletions fast/stages/0-bootstrap/cicd.tf
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,9 @@ locals {
contains(
keys(local.workload_identity_providers),
coalesce(try(v.identity_provider, null), ":")
)
&&
fileexists(
format("${path.module}/templates/workflow-%s.yaml", try(v.type, ""))
) && (
try(v.type, "") == "terraform" ||
fileexists(format("${path.module}/templates/workflow-%s.yaml", try(v.type, "")))
)
)
}
Expand Down Expand Up @@ -90,6 +89,12 @@ module "automation-tf-cicd-sa" {
google_iam_workload_identity_pool.default[0].name,
each.value.name
)
: length(regexall("%s", local.workload_identity_providers_defs[each.value.type].principal_branch)) == 2
? format(
local.workload_identity_providers_defs[each.value.type].principal_branch,
google_iam_workload_identity_pool.default[0].name,
each.value.branch
)
: format(
local.workload_identity_providers_defs[each.value.type].principal_branch,
google_iam_workload_identity_pool.default[0].name,
Expand Down
4 changes: 2 additions & 2 deletions fast/stages/0-bootstrap/identity-providers-defs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ locals {
"attribute.terraform_full_workspace" = "assertion.terraform_full_workspace"
}
issuer_uri = "https://app.terraform.io"
principal_branch = null
principal_repo = "principalSet://iam.googleapis.com/%s/attribute.tfc_workspace_name/%s"
principal_branch = "principalSet://iam.googleapis.com/%s/attribute.terraform_workspace_id/%s"
principal_repo = "principalSet://iam.googleapis.com/%s/attribute.terraform_project_id/%s"
}
}
}
122 changes: 80 additions & 42 deletions fast/stages/0-bootstrap/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
*/

locals {
_tpl_providers = "${path.module}/templates/providers.tf.tpl"
_tpl_providers_gcs = "${path.module}/templates/providers_gcs.tf.tpl"
_tpl_providers_terraform = "${path.module}/templates/providers_terraform.tf.tpl"
# render CI/CD workflow templates
cicd_workflows = {
for k, v in local.cicd_repositories : k => templatefile(
Expand All @@ -41,59 +42,96 @@ locals {
tf_var_files = local.cicd_workflow_var_files[k]
}
)
if v.type != "terraform"
}
providers = {
"0-bootstrap" = templatefile(local._tpl_providers, {
providers_config = {
"0-bootstrap" = {
name = "bootstrap",
sa = module.automation-tf-bootstrap-sa.email,
bucket = module.automation-tf-bootstrap-gcs.name,
backend_extra = null
bucket = module.automation-tf-bootstrap-gcs.name
name = "bootstrap"
sa = module.automation-tf-bootstrap-sa.email
})
"0-bootstrap-r" = templatefile(local._tpl_providers, {
},
"0-bootstrap-r" = {
name = "bootstrap",
sa = module.automation-tf-bootstrap-r-sa.email,
bucket = module.automation-tf-bootstrap-gcs.name,
backend_extra = null
bucket = module.automation-tf-bootstrap-gcs.name
name = "bootstrap"
sa = module.automation-tf-bootstrap-r-sa.email
})
"1-resman" = templatefile(local._tpl_providers, {
},
"1-resman" = {
name = "resman",
sa = module.automation-tf-resman-sa.email,
bucket = module.automation-tf-resman-gcs.name,
backend_extra = null
bucket = module.automation-tf-resman-gcs.name
name = "resman"
sa = module.automation-tf-resman-sa.email
})
"1-resman-r" = templatefile(local._tpl_providers, {
},
"1-resman-r" = {
name = "resman",
sa = module.automation-tf-resman-r-sa.email,
bucket = module.automation-tf-resman-gcs.name,
backend_extra = null
bucket = module.automation-tf-resman-gcs.name
name = "resman"
sa = module.automation-tf-resman-r-sa.email
})
"1-tenant-factory" = templatefile(local._tpl_providers, {
},
"1-tenant-factory" = {
name = "tenant-factory",
sa = module.automation-tf-resman-sa.email,
bucket = module.automation-tf-resman-gcs.name,
backend_extra = "prefix = \"tenant-factory\""
bucket = module.automation-tf-resman-gcs.name
name = "tenant-factory"
sa = module.automation-tf-resman-sa.email
})
"1-tenant-factory-r" = templatefile(local._tpl_providers, {
},
"1-tenant-factory-r" = {
name = "tenant-factory",
sa = module.automation-tf-resman-r-sa.email,
bucket = module.automation-tf-resman-gcs.name,
backend_extra = "prefix = \"tenant-factory\""
bucket = module.automation-tf-resman-gcs.name
name = "tenant-factory"
sa = module.automation-tf-resman-r-sa.email
})
"1-vpcsc" = templatefile(local._tpl_providers, {
},
"1-vpcsc" = {
name = "vpcsc",
sa = module.automation-tf-vpcsc-sa.email,
bucket = module.automation-tf-vpcsc-gcs.name,
backend_extra = "prefix = \"vpcsc\""
bucket = module.automation-tf-vpcsc-gcs.name
name = "vpcsc"
sa = module.automation-tf-vpcsc-sa.email
})
"1-vpcsc-r" = templatefile(local._tpl_providers, {
},
"1-vpcsc-r" = {
name = "vpcsc",
sa = module.automation-tf-vpcsc-r-sa.email,
bucket = module.automation-tf-vpcsc-gcs.name,
backend_extra = "prefix = \"vpcsc\""
bucket = module.automation-tf-vpcsc-gcs.name
name = "vpcsc"
sa = module.automation-tf-vpcsc-r-sa.email
})
},
}
providers = {
for k, v in local.providers_config : k => (
var.cicd_backends != null && try(var.cicd_backends.terraform, null) != null ?
templatefile(
local._tpl_providers_terraform,
merge(
{
name = v.name,
sa = v.sa
},
{
workspaces = lookup(
var.cicd_backends.terraform.workspaces,
v.name,
{
tags = null,
name = null,
project = null
}
)
},
{
organization = var.cicd_backends.terraform.organization,
hostname = var.cicd_backends.terraform.hostname
}
)
) :
templatefile(local._tpl_providers_gcs, {
name = v.name,
sa = v.sa,
bucket = v.bucket,
backend_extra = v.backend_extra
})
)
}
tfvars = {
automation = {
cicd_backends = var.cicd_backends
federated_identity_pool = try(
google_iam_workload_identity_pool.default[0].name, null
)
Expand Down
44 changes: 44 additions & 0 deletions fast/stages/0-bootstrap/templates/providers_terraform.tf.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

terraform {
cloud {
organization = "${organization}"
%{~ if hostname != null ~}
hostname = "${hostname}"
%{~ endif ~}
workspaces {
%{~ if workspaces.name != null ~}
name = "${workspaces.name}"
%{~ endif ~}
%{~ if workspaces.tags != null ~}
tags = [ %{ for tags in workspaces.tags ~} "${tags}", %{ endfor ~} ]
%{~ endif ~}
%{~ if workspaces.project != null ~}
project = "${workspaces.project}"
%{~ endif ~}
}
}
}

provider "google" {
impersonate_service_account = "${sa}"
}
provider "google-beta" {
impersonate_service_account = "${sa}"
}

# end provider.tf for ${name}
40 changes: 38 additions & 2 deletions fast/stages/0-bootstrap/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,42 @@ variable "bootstrap_user" {
default = null
}

variable "cicd_backends" {
description = "CI/CD backend configuration. Leave null to use GCS buckets for state."
type = object({
terraform = optional(object({
organization = string
workspaces = map(object({
tags = optional(list(string), null)
name = optional(string, null)
project = optional(string, null)
}))
hostname = optional(string, null)
}))
})
default = null
validation {
condition = (
var.cicd_backends == null ||
(
length([for k, v in coalesce(var.cicd_backends, {}) : true if v != null]) == 1
)
)
error_message = "cicd_backends must be either null or contain exactly one backend configuration."
}
validation {
condition = (
var.cicd_backends == null ||
try(var.cicd_backends.terraform, null) == null ||
alltrue([
for k, v in try(var.cicd_backends.terraform.workspaces, {}) :
v.tags != null || v.name != null || v.project != null
])
)
error_message = "At least one of 'tags', 'name', or 'project' must be defined for each workspace in the 'workspaces' map when 'terraform' is defined."
}
}

variable "cicd_repositories" {
description = "CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed."
type = object({
Expand Down Expand Up @@ -77,10 +113,10 @@ variable "cicd_repositories" {
condition = alltrue([
for k, v in coalesce(var.cicd_repositories, {}) :
v == null || (
contains(["github", "gitlab"], coalesce(try(v.type, null), "null"))
contains(["github", "gitlab", "terraform"], coalesce(try(v.type, null), "null"))
)
])
error_message = "Invalid repository type, supported types: 'github' or 'gitlab'."
error_message = "Invalid repository type, supported types: 'github', 'gitlab', or 'terraform'."
}
}

Expand Down
Loading