Add support for project templates to project factory module (#3317)
* add support for project templates to project factory module * align project factory features in FAST org setup stage
This commit is contained in:
committed by
GitHub
parent
740ed270af
commit
78966f66c3
@@ -29,6 +29,7 @@ The code is meant to be executed by a high level service account with powerful p
|
||||
- [Folder hierarchy](#folder-hierarchy)
|
||||
- [Projects](#projects)
|
||||
- [Factory-wide project defaults, merges, optionals](#factory-wide-project-defaults-merges-optionals)
|
||||
- [Project templates](#project-templates)
|
||||
- [Service accounts and buckets](#service-accounts-and-buckets)
|
||||
- [Automation project and resources](#automation-project-and-resources)
|
||||
- [Billing budgets](#billing-budgets)
|
||||
@@ -74,6 +75,16 @@ In addition to the YAML-based project configurations, the factory accepts three
|
||||
|
||||
Some examples on where to use each of the three sets are [provided below](#example).
|
||||
|
||||
### Project templates
|
||||
|
||||
Project templates are project definitions that can be "inherited" and extended in YAML-based project configurations. Templates are YAML files which use the same schema as a project, but which don't directly trigger project creation by themselves.
|
||||
|
||||
When referenced in a project configuration file, a template attributes are used as the initial project definition, over which the project's own attributes are merged. The merge is shallow, so any attribute which is defined in the project configuration will take precedence and completely override the template's own definition.
|
||||
|
||||
For example, declaring `iam` or `org_policies` in the template and then doing the same in the project file will result in those two attributes in the template being ignored.
|
||||
|
||||
The set of available templates is defined via a dedicated path in the `factories_config` file, and then a template can be referenced from a project definition via the `project_template` YAML attribute.
|
||||
|
||||
### Service accounts and buckets
|
||||
|
||||
Service accounts and GCS buckets can be managed as part of each project's YAML configuration. This allows creation of default service accounts used for GCE instances, in firewall rules, or for application-level credentials without resorting to a separate Terraform configuration.
|
||||
@@ -343,6 +354,7 @@ module "project-factory" {
|
||||
gcp-devops = "group:gcp-devops@example.org"
|
||||
}
|
||||
tag_values = {
|
||||
"context/gke" = "tagValues/654321"
|
||||
"org-policies/drs-allow-all" = "tagValues/123456"
|
||||
}
|
||||
vpc_host_projects = {
|
||||
@@ -376,8 +388,9 @@ module "project-factory" {
|
||||
billing_account_id = var.billing_account_id
|
||||
data = "data/budgets"
|
||||
}
|
||||
folders = "data/hierarchy"
|
||||
projects = "data/projects"
|
||||
folders = "data/hierarchy"
|
||||
project_templates = "data/templates"
|
||||
projects = "data/projects"
|
||||
}
|
||||
notification_channels = {
|
||||
billing-default = {
|
||||
@@ -389,7 +402,24 @@ module "project-factory" {
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest files=0,1,2,3,4,5,6,7,8,9 inventory=example.yaml
|
||||
# tftest files=t0,0,1,2,3,4,5,6,7,8,9 inventory=example.yaml
|
||||
```
|
||||
|
||||
A project template for GKE projects:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
- compute.googleapis.com
|
||||
- container.googleapis.com
|
||||
- storage.googleapis.com
|
||||
service_encryption_key_ids:
|
||||
storage.googleapis.com:
|
||||
- projects/kms-central-prj/locations/europe-west3/keyRings/my-keyring/cryptoKeys/europe3-gce
|
||||
compute.googleapis.com:
|
||||
- $kms_keys:compute-prod-ew1
|
||||
tag_bindings:
|
||||
context: $tag_values:context/gke
|
||||
# tftest-file id=t0 path=data/templates/container/base.yaml schema=project.schema.json
|
||||
```
|
||||
|
||||
A simple hierarchy of folders:
|
||||
@@ -443,20 +473,14 @@ services:
|
||||
More traditional project definitions via the project factory data:
|
||||
|
||||
```yaml
|
||||
# inherit template attributes
|
||||
project_template: container/base
|
||||
# define project attributes (potentially overriding template)
|
||||
billing_account: 012345-67890A-BCDEF0
|
||||
labels:
|
||||
app: app-0
|
||||
team: team-a
|
||||
parent: $folder_ids:team-a/app-0
|
||||
service_encryption_key_ids:
|
||||
storage.googleapis.com:
|
||||
- projects/kms-central-prj/locations/europe-west3/keyRings/my-keyring/cryptoKeys/europe3-gce
|
||||
compute.googleapis.com:
|
||||
- $kms_keys:compute-prod-ew1
|
||||
services:
|
||||
- compute.googleapis.com
|
||||
- container.googleapis.com
|
||||
- storage.googleapis.com
|
||||
iam_by_principals:
|
||||
$iam_principals:service_accounts/dev-ta-app0-be/app-0-be:
|
||||
- roles/storage.objectViewer
|
||||
@@ -623,7 +647,7 @@ service_accounts:
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [factories_config](variables.tf#L173) | Path to folder with YAML resource description data files. | <code title="object({ folders = optional(string) projects = optional(string) budgets = optional(object({ billing_account_id = string data = string })) })">object({…})</code> | ✓ | |
|
||||
| [factories_config](variables.tf#L173) | Path to folder with YAML resource description data files. | <code title="object({ folders = optional(string) project_templates = optional(string) projects = optional(string) budgets = optional(object({ billing_account_id = string data = string })) })">object({…})</code> | ✓ | |
|
||||
| [context](variables.tf#L17) | Context-specific interpolations. | <code title="object({ condition_vars = optional(map(map(string)), {}) custom_roles = optional(map(string), {}) folder_ids = optional(map(string), {}) iam_principals = optional(map(string), {}) kms_keys = optional(map(string), {}) locations = optional(map(string), {}) notification_channels = optional(map(string), {}) project_ids = optional(map(string), {}) tag_values = optional(map(string), {}) vpc_host_projects = optional(map(string), {}) vpc_sc_perimeters = optional(map(string), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
| [data_defaults](variables.tf#L36) | Optional default values used when corresponding project or folder data from files are missing. | <code title="object({ billing_account = optional(string) bucket = optional(object({ force_destroy = optional(bool) }), {}) contacts = optional(map(list(string)), {}) deletion_policy = optional(string) factories_config = optional(object({ custom_roles = optional(string) observability = optional(string) org_policies = optional(string) quotas = optional(string) }), {}) labels = optional(map(string), {}) metric_scopes = optional(list(string), []) parent = optional(string) prefix = optional(string) project_reuse = optional(object({ use_data_source = optional(bool, true) attributes = optional(object({ name = string number = number services_enabled = optional(list(string), []) })) })) service_encryption_key_ids = optional(map(list(string)), {}) services = optional(list(string), []) shared_vpc_service_config = optional(object({ host_project = string iam_bindings_additive = optional(map(object({ member = string role = string condition = optional(object({ expression = string title = string description = optional(string) })) })), {}) network_users = optional(list(string), []) service_agent_iam = optional(map(list(string)), {}) service_agent_subnet_iam = optional(map(list(string)), {}) service_iam_grants = optional(list(string), []) network_subnet_users = optional(map(list(string)), {}) })) storage_location = optional(string) tag_bindings = optional(map(string), {}) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) })), {}) universe = optional(object({ prefix = string unavailable_service_identities = optional(list(string), []) unavailable_services = optional(list(string), []) })) vpc_sc = optional(object({ perimeter_name = string is_dry_run = optional(bool, false) })) logging_data_access = optional(map(object({ ADMIN_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_READ = optional(object({ exempted_members = optional(list(string)) })), DATA_WRITE = optional(object({ exempted_members = optional(list(string)) })) })), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
| [data_merges](variables.tf#L108) | Optional values that will be merged with corresponding data from files. Combines with `data_defaults`, file data, and `data_overrides`. | <code title="object({ contacts = optional(map(list(string)), {}) labels = optional(map(string), {}) metric_scopes = optional(list(string), []) service_encryption_key_ids = optional(map(list(string)), {}) services = optional(list(string), []) tag_bindings = optional(map(string), {}) service_accounts = optional(map(object({ display_name = optional(string, "Terraform-managed.") iam_self_roles = optional(list(string)) })), {}) })">object({…})</code> | | <code>{}</code> |
|
||||
|
||||
@@ -27,7 +27,9 @@ locals {
|
||||
}
|
||||
_projects_input = {
|
||||
for k, v in merge(local._folder_projects_raw, local._projects_raw) :
|
||||
basename(k) => v
|
||||
basename(k) => merge(
|
||||
try(local._templates_raw[v.project_template], {}), v
|
||||
)
|
||||
}
|
||||
_projects_path = try(
|
||||
pathexpand(var.factories_config.projects), null
|
||||
@@ -36,6 +38,13 @@ locals {
|
||||
for f in try(fileset(local._projects_path, "**/*.yaml"), []) :
|
||||
trimsuffix(f, ".yaml") => yamldecode(file("${local._projects_path}/${f}"))
|
||||
}
|
||||
_templates_path = try(
|
||||
pathexpand(var.factories_config.project_templates), null
|
||||
)
|
||||
_templates_raw = {
|
||||
for f in try(fileset(local._templates_path, "**/*.yaml"), []) :
|
||||
trimsuffix(f, ".yaml") => yamldecode(file("${local._templates_path}/${f}"))
|
||||
}
|
||||
ctx_project_ids = merge(local.ctx.project_ids, local.project_ids)
|
||||
project_ids = {
|
||||
for k, v in module.projects : k => v.project_id
|
||||
|
||||
@@ -295,6 +295,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"project_template": {
|
||||
"type": "string"
|
||||
},
|
||||
"service_accounts": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
|
||||
@@ -173,8 +173,9 @@ variable "data_overrides" {
|
||||
variable "factories_config" {
|
||||
description = "Path to folder with YAML resource description data files."
|
||||
type = object({
|
||||
folders = optional(string)
|
||||
projects = optional(string)
|
||||
folders = optional(string)
|
||||
project_templates = optional(string)
|
||||
projects = optional(string)
|
||||
budgets = optional(object({
|
||||
billing_account_id = string
|
||||
data = string
|
||||
|
||||
Reference in New Issue
Block a user