Merge branch 'master' into maunope/static_routes
This commit is contained in:
commit
9a6b6fd202
|
@ -53,12 +53,12 @@ jobs:
|
|||
run: |
|
||||
terraform fmt -recursive -check -diff $GITHUB_WORKSPACE
|
||||
|
||||
- name: Check documentation (fabric)
|
||||
- name: Check documentation
|
||||
id: documentation-fabric
|
||||
run: |
|
||||
python3 tools/check_documentation.py examples modules fast
|
||||
python3 tools/check_documentation.py modules fast blueprints
|
||||
|
||||
- name: Check documentation links (fabric)
|
||||
- name: Check documentation links
|
||||
id: documentation-links-fabric
|
||||
run: |
|
||||
python3 tools/check_links.py .
|
||||
|
|
|
@ -8,6 +8,9 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
### BLUEPRINTS
|
||||
|
||||
- [[#931](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/931)] **incompatible change:** Refactor compute-mig module for Terraform 1.3 ([ludoo](https://github.com/ludoo)) <!-- 2022-11-01 08:39:00+00:00 -->
|
||||
- [[#932](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/932)] feat(project-factory): introduce additive iam bindings to project-fac… ([Malet](https://github.com/Malet)) <!-- 2022-10-31 17:24:25+00:00 -->
|
||||
- [[#925](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/925)] Network dashboard: update main.tf and README following #922 ([brianhmj](https://github.com/brianhmj)) <!-- 2022-10-28 15:49:12+00:00 -->
|
||||
- [[#924](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/924)] Fix formatting for gcloud dataflow job launch command ([aymanfarhat](https://github.com/aymanfarhat)) <!-- 2022-10-27 14:07:25+00:00 -->
|
||||
- [[#921](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/921)] Align documentation, move glb blueprint ([ludoo](https://github.com/ludoo)) <!-- 2022-10-26 12:31:04+00:00 -->
|
||||
- [[#915](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/915)] TFE OIDC with GCP WIF blueprint added ([averbuks](https://github.com/averbuks)) <!-- 2022-10-25 19:06:43+00:00 -->
|
||||
|
@ -51,6 +54,9 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
### FAST
|
||||
|
||||
- [[#935](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/935)] FAST: enable org policy API, fix run.allowedIngress value ([ludoo](https://github.com/ludoo)) <!-- 2022-11-01 08:52:03+00:00 -->
|
||||
- [[#931](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/931)] **incompatible change:** Refactor compute-mig module for Terraform 1.3 ([ludoo](https://github.com/ludoo)) <!-- 2022-11-01 08:39:00+00:00 -->
|
||||
- [[#930](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/930)] **incompatible change:** Update organization/folder/project modules to use new org policies API and tf1.3 optionals ([juliocc](https://github.com/juliocc)) <!-- 2022-10-28 16:21:06+00:00 -->
|
||||
- [[#911](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/911)] FAST: Additional PGA DNS records ([sruffilli](https://github.com/sruffilli)) <!-- 2022-10-25 12:28:29+00:00 -->
|
||||
- [[#903](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/903)] Initial replacement for CI/CD stage ([ludoo](https://github.com/ludoo)) <!-- 2022-10-23 17:52:46+00:00 -->
|
||||
- [[#898](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/898)] Update FAST bootstrap README.md ([juliocc](https://github.com/juliocc)) <!-- 2022-10-19 15:15:36+00:00 -->
|
||||
|
@ -71,6 +77,8 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
### MODULES
|
||||
|
||||
- [[#931](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/931)] **incompatible change:** Refactor compute-mig module for Terraform 1.3 ([ludoo](https://github.com/ludoo)) <!-- 2022-11-01 08:39:00+00:00 -->
|
||||
- [[#930](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/930)] **incompatible change:** Update organization/folder/project modules to use new org policies API and tf1.3 optionals ([juliocc](https://github.com/juliocc)) <!-- 2022-10-28 16:21:06+00:00 -->
|
||||
- [[#926](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/926)] Fix backwards compatibility for vpc subnet descriptions ([ludoo](https://github.com/ludoo)) <!-- 2022-10-28 06:13:04+00:00 -->
|
||||
- [[#927](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/927)] Add support for deployment type and api proxy type for Apigee org ([kmucha555](https://github.com/kmucha555)) <!-- 2022-10-27 19:56:41+00:00 -->
|
||||
- [[#923](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/923)] Fix service account creation error in gke nodepool module ([ludoo](https://github.com/ludoo)) <!-- 2022-10-27 15:12:05+00:00 -->
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
# Terraform end-to-end blueprints for Google Cloud
|
||||
|
||||
This section **[networking blueprints](./networking/)** that implement core patterns or features, **[data solutions blueprints](./data-solutions/)** that demonstrate how to integrate data services in complete scenarios, **[cloud operations blueprints](./cloud-operations/)** that leverage specific products to meet specific operational needs, **[GKE](./gke/)** and **[Serverless](./serverless/)** blueprints, and **[factories](./factories/)** that implement resource factories for the repetitive creation of specific resources.
|
||||
This section provides **[networking blueprints](./networking/)** that implement core patterns or features, **[data solutions blueprints](./data-solutions/)** that demonstrate how to integrate data services in complete scenarios, **[cloud operations blueprints](./cloud-operations/)** that leverage specific products to meet specific operational needs, **[GKE](./gke/)** and **[Serverless](./serverless/)** blueprints, and **[factories](./factories/)** that implement resource factories for the repetitive creation of specific resources.
|
||||
|
||||
Currently available blueprints:
|
||||
|
||||
- **cloud operations** - [Active Directory Federation Services](./cloud-operations/adfs), [Cloud Asset Inventory feeds for resource change tracking and remediation](./cloud-operations/asset-inventory-feed-remediation), [Fine-grained Cloud DNS IAM via Service Directory](./cloud-operations/dns-fine-grained-iam), [Cloud DNS & Shared VPC design](./cloud-operations/dns-shared-vpc), [Delegated Role Grants](./cloud-operations/iam-delegated-role-grants), [Networking Dashboard](./cloud-operations/network-dashboard), [Managing on-prem service account keys by uploading public keys](./cloud-operations/onprem-sa-key-management), [Compute Image builder with Hashicorp Packer](./cloud-operations/packer-image-builder), [Packer example](./cloud-operations/packer-image-builder/packer), [Compute Engine quota monitoring](./cloud-operations/quota-monitoring), [Scheduled Cloud Asset Inventory Export to Bigquery](./cloud-operations/scheduled-asset-inventory-export-bq), [Configuring workload identity federation for Terraform Cloud/Enterprise workflow](./cloud-operations/terraform-enterprise-wif), [TCP healthcheck and restart for unmanaged GCE instances](./cloud-operations/unmanaged-instances-healthcheck), [Migrate for Compute Engine (v5) blueprints](./cloud-operations/vm-migration), [Configuring workload identity federation to access Google Cloud resources from apps running on Azure](./cloud-operations/workload-identity-federation)
|
||||
- **data solutions** - [GCE and GCS CMEK via centralized Cloud KMS](./data-solutions/cmek-via-centralized-kms), [Cloud Composer version 2 private instance, supporting Shared VPC and external CMEK key](./data-solutions/composer-2), [Cloud SQL instance with multi-region read replicas](./data-solutions/cloudsql-multiregion), [Data Platform](./data-solutions/data-platform-foundations), [Spinning up a foundation data pipeline on Google Cloud using Cloud Storage, Dataflow and BigQuery](./data-solutions/gcs-to-bq-with-least-privileges), [#SQL Server Always On Groups blueprint](./data-solutions/sqlserver-alwayson), [Data Playground](./data-solutions/data-playground)
|
||||
- **factories** - [[The why and the how of Resource Factories](./factories), [Google Cloud Identity Group Factory](./factories/cloud-identity-group-factory), [Google Cloud BQ Factory](./factories/bigquery-factory), [Google Cloud VPC Firewall Factory](./factories/net-vpc-firewall-yaml), [Minimal Project Factory](./factories/project-factory)
|
||||
- **factories** - [The why and the how of Resource Factories](./factories), [Google Cloud Identity Group Factory](./factories/cloud-identity-group-factory), [Google Cloud BQ Factory](./factories/bigquery-factory), [Google Cloud VPC Firewall Factory](./factories/net-vpc-firewall-yaml), [Minimal Project Factory](./factories/project-factory)
|
||||
- **GKE** - [Binary Authorization Pipeline Blueprint](./gke/binauthz), [Storage API](./gke/binauthz/image), [Multi-cluster mesh on GKE (fleet API)](./gke/multi-cluster-mesh-gke-fleet-api), [GKE Multitenant Blueprint](./gke/multitenant-fleet), [Shared VPC with GKE support](./networking/shared-vpc-gke/)
|
||||
- **networking** - [Decentralized firewall management](./networking/decentralized-firewall), [Decentralized firewall validator](./networking/decentralized-firewall/validator), [Network filtering with Squid](./networking/filtering-proxy), [HTTP Load Balancer with Cloud Armor](./networking/glb-and-armor), [Hub and Spoke via VPN](./networking/hub-and-spoke-vpn), [Hub and Spoke via VPC Peering](./networking/hub-and-spoke-peering), [Internal Load Balancer as Next Hop](./networking/ilb-next-hop), [Nginx-based reverse proxy cluster](./networking/nginx-reverse-proxy-cluster), [On-prem DNS and Google Private Access](./networking/onprem-google-access-dns), [Calling a private Cloud Function from On-premises](./networking/private-cloud-function-from-onprem), [Hybrid connectivity to on-premise services through PSC](./networking/psc-hybrid), [PSC Producer](./networking/psc-hybrid/psc-producer), [PSC Consumer](./networking/psc-hybrid/psc-consumer), [Shared VPC with optional GKE cluster](./networking/shared-vpc-gke)
|
||||
- **serverless** - [Creating multi-region deployments for API Gateway](./serverless/api-gateway)
|
||||
|
|
|
@ -27,6 +27,8 @@ Clone this repository, then go through the following steps to create resources:
|
|||
- `terraform init`
|
||||
- `terraform apply`
|
||||
|
||||
Note: Org level viewing permission is required for some metrics such as firewall policies.
|
||||
|
||||
Once the resources are deployed, go to the following page to see the dashboard: https://console.cloud.google.com/monitoring/dashboards?project=<YOUR-MONITORING-PROJECT>.
|
||||
A dashboard called "quotas-utilization" should be created.
|
||||
|
||||
|
@ -75,6 +77,7 @@ In a future release, we could support:
|
|||
- Dynamic routes calculation for VPCs/PPGs with "global routing" set to OFF
|
||||
- Static routes calculation for projects/PPGs with "custom routes importing/exporting" set to OFF
|
||||
- Calculations for cross Organization peering groups
|
||||
- Support different scopes (reduced and fine-grained)
|
||||
|
||||
If you are interested in this and/or would like to contribute, please contact legranda@google.com.
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
|
|
@ -50,7 +50,6 @@ module "service-account-function" {
|
|||
# Required IAM permissions for this service account are:
|
||||
# 1) compute.networkViewer on projects to be monitored (I gave it at organization level for now for simplicity)
|
||||
# 2) monitoring viewer on the projects to be monitored (I gave it at organization level for now for simplicity)
|
||||
# 3) if you dont have permission to create service account and assign permission at organization Level, move these 3 roles to project level.
|
||||
|
||||
iam_organization_roles = {
|
||||
"${var.organization_id}" = [
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# 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
|
||||
#
|
||||
# https://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 {
|
||||
required_version = ">= 1.3.1"
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 4.40.0" # tftest
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = ">= 4.40.0" # tftest
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,22 +12,22 @@ The codebase provisions the following list of resources:
|
|||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [billing_account](variables.tf#L16) | Billing account id used as default for new projects. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L38) | Existing project id. | <code>string</code> | ✓ | |
|
||||
| [tfe_organization_id](variables.tf#L43) | | <code></code> | ✓ | |
|
||||
| [tfe_workspace_id](variables.tf#L48) | | <code></code> | ✓ | |
|
||||
| [issuer_uri](variables.tf#L65) | Terraform Enterprise uri. Replace the uri if a self hosted instance is used. | <code>string</code> | | <code>"https://app.terraform.io/"</code> |
|
||||
| [project_id](variables.tf#L43) | Existing project id. | <code>string</code> | ✓ | |
|
||||
| [tfe_organization_id](variables.tf#L48) | TFE organization id. | <code>string</code> | ✓ | |
|
||||
| [tfe_workspace_id](variables.tf#L53) | TFE workspace id. | <code>string</code> | ✓ | |
|
||||
| [issuer_uri](variables.tf#L21) | Terraform Enterprise uri. Replace the uri if a self hosted instance is used. | <code>string</code> | | <code>"https://app.terraform.io/"</code> |
|
||||
| [parent](variables.tf#L27) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | <code>string</code> | | <code>null</code> |
|
||||
| [project_create](variables.tf#L21) | Create project instead of using an existing one. | <code>bool</code> | | <code>true</code> |
|
||||
| [workload_identity_pool_id](variables.tf#L53) | Workload identity pool id. | <code>string</code> | | <code>"tfe-pool"</code> |
|
||||
| [workload_identity_pool_provider_id](variables.tf#L59) | Workload identity pool provider id. | <code>string</code> | | <code>"tfe-provider"</code> |
|
||||
| [project_create](variables.tf#L37) | Create project instead of using an existing one. | <code>bool</code> | | <code>true</code> |
|
||||
| [workload_identity_pool_id](variables.tf#L58) | Workload identity pool id. | <code>string</code> | | <code>"tfe-pool"</code> |
|
||||
| [workload_identity_pool_provider_id](variables.tf#L64) | Workload identity pool provider id. | <code>string</code> | | <code>"tfe-provider"</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [impersonate_service_account_email](outputs.tf#L31) | | |
|
||||
| [project_id](outputs.tf#L16) | | |
|
||||
| [workload_identity_audience](outputs.tf#L26) | | |
|
||||
| [workload_identity_pool_provider_id](outputs.tf#L21) | GCP workload identity pool provider ID. | |
|
||||
| [impersonate_service_account_email](outputs.tf#L16) | Service account to be impersonated by workload identity. | |
|
||||
| [project_id](outputs.tf#L21) | GCP Project ID. | |
|
||||
| [workload_identity_audience](outputs.tf#L26) | TFC Workload Identity Audience. | |
|
||||
| [workload_identity_pool_provider_id](outputs.tf#L31) | GCP workload identity pool provider ID. | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
|
|
@ -13,22 +13,22 @@
|
|||
# limitations under the License.
|
||||
|
||||
|
||||
output "impersonate_service_account_email" {
|
||||
description = "Service account to be impersonated by workload identity."
|
||||
value = module.sa-tfe.email
|
||||
}
|
||||
|
||||
output "project_id" {
|
||||
description = "GCP Project ID."
|
||||
value = module.project.project_id
|
||||
}
|
||||
|
||||
output "workload_identity_pool_provider_id" {
|
||||
description = "GCP workload identity pool provider ID."
|
||||
value = google_iam_workload_identity_pool_provider.tfe-pool-provider.name
|
||||
}
|
||||
|
||||
output "workload_identity_audience" {
|
||||
description = "TFC Workload Identity Audience."
|
||||
value = "//iam.googleapis.com/${google_iam_workload_identity_pool_provider.tfe-pool-provider.name}"
|
||||
}
|
||||
|
||||
output "impersonate_service_account_email" {
|
||||
description = "Service account to be impersonated by workload identity."
|
||||
value = module.sa-tfe.email
|
||||
output "workload_identity_pool_provider_id" {
|
||||
description = "GCP workload identity pool provider ID."
|
||||
value = google_iam_workload_identity_pool_provider.tfe-pool-provider.name
|
||||
}
|
||||
|
|
|
@ -18,10 +18,10 @@ variable "billing_account" {
|
|||
type = string
|
||||
}
|
||||
|
||||
variable "project_create" {
|
||||
description = "Create project instead of using an existing one."
|
||||
type = bool
|
||||
default = true
|
||||
variable "issuer_uri" {
|
||||
description = "Terraform Enterprise uri. Replace the uri if a self hosted instance is used."
|
||||
type = string
|
||||
default = "https://app.terraform.io/"
|
||||
}
|
||||
|
||||
variable "parent" {
|
||||
|
@ -34,6 +34,11 @@ variable "parent" {
|
|||
}
|
||||
}
|
||||
|
||||
variable "project_create" {
|
||||
description = "Create project instead of using an existing one."
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
description = "Existing project id."
|
||||
|
@ -61,9 +66,3 @@ variable "workload_identity_pool_provider_id" {
|
|||
type = string
|
||||
default = "tfe-provider"
|
||||
}
|
||||
|
||||
variable "issuer_uri" {
|
||||
description = "Terraform Enterprise uri. Replace the uri if a self hosted instance is used."
|
||||
type = string
|
||||
default = "https://app.terraform.io/"
|
||||
}
|
||||
|
|
|
@ -5,15 +5,14 @@ This terraform code is a part of [GCP Workload Identity Federation for Terraform
|
|||
The codebase provisions the following list of resources:
|
||||
|
||||
- GCS Bucket
|
||||
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [impersonate_service_account_email](variables.tf#L26) | | <code></code> | ✓ | |
|
||||
| [project_id](variables.tf#L16) | | <code></code> | ✓ | |
|
||||
| [workload_identity_pool_provider_id](variables.tf#L21) | GCP workload identity pool provider ID. | <code>string</code> | ✓ | |
|
||||
| [impersonate_service_account_email](variables.tf#L21) | Service account to be impersonated by workload identity. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L16) | GCP project ID. | <code>string</code> | ✓ | |
|
||||
| [workload_identity_pool_provider_id](variables.tf#L26) | GCP workload identity pool provider ID. | <code>string</code> | ✓ | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
|
|
@ -18,12 +18,12 @@ variable "project_id" {
|
|||
type = string
|
||||
}
|
||||
|
||||
variable "workload_identity_pool_provider_id" {
|
||||
description = "GCP workload identity pool provider ID."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "impersonate_service_account_email" {
|
||||
description = "Service account to be impersonated by workload identity."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "workload_identity_pool_provider_id" {
|
||||
description = "GCP workload identity pool provider ID."
|
||||
type = string
|
||||
}
|
||||
|
|
|
@ -67,8 +67,10 @@ module "orch-project" {
|
|||
"roles/storage.objectViewer" = [module.load-sa-df-0.iam_email]
|
||||
}
|
||||
oslogin = false
|
||||
policy_boolean = {
|
||||
"constraints/compute.requireOsLogin" = false
|
||||
org_policies = {
|
||||
"constraints/compute.requireOsLogin" = {
|
||||
enforce = false
|
||||
}
|
||||
}
|
||||
services = concat(var.project_services, [
|
||||
"artifactregistry.googleapis.com",
|
||||
|
@ -82,6 +84,7 @@ module "orch-project" {
|
|||
"container.googleapis.com",
|
||||
"containerregistry.googleapis.com",
|
||||
"dataflow.googleapis.com",
|
||||
"orgpolicy.googleapis.com",
|
||||
"pubsub.googleapis.com",
|
||||
"servicenetworking.googleapis.com",
|
||||
"storage.googleapis.com",
|
||||
|
|
|
@ -160,9 +160,10 @@ You can find more details and best practices on using DLP to De-identification a
|
|||
[Data Catalog](https://cloud.google.com/data-catalog) helps you to document your data entry at scale. Data Catalog relies on [tags](https://cloud.google.com/data-catalog/docs/tags-and-tag-templates#tags) and [tag template](https://cloud.google.com/data-catalog/docs/tags-and-tag-templates#tag-templates) to manage metadata for all data entries in a unified and centralized service. To implement [column-level security](https://cloud.google.com/bigquery/docs/column-level-security-intro) on BigQuery, we suggest to use `Tags` and `Tag templates`.
|
||||
|
||||
The default configuration will implement 3 tags:
|
||||
- `3_Confidential`: policy tag for columns that include very sensitive information, such as credit card numbers.
|
||||
- `2_Private`: policy tag for columns that include sensitive personal identifiable information (PII) information, such as a person's first name.
|
||||
- `1_Sensitive`: policy tag for columns that include data that cannot be made public, such as the credit limit.
|
||||
|
||||
- `3_Confidential`: policy tag for columns that include very sensitive information, such as credit card numbers.
|
||||
- `2_Private`: policy tag for columns that include sensitive personal identifiable information (PII) information, such as a person's first name.
|
||||
- `1_Sensitive`: policy tag for columns that include data that cannot be made public, such as the credit limit.
|
||||
|
||||
Anything that is not tagged is available to all users who have access to the data warehouse.
|
||||
|
||||
|
@ -222,7 +223,7 @@ module "data-platform" {
|
|||
prefix = "myprefix"
|
||||
}
|
||||
|
||||
# tftest modules=42 resources=315
|
||||
# tftest modules=42 resources=316
|
||||
```
|
||||
|
||||
## Customizations
|
||||
|
|
|
@ -35,13 +35,16 @@ module "project" {
|
|||
"dataflow.googleapis.com",
|
||||
"ml.googleapis.com",
|
||||
"notebooks.googleapis.com",
|
||||
"orgpolicy.googleapis.com",
|
||||
"servicenetworking.googleapis.com",
|
||||
"stackdriver.googleapis.com",
|
||||
"storage.googleapis.com",
|
||||
"storage-component.googleapis.com"
|
||||
]
|
||||
policy_boolean = {
|
||||
# "constraints/compute.requireOsLogin" = false
|
||||
org_policies = {
|
||||
# "constraints/compute.requireOsLogin" = {
|
||||
# enforce = false
|
||||
# }
|
||||
# Example of applying a project wide policy, mainly useful for Composer
|
||||
}
|
||||
service_encryption_key_ids = {
|
||||
|
|
|
@ -68,13 +68,13 @@ module "projects" {
|
|||
iam = try(each.value.iam, {})
|
||||
kms_service_agents = try(each.value.kms, {})
|
||||
labels = try(each.value.labels, {})
|
||||
org_policies = try(each.value.org_policies, null)
|
||||
org_policies = try(each.value.org_policies, {})
|
||||
service_accounts = try(each.value.service_accounts, {})
|
||||
services = try(each.value.services, [])
|
||||
service_identities_iam = try(each.value.service_identities_iam, {})
|
||||
vpc = try(each.value.vpc, null)
|
||||
}
|
||||
# tftest modules=7 resources=27
|
||||
# tftest modules=7 resources=29
|
||||
```
|
||||
|
||||
### Projects configuration
|
||||
|
@ -154,15 +154,15 @@ labels:
|
|||
|
||||
# [opt] Org policy overrides defined at project level
|
||||
org_policies:
|
||||
policy_boolean:
|
||||
constraints/compute.disableGuestAttributesAccess: true
|
||||
policy_list:
|
||||
constraints/compute.trustedImageProjects:
|
||||
inherit_from_parent: null
|
||||
status: true
|
||||
suggested_value: null
|
||||
constraints/compute.disableGuestAttributesAccess:
|
||||
enforce: true
|
||||
constraints/compute.trustedImageProjects:
|
||||
allow:
|
||||
values:
|
||||
- projects/fast-prod-iac-core-0
|
||||
- projects/fast-dev-iac-core-0
|
||||
constraints/compute.vmExternalIpAccess:
|
||||
deny:
|
||||
all: true
|
||||
|
||||
# [opt] Service account to create for the project and their roles on the project
|
||||
# in name => [roles] format
|
||||
|
@ -221,23 +221,28 @@ vpc:
|
|||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [billing_account_id](variables.tf#L17) | Billing account id. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L119) | Project id. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L157) | Project id. | <code>string</code> | ✓ | |
|
||||
| [billing_alert](variables.tf#L22) | Billing alert configuration. | <code title="object({ amount = number thresholds = object({ current = list(number) forecasted = list(number) }) credit_treatment = string })">object({…})</code> | | <code>null</code> |
|
||||
| [defaults](variables.tf#L35) | Project factory default values. | <code title="object({ billing_account_id = string billing_alert = object({ amount = number thresholds = object({ current = list(number) forecasted = list(number) }) credit_treatment = string }) environment_dns_zone = string essential_contacts = list(string) labels = map(string) notification_channels = list(string) shared_vpc_self_link = string vpc_host_project = string })">object({…})</code> | | <code>null</code> |
|
||||
| [dns_zones](variables.tf#L57) | DNS private zones to create as child of var.defaults.environment_dns_zone. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [essential_contacts](variables.tf#L63) | Email contacts to be used for billing and GCP notifications. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [folder_id](variables.tf#L69) | Folder ID for the folder where the project will be created. | <code>string</code> | | <code>null</code> |
|
||||
| [group_iam](variables.tf#L75) | Custom IAM settings in group => [role] format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam](variables.tf#L81) | Custom IAM settings in role => [principal] format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [kms_service_agents](variables.tf#L87) | KMS IAM configuration in as service => [key]. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [labels](variables.tf#L93) | Labels to be assigned at project level. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [org_policies](variables.tf#L99) | Org-policy overrides at project level. | <code title="object({ policy_boolean = map(bool) policy_list = map(object({ inherit_from_parent = bool suggested_value = string status = bool values = list(string) })) })">object({…})</code> | | <code>null</code> |
|
||||
| [prefix](variables.tf#L113) | Prefix used for the project id. | <code>string</code> | | <code>null</code> |
|
||||
| [service_accounts](variables.tf#L124) | Service accounts to be created, and roles assigned them on the project. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [service_accounts_iam](variables.tf#L130) | IAM bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]} | <code>map(map(list(string)))</code> | | <code>{}</code> |
|
||||
| [service_identities_iam](variables.tf#L144) | Custom IAM settings for service identities in service => [role] format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [services](variables.tf#L137) | Services to be enabled for the project. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [vpc](variables.tf#L151) | VPC configuration for the project. | <code title="object({ host_project = string gke_setup = object({ enable_security_admin = bool enable_host_service_agent = bool }) subnets_iam = map(list(string)) })">object({…})</code> | | <code>null</code> |
|
||||
| [group_iam_additive](variables.tf#L81) | Custom additive IAM settings in group => [role] format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam](variables.tf#L87) | Custom IAM settings in role => [principal] format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_additive](variables.tf#L93) | Custom additive IAM settings in role => [principal] format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [kms_service_agents](variables.tf#L99) | KMS IAM configuration in as service => [key]. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [labels](variables.tf#L105) | Labels to be assigned at project level. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [org_policies](variables.tf#L111) | Org-policy overrides at project level. | <code title="map(object({ inherit_from_parent = optional(bool) # for list policies only. reset = optional(bool) allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool, true) # for boolean policies only. rules = optional(list(object({ allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool, true) # for boolean policies only. condition = object({ description = optional(string) expression = optional(string) location = optional(string) title = optional(string) }) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [prefix](variables.tf#L151) | Prefix used for the project id. | <code>string</code> | | <code>null</code> |
|
||||
| [service_accounts](variables.tf#L162) | Service accounts to be created, and roles assigned them on the project. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [service_accounts_additive](variables.tf#L168) | Service accounts to be created, and roles assigned them on the project additively. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [service_accounts_iam](variables.tf#L174) | IAM bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]} | <code>map(map(list(string)))</code> | | <code>{}</code> |
|
||||
| [service_accounts_iam_additive](variables.tf#L181) | IAM additive bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]} | <code>map(map(list(string)))</code> | | <code>{}</code> |
|
||||
| [service_identities_iam](variables.tf#L195) | Custom IAM settings for service identities in service => [role] format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [service_identities_iam_additive](variables.tf#L202) | Custom additive IAM settings for service identities in service => [role] format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [services](variables.tf#L188) | Services to be enabled for the project. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [vpc](variables.tf#L209) | VPC configuration for the project. | <code title="object({ host_project = string gke_setup = object({ enable_security_admin = bool enable_host_service_agent = bool }) subnets_iam = map(list(string)) })">object({…})</code> | | <code>null</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
|
@ -21,7 +21,14 @@ locals {
|
|||
"group:${k}" if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
_group_iam_bindings = distinct(flatten(values(var.group_iam)))
|
||||
_group_iam_additive = {
|
||||
for r in local._group_iam_additive_bindings : r => [
|
||||
for k, v in var.group_iam_additive :
|
||||
"group:${k}" if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
_group_iam_bindings = distinct(flatten(values(var.group_iam)))
|
||||
_group_iam_additive_bindings = distinct(flatten(values(var.group_iam_additive)))
|
||||
_project_id = (
|
||||
var.prefix == null || var.prefix == ""
|
||||
? var.project_id
|
||||
|
@ -37,9 +44,20 @@ locals {
|
|||
_service_accounts_iam_bindings = distinct(flatten(
|
||||
values(var.service_accounts)
|
||||
))
|
||||
_service_accounts_iam_additive = {
|
||||
for r in local._service_accounts_iam_additive_bindings : r => [
|
||||
for k, v in var.service_accounts_additive :
|
||||
module.service-accounts[k].iam_email
|
||||
if try(index(v, r), null) != null
|
||||
]
|
||||
}
|
||||
_service_accounts_iam_additive_bindings = distinct(flatten(
|
||||
values(var.service_accounts_additive)
|
||||
))
|
||||
_services = concat([
|
||||
"billingbudgets.googleapis.com",
|
||||
"essentialcontacts.googleapis.com"
|
||||
"essentialcontacts.googleapis.com",
|
||||
"orgpolicy.googleapis.com",
|
||||
],
|
||||
length(var.dns_zones) > 0 ? ["dns.googleapis.com"] : [],
|
||||
try(var.vpc.gke_setup, null) != null ? ["container.googleapis.com"] : [],
|
||||
|
@ -53,6 +71,14 @@ locals {
|
|||
if contains(roles, role)
|
||||
]
|
||||
}
|
||||
_service_identities_roles_additive = distinct(flatten(values(var.service_identities_iam_additive)))
|
||||
_service_identities_iam_additive = {
|
||||
for role in local._service_identities_roles_additive : role => [
|
||||
for service, roles in var.service_identities_iam_additive :
|
||||
"serviceAccount:${module.project.service_accounts.robots[service]}"
|
||||
if contains(roles, role)
|
||||
]
|
||||
}
|
||||
_vpc_subnet_bindings = (
|
||||
local.vpc.subnets_iam == null || local.vpc.host_project == null
|
||||
? []
|
||||
|
@ -91,6 +117,20 @@ locals {
|
|||
try(local._service_identities_iam[role], []),
|
||||
)
|
||||
}
|
||||
iam_additive = {
|
||||
for role in distinct(concat(
|
||||
keys(var.iam_additive),
|
||||
keys(local._group_iam_additive),
|
||||
keys(local._service_accounts_iam_additive),
|
||||
keys(local._service_identities_iam_additive),
|
||||
)) :
|
||||
role => concat(
|
||||
try(var.iam_additive[role], []),
|
||||
try(local._group_iam_additive[role], []),
|
||||
try(local._service_accounts_iam_additive[role], []),
|
||||
try(local._service_identities_iam_additive[role], []),
|
||||
)
|
||||
}
|
||||
labels = merge(
|
||||
coalesce(var.labels, {}), coalesce(try(var.defaults.labels, {}), {})
|
||||
)
|
||||
|
@ -147,10 +187,10 @@ module "project" {
|
|||
prefix = var.prefix
|
||||
contacts = { for c in local.essential_contacts : c => ["ALL"] }
|
||||
iam = local.iam
|
||||
iam_additive = local.iam_additive
|
||||
labels = local.labels
|
||||
org_policies = try(var.org_policies, {})
|
||||
parent = var.folder_id
|
||||
policy_boolean = try(var.org_policies.policy_boolean, {})
|
||||
policy_list = try(var.org_policies.policy_list, {})
|
||||
service_encryption_key_ids = var.kms_service_agents
|
||||
services = local.services
|
||||
shared_vpc_service_config = var.vpc == null ? null : {
|
||||
|
|
|
@ -48,15 +48,15 @@ labels:
|
|||
|
||||
# [opt] Org policy overrides defined at project level
|
||||
org_policies:
|
||||
policy_boolean:
|
||||
constraints/compute.disableGuestAttributesAccess: true
|
||||
policy_list:
|
||||
constraints/compute.trustedImageProjects:
|
||||
inherit_from_parent: null
|
||||
status: true
|
||||
suggested_value: null
|
||||
constraints/compute.disableGuestAttributesAccess:
|
||||
enforce: true
|
||||
constraints/compute.trustedImageProjects:
|
||||
allow:
|
||||
values:
|
||||
- projects/fast-dev-iac-core-0
|
||||
constraints/compute.vmExternalIpAccess:
|
||||
deny:
|
||||
all: true
|
||||
|
||||
# [opt] Service account to create for the project and their roles on the project
|
||||
# in name => [roles] format
|
||||
|
|
|
@ -78,12 +78,24 @@ variable "group_iam" {
|
|||
default = {}
|
||||
}
|
||||
|
||||
variable "group_iam_additive" {
|
||||
description = "Custom additive IAM settings in group => [role] format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam" {
|
||||
description = "Custom IAM settings in role => [principal] format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "iam_additive" {
|
||||
description = "Custom additive IAM settings in role => [principal] format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "kms_service_agents" {
|
||||
description = "KMS IAM configuration in as service => [key]."
|
||||
type = map(list(string))
|
||||
|
@ -98,16 +110,42 @@ variable "labels" {
|
|||
|
||||
variable "org_policies" {
|
||||
description = "Org-policy overrides at project level."
|
||||
type = object({
|
||||
policy_boolean = map(bool)
|
||||
policy_list = map(object({
|
||||
inherit_from_parent = bool
|
||||
suggested_value = string
|
||||
status = bool
|
||||
values = list(string)
|
||||
type = map(object({
|
||||
inherit_from_parent = optional(bool) # for list policies only.
|
||||
reset = optional(bool)
|
||||
|
||||
# default (unconditional) values
|
||||
allow = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
})
|
||||
default = null
|
||||
deny = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
enforce = optional(bool, true) # for boolean policies only.
|
||||
|
||||
# conditional values
|
||||
rules = optional(list(object({
|
||||
allow = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
deny = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
enforce = optional(bool, true) # for boolean policies only.
|
||||
condition = object({
|
||||
description = optional(string)
|
||||
expression = optional(string)
|
||||
location = optional(string)
|
||||
title = optional(string)
|
||||
})
|
||||
})), [])
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "prefix" {
|
||||
|
@ -127,6 +165,12 @@ variable "service_accounts" {
|
|||
default = {}
|
||||
}
|
||||
|
||||
variable "service_accounts_additive" {
|
||||
description = "Service accounts to be created, and roles assigned them on the project additively."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "service_accounts_iam" {
|
||||
description = "IAM bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]}"
|
||||
type = map(map(list(string)))
|
||||
|
@ -134,6 +178,13 @@ variable "service_accounts_iam" {
|
|||
nullable = false
|
||||
}
|
||||
|
||||
variable "service_accounts_iam_additive" {
|
||||
description = "IAM additive bindings on service account resources. Format is KEY => {ROLE => [MEMBERS]}"
|
||||
type = map(map(list(string)))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "services" {
|
||||
description = "Services to be enabled for the project."
|
||||
type = list(string)
|
||||
|
@ -148,6 +199,13 @@ variable "service_identities_iam" {
|
|||
nullable = false
|
||||
}
|
||||
|
||||
variable "service_identities_iam_additive" {
|
||||
description = "Custom additive IAM settings for service identities in service => [role] format."
|
||||
type = map(list(string))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "vpc" {
|
||||
description = "VPC configuration for the project."
|
||||
type = object({
|
||||
|
@ -160,6 +218,3 @@ variable "vpc" {
|
|||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -165,33 +165,31 @@ module "squid-vm" {
|
|||
}
|
||||
|
||||
module "squid-mig" {
|
||||
count = var.mig ? 1 : 0
|
||||
source = "../../../modules/compute-mig"
|
||||
project_id = module.project-host.project_id
|
||||
location = "${var.region}-b"
|
||||
name = "squid-mig"
|
||||
target_size = 1
|
||||
autoscaler_config = {
|
||||
max_replicas = 10
|
||||
min_replicas = 1
|
||||
cooldown_period = 30
|
||||
cpu_utilization_target = 0.65
|
||||
load_balancing_utilization_target = null
|
||||
metric = null
|
||||
count = var.mig ? 1 : 0
|
||||
source = "../../../modules/compute-mig"
|
||||
project_id = module.project-host.project_id
|
||||
location = "${var.region}-b"
|
||||
name = "squid-mig"
|
||||
instance_template = module.squid-vm.template.self_link
|
||||
target_size = 1
|
||||
auto_healing_policies = {
|
||||
initial_delay_sec = 60
|
||||
}
|
||||
default_version = {
|
||||
instance_template = module.squid-vm.template.self_link
|
||||
name = "default"
|
||||
autoscaler_config = {
|
||||
max_replicas = 10
|
||||
min_replicas = 1
|
||||
cooldown_period = 30
|
||||
scaling_signals = {
|
||||
cpu_utilization = {
|
||||
target = 0.65
|
||||
}
|
||||
}
|
||||
}
|
||||
health_check_config = {
|
||||
type = "tcp"
|
||||
check = { port = 3128 }
|
||||
config = {}
|
||||
logging = true
|
||||
}
|
||||
auto_healing_policies = {
|
||||
health_check = module.squid-mig.0.health_check.self_link
|
||||
initial_delay_sec = 60
|
||||
enable_logging = true
|
||||
tcp = {
|
||||
port = 3128
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,13 +224,10 @@ module "folder-apps" {
|
|||
source = "../../../modules/folder"
|
||||
parent = var.root_node
|
||||
name = "apps"
|
||||
policy_list = {
|
||||
org_policies = {
|
||||
# prevent VMs with public IPs in the apps folder
|
||||
"constraints/compute.vmExternalIpAccess" = {
|
||||
inherit_from_parent = false
|
||||
suggested_value = null
|
||||
status = false
|
||||
values = []
|
||||
deny = { all = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,22 +153,20 @@ module "vm_siege" {
|
|||
}
|
||||
|
||||
module "mig_ew1" {
|
||||
source = "../../../modules/compute-mig"
|
||||
project_id = module.project.project_id
|
||||
location = "europe-west1"
|
||||
name = "${local.prefix}europe-west1-mig"
|
||||
regional = true
|
||||
default_version = {
|
||||
instance_template = module.instance_template_ew1.template.self_link
|
||||
name = "default"
|
||||
}
|
||||
source = "../../../modules/compute-mig"
|
||||
project_id = module.project.project_id
|
||||
location = "europe-west1"
|
||||
name = "${local.prefix}europe-west1-mig"
|
||||
instance_template = module.instance_template_ew1.template.self_link
|
||||
autoscaler_config = {
|
||||
max_replicas = 5
|
||||
min_replicas = 1
|
||||
cooldown_period = 45
|
||||
cpu_utilization_target = 0.8
|
||||
load_balancing_utilization_target = null
|
||||
metric = null
|
||||
max_replicas = 5
|
||||
min_replicas = 1
|
||||
cooldown_period = 45
|
||||
scaling_signals = {
|
||||
cpu_utilization = {
|
||||
target = 0.65
|
||||
}
|
||||
}
|
||||
}
|
||||
named_ports = {
|
||||
http = 80
|
||||
|
@ -179,22 +177,20 @@ module "mig_ew1" {
|
|||
}
|
||||
|
||||
module "mig_ue1" {
|
||||
source = "../../../modules/compute-mig"
|
||||
project_id = module.project.project_id
|
||||
location = "us-east1"
|
||||
name = "${local.prefix}us-east1-mig"
|
||||
regional = true
|
||||
default_version = {
|
||||
instance_template = module.instance_template_ue1.template.self_link
|
||||
name = "default"
|
||||
}
|
||||
source = "../../../modules/compute-mig"
|
||||
project_id = module.project.project_id
|
||||
location = "us-east1"
|
||||
name = "${local.prefix}us-east1-mig"
|
||||
instance_template = module.instance_template_ue1.template.self_link
|
||||
autoscaler_config = {
|
||||
max_replicas = 5
|
||||
min_replicas = 1
|
||||
cooldown_period = 45
|
||||
cpu_utilization_target = 0.8
|
||||
load_balancing_utilization_target = null
|
||||
metric = null
|
||||
max_replicas = 5
|
||||
min_replicas = 1
|
||||
cooldown_period = 45
|
||||
scaling_signals = {
|
||||
cpu_utilization = {
|
||||
target = 0.65
|
||||
}
|
||||
}
|
||||
}
|
||||
named_ports = {
|
||||
http = 80
|
||||
|
|
|
@ -72,6 +72,7 @@ module "automation-project" {
|
|||
"essentialcontacts.googleapis.com",
|
||||
"iam.googleapis.com",
|
||||
"iamcredentials.googleapis.com",
|
||||
"orgpolicy.googleapis.com",
|
||||
"pubsub.googleapis.com",
|
||||
"servicenetworking.googleapis.com",
|
||||
"serviceusage.googleapis.com",
|
||||
|
|
|
@ -32,16 +32,9 @@ module "branch-sandbox-folder" {
|
|||
"roles/resourcemanager.folderAdmin" = [module.branch-sandbox-sa.0.iam_email]
|
||||
"roles/resourcemanager.projectCreator" = [module.branch-sandbox-sa.0.iam_email]
|
||||
}
|
||||
policy_boolean = {
|
||||
"constraints/sql.restrictPublicIp" = false
|
||||
}
|
||||
policy_list = {
|
||||
"constraints/compute.vmExternalIpAccess" = {
|
||||
inherit_from_parent = false
|
||||
suggested_value = null
|
||||
status = true
|
||||
values = []
|
||||
}
|
||||
org_policies = {
|
||||
"constraints/sql.restrictPublicIp" = { enforce = false }
|
||||
"constraints/compute.vmExternalIpAccess" = { allow = { all = true } }
|
||||
}
|
||||
tag_bindings = {
|
||||
context = try(
|
||||
|
|
|
@ -18,18 +18,11 @@
|
|||
|
||||
|
||||
locals {
|
||||
list_allow = {
|
||||
inherit_from_parent = false
|
||||
suggested_value = null
|
||||
status = true
|
||||
values = []
|
||||
}
|
||||
list_deny = {
|
||||
inherit_from_parent = false
|
||||
suggested_value = null
|
||||
status = false
|
||||
values = []
|
||||
}
|
||||
all_drs_domains = concat(
|
||||
[var.organization.customer_id],
|
||||
try(local.policy_configs.allowed_policy_member_domains, [])
|
||||
)
|
||||
|
||||
policy_configs = (
|
||||
var.organization_policy_configs == null
|
||||
? {}
|
||||
|
@ -74,74 +67,54 @@ module "organization" {
|
|||
} : {}
|
||||
)
|
||||
# sample subset of useful organization policies, edit to suit requirements
|
||||
policy_boolean = {
|
||||
# "constraints/cloudfunctions.requireVPCConnector" = true
|
||||
# "constraints/compute.disableGuestAttributesAccess" = true
|
||||
# "constraints/compute.disableInternetNetworkEndpointGroup" = true
|
||||
# "constraints/compute.disableNestedVirtualization" = true
|
||||
# "constraints/compute.disableSerialPortAccess" = true
|
||||
"constraints/compute.requireOsLogin" = true
|
||||
# "constraints/compute.restrictXpnProjectLienRemoval" = true
|
||||
"constraints/compute.skipDefaultNetworkCreation" = true
|
||||
# "constraints/compute.setNewProjectDefaultToZonalDNSOnly" = true
|
||||
"constraints/iam.automaticIamGrantsForDefaultServiceAccounts" = true
|
||||
"constraints/iam.disableServiceAccountKeyCreation" = true
|
||||
# "constraints/iam.disableServiceAccountKeyUpload" = true
|
||||
"constraints/sql.restrictPublicIp" = true
|
||||
"constraints/sql.restrictAuthorizedNetworks" = true
|
||||
"constraints/storage.uniformBucketLevelAccess" = true
|
||||
}
|
||||
policy_list = {
|
||||
# "constraints/cloudfunctions.allowedIngressSettings" = merge(
|
||||
# local.list_allow, { values = ["is:ALLOW_INTERNAL_ONLY"] }
|
||||
# )
|
||||
# "constraints/cloudfunctions.allowedVpcConnectorEgressSettings" = merge(
|
||||
# local.list_allow, { values = ["is:PRIVATE_RANGES_ONLY"] }
|
||||
# )
|
||||
"constraints/compute.restrictLoadBalancerCreationForTypes" = merge(
|
||||
local.list_allow, { values = ["in:INTERNAL"] }
|
||||
)
|
||||
"constraints/compute.vmExternalIpAccess" = local.list_deny
|
||||
"constraints/iam.allowedPolicyMemberDomains" = merge(
|
||||
local.list_allow, {
|
||||
values = concat(
|
||||
[var.organization.customer_id],
|
||||
try(local.policy_configs.allowed_policy_member_domains, [])
|
||||
)
|
||||
})
|
||||
"constraints/run.allowedIngress" = merge(
|
||||
local.list_allow, { values = ["is:internal"] }
|
||||
)
|
||||
# "constraints/run.allowedVPCEgress" = merge(
|
||||
# local.list_allow, { values = ["is:private-ranges-only"] }
|
||||
# )
|
||||
# "constraints/compute.restrictCloudNATUsage" = local.list_deny
|
||||
# "constraints/compute.restrictDedicatedInterconnectUsage" = local.list_deny
|
||||
# "constraints/compute.restrictPartnerInterconnectUsage" = local.list_deny
|
||||
# "constraints/compute.restrictProtocolForwardingCreationForTypes" = local.list_deny
|
||||
# "constraints/compute.restrictSharedVpcHostProjects" = local.list_deny
|
||||
# "constraints/compute.restrictSharedVpcSubnetworks" = local.list_deny
|
||||
# "constraints/compute.restrictVpcPeering" = local.list_deny
|
||||
# "constraints/compute.restrictVpnPeerIPs" = local.list_deny
|
||||
# "constraints/compute.vmCanIpForward" = local.list_deny
|
||||
# "constraints/gcp.resourceLocations" = {
|
||||
# inherit_from_parent = false
|
||||
# suggested_value = null
|
||||
# status = true
|
||||
# values = local.allowed_regions
|
||||
|
||||
org_policies = {
|
||||
"compute.disableGuestAttributesAccess" = { enforce = true }
|
||||
"compute.requireOsLogin" = { enforce = true }
|
||||
"compute.restrictLoadBalancerCreationForTypes" = { allow = { values = ["in:INTERNAL"] } }
|
||||
"compute.skipDefaultNetworkCreation" = { enforce = true }
|
||||
"compute.vmExternalIpAccess" = { deny = { all = true } }
|
||||
"iam.allowedPolicyMemberDomains" = { allow = { values = local.all_drs_domains } }
|
||||
"iam.automaticIamGrantsForDefaultServiceAccounts" = { enforce = true }
|
||||
"iam.disableServiceAccountKeyCreation" = { enforce = true }
|
||||
"iam.disableServiceAccountKeyUpload" = { enforce = true }
|
||||
"run.allowedIngress" = { allow = { values = ["is:internal"] } }
|
||||
"sql.restrictAuthorizedNetworks" = { enforce = true }
|
||||
"sql.restrictPublicIp" = { enforce = true }
|
||||
"storage.uniformBucketLevelAccess" = { enforce = true }
|
||||
# "cloudfunctions.allowedIngressSettings" = {
|
||||
# allow = { values = ["is:ALLOW_INTERNAL_ONLY"] }
|
||||
# }
|
||||
# https://cloud.google.com/iam/docs/manage-workload-identity-pools-providers#restrict
|
||||
# "constraints/iam.workloadIdentityPoolProviders" = merge(
|
||||
# local.list_allow, { values = [
|
||||
# for k, v in coalesce(var.automation.federated_identity_providers, {}) :
|
||||
# v.issuer_uri
|
||||
# ] }
|
||||
# )
|
||||
# "constraints/iam.workloadIdentityPoolAwsAccounts" = merge(
|
||||
# local.list_allow, { values = [
|
||||
#
|
||||
# ] }
|
||||
# )
|
||||
# "cloudfunctions.allowedVpcConnectorEgressSettings" = {
|
||||
# allow = { values = ["is:PRIVATE_RANGES_ONLY"] }
|
||||
# }
|
||||
# "cloudfunctions.requireVPCConnector" = { enforce = true }
|
||||
# "compute.disableInternetNetworkEndpointGroup" = { enforce = true }
|
||||
# "compute.disableNestedVirtualization" = { enforce = true }
|
||||
# "compute.disableSerialPortAccess" = { enforce = true }
|
||||
# "compute.restrictCloudNATUsage" = { deny = { all = true }}
|
||||
# "compute.restrictDedicatedInterconnectUsage" = { deny = { all = true }}
|
||||
# "compute.restrictPartnerInterconnectUsage" = { deny = { all = true }}
|
||||
# "compute.restrictProtocolForwardingCreationForTypes" = { deny = { all = true }}
|
||||
# "compute.restrictSharedVpcHostProjects" = { deny = { all = true }}
|
||||
# "compute.restrictSharedVpcSubnetworks" = { deny = { all = true }}
|
||||
# "compute.restrictVpcPeering" = { deny = { all = true }}
|
||||
# "compute.restrictVpnPeerIPs" = { deny = { all = true }}
|
||||
# "compute.restrictXpnProjectLienRemoval" = { enforce = true }
|
||||
# "compute.setNewProjectDefaultToZonalDNSOnly" = { enforce = true }
|
||||
# "compute.vmCanIpForward" = { deny = { all = true }}
|
||||
# "gcp.resourceLocations" = {
|
||||
# allow = { values = local.allowed_regions }
|
||||
# }
|
||||
# "iam.workloadIdentityPoolProviders" = {
|
||||
# allow = {
|
||||
# values = [
|
||||
# for k, v in coalesce(var.automation.federated_identity_providers, {}) :
|
||||
# v.issuer_uri
|
||||
# ]
|
||||
# }
|
||||
# }
|
||||
# "run.allowedVPCEgress" = { allow = { values = ["is:private-ranges-only"] } }
|
||||
}
|
||||
tags = {
|
||||
(var.tag_names.context) = {
|
||||
|
|
|
@ -94,27 +94,21 @@ module "nva-template" {
|
|||
}
|
||||
|
||||
module "nva-mig" {
|
||||
for_each = local.nva_locality
|
||||
source = "../../../modules/compute-mig"
|
||||
project_id = module.landing-project.project_id
|
||||
regional = true
|
||||
location = each.value.region
|
||||
name = "nva-cos-${each.value.trigram}-${each.value.zone}"
|
||||
target_size = 1
|
||||
# FIXME: cycle
|
||||
# auto_healing_policies = {
|
||||
# health_check = module.nva-mig[each.key].health_check.self_link
|
||||
# initial_delay_sec = 30
|
||||
# }
|
||||
health_check_config = {
|
||||
type = "tcp"
|
||||
check = { port = 22 }
|
||||
config = {}
|
||||
logging = true
|
||||
for_each = local.nva_locality
|
||||
source = "../../../modules/compute-mig"
|
||||
project_id = module.landing-project.project_id
|
||||
location = each.value.region
|
||||
name = "nva-cos-${each.value.trigram}-${each.value.zone}"
|
||||
instance_template = module.nva-template[each.key].template.self_link
|
||||
target_size = 1
|
||||
auto_healing_policies = {
|
||||
initial_delay_sec = 30
|
||||
}
|
||||
default_version = {
|
||||
instance_template = module.nva-template[each.key].template.self_link
|
||||
name = "default"
|
||||
health_check_config = {
|
||||
enable_logging = true
|
||||
tcp = {
|
||||
port = 22
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,15 +48,15 @@ labels:
|
|||
|
||||
# [opt] Org policy overrides defined at project level
|
||||
org_policies:
|
||||
policy_boolean:
|
||||
constraints/compute.disableGuestAttributesAccess: true
|
||||
policy_list:
|
||||
constraints/compute.trustedImageProjects:
|
||||
inherit_from_parent: null
|
||||
status: true
|
||||
suggested_value: null
|
||||
constraints/compute.disableGuestAttributesAccess:
|
||||
enforce: true
|
||||
constraints/compute.trustedImageProjects:
|
||||
allow:
|
||||
values:
|
||||
- projects/fast-dev-iac-core-0
|
||||
constraints/compute.vmExternalIpAccess:
|
||||
deny:
|
||||
all: true
|
||||
|
||||
# [opt] Service account to create for the project and their roles on the project
|
||||
# in name => [roles] format
|
||||
|
|
|
@ -39,15 +39,12 @@ module "nginx-template" {
|
|||
}
|
||||
|
||||
module "nginx-mig" {
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 2
|
||||
default_version = {
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
name = "default"
|
||||
}
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 2
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
}
|
||||
# tftest modules=2 resources=2
|
||||
```
|
||||
|
@ -85,20 +82,18 @@ module "nginx-template" {
|
|||
}
|
||||
|
||||
module "nginx-mig" {
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 3
|
||||
default_version = {
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
name = "default"
|
||||
}
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 3
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
versions = {
|
||||
canary = {
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
target_type = "fixed"
|
||||
target_size = 1
|
||||
target_size = {
|
||||
fixed = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,24 +133,20 @@ module "nginx-template" {
|
|||
}
|
||||
|
||||
module "nginx-mig" {
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 3
|
||||
default_version = {
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
name = "default"
|
||||
}
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 3
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
auto_healing_policies = {
|
||||
health_check = module.nginx-mig.health_check.self_link
|
||||
initial_delay_sec = 30
|
||||
}
|
||||
health_check_config = {
|
||||
type = "http"
|
||||
check = { port = 80 }
|
||||
config = {}
|
||||
logging = true
|
||||
enable_logging = true
|
||||
http = {
|
||||
port = 80
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=2 resources=3
|
||||
|
@ -194,22 +185,21 @@ module "nginx-template" {
|
|||
}
|
||||
|
||||
module "nginx-mig" {
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 3
|
||||
default_version = {
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
name = "default"
|
||||
}
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 3
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
autoscaler_config = {
|
||||
max_replicas = 3
|
||||
min_replicas = 1
|
||||
cooldown_period = 30
|
||||
cpu_utilization_target = 0.65
|
||||
load_balancing_utilization_target = null
|
||||
metric = null
|
||||
max_replicas = 3
|
||||
min_replicas = 1
|
||||
cooldown_period = 30
|
||||
scaling_signals = {
|
||||
cpu_utilization = {
|
||||
target = 0.65
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=2 resources=3
|
||||
|
@ -246,23 +236,19 @@ module "nginx-template" {
|
|||
}
|
||||
|
||||
module "nginx-mig" {
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 3
|
||||
default_version = {
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
name = "default"
|
||||
}
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 3
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
update_policy = {
|
||||
type = "PROACTIVE"
|
||||
minimal_action = "REPLACE"
|
||||
type = "PROACTIVE"
|
||||
min_ready_sec = 30
|
||||
max_surge_type = "fixed"
|
||||
max_surge = 1
|
||||
max_unavailable_type = null
|
||||
max_unavailable = null
|
||||
max_surge = {
|
||||
fixed = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=2 resources=2
|
||||
|
@ -278,7 +264,6 @@ An example using only the configuration at the MIG level can be seen below.
|
|||
|
||||
Note that when referencing the stateful disk, you use `device_name` and not `disk_name`.
|
||||
|
||||
|
||||
```hcl
|
||||
module "cos-nginx" {
|
||||
source = "./fabric/modules/cloud-config-container/nginx"
|
||||
|
@ -319,39 +304,32 @@ module "nginx-template" {
|
|||
}
|
||||
|
||||
module "nginx-mig" {
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 3
|
||||
default_version = {
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
name = "default"
|
||||
}
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 3
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
autoscaler_config = {
|
||||
max_replicas = 3
|
||||
min_replicas = 1
|
||||
cooldown_period = 30
|
||||
cpu_utilization_target = 0.65
|
||||
load_balancing_utilization_target = null
|
||||
metric = null
|
||||
}
|
||||
stateful_config = {
|
||||
per_instance_config = {},
|
||||
mig_config = {
|
||||
stateful_disks = {
|
||||
repd-1 = {
|
||||
delete_rule = "NEVER"
|
||||
}
|
||||
max_replicas = 3
|
||||
min_replicas = 1
|
||||
cooldown_period = 30
|
||||
scaling_signals = {
|
||||
cpu_utilization = {
|
||||
target = 0.65
|
||||
}
|
||||
}
|
||||
}
|
||||
stateful_disks = {
|
||||
repd-1 = null
|
||||
}
|
||||
}
|
||||
# tftest modules=2 resources=3
|
||||
|
||||
```
|
||||
|
||||
### Stateful MIGs - Instance Config
|
||||
|
||||
Here is an example defining the stateful config at the instance level.
|
||||
|
||||
Note that you will need to know the instance name in order to use this configuration.
|
||||
|
@ -396,46 +374,36 @@ module "nginx-template" {
|
|||
}
|
||||
|
||||
module "nginx-mig" {
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 3
|
||||
default_version = {
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
name = "default"
|
||||
}
|
||||
source = "./fabric/modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1-b"
|
||||
name = "mig-test"
|
||||
target_size = 3
|
||||
instance_template = module.nginx-template.template.self_link
|
||||
autoscaler_config = {
|
||||
max_replicas = 3
|
||||
min_replicas = 1
|
||||
cooldown_period = 30
|
||||
cpu_utilization_target = 0.65
|
||||
load_balancing_utilization_target = null
|
||||
metric = null
|
||||
max_replicas = 3
|
||||
min_replicas = 1
|
||||
cooldown_period = 30
|
||||
scaling_signals = {
|
||||
cpu_utilization = {
|
||||
target = 0.65
|
||||
}
|
||||
}
|
||||
}
|
||||
stateful_config = {
|
||||
per_instance_config = {
|
||||
# note that this needs to be the name of an existing instance within the Managed Instance Group
|
||||
instance-1 = {
|
||||
stateful_disks = {
|
||||
# name needs to match a MIG instance name
|
||||
instance-1 = {
|
||||
minimal_action = "NONE",
|
||||
most_disruptive_allowed_action = "REPLACE"
|
||||
preserved_state = {
|
||||
disks = {
|
||||
persistent-disk-1 = {
|
||||
source = "test-disk",
|
||||
mode = "READ_ONLY",
|
||||
delete_rule= "NEVER",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
metadata = {
|
||||
foo = "bar"
|
||||
},
|
||||
update_config = {
|
||||
minimal_action = "NONE",
|
||||
most_disruptive_allowed_action = "REPLACE",
|
||||
remove_instance_state_on_destroy = false,
|
||||
},
|
||||
},
|
||||
},
|
||||
mig_config = {
|
||||
stateful_disks = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -449,21 +417,25 @@ module "nginx-mig" {
|
|||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [default_version](variables.tf#L45) | Default application version template. Additional versions can be specified via the `versions` variable. | <code title="object({ instance_template = string name = string })">object({…})</code> | ✓ | |
|
||||
| [location](variables.tf#L64) | Compute zone, or region if `regional` is set to true. | <code>string</code> | ✓ | |
|
||||
| [name](variables.tf#L68) | Managed group name. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L79) | Project id. | <code>string</code> | ✓ | |
|
||||
| [auto_healing_policies](variables.tf#L17) | Auto-healing policies for this group. | <code title="object({ health_check = string initial_delay_sec = number })">object({…})</code> | | <code>null</code> |
|
||||
| [autoscaler_config](variables.tf#L26) | Optional autoscaler configuration. Only one of 'cpu_utilization_target' 'load_balancing_utilization_target' or 'metric' can be not null. | <code title="object({ max_replicas = number min_replicas = number cooldown_period = number cpu_utilization_target = number load_balancing_utilization_target = number metric = object({ name = string single_instance_assignment = number target = number type = string # GAUGE, DELTA_PER_SECOND, DELTA_PER_MINUTE filter = string }) })">object({…})</code> | | <code>null</code> |
|
||||
| [health_check_config](variables.tf#L53) | Optional auto-created health check configuration, use the output self-link to set it in the auto healing policy. Refer to examples for usage. | <code title="object({ type = string # http https tcp ssl http2 check = map(any) # actual health check block attributes config = map(number) # interval, thresholds, timeout logging = bool })">object({…})</code> | | <code>null</code> |
|
||||
| [named_ports](variables.tf#L73) | Named ports. | <code>map(number)</code> | | <code>null</code> |
|
||||
| [regional](variables.tf#L84) | Use regional instance group. When set, `location` should be set to the region. | <code>bool</code> | | <code>false</code> |
|
||||
| [stateful_config](variables.tf#L90) | Stateful configuration can be done by individual instances or for all instances in the MIG. They key in per_instance_config is the name of the specific instance. The key of the stateful_disks is the 'device_name' field of the resource. Please note that device_name is defined at the OS mount level, unlike the disk name. | <code title="object({ per_instance_config = map(object({ stateful_disks = map(object({ source = string mode = string # READ_WRITE | READ_ONLY delete_rule = string # NEVER | ON_PERMANENT_INSTANCE_DELETION })) metadata = map(string) update_config = object({ minimal_action = string # NONE | REPLACE | RESTART | REFRESH most_disruptive_allowed_action = string # REPLACE | RESTART | REFRESH | NONE remove_instance_state_on_destroy = bool }) })) mig_config = object({ stateful_disks = map(object({ delete_rule = string # NEVER | ON_PERMANENT_INSTANCE_DELETION })) }) })">object({…})</code> | | <code>null</code> |
|
||||
| [target_pools](variables.tf#L121) | Optional list of URLs for target pools to which new instances in the group are added. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [target_size](variables.tf#L127) | Group target size, leave null when using an autoscaler. | <code>number</code> | | <code>null</code> |
|
||||
| [update_policy](variables.tf#L133) | Update policy. Type can be 'OPPORTUNISTIC' or 'PROACTIVE', action 'REPLACE' or 'restart', surge type 'fixed' or 'percent'. | <code title="object({ instance_redistribution_type = optional(string, "PROACTIVE") # NONE | PROACTIVE. The attribute is ignored if regional is set to false. max_surge_type = string # fixed | percent max_surge = number max_unavailable_type = string max_unavailable = number minimal_action = string # REPLACE | RESTART min_ready_sec = number type = string # OPPORTUNISTIC | PROACTIVE })">object({…})</code> | | <code>null</code> |
|
||||
| [versions](variables.tf#L148) | Additional application versions, target_type is either 'fixed' or 'percent'. | <code title="map(object({ instance_template = string target_type = string # fixed | percent target_size = number }))">map(object({…}))</code> | | <code>null</code> |
|
||||
| [wait_for_instances](variables.tf#L158) | Wait for all instances to be created/updated before returning. | <code>bool</code> | | <code>null</code> |
|
||||
| [instance_template](variables.tf#L150) | Instance template for the default version. | <code>string</code> | ✓ | |
|
||||
| [location](variables.tf#L155) | Compute zone or region. | <code>string</code> | ✓ | |
|
||||
| [name](variables.tf#L160) | Managed group name. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L171) | Project id. | <code>string</code> | ✓ | |
|
||||
| [all_instances_config](variables.tf#L17) | Metadata and labels set to all instances in the group. | <code title="object({ labels = optional(map(string)) metadata = optional(map(string)) })">object({…})</code> | | <code>null</code> |
|
||||
| [auto_healing_policies](variables.tf#L26) | Auto-healing policies for this group. | <code title="object({ health_check = optional(string) initial_delay_sec = number })">object({…})</code> | | <code>null</code> |
|
||||
| [autoscaler_config](variables.tf#L35) | Optional autoscaler configuration. | <code title="object({ max_replicas = number min_replicas = number cooldown_period = optional(number) mode = optional(string) # OFF, ONLY_UP, ON scaling_control = optional(object({ down = optional(object({ max_replicas_fixed = optional(number) max_replicas_percent = optional(number) time_window_sec = optional(number) })) in = optional(object({ max_replicas_fixed = optional(number) max_replicas_percent = optional(number) time_window_sec = optional(number) })) }), {}) scaling_signals = optional(object({ cpu_utilization = optional(object({ target = number optimize_availability = optional(bool) })) load_balancing_utilization = optional(object({ target = number })) metrics = optional(list(object({ name = string type = string # GAUGE, DELTA_PER_SECOND, DELTA_PER_MINUTE target_value = number single_instance_assignment = optional(number) time_series_filter = optional(string) }))) schedules = optional(list(object({ duration_sec = number name = string min_required_replicas = number cron_schedule = string description = optional(bool) timezone = optional(string) disabled = optional(bool) }))) }), {}) })">object({…})</code> | | <code>null</code> |
|
||||
| [default_version_name](variables.tf#L83) | Name used for the default version. | <code>string</code> | | <code>"default"</code> |
|
||||
| [description](variables.tf#L89) | Optional description used for all resources managed by this module. | <code>string</code> | | <code>"Terraform managed."</code> |
|
||||
| [distribution_policy](variables.tf#L95) | DIstribution policy for regional MIG. | <code title="object({ target_shape = optional(string) zones = optional(list(string)) })">object({…})</code> | | <code>null</code> |
|
||||
| [health_check_config](variables.tf#L104) | Optional auto-created health check configuration, use the output self-link to set it in the auto healing policy. Refer to examples for usage. | <code title="object({ check_interval_sec = optional(number) description = optional(string, "Terraform managed.") enable_logging = optional(bool, false) healthy_threshold = optional(number) timeout_sec = optional(number) unhealthy_threshold = optional(number) grpc = optional(object({ port = optional(number) port_name = optional(string) port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT service_name = optional(string) })) http = optional(object({ host = optional(string) port = optional(number) port_name = optional(string) port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT proxy_header = optional(string) request_path = optional(string) response = optional(string) use_protocol = optional(string, "http") # http http2 https })) tcp = optional(object({ port = optional(number) port_name = optional(string) port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT proxy_header = optional(string) request = optional(string) response = optional(string) use_ssl = optional(bool, false) })) })">object({…})</code> | | <code>null</code> |
|
||||
| [named_ports](variables.tf#L165) | Named ports. | <code>map(number)</code> | | <code>null</code> |
|
||||
| [stateful_config](variables.tf#L183) | Stateful configuration for individual instances. | <code title="map(object({ minimal_action = optional(string) most_disruptive_action = optional(string) remove_state_on_destroy = optional(bool) preserved_state = optional(object({ disks = optional(map(object({ source = string delete_on_instance_deletion = optional(bool) read_only = optional(bool) }))) metadata = optional(map(string)) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [stateful_disks](variables.tf#L176) | Stateful disk configuration applied at the MIG level to all instances, in device name => on permanent instance delete rule as boolean. | <code>map(bool)</code> | | <code>{}</code> |
|
||||
| [target_pools](variables.tf#L202) | Optional list of URLs for target pools to which new instances in the group are added. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [target_size](variables.tf#L208) | Group target size, leave null when using an autoscaler. | <code>number</code> | | <code>null</code> |
|
||||
| [update_policy](variables.tf#L214) | Update policy. Minimal action and type are required. | <code title="object({ minimal_action = string type = string max_surge = optional(object({ fixed = optional(number) percent = optional(number) })) max_unavailable = optional(object({ fixed = optional(number) percent = optional(number) })) min_ready_sec = optional(number) most_disruptive_action = optional(string) regional_redistribution_type = optional(string) replacement_method = optional(string) })">object({…})</code> | | <code>null</code> |
|
||||
| [versions](variables.tf#L235) | Additional application versions, target_size is optional. | <code title="map(object({ instance_template = string target_size = optional(object({ fixed = optional(number) percent = optional(number) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [wait_for_instances](variables.tf#L248) | Wait for all instances to be created/updated before returning. | <code title="object({ enabled = bool status = optional(string) })">object({…})</code> | | <code>null</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Autoscaler resource.
|
||||
|
||||
locals {
|
||||
as_enabled = true
|
||||
as_scaling = try(var.autoscaler_config.scaling_control, null)
|
||||
as_signals = try(var.autoscaler_config.scaling_signals, null)
|
||||
}
|
||||
|
||||
resource "google_compute_autoscaler" "default" {
|
||||
provider = google-beta
|
||||
count = local.is_regional || var.autoscaler_config == null ? 0 : 1
|
||||
project = var.project_id
|
||||
name = var.name
|
||||
zone = var.location
|
||||
description = var.description
|
||||
target = google_compute_instance_group_manager.default.0.id
|
||||
|
||||
autoscaling_policy {
|
||||
max_replicas = var.autoscaler_config.max_replicas
|
||||
min_replicas = var.autoscaler_config.min_replicas
|
||||
cooldown_period = var.autoscaler_config.cooldown_period
|
||||
|
||||
dynamic "scale_down_control" {
|
||||
for_each = local.as_scaling.down == null ? [] : [""]
|
||||
content {
|
||||
time_window_sec = local.as_scaling.down.time_window_sec
|
||||
dynamic "max_scaled_down_replicas" {
|
||||
for_each = (
|
||||
local.as_scaling.down.max_replicas_fixed == null &&
|
||||
local.as_scaling.down.max_replicas_percent == null
|
||||
? []
|
||||
: [""]
|
||||
)
|
||||
content {
|
||||
fixed = local.as_scaling.down.max_replicas_fixed
|
||||
percent = local.as_scaling.down.max_replicas_percent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "scale_in_control" {
|
||||
for_each = local.as_scaling.in == null ? [] : [""]
|
||||
content {
|
||||
time_window_sec = local.as_scaling.in.time_window_sec
|
||||
dynamic "max_scaled_in_replicas" {
|
||||
for_each = (
|
||||
local.as_scaling.in.max_replicas_fixed == null &&
|
||||
local.as_scaling.in.max_replicas_percent == null
|
||||
? []
|
||||
: [""]
|
||||
)
|
||||
content {
|
||||
fixed = local.as_scaling.in.max_replicas_fixed
|
||||
percent = local.as_scaling.in.max_replicas_percent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "cpu_utilization" {
|
||||
for_each = local.as_signals.cpu_utilization == null ? [] : [""]
|
||||
content {
|
||||
target = local.as_signals.cpu_utilization.target
|
||||
predictive_method = (
|
||||
local.as_signals.cpu_utilization.optimize_availability == true
|
||||
? "OPTIMIZE_AVAILABILITY"
|
||||
: null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "load_balancing_utilization" {
|
||||
for_each = local.as_signals.load_balancing_utilization == null ? [] : [""]
|
||||
content {
|
||||
target = local.as_signals.load_balancing_utilization.target
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "metric" {
|
||||
for_each = toset(
|
||||
local.as_signals.metrics == null ? [] : local.as_signals.metrics
|
||||
)
|
||||
content {
|
||||
name = metric.value.name
|
||||
type = metric.value.type
|
||||
target = metric.value.target_value
|
||||
single_instance_assignment = metric.value.single_instance_assignment
|
||||
filter = metric.value.time_series_filter
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "scaling_schedules" {
|
||||
for_each = toset(
|
||||
local.as_signals.schedules == null ? [] : local.as_signals.schedules
|
||||
)
|
||||
iterator = schedule
|
||||
content {
|
||||
duration_sec = schedule.value.duration_sec
|
||||
min_required_replicas = schedule.value.min_required_replicas
|
||||
name = schedule.value.name
|
||||
schedule = schedule.value.cron_schedule
|
||||
description = schedule.value.description
|
||||
disabled = schedule.value.disabled
|
||||
time_zone = schedule.value.timezone
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_region_autoscaler" "default" {
|
||||
provider = google-beta
|
||||
count = local.is_regional && var.autoscaler_config != null ? 1 : 0
|
||||
project = var.project_id
|
||||
name = var.name
|
||||
region = var.location
|
||||
description = var.description
|
||||
target = google_compute_region_instance_group_manager.default.0.id
|
||||
|
||||
autoscaling_policy {
|
||||
max_replicas = var.autoscaler_config.max_replicas
|
||||
min_replicas = var.autoscaler_config.min_replicas
|
||||
cooldown_period = var.autoscaler_config.cooldown_period
|
||||
|
||||
dynamic "scale_down_control" {
|
||||
for_each = local.as_scaling.down == null ? [] : [""]
|
||||
content {
|
||||
time_window_sec = local.as_scaling.down.time_window_sec
|
||||
dynamic "max_scaled_down_replicas" {
|
||||
for_each = (
|
||||
local.as_scaling.down.max_replicas_fixed == null &&
|
||||
local.as_scaling.down.max_replicas_percent == null
|
||||
? []
|
||||
: [""]
|
||||
)
|
||||
content {
|
||||
fixed = local.as_scaling.down.max_replicas_fixed
|
||||
percent = local.as_scaling.down.max_replicas_percent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "scale_in_control" {
|
||||
for_each = local.as_scaling.in == null ? [] : [""]
|
||||
content {
|
||||
time_window_sec = local.as_scaling.in.time_window_sec
|
||||
dynamic "max_scaled_in_replicas" {
|
||||
for_each = (
|
||||
local.as_scaling.in.max_replicas_fixed == null &&
|
||||
local.as_scaling.in.max_replicas_percent == null
|
||||
? []
|
||||
: [""]
|
||||
)
|
||||
content {
|
||||
fixed = local.as_scaling.in.max_replicas_fixed
|
||||
percent = local.as_scaling.in.max_replicas_percent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "cpu_utilization" {
|
||||
for_each = local.as_signals.cpu_utilization == null ? [] : [""]
|
||||
content {
|
||||
target = local.as_signals.cpu_utilization.target
|
||||
predictive_method = (
|
||||
local.as_signals.cpu_utilization.optimize_availability == true
|
||||
? "OPTIMIZE_AVAILABILITY"
|
||||
: null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "load_balancing_utilization" {
|
||||
for_each = local.as_signals.load_balancing_utilization == null ? [] : [""]
|
||||
content {
|
||||
target = local.as_signals.load_balancing_utilization.target
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "metric" {
|
||||
for_each = toset(
|
||||
local.as_signals.metrics == null ? [] : local.as_signals.metrics
|
||||
)
|
||||
content {
|
||||
name = metric.value.name
|
||||
type = metric.value.type
|
||||
target = metric.value.target_value
|
||||
single_instance_assignment = metric.value.single_instance_assignment
|
||||
filter = metric.value.time_series_filter
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "scaling_schedules" {
|
||||
for_each = toset(
|
||||
local.as_signals.schedules == null ? [] : local.as_signals.schedules
|
||||
)
|
||||
iterator = schedule
|
||||
content {
|
||||
duration_sec = schedule.value.duration_sec
|
||||
min_required_replicas = schedule.value.min_required_replicas
|
||||
name = schedule.value.name
|
||||
schedule = schedule.cron_schedule
|
||||
description = schedule.value.description
|
||||
disabled = schedule.value.disabled
|
||||
time_zone = schedule.value.timezone
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Health check resource.
|
||||
|
||||
locals {
|
||||
hc = var.health_check_config
|
||||
hc_grpc = try(local.hc.grpc, null) != null
|
||||
hc_http = (
|
||||
try(local.hc.http, null) != null &&
|
||||
lower(try(local.hc.http.use_protocol, "")) == "http"
|
||||
)
|
||||
hc_http2 = (
|
||||
try(local.hc.http, null) != null &&
|
||||
lower(try(local.hc.http.use_protocol, "")) == "http2"
|
||||
)
|
||||
hc_https = (
|
||||
try(local.hc.http, null) != null &&
|
||||
lower(try(local.hc.http.use_protocol, "")) == "https"
|
||||
)
|
||||
hc_ssl = try(local.hc.tcp.use_ssl, null) == true
|
||||
hc_tcp = try(local.hc.tcp, null) != null && !local.hc_ssl
|
||||
}
|
||||
|
||||
resource "google_compute_health_check" "autohealing" {
|
||||
provider = google-beta
|
||||
count = local.hc != null ? 1 : 0
|
||||
project = var.project_id
|
||||
name = var.name
|
||||
description = local.hc.description
|
||||
check_interval_sec = local.hc.check_interval_sec
|
||||
healthy_threshold = local.hc.healthy_threshold
|
||||
timeout_sec = local.hc.timeout_sec
|
||||
unhealthy_threshold = local.hc.unhealthy_threshold
|
||||
|
||||
dynamic "grpc_health_check" {
|
||||
for_each = local.hc_grpc ? [""] : []
|
||||
content {
|
||||
port = local.hc.grpc.port
|
||||
port_name = local.hc.grpc.port_name
|
||||
port_specification = local.hc.grpc.port_specification
|
||||
grpc_service_name = local.hc.grpc.service_name
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "http_health_check" {
|
||||
for_each = local.hc_http ? [""] : []
|
||||
content {
|
||||
host = local.hc.http.host
|
||||
port = local.hc.http.port
|
||||
port_name = local.hc.http.port_name
|
||||
port_specification = local.hc.http.port_specification
|
||||
proxy_header = local.hc.http.proxy_header
|
||||
request_path = local.hc.http.request_path
|
||||
response = local.hc.http.response
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "http2_health_check" {
|
||||
for_each = local.hc_http2 ? [""] : []
|
||||
content {
|
||||
host = local.hc.http.host
|
||||
port = local.hc.http.port
|
||||
port_name = local.hc.http.port_name
|
||||
port_specification = local.hc.http.port_specification
|
||||
proxy_header = local.hc.http.proxy_header
|
||||
request_path = local.hc.http.request_path
|
||||
response = local.hc.http.response
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "https_health_check" {
|
||||
for_each = local.hc_https ? [""] : []
|
||||
content {
|
||||
host = local.hc.http.host
|
||||
port = local.hc.http.port
|
||||
port_name = local.hc.http.port_name
|
||||
port_specification = local.hc.http.port_specification
|
||||
proxy_header = local.hc.http.proxy_header
|
||||
request_path = local.hc.http.request_path
|
||||
response = local.hc.http.response
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "ssl_health_check" {
|
||||
for_each = local.hc_ssl ? [""] : []
|
||||
content {
|
||||
port = local.hc.tcp.port
|
||||
port_name = local.hc.tcp.port_name
|
||||
port_specification = local.hc.tcp.port_specification
|
||||
proxy_header = local.hc.tcp.proxy_header
|
||||
request = local.hc.tcp.request
|
||||
response = local.hc.tcp.response
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "tcp_health_check" {
|
||||
for_each = local.hc_tcp ? [""] : []
|
||||
content {
|
||||
port = local.hc.tcp.port
|
||||
port_name = local.hc.tcp.port_name
|
||||
port_specification = local.hc.tcp.port_specification
|
||||
proxy_header = local.hc.tcp.proxy_header
|
||||
request = local.hc.tcp.request
|
||||
response = local.hc.tcp.response
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "log_config" {
|
||||
for_each = try(local.hc.enable_logging, null) == true ? [""] : []
|
||||
content {
|
||||
enable = true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,105 +14,50 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
resource "google_compute_autoscaler" "default" {
|
||||
provider = google-beta
|
||||
count = var.regional || var.autoscaler_config == null ? 0 : 1
|
||||
project = var.project_id
|
||||
name = var.name
|
||||
description = "Terraform managed."
|
||||
zone = var.location
|
||||
target = google_compute_instance_group_manager.default.0.id
|
||||
|
||||
autoscaling_policy {
|
||||
max_replicas = var.autoscaler_config.max_replicas
|
||||
min_replicas = var.autoscaler_config.min_replicas
|
||||
cooldown_period = var.autoscaler_config.cooldown_period
|
||||
|
||||
dynamic "cpu_utilization" {
|
||||
for_each = (
|
||||
var.autoscaler_config.cpu_utilization_target == null ? [] : [""]
|
||||
)
|
||||
content {
|
||||
target = var.autoscaler_config.cpu_utilization_target
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "load_balancing_utilization" {
|
||||
for_each = (
|
||||
var.autoscaler_config.load_balancing_utilization_target == null ? [] : [""]
|
||||
)
|
||||
content {
|
||||
target = var.autoscaler_config.load_balancing_utilization_target
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "metric" {
|
||||
for_each = (
|
||||
var.autoscaler_config.metric == null
|
||||
? []
|
||||
: [var.autoscaler_config.metric]
|
||||
)
|
||||
iterator = config
|
||||
content {
|
||||
name = config.value.name
|
||||
single_instance_assignment = config.value.single_instance_assignment
|
||||
target = config.value.target
|
||||
type = config.value.type
|
||||
filter = config.value.filter
|
||||
}
|
||||
}
|
||||
}
|
||||
locals {
|
||||
health_check = (
|
||||
try(var.auto_healing_policies.health_check, null) == null
|
||||
? try(google_compute_health_check.autohealing.0.self_link, null)
|
||||
: try(var.auto_healing_policies.health_check, null)
|
||||
)
|
||||
instance_group_manager = (
|
||||
local.is_regional ?
|
||||
google_compute_region_instance_group_manager.default :
|
||||
google_compute_instance_group_manager.default
|
||||
)
|
||||
is_regional = length(split("-", var.location)) == 2
|
||||
}
|
||||
|
||||
|
||||
resource "google_compute_instance_group_manager" "default" {
|
||||
provider = google-beta
|
||||
count = var.regional ? 0 : 1
|
||||
project = var.project_id
|
||||
zone = var.location
|
||||
name = var.name
|
||||
base_instance_name = var.name
|
||||
description = "Terraform-managed."
|
||||
target_size = var.target_size
|
||||
target_pools = var.target_pools
|
||||
wait_for_instances = var.wait_for_instances
|
||||
provider = google-beta
|
||||
count = local.is_regional ? 0 : 1
|
||||
project = var.project_id
|
||||
zone = var.location
|
||||
name = var.name
|
||||
base_instance_name = var.name
|
||||
description = var.description
|
||||
target_size = var.target_size
|
||||
target_pools = var.target_pools
|
||||
wait_for_instances = try(var.wait_for_instances.enabled, null)
|
||||
wait_for_instances_status = try(var.wait_for_instances.status, null)
|
||||
|
||||
dynamic "all_instances_config" {
|
||||
for_each = var.all_instances_config == null ? [] : [""]
|
||||
content {
|
||||
labels = try(var.all_instances_config.labels, null)
|
||||
metadata = try(var.all_instances_config.metadata, null)
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "auto_healing_policies" {
|
||||
for_each = var.auto_healing_policies == null ? [] : [var.auto_healing_policies]
|
||||
for_each = var.auto_healing_policies == null ? [] : [""]
|
||||
iterator = config
|
||||
content {
|
||||
health_check = config.value.health_check
|
||||
initial_delay_sec = config.value.initial_delay_sec
|
||||
}
|
||||
}
|
||||
dynamic "stateful_disk" {
|
||||
for_each = try(var.stateful_config.mig_config.stateful_disks, {})
|
||||
iterator = config
|
||||
content {
|
||||
device_name = config.key
|
||||
delete_rule = config.value.delete_rule
|
||||
}
|
||||
}
|
||||
dynamic "update_policy" {
|
||||
for_each = var.update_policy == null ? [] : [var.update_policy]
|
||||
iterator = config
|
||||
content {
|
||||
type = config.value.type
|
||||
minimal_action = config.value.minimal_action
|
||||
min_ready_sec = config.value.min_ready_sec
|
||||
max_surge_fixed = (
|
||||
config.value.max_surge_type == "fixed" ? config.value.max_surge : null
|
||||
)
|
||||
max_surge_percent = (
|
||||
config.value.max_surge_type == "percent" ? config.value.max_surge : null
|
||||
)
|
||||
max_unavailable_fixed = (
|
||||
config.value.max_unavailable_type == "fixed" ? config.value.max_unavailable : null
|
||||
)
|
||||
max_unavailable_percent = (
|
||||
config.value.max_unavailable_type == "percent" ? config.value.max_unavailable : null
|
||||
)
|
||||
health_check = local.health_check
|
||||
initial_delay_sec = var.auto_healing_policies.initial_delay_sec
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "named_port" {
|
||||
for_each = var.named_ports == null ? {} : var.named_ports
|
||||
iterator = config
|
||||
|
@ -121,167 +66,88 @@ resource "google_compute_instance_group_manager" "default" {
|
|||
port = config.value
|
||||
}
|
||||
}
|
||||
version {
|
||||
instance_template = var.default_version.instance_template
|
||||
name = var.default_version.name
|
||||
|
||||
dynamic "stateful_disk" {
|
||||
for_each = var.stateful_disks
|
||||
content {
|
||||
device_name = stateful_disk.key
|
||||
delete_rule = stateful_disk.value
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "update_policy" {
|
||||
for_each = var.update_policy == null ? [] : [var.update_policy]
|
||||
iterator = p
|
||||
content {
|
||||
minimal_action = p.value.minimal_action
|
||||
type = p.value.type
|
||||
max_surge_fixed = try(p.value.max_surge.fixed, null)
|
||||
max_surge_percent = try(p.value.max_surge.percent, null)
|
||||
max_unavailable_fixed = try(p.value.max_unavailable.fixed, null)
|
||||
max_unavailable_percent = try(p.value.max_unavailable.percent, null)
|
||||
min_ready_sec = p.value.min_ready_sec
|
||||
most_disruptive_allowed_action = p.value.most_disruptive_action
|
||||
replacement_method = p.value.replacement_method
|
||||
}
|
||||
}
|
||||
|
||||
version {
|
||||
instance_template = var.instance_template
|
||||
name = var.default_version_name
|
||||
}
|
||||
|
||||
dynamic "version" {
|
||||
for_each = var.versions == null ? {} : var.versions
|
||||
iterator = version
|
||||
for_each = var.versions
|
||||
content {
|
||||
name = version.key
|
||||
instance_template = version.value.instance_template
|
||||
target_size {
|
||||
fixed = (
|
||||
version.value.target_type == "fixed" ? version.value.target_size : null
|
||||
)
|
||||
percent = (
|
||||
version.value.target_type == "percent" ? version.value.target_size : null
|
||||
)
|
||||
dynamic "target_size" {
|
||||
for_each = version.value.target_size == null ? [] : [""]
|
||||
content {
|
||||
fixed = version.value.target_size.fixed
|
||||
percent = version.value.target_size.percent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
locals {
|
||||
instance_group_manager = (
|
||||
var.regional ?
|
||||
google_compute_region_instance_group_manager.default :
|
||||
google_compute_instance_group_manager.default
|
||||
)
|
||||
}
|
||||
|
||||
resource "google_compute_per_instance_config" "default" {
|
||||
for_each = try(var.stateful_config.per_instance_config, {})
|
||||
#for_each = var.stateful_config && var.stateful_config.per_instance_config == null ? {} : length(var.stateful_config.per_instance_config)
|
||||
zone = var.location
|
||||
# terraform error, solved with locals
|
||||
#instance_group_manager = var.regional ? google_compute_region_instance_group_manager.default : google_compute_instance_group_manager.default
|
||||
instance_group_manager = local.instance_group_manager[0].id
|
||||
name = each.key
|
||||
project = var.project_id
|
||||
minimal_action = try(each.value.update_config.minimal_action, null)
|
||||
most_disruptive_allowed_action = try(each.value.update_config.most_disruptive_allowed_action, null)
|
||||
remove_instance_state_on_destroy = try(each.value.update_config.remove_instance_state_on_destroy, null)
|
||||
preserved_state {
|
||||
|
||||
metadata = each.value.metadata
|
||||
|
||||
dynamic "disk" {
|
||||
for_each = try(each.value.stateful_disks, {})
|
||||
#for_each = var.stateful_config.mig_config.stateful_disks == null ? {} : var.stateful_config.mig_config.stateful_disks
|
||||
iterator = config
|
||||
content {
|
||||
device_name = config.key
|
||||
source = config.value.source
|
||||
mode = config.value.mode
|
||||
delete_rule = config.value.delete_rule
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_region_autoscaler" "default" {
|
||||
provider = google-beta
|
||||
count = var.regional && var.autoscaler_config != null ? 1 : 0
|
||||
project = var.project_id
|
||||
name = var.name
|
||||
description = "Terraform managed."
|
||||
region = var.location
|
||||
target = google_compute_region_instance_group_manager.default.0.id
|
||||
|
||||
autoscaling_policy {
|
||||
max_replicas = var.autoscaler_config.max_replicas
|
||||
min_replicas = var.autoscaler_config.min_replicas
|
||||
cooldown_period = var.autoscaler_config.cooldown_period
|
||||
|
||||
dynamic "cpu_utilization" {
|
||||
for_each = (
|
||||
var.autoscaler_config.cpu_utilization_target == null ? [] : [""]
|
||||
)
|
||||
content {
|
||||
target = var.autoscaler_config.cpu_utilization_target
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "load_balancing_utilization" {
|
||||
for_each = (
|
||||
var.autoscaler_config.load_balancing_utilization_target == null ? [] : [""]
|
||||
)
|
||||
content {
|
||||
target = var.autoscaler_config.load_balancing_utilization_target
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "metric" {
|
||||
for_each = (
|
||||
var.autoscaler_config.metric == null
|
||||
? []
|
||||
: [var.autoscaler_config.metric]
|
||||
)
|
||||
iterator = config
|
||||
content {
|
||||
name = config.value.name
|
||||
single_instance_assignment = config.value.single_instance_assignment
|
||||
target = config.value.target
|
||||
type = config.value.type
|
||||
filter = config.value.filter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resource "google_compute_region_instance_group_manager" "default" {
|
||||
provider = google-beta
|
||||
count = var.regional ? 1 : 0
|
||||
count = local.is_regional ? 1 : 0
|
||||
project = var.project_id
|
||||
region = var.location
|
||||
name = var.name
|
||||
base_instance_name = var.name
|
||||
description = "Terraform-managed."
|
||||
target_size = var.target_size
|
||||
target_pools = var.target_pools
|
||||
wait_for_instances = var.wait_for_instances
|
||||
dynamic "auto_healing_policies" {
|
||||
for_each = var.auto_healing_policies == null ? [] : [var.auto_healing_policies]
|
||||
iterator = config
|
||||
description = var.description
|
||||
distribution_policy_target_shape = try(
|
||||
var.distribution_policy.target_shape, null
|
||||
)
|
||||
distribution_policy_zones = try(
|
||||
var.distribution_policy.zones, null
|
||||
)
|
||||
target_size = var.target_size
|
||||
target_pools = var.target_pools
|
||||
wait_for_instances = try(var.wait_for_instances.enabled, null)
|
||||
wait_for_instances_status = try(var.wait_for_instances.status, null)
|
||||
|
||||
dynamic "all_instances_config" {
|
||||
for_each = var.all_instances_config == null ? [] : [""]
|
||||
content {
|
||||
health_check = config.value.health_check
|
||||
initial_delay_sec = config.value.initial_delay_sec
|
||||
}
|
||||
}
|
||||
dynamic "stateful_disk" {
|
||||
for_each = try(var.stateful_config.mig_config.stateful_disks, {})
|
||||
iterator = config
|
||||
content {
|
||||
device_name = config.key
|
||||
delete_rule = config.value.delete_rule
|
||||
labels = try(var.all_instances_config.labels, null)
|
||||
metadata = try(var.all_instances_config.metadata, null)
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "update_policy" {
|
||||
for_each = var.update_policy == null ? [] : [var.update_policy]
|
||||
dynamic "auto_healing_policies" {
|
||||
for_each = var.auto_healing_policies == null ? [] : [""]
|
||||
iterator = config
|
||||
content {
|
||||
instance_redistribution_type = config.value.instance_redistribution_type
|
||||
type = config.value.type
|
||||
minimal_action = config.value.minimal_action
|
||||
min_ready_sec = config.value.min_ready_sec
|
||||
max_surge_fixed = (
|
||||
config.value.max_surge_type == "fixed" ? config.value.max_surge : null
|
||||
)
|
||||
max_surge_percent = (
|
||||
config.value.max_surge_type == "percent" ? config.value.max_surge : null
|
||||
)
|
||||
max_unavailable_fixed = (
|
||||
config.value.max_unavailable_type == "fixed" ? config.value.max_unavailable : null
|
||||
)
|
||||
max_unavailable_percent = (
|
||||
config.value.max_unavailable_type == "percent" ? config.value.max_unavailable : null
|
||||
)
|
||||
health_check = local.health_check
|
||||
initial_delay_sec = var.auto_healing_policies.initial_delay_sec
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "named_port" {
|
||||
for_each = var.named_ports == null ? {} : var.named_ports
|
||||
iterator = config
|
||||
|
@ -290,172 +156,49 @@ resource "google_compute_region_instance_group_manager" "default" {
|
|||
port = config.value
|
||||
}
|
||||
}
|
||||
version {
|
||||
instance_template = var.default_version.instance_template
|
||||
name = var.default_version.name
|
||||
|
||||
dynamic "stateful_disk" {
|
||||
for_each = var.stateful_disks
|
||||
content {
|
||||
device_name = stateful_disk.key
|
||||
delete_rule = stateful_disk.value
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "update_policy" {
|
||||
for_each = var.update_policy == null ? [] : [var.update_policy]
|
||||
iterator = p
|
||||
content {
|
||||
minimal_action = p.value.minimal_action
|
||||
type = p.value.type
|
||||
instance_redistribution_type = p.value.regional_redistribution_type
|
||||
max_surge_fixed = try(p.value.max_surge.fixed, null)
|
||||
max_surge_percent = try(p.value.max_surge.percent, null)
|
||||
max_unavailable_fixed = try(p.value.max_unavailable.fixed, null)
|
||||
max_unavailable_percent = try(p.value.max_unavailable.percent, null)
|
||||
min_ready_sec = p.value.min_ready_sec
|
||||
most_disruptive_allowed_action = p.value.most_disruptive_action
|
||||
replacement_method = p.value.replacement_method
|
||||
}
|
||||
}
|
||||
|
||||
version {
|
||||
instance_template = var.instance_template
|
||||
name = var.default_version_name
|
||||
}
|
||||
|
||||
dynamic "version" {
|
||||
for_each = var.versions == null ? {} : var.versions
|
||||
iterator = version
|
||||
for_each = var.versions
|
||||
content {
|
||||
name = version.key
|
||||
instance_template = version.value.instance_template
|
||||
target_size {
|
||||
fixed = (
|
||||
version.value.target_type == "fixed" ? version.value.target_size : null
|
||||
)
|
||||
percent = (
|
||||
version.value.target_type == "percent" ? version.value.target_size : null
|
||||
)
|
||||
dynamic "target_size" {
|
||||
for_each = version.value.target_size == null ? [] : [""]
|
||||
content {
|
||||
fixed = version.value.target_size.fixed
|
||||
percent = version.value.target_size.percent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_health_check" "http" {
|
||||
provider = google-beta
|
||||
count = try(var.health_check_config.type, null) == "http" ? 1 : 0
|
||||
project = var.project_id
|
||||
name = var.name
|
||||
description = "Terraform managed."
|
||||
|
||||
check_interval_sec = try(var.health_check_config.config.check_interval_sec, null)
|
||||
healthy_threshold = try(var.health_check_config.config.healthy_threshold, null)
|
||||
timeout_sec = try(var.health_check_config.config.timeout_sec, null)
|
||||
unhealthy_threshold = try(var.health_check_config.config.unhealthy_threshold, null)
|
||||
|
||||
http_health_check {
|
||||
host = try(var.health_check_config.check.host, null)
|
||||
port = try(var.health_check_config.check.port, null)
|
||||
port_name = try(var.health_check_config.check.port_name, null)
|
||||
port_specification = try(var.health_check_config.check.port_specification, null)
|
||||
proxy_header = try(var.health_check_config.check.proxy_header, null)
|
||||
request_path = try(var.health_check_config.check.request_path, null)
|
||||
response = try(var.health_check_config.check.response, null)
|
||||
}
|
||||
|
||||
dynamic "log_config" {
|
||||
for_each = try(var.health_check_config.logging, false) ? [""] : []
|
||||
content {
|
||||
enable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_health_check" "https" {
|
||||
provider = google-beta
|
||||
count = try(var.health_check_config.type, null) == "https" ? 1 : 0
|
||||
project = var.project_id
|
||||
name = var.name
|
||||
description = "Terraform managed."
|
||||
|
||||
check_interval_sec = try(var.health_check_config.config.check_interval_sec, null)
|
||||
healthy_threshold = try(var.health_check_config.config.healthy_threshold, null)
|
||||
timeout_sec = try(var.health_check_config.config.timeout_sec, null)
|
||||
unhealthy_threshold = try(var.health_check_config.config.unhealthy_threshold, null)
|
||||
|
||||
https_health_check {
|
||||
host = try(var.health_check_config.check.host, null)
|
||||
port = try(var.health_check_config.check.port, null)
|
||||
port_name = try(var.health_check_config.check.port_name, null)
|
||||
port_specification = try(var.health_check_config.check.port_specification, null)
|
||||
proxy_header = try(var.health_check_config.check.proxy_header, null)
|
||||
request_path = try(var.health_check_config.check.request_path, null)
|
||||
response = try(var.health_check_config.check.response, null)
|
||||
}
|
||||
|
||||
dynamic "log_config" {
|
||||
for_each = try(var.health_check_config.logging, false) ? [""] : []
|
||||
content {
|
||||
enable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_health_check" "tcp" {
|
||||
provider = google-beta
|
||||
count = try(var.health_check_config.type, null) == "tcp" ? 1 : 0
|
||||
project = var.project_id
|
||||
name = var.name
|
||||
description = "Terraform managed."
|
||||
|
||||
check_interval_sec = try(var.health_check_config.config.check_interval_sec, null)
|
||||
healthy_threshold = try(var.health_check_config.config.healthy_threshold, null)
|
||||
timeout_sec = try(var.health_check_config.config.timeout_sec, null)
|
||||
unhealthy_threshold = try(var.health_check_config.config.unhealthy_threshold, null)
|
||||
|
||||
tcp_health_check {
|
||||
port = try(var.health_check_config.check.port, null)
|
||||
port_name = try(var.health_check_config.check.port_name, null)
|
||||
port_specification = try(var.health_check_config.check.port_specification, null)
|
||||
proxy_header = try(var.health_check_config.check.proxy_header, null)
|
||||
request = try(var.health_check_config.check.request, null)
|
||||
response = try(var.health_check_config.check.response, null)
|
||||
}
|
||||
|
||||
dynamic "log_config" {
|
||||
for_each = try(var.health_check_config.logging, false) ? [""] : []
|
||||
content {
|
||||
enable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_health_check" "ssl" {
|
||||
provider = google-beta
|
||||
count = try(var.health_check_config.type, null) == "ssl" ? 1 : 0
|
||||
project = var.project_id
|
||||
name = var.name
|
||||
description = "Terraform managed."
|
||||
|
||||
check_interval_sec = try(var.health_check_config.config.check_interval_sec, null)
|
||||
healthy_threshold = try(var.health_check_config.config.healthy_threshold, null)
|
||||
timeout_sec = try(var.health_check_config.config.timeout_sec, null)
|
||||
unhealthy_threshold = try(var.health_check_config.config.unhealthy_threshold, null)
|
||||
|
||||
ssl_health_check {
|
||||
port = try(var.health_check_config.check.port, null)
|
||||
port_name = try(var.health_check_config.check.port_name, null)
|
||||
port_specification = try(var.health_check_config.check.port_specification, null)
|
||||
proxy_header = try(var.health_check_config.check.proxy_header, null)
|
||||
request = try(var.health_check_config.check.request, null)
|
||||
response = try(var.health_check_config.check.response, null)
|
||||
}
|
||||
|
||||
dynamic "log_config" {
|
||||
for_each = try(var.health_check_config.logging, false) ? [""] : []
|
||||
content {
|
||||
enable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_health_check" "http2" {
|
||||
provider = google-beta
|
||||
count = try(var.health_check_config.type, null) == "http2" ? 1 : 0
|
||||
project = var.project_id
|
||||
name = var.name
|
||||
description = "Terraform managed."
|
||||
|
||||
check_interval_sec = try(var.health_check_config.config.check_interval_sec, null)
|
||||
healthy_threshold = try(var.health_check_config.config.healthy_threshold, null)
|
||||
timeout_sec = try(var.health_check_config.config.timeout_sec, null)
|
||||
unhealthy_threshold = try(var.health_check_config.config.unhealthy_threshold, null)
|
||||
|
||||
http2_health_check {
|
||||
host = try(var.health_check_config.check.host, null)
|
||||
port = try(var.health_check_config.check.port, null)
|
||||
port_name = try(var.health_check_config.check.port_name, null)
|
||||
port_specification = try(var.health_check_config.check.port_specification, null)
|
||||
proxy_header = try(var.health_check_config.check.proxy_header, null)
|
||||
request_path = try(var.health_check_config.check.request_path, null)
|
||||
response = try(var.health_check_config.check.response, null)
|
||||
}
|
||||
|
||||
dynamic "log_config" {
|
||||
for_each = try(var.health_check_config.logging, false) ? [""] : []
|
||||
content {
|
||||
enable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,13 +37,6 @@ output "health_check" {
|
|||
value = (
|
||||
var.health_check_config == null
|
||||
? null
|
||||
: try(
|
||||
google_compute_health_check.http.0,
|
||||
google_compute_health_check.https.0,
|
||||
google_compute_health_check.tcp.0,
|
||||
google_compute_health_check.ssl.0,
|
||||
google_compute_health_check.http2.0,
|
||||
{}
|
||||
)
|
||||
: google_compute_health_check.autohealing.0
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
# tfdoc:file:description Instance-level stateful configuration resources.
|
||||
|
||||
resource "google_compute_per_instance_config" "default" {
|
||||
for_each = local.is_regional ? {} : var.stateful_config
|
||||
project = var.project_id
|
||||
zone = var.location
|
||||
name = each.key
|
||||
instance_group_manager = try(
|
||||
google_compute_instance_group_manager.default.0.id, null
|
||||
)
|
||||
minimal_action = each.value.minimal_action
|
||||
most_disruptive_allowed_action = each.value.most_disruptive_action
|
||||
remove_instance_state_on_destroy = each.value.remove_state_on_destroy
|
||||
|
||||
dynamic "preserved_state" {
|
||||
for_each = each.value.preserved_state == null ? [] : [""]
|
||||
content {
|
||||
metadata = each.value.preserved_state.metadata
|
||||
dynamic "disk" {
|
||||
for_each = (
|
||||
each.value.preserved_state.disks == null
|
||||
? {}
|
||||
: each.value.preserved_state.disks
|
||||
)
|
||||
content {
|
||||
device_name = disk.key
|
||||
source = disk.value.source
|
||||
delete_rule = (
|
||||
disk.value.delete_on_instance_deletion == true
|
||||
? "ON_PERMANENT_INSTANCE_DELETION"
|
||||
: "NEVER"
|
||||
)
|
||||
mode = disk.value.read_only == true ? "READ_ONLY" : "READ_WRITE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_region_per_instance_config" "default" {
|
||||
for_each = local.is_regional ? var.stateful_config : {}
|
||||
project = var.project_id
|
||||
region = var.location
|
||||
name = each.key
|
||||
region_instance_group_manager = try(
|
||||
google_compute_region_instance_group_manager.default.0.id, null
|
||||
)
|
||||
minimal_action = each.value.minimal_action
|
||||
most_disruptive_allowed_action = each.value.most_disruptive_action
|
||||
remove_instance_state_on_destroy = each.value.remove_state_on_destroy
|
||||
|
||||
dynamic "preserved_state" {
|
||||
for_each = each.value.preserved_state == null ? [] : [""]
|
||||
content {
|
||||
metadata = each.value.preserved_state.metadata
|
||||
dynamic "disk" {
|
||||
for_each = (
|
||||
each.value.preserved_state.disks == null
|
||||
? {}
|
||||
: each.value.preserved_state.disks
|
||||
)
|
||||
content {
|
||||
device_name = disk.key
|
||||
source = disk.value.source
|
||||
delete_rule = (
|
||||
disk.value.delete_on_instance_deletion == true
|
||||
? "ON_PERMANENT_INSTANCE_DELETION"
|
||||
: "NEVER"
|
||||
)
|
||||
mode = disk.value.read_only == true ? "READ_ONLY" : "READ_WRITE"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,57 +14,149 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
variable "all_instances_config" {
|
||||
description = "Metadata and labels set to all instances in the group."
|
||||
type = object({
|
||||
labels = optional(map(string))
|
||||
metadata = optional(map(string))
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "auto_healing_policies" {
|
||||
description = "Auto-healing policies for this group."
|
||||
type = object({
|
||||
health_check = string
|
||||
health_check = optional(string)
|
||||
initial_delay_sec = number
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "autoscaler_config" {
|
||||
description = "Optional autoscaler configuration. Only one of 'cpu_utilization_target' 'load_balancing_utilization_target' or 'metric' can be not null."
|
||||
description = "Optional autoscaler configuration."
|
||||
type = object({
|
||||
max_replicas = number
|
||||
min_replicas = number
|
||||
cooldown_period = number
|
||||
cpu_utilization_target = number
|
||||
load_balancing_utilization_target = number
|
||||
metric = object({
|
||||
name = string
|
||||
single_instance_assignment = number
|
||||
target = number
|
||||
type = string # GAUGE, DELTA_PER_SECOND, DELTA_PER_MINUTE
|
||||
filter = string
|
||||
})
|
||||
max_replicas = number
|
||||
min_replicas = number
|
||||
cooldown_period = optional(number)
|
||||
mode = optional(string) # OFF, ONLY_UP, ON
|
||||
scaling_control = optional(object({
|
||||
down = optional(object({
|
||||
max_replicas_fixed = optional(number)
|
||||
max_replicas_percent = optional(number)
|
||||
time_window_sec = optional(number)
|
||||
}))
|
||||
in = optional(object({
|
||||
max_replicas_fixed = optional(number)
|
||||
max_replicas_percent = optional(number)
|
||||
time_window_sec = optional(number)
|
||||
}))
|
||||
}), {})
|
||||
scaling_signals = optional(object({
|
||||
cpu_utilization = optional(object({
|
||||
target = number
|
||||
optimize_availability = optional(bool)
|
||||
}))
|
||||
load_balancing_utilization = optional(object({
|
||||
target = number
|
||||
}))
|
||||
metrics = optional(list(object({
|
||||
name = string
|
||||
type = string # GAUGE, DELTA_PER_SECOND, DELTA_PER_MINUTE
|
||||
target_value = number
|
||||
single_instance_assignment = optional(number)
|
||||
time_series_filter = optional(string)
|
||||
})))
|
||||
schedules = optional(list(object({
|
||||
duration_sec = number
|
||||
name = string
|
||||
min_required_replicas = number
|
||||
cron_schedule = string
|
||||
description = optional(bool)
|
||||
timezone = optional(string)
|
||||
disabled = optional(bool)
|
||||
})))
|
||||
}), {})
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "default_version" {
|
||||
description = "Default application version template. Additional versions can be specified via the `versions` variable."
|
||||
variable "default_version_name" {
|
||||
description = "Name used for the default version."
|
||||
type = string
|
||||
default = "default"
|
||||
}
|
||||
|
||||
variable "description" {
|
||||
description = "Optional description used for all resources managed by this module."
|
||||
type = string
|
||||
default = "Terraform managed."
|
||||
}
|
||||
|
||||
variable "distribution_policy" {
|
||||
description = "DIstribution policy for regional MIG."
|
||||
type = object({
|
||||
instance_template = string
|
||||
name = string
|
||||
target_shape = optional(string)
|
||||
zones = optional(list(string))
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "health_check_config" {
|
||||
description = "Optional auto-created health check configuration, use the output self-link to set it in the auto healing policy. Refer to examples for usage."
|
||||
type = object({
|
||||
type = string # http https tcp ssl http2
|
||||
check = map(any) # actual health check block attributes
|
||||
config = map(number) # interval, thresholds, timeout
|
||||
logging = bool
|
||||
check_interval_sec = optional(number)
|
||||
description = optional(string, "Terraform managed.")
|
||||
enable_logging = optional(bool, false)
|
||||
healthy_threshold = optional(number)
|
||||
timeout_sec = optional(number)
|
||||
unhealthy_threshold = optional(number)
|
||||
grpc = optional(object({
|
||||
port = optional(number)
|
||||
port_name = optional(string)
|
||||
port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT
|
||||
service_name = optional(string)
|
||||
}))
|
||||
http = optional(object({
|
||||
host = optional(string)
|
||||
port = optional(number)
|
||||
port_name = optional(string)
|
||||
port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT
|
||||
proxy_header = optional(string)
|
||||
request_path = optional(string)
|
||||
response = optional(string)
|
||||
use_protocol = optional(string, "http") # http http2 https
|
||||
}))
|
||||
tcp = optional(object({
|
||||
port = optional(number)
|
||||
port_name = optional(string)
|
||||
port_specification = optional(string) # USE_FIXED_PORT USE_NAMED_PORT USE_SERVING_PORT
|
||||
proxy_header = optional(string)
|
||||
request = optional(string)
|
||||
response = optional(string)
|
||||
use_ssl = optional(bool, false)
|
||||
}))
|
||||
})
|
||||
default = null
|
||||
validation {
|
||||
condition = (
|
||||
(try(var.health_check_config.grpc, null) == null ? 0 : 1) +
|
||||
(try(var.health_check_config.http, null) == null ? 0 : 1) +
|
||||
(try(var.health_check_config.tcp, null) == null ? 0 : 1) <= 1
|
||||
)
|
||||
error_message = "Only one health check type can be configured at a time."
|
||||
}
|
||||
}
|
||||
|
||||
variable "instance_template" {
|
||||
description = "Instance template for the default version."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "location" {
|
||||
description = "Compute zone, or region if `regional` is set to true."
|
||||
description = "Compute zone or region."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "name" {
|
||||
description = "Managed group name."
|
||||
type = string
|
||||
|
@ -81,41 +173,30 @@ variable "project_id" {
|
|||
type = string
|
||||
}
|
||||
|
||||
variable "regional" {
|
||||
description = "Use regional instance group. When set, `location` should be set to the region."
|
||||
type = bool
|
||||
default = false
|
||||
variable "stateful_disks" {
|
||||
description = "Stateful disk configuration applied at the MIG level to all instances, in device name => on permanent instance delete rule as boolean."
|
||||
type = map(bool)
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "stateful_config" {
|
||||
description = "Stateful configuration can be done by individual instances or for all instances in the MIG. They key in per_instance_config is the name of the specific instance. The key of the stateful_disks is the 'device_name' field of the resource. Please note that device_name is defined at the OS mount level, unlike the disk name."
|
||||
type = object({
|
||||
per_instance_config = map(object({
|
||||
#name is the key
|
||||
#name = string
|
||||
stateful_disks = map(object({
|
||||
#device_name is the key
|
||||
source = string
|
||||
mode = string # READ_WRITE | READ_ONLY
|
||||
delete_rule = string # NEVER | ON_PERMANENT_INSTANCE_DELETION
|
||||
}))
|
||||
metadata = map(string)
|
||||
update_config = object({
|
||||
minimal_action = string # NONE | REPLACE | RESTART | REFRESH
|
||||
most_disruptive_allowed_action = string # REPLACE | RESTART | REFRESH | NONE
|
||||
remove_instance_state_on_destroy = bool
|
||||
})
|
||||
description = "Stateful configuration for individual instances."
|
||||
type = map(object({
|
||||
minimal_action = optional(string)
|
||||
most_disruptive_action = optional(string)
|
||||
remove_state_on_destroy = optional(bool)
|
||||
preserved_state = optional(object({
|
||||
disks = optional(map(object({
|
||||
source = string
|
||||
delete_on_instance_deletion = optional(bool)
|
||||
read_only = optional(bool)
|
||||
})))
|
||||
metadata = optional(map(string))
|
||||
}))
|
||||
|
||||
mig_config = object({
|
||||
stateful_disks = map(object({
|
||||
#device_name is the key
|
||||
delete_rule = string # NEVER | ON_PERMANENT_INSTANCE_DELETION
|
||||
}))
|
||||
})
|
||||
|
||||
})
|
||||
default = null
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "target_pools" {
|
||||
|
@ -131,32 +212,44 @@ variable "target_size" {
|
|||
}
|
||||
|
||||
variable "update_policy" {
|
||||
description = "Update policy. Type can be 'OPPORTUNISTIC' or 'PROACTIVE', action 'REPLACE' or 'restart', surge type 'fixed' or 'percent'."
|
||||
description = "Update policy. Minimal action and type are required."
|
||||
type = object({
|
||||
instance_redistribution_type = optional(string, "PROACTIVE") # NONE | PROACTIVE. The attribute is ignored if regional is set to false.
|
||||
max_surge_type = string # fixed | percent
|
||||
max_surge = number
|
||||
max_unavailable_type = string
|
||||
max_unavailable = number
|
||||
minimal_action = string # REPLACE | RESTART
|
||||
min_ready_sec = number
|
||||
type = string # OPPORTUNISTIC | PROACTIVE
|
||||
minimal_action = string
|
||||
type = string
|
||||
max_surge = optional(object({
|
||||
fixed = optional(number)
|
||||
percent = optional(number)
|
||||
}))
|
||||
max_unavailable = optional(object({
|
||||
fixed = optional(number)
|
||||
percent = optional(number)
|
||||
}))
|
||||
min_ready_sec = optional(number)
|
||||
most_disruptive_action = optional(string)
|
||||
regional_redistribution_type = optional(string)
|
||||
replacement_method = optional(string)
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "versions" {
|
||||
description = "Additional application versions, target_type is either 'fixed' or 'percent'."
|
||||
description = "Additional application versions, target_size is optional."
|
||||
type = map(object({
|
||||
instance_template = string
|
||||
target_type = string # fixed | percent
|
||||
target_size = number
|
||||
target_size = optional(object({
|
||||
fixed = optional(number)
|
||||
percent = optional(number)
|
||||
}))
|
||||
}))
|
||||
default = null
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "wait_for_instances" {
|
||||
description = "Wait for all instances to be created/updated before returning."
|
||||
type = bool
|
||||
default = null
|
||||
type = object({
|
||||
enabled = bool
|
||||
status = optional(string)
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
|
|
@ -26,25 +26,53 @@ module "folder" {
|
|||
|
||||
### Organization policies
|
||||
|
||||
To manage organization policies, the `orgpolicy.googleapis.com` service should be enabled in the quota project.
|
||||
|
||||
```hcl
|
||||
module "folder" {
|
||||
source = "./fabric/modules/folder"
|
||||
parent = "organizations/1234567890"
|
||||
name = "Folder name"
|
||||
policy_boolean = {
|
||||
"constraints/compute.disableGuestAttributesAccess" = true
|
||||
"constraints/compute.skipDefaultNetworkCreation" = true
|
||||
}
|
||||
policy_list = {
|
||||
org_policies = {
|
||||
"compute.disableGuestAttributesAccess" = {
|
||||
enforce = true
|
||||
}
|
||||
"constraints/compute.skipDefaultNetworkCreation" = {
|
||||
enforce = true
|
||||
}
|
||||
"iam.disableServiceAccountKeyCreation" = {
|
||||
enforce = true
|
||||
}
|
||||
"iam.disableServiceAccountKeyUpload" = {
|
||||
enforce = false
|
||||
rules = [
|
||||
{
|
||||
condition = {
|
||||
expression = "resource.matchTagId(\"tagKeys/1234\", \"tagValues/1234\")"
|
||||
title = "condition"
|
||||
description = "test condition"
|
||||
location = "somewhere"
|
||||
}
|
||||
enforce = true
|
||||
}
|
||||
]
|
||||
}
|
||||
"constraints/iam.allowedPolicyMemberDomains" = {
|
||||
allow = {
|
||||
values = ["C0xxxxxxx", "C0yyyyyyy"]
|
||||
}
|
||||
}
|
||||
"constraints/compute.trustedImageProjects" = {
|
||||
inherit_from_parent = null
|
||||
suggested_value = null
|
||||
status = true
|
||||
values = ["projects/my-project"]
|
||||
allow = {
|
||||
values = ["projects/my-project"]
|
||||
}
|
||||
}
|
||||
"constraints/compute.vmExternalIpAccess" = {
|
||||
deny = { all = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=4
|
||||
# tftest modules=1 resources=8
|
||||
```
|
||||
|
||||
### Firewall policy factory
|
||||
|
@ -259,7 +287,7 @@ module "folder" {
|
|||
| [iam.tf](./iam.tf) | IAM bindings, roles and audit logging resources. | <code>google_folder_iam_binding</code> · <code>google_folder_iam_member</code> |
|
||||
| [logging.tf](./logging.tf) | Log sinks and supporting resources. | <code>google_bigquery_dataset_iam_member</code> · <code>google_logging_folder_exclusion</code> · <code>google_logging_folder_sink</code> · <code>google_project_iam_member</code> · <code>google_pubsub_topic_iam_member</code> · <code>google_storage_bucket_iam_member</code> |
|
||||
| [main.tf](./main.tf) | Module-level locals and resources. | <code>google_essential_contacts_contact</code> · <code>google_folder</code> |
|
||||
| [organization-policies.tf](./organization-policies.tf) | Folder-level organization policies. | <code>google_folder_organization_policy</code> |
|
||||
| [organization-policies.tf](./organization-policies.tf) | Folder-level organization policies. | <code>google_org_policy_policy</code> |
|
||||
| [outputs.tf](./outputs.tf) | Module outputs. | |
|
||||
| [tags.tf](./tags.tf) | None | <code>google_tags_tag_binding</code> |
|
||||
| [variables.tf](./variables.tf) | Module variables. | |
|
||||
|
@ -282,10 +310,9 @@ module "folder" {
|
|||
| [logging_exclusions](variables.tf#L98) | Logging exclusions for this folder in the form {NAME -> FILTER}. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [logging_sinks](variables.tf#L105) | Logging sinks to create for this folder. | <code title="map(object({ destination = string type = string filter = string include_children = bool exclusions = map(string) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [name](variables.tf#L126) | Folder name. | <code>string</code> | | <code>null</code> |
|
||||
| [parent](variables.tf#L132) | Parent in folders/folder_id or organizations/org_id format. | <code>string</code> | | <code>null</code> |
|
||||
| [policy_boolean](variables.tf#L142) | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code>map(bool)</code> | | <code>{}</code> |
|
||||
| [policy_list](variables.tf#L149) | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map(object({ inherit_from_parent = bool suggested_value = string status = bool values = list(string) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [tag_bindings](variables.tf#L161) | Tag bindings for this folder, in key => tag value id format. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [org_policies](variables.tf#L132) | Organization policies applied to this folder keyed by policy name. | <code title="map(object({ inherit_from_parent = optional(bool) # for list policies only. reset = optional(bool) allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool, true) # for boolean policies only. rules = optional(list(object({ allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool, true) # for boolean policies only. condition = object({ description = optional(string) expression = optional(string) location = optional(string) title = optional(string) }) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [parent](variables.tf#L172) | Parent in folders/folder_id or organizations/org_id format. | <code>string</code> | | <code>null</code> |
|
||||
| [tag_bindings](variables.tf#L182) | Tag bindings for this folder, in key => tag value id format. | <code>map(string)</code> | | <code>null</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
@ -295,7 +322,7 @@ module "folder" {
|
|||
| [firewall_policy_id](outputs.tf#L21) | Map of firewall policy ids created in this folder. | |
|
||||
| [folder](outputs.tf#L26) | Folder resource. | |
|
||||
| [id](outputs.tf#L31) | Folder id. | |
|
||||
| [name](outputs.tf#L41) | Folder name. | |
|
||||
| [sink_writer_identities](outputs.tf#L46) | Writer identities created for each sink. | |
|
||||
| [name](outputs.tf#L40) | Folder name. | |
|
||||
| [sink_writer_identities](outputs.tf#L45) | Writer identities created for each sink. | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
|
|
@ -16,75 +16,79 @@
|
|||
|
||||
# tfdoc:file:description Folder-level organization policies.
|
||||
|
||||
resource "google_folder_organization_policy" "boolean" {
|
||||
for_each = var.policy_boolean
|
||||
folder = local.folder.name
|
||||
constraint = each.key
|
||||
|
||||
dynamic "boolean_policy" {
|
||||
for_each = each.value == null ? [] : [each.value]
|
||||
iterator = policy
|
||||
content {
|
||||
enforced = policy.value
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "restore_policy" {
|
||||
for_each = each.value == null ? [""] : []
|
||||
content {
|
||||
default = true
|
||||
}
|
||||
locals {
|
||||
org_policies = {
|
||||
for k, v in var.org_policies :
|
||||
k => merge(v, {
|
||||
is_boolean_policy = v.allow == null && v.deny == null
|
||||
has_values = (
|
||||
length(coalesce(try(v.allow.values, []), [])) > 0 ||
|
||||
length(coalesce(try(v.deny.values, []), [])) > 0
|
||||
)
|
||||
rules = [
|
||||
for r in v.rules :
|
||||
merge(r, {
|
||||
has_values = (
|
||||
length(coalesce(try(r.allow.values, []), [])) > 0 ||
|
||||
length(coalesce(try(r.deny.values, []), [])) > 0
|
||||
)
|
||||
})
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_folder_organization_policy" "list" {
|
||||
for_each = var.policy_list
|
||||
folder = local.folder.name
|
||||
constraint = each.key
|
||||
resource "google_org_policy_policy" "default" {
|
||||
for_each = local.org_policies
|
||||
name = "${local.folder.name}/policies/${each.key}"
|
||||
parent = local.folder.name
|
||||
|
||||
dynamic "list_policy" {
|
||||
for_each = each.value.status == null ? [] : [each.value]
|
||||
iterator = policy
|
||||
content {
|
||||
inherit_from_parent = policy.value.inherit_from_parent
|
||||
suggested_value = policy.value.suggested_value
|
||||
dynamic "allow" {
|
||||
for_each = policy.value.status ? [""] : []
|
||||
spec {
|
||||
inherit_from_parent = each.value.inherit_from_parent
|
||||
reset = each.value.reset
|
||||
|
||||
rules {
|
||||
allow_all = try(each.value.allow.all, null) == true ? "TRUE" : null
|
||||
deny_all = try(each.value.deny.all, null) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
each.value.is_boolean_policy && each.value.enforce != null
|
||||
? upper(tostring(each.value.enforce))
|
||||
: null
|
||||
)
|
||||
dynamic "values" {
|
||||
for_each = each.value.has_values ? [1] : []
|
||||
content {
|
||||
values = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? policy.value.values
|
||||
: null
|
||||
)
|
||||
all = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? null
|
||||
: true
|
||||
)
|
||||
allowed_values = try(each.value.allow.values, null)
|
||||
denied_values = try(each.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
dynamic "deny" {
|
||||
for_each = policy.value.status ? [] : [""]
|
||||
content {
|
||||
values = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? policy.value.values
|
||||
: null
|
||||
)
|
||||
all = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? null
|
||||
: true
|
||||
)
|
||||
}
|
||||
|
||||
dynamic "rules" {
|
||||
for_each = each.value.rules
|
||||
iterator = rule
|
||||
content {
|
||||
allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null
|
||||
deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
each.value.is_boolean_policy && rule.value.enforce != null
|
||||
? upper(tostring(rule.value.enforce))
|
||||
: null
|
||||
)
|
||||
condition {
|
||||
description = rule.value.condition.description
|
||||
expression = rule.value.condition.expression
|
||||
location = rule.value.condition.location
|
||||
title = rule.value.condition.title
|
||||
}
|
||||
dynamic "values" {
|
||||
for_each = rule.value.has_values ? [1] : []
|
||||
content {
|
||||
allowed_values = try(rule.value.allow.values, null)
|
||||
denied_values = try(rule.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "restore_policy" {
|
||||
for_each = each.value.status == null ? [true] : []
|
||||
content {
|
||||
default = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,8 +33,7 @@ output "id" {
|
|||
value = local.folder.name
|
||||
depends_on = [
|
||||
google_folder_iam_binding.authoritative,
|
||||
google_folder_organization_policy.boolean,
|
||||
google_folder_organization_policy.list
|
||||
google_org_policy_policy.default,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -129,6 +129,46 @@ variable "name" {
|
|||
default = null
|
||||
}
|
||||
|
||||
variable "org_policies" {
|
||||
description = "Organization policies applied to this folder keyed by policy name."
|
||||
type = map(object({
|
||||
inherit_from_parent = optional(bool) # for list policies only.
|
||||
reset = optional(bool)
|
||||
|
||||
# default (unconditional) values
|
||||
allow = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
deny = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
enforce = optional(bool, true) # for boolean policies only.
|
||||
|
||||
# conditional values
|
||||
rules = optional(list(object({
|
||||
allow = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
deny = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
enforce = optional(bool, true) # for boolean policies only.
|
||||
condition = object({
|
||||
description = optional(string)
|
||||
expression = optional(string)
|
||||
location = optional(string)
|
||||
title = optional(string)
|
||||
})
|
||||
})), [])
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "parent" {
|
||||
description = "Parent in folders/folder_id or organizations/org_id format."
|
||||
type = string
|
||||
|
@ -139,25 +179,6 @@ variable "parent" {
|
|||
}
|
||||
}
|
||||
|
||||
variable "policy_boolean" {
|
||||
description = "Map of boolean org policies and enforcement value, set value to null for policy restore."
|
||||
type = map(bool)
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "policy_list" {
|
||||
description = "Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny."
|
||||
type = map(object({
|
||||
inherit_from_parent = bool
|
||||
suggested_value = string
|
||||
status = bool
|
||||
values = list(string)
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "tag_bindings" {
|
||||
description = "Tag bindings for this folder, in key => tag value id format."
|
||||
type = map(string)
|
||||
|
|
|
@ -7,6 +7,8 @@ This module allows managing several organization properties:
|
|||
- audit logging configuration for services
|
||||
- organization policies
|
||||
|
||||
To manage organization policies, the `orgpolicy.googleapis.com` service should be enabled in the quota project.
|
||||
|
||||
## Example
|
||||
|
||||
```hcl
|
||||
|
@ -19,20 +21,47 @@ module "org" {
|
|||
iam = {
|
||||
"roles/resourcemanager.projectCreator" = ["group:cloud-admins@example.org"]
|
||||
}
|
||||
policy_boolean = {
|
||||
"constraints/compute.disableGuestAttributesAccess" = true
|
||||
"constraints/compute.skipDefaultNetworkCreation" = true
|
||||
}
|
||||
policy_list = {
|
||||
|
||||
org_policies = {
|
||||
"compute.disableGuestAttributesAccess" = {
|
||||
enforce = true
|
||||
}
|
||||
"constraints/compute.skipDefaultNetworkCreation" = {
|
||||
enforce = true
|
||||
}
|
||||
"iam.disableServiceAccountKeyCreation" = {
|
||||
enforce = true
|
||||
}
|
||||
"iam.disableServiceAccountKeyUpload" = {
|
||||
enforce = false
|
||||
rules = [
|
||||
{
|
||||
condition = {
|
||||
expression = "resource.matchTagId(\"tagKeys/1234\", \"tagValues/1234\")"
|
||||
title = "condition"
|
||||
description = "test condition"
|
||||
location = "somewhere"
|
||||
}
|
||||
enforce = true
|
||||
}
|
||||
]
|
||||
}
|
||||
"constraints/iam.allowedPolicyMemberDomains" = {
|
||||
allow = {
|
||||
values = ["C0xxxxxxx", "C0yyyyyyy"]
|
||||
}
|
||||
}
|
||||
"constraints/compute.trustedImageProjects" = {
|
||||
inherit_from_parent = null
|
||||
suggested_value = null
|
||||
status = true
|
||||
values = ["projects/my-project"]
|
||||
allow = {
|
||||
values = ["projects/my-project"]
|
||||
}
|
||||
}
|
||||
"constraints/compute.vmExternalIpAccess" = {
|
||||
deny = { all = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=6
|
||||
# tftest modules=1 resources=10
|
||||
```
|
||||
|
||||
## IAM
|
||||
|
@ -281,7 +310,7 @@ module "org" {
|
|||
| [iam.tf](./iam.tf) | IAM bindings, roles and audit logging resources. | <code>google_organization_iam_audit_config</code> · <code>google_organization_iam_binding</code> · <code>google_organization_iam_custom_role</code> · <code>google_organization_iam_member</code> · <code>google_organization_iam_policy</code> |
|
||||
| [logging.tf](./logging.tf) | Log sinks and supporting resources. | <code>google_bigquery_dataset_iam_member</code> · <code>google_logging_organization_exclusion</code> · <code>google_logging_organization_sink</code> · <code>google_project_iam_member</code> · <code>google_pubsub_topic_iam_member</code> · <code>google_storage_bucket_iam_member</code> |
|
||||
| [main.tf](./main.tf) | Module-level locals and resources. | <code>google_essential_contacts_contact</code> |
|
||||
| [organization-policies.tf](./organization-policies.tf) | Organization-level organization policies. | <code>google_organization_policy</code> |
|
||||
| [organization-policies.tf](./organization-policies.tf) | Organization-level organization policies. | <code>google_org_policy_policy</code> |
|
||||
| [outputs.tf](./outputs.tf) | Module outputs. | |
|
||||
| [tags.tf](./tags.tf) | None | <code>google_tags_tag_binding</code> · <code>google_tags_tag_key</code> · <code>google_tags_tag_key_iam_binding</code> · <code>google_tags_tag_value</code> · <code>google_tags_tag_value_iam_binding</code> |
|
||||
| [variables.tf](./variables.tf) | Module variables. | |
|
||||
|
@ -291,7 +320,7 @@ module "org" {
|
|||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [organization_id](variables.tf#L151) | Organization id in organizations/nnnnnn format. | <code>string</code> | ✓ | |
|
||||
| [organization_id](variables.tf#L191) | Organization id in organizations/nnnnnn format. | <code>string</code> | ✓ | |
|
||||
| [contacts](variables.tf#L17) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [custom_roles](variables.tf#L24) | Map of role name => list of permissions to create in this project. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [firewall_policies](variables.tf#L31) | Hierarchical firewall policy rules created in the organization. | <code title="map(map(object({ action = string description = string direction = string logging = bool ports = map(list(string)) priority = number ranges = list(string) target_resources = list(string) target_service_accounts = list(string) })))">map(map(object({…})))</code> | | <code>{}</code> |
|
||||
|
@ -306,10 +335,9 @@ module "org" {
|
|||
| [iam_bindings_authoritative](variables.tf#L116) | IAM authoritative bindings, in {ROLE => [MEMBERS]} format. Roles and members not explicitly listed will be cleared. Bindings should also be authoritative when using authoritative audit config. Use with caution. | <code>map(list(string))</code> | | <code>null</code> |
|
||||
| [logging_exclusions](variables.tf#L122) | Logging exclusions for this organization in the form {NAME -> FILTER}. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [logging_sinks](variables.tf#L129) | Logging sinks to create for this organization. | <code title="map(object({ destination = string type = string filter = string include_children = bool bq_partitioned_table = bool exclusions = map(string) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [policy_boolean](variables.tf#L160) | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code>map(bool)</code> | | <code>{}</code> |
|
||||
| [policy_list](variables.tf#L167) | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map(object({ inherit_from_parent = bool suggested_value = string status = bool values = list(string) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [tag_bindings](variables.tf#L179) | Tag bindings for this organization, in key => tag value id format. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [tags](variables.tf#L185) | Tags by key name. The `iam` attribute behaves like the similarly named one at module level. | <code title="map(object({ description = string iam = map(list(string)) values = map(object({ description = string iam = map(list(string)) })) }))">map(object({…}))</code> | | <code>null</code> |
|
||||
| [org_policies](variables.tf#L151) | Organization policies applied to this organization keyed by policy name. | <code title="map(object({ inherit_from_parent = optional(bool) # for list policies only. reset = optional(bool) allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool, true) # for boolean policies only. rules = optional(list(object({ allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool, true) # for boolean policies only. condition = object({ description = optional(string) expression = optional(string) location = optional(string) title = optional(string) }) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [tag_bindings](variables.tf#L200) | Tag bindings for this organization, in key => tag value id format. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [tags](variables.tf#L206) | Tags by key name. The `iam` attribute behaves like the similarly named one at module level. | <code title="map(object({ description = string iam = map(list(string)) values = map(object({ description = string iam = map(list(string)) })) }))">map(object({…}))</code> | | <code>null</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
@ -320,8 +348,8 @@ module "org" {
|
|||
| [firewall_policies](outputs.tf#L36) | Map of firewall policy resources created in the organization. | |
|
||||
| [firewall_policy_id](outputs.tf#L41) | Map of firewall policy ids created in the organization. | |
|
||||
| [organization_id](outputs.tf#L46) | Organization id dependent on module resources. | |
|
||||
| [sink_writer_identities](outputs.tf#L64) | Writer identities created for each sink. | |
|
||||
| [tag_keys](outputs.tf#L72) | Tag key resources. | |
|
||||
| [tag_values](outputs.tf#L79) | Tag value resources. | |
|
||||
| [sink_writer_identities](outputs.tf#L63) | Writer identities created for each sink. | |
|
||||
| [tag_keys](outputs.tf#L71) | Tag key resources. | |
|
||||
| [tag_values](outputs.tf#L78) | Tag value resources. | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
|
|
@ -16,83 +16,79 @@
|
|||
|
||||
# tfdoc:file:description Organization-level organization policies.
|
||||
|
||||
resource "google_organization_policy" "boolean" {
|
||||
for_each = var.policy_boolean
|
||||
org_id = local.organization_id_numeric
|
||||
constraint = each.key
|
||||
|
||||
dynamic "boolean_policy" {
|
||||
for_each = each.value == null ? [] : [each.value]
|
||||
iterator = policy
|
||||
content {
|
||||
enforced = policy.value
|
||||
}
|
||||
locals {
|
||||
org_policies = {
|
||||
for k, v in var.org_policies :
|
||||
k => merge(v, {
|
||||
is_boolean_policy = v.allow == null && v.deny == null
|
||||
has_values = (
|
||||
length(coalesce(try(v.allow.values, []), [])) > 0 ||
|
||||
length(coalesce(try(v.deny.values, []), [])) > 0
|
||||
)
|
||||
rules = [
|
||||
for r in v.rules :
|
||||
merge(r, {
|
||||
has_values = (
|
||||
length(coalesce(try(r.allow.values, []), [])) > 0 ||
|
||||
length(coalesce(try(r.deny.values, []), [])) > 0
|
||||
)
|
||||
})
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "restore_policy" {
|
||||
for_each = each.value == null ? [""] : []
|
||||
content {
|
||||
default = true
|
||||
}
|
||||
}
|
||||
|
||||
depends_on = [
|
||||
google_organization_iam_audit_config.config,
|
||||
google_organization_iam_binding.authoritative,
|
||||
google_organization_iam_custom_role.roles,
|
||||
google_organization_iam_member.additive,
|
||||
google_organization_iam_policy.authoritative,
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_organization_policy" "list" {
|
||||
for_each = var.policy_list
|
||||
org_id = local.organization_id_numeric
|
||||
constraint = each.key
|
||||
|
||||
dynamic "list_policy" {
|
||||
for_each = each.value.status == null ? [] : [each.value]
|
||||
iterator = policy
|
||||
content {
|
||||
inherit_from_parent = policy.value.inherit_from_parent
|
||||
suggested_value = policy.value.suggested_value
|
||||
dynamic "allow" {
|
||||
for_each = policy.value.status ? [""] : []
|
||||
content {
|
||||
values = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? policy.value.values
|
||||
: null
|
||||
)
|
||||
all = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? null
|
||||
: true
|
||||
)
|
||||
}
|
||||
}
|
||||
dynamic "deny" {
|
||||
for_each = policy.value.status ? [] : [""]
|
||||
content {
|
||||
values = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? policy.value.values
|
||||
: null
|
||||
)
|
||||
all = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? null
|
||||
: true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "restore_policy" {
|
||||
for_each = each.value.status == null ? [true] : []
|
||||
content {
|
||||
default = true
|
||||
resource "google_org_policy_policy" "default" {
|
||||
for_each = local.org_policies
|
||||
name = "${var.organization_id}/policies/${each.key}"
|
||||
parent = var.organization_id
|
||||
|
||||
spec {
|
||||
inherit_from_parent = each.value.inherit_from_parent
|
||||
reset = each.value.reset
|
||||
|
||||
rules {
|
||||
allow_all = try(each.value.allow.all, null) == true ? "TRUE" : null
|
||||
deny_all = try(each.value.deny.all, null) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
each.value.is_boolean_policy && each.value.enforce != null
|
||||
? upper(tostring(each.value.enforce))
|
||||
: null
|
||||
)
|
||||
dynamic "values" {
|
||||
for_each = each.value.has_values ? [1] : []
|
||||
content {
|
||||
allowed_values = try(each.value.allow.values, null)
|
||||
denied_values = try(each.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "rules" {
|
||||
for_each = each.value.rules
|
||||
iterator = rule
|
||||
content {
|
||||
allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null
|
||||
deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
each.value.is_boolean_policy && rule.value.enforce != null
|
||||
? upper(tostring(rule.value.enforce))
|
||||
: null
|
||||
)
|
||||
condition {
|
||||
description = rule.value.condition.description
|
||||
expression = rule.value.condition.expression
|
||||
location = rule.value.condition.location
|
||||
title = rule.value.condition.title
|
||||
}
|
||||
dynamic "values" {
|
||||
for_each = rule.value.has_values ? [1] : []
|
||||
content {
|
||||
allowed_values = try(rule.value.allow.values, null)
|
||||
denied_values = try(rule.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,4 +99,5 @@ resource "google_organization_policy" "list" {
|
|||
google_organization_iam_member.additive,
|
||||
google_organization_iam_policy.authoritative,
|
||||
]
|
||||
|
||||
}
|
||||
|
|
|
@ -52,8 +52,7 @@ output "organization_id" {
|
|||
google_organization_iam_custom_role.roles,
|
||||
google_organization_iam_member.additive,
|
||||
google_organization_iam_policy.authoritative,
|
||||
google_organization_policy.boolean,
|
||||
google_organization_policy.list,
|
||||
google_org_policy_policy.default,
|
||||
google_tags_tag_key.default,
|
||||
google_tags_tag_key_iam_binding.default,
|
||||
google_tags_tag_value.default,
|
||||
|
|
|
@ -148,6 +148,46 @@ variable "logging_sinks" {
|
|||
nullable = false
|
||||
}
|
||||
|
||||
variable "org_policies" {
|
||||
description = "Organization policies applied to this organization keyed by policy name."
|
||||
type = map(object({
|
||||
inherit_from_parent = optional(bool) # for list policies only.
|
||||
reset = optional(bool)
|
||||
|
||||
# default (unconditional) values
|
||||
allow = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
deny = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
enforce = optional(bool, true) # for boolean policies only.
|
||||
|
||||
# conditional values
|
||||
rules = optional(list(object({
|
||||
allow = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
deny = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
enforce = optional(bool, true) # for boolean policies only.
|
||||
condition = object({
|
||||
description = optional(string)
|
||||
expression = optional(string)
|
||||
location = optional(string)
|
||||
title = optional(string)
|
||||
})
|
||||
})), [])
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "organization_id" {
|
||||
description = "Organization id in organizations/nnnnnn format."
|
||||
type = string
|
||||
|
@ -157,25 +197,6 @@ variable "organization_id" {
|
|||
}
|
||||
}
|
||||
|
||||
variable "policy_boolean" {
|
||||
description = "Map of boolean org policies and enforcement value, set value to null for policy restore."
|
||||
type = map(bool)
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "policy_list" {
|
||||
description = "Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny."
|
||||
type = map(object({
|
||||
inherit_from_parent = bool
|
||||
suggested_value = string
|
||||
status = bool
|
||||
values = list(string)
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "tag_bindings" {
|
||||
description = "Tag bindings for this organization, in key => tag value id format."
|
||||
type = map(string)
|
||||
|
|
|
@ -156,6 +156,8 @@ module "project" {
|
|||
|
||||
## Organization policies
|
||||
|
||||
To manage organization policies, the `orgpolicy.googleapis.com` service should be enabled in the quota project.
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
source = "./fabric/modules/project"
|
||||
|
@ -167,20 +169,46 @@ module "project" {
|
|||
"container.googleapis.com",
|
||||
"stackdriver.googleapis.com"
|
||||
]
|
||||
policy_boolean = {
|
||||
"constraints/compute.disableGuestAttributesAccess" = true
|
||||
"constraints/compute.skipDefaultNetworkCreation" = true
|
||||
}
|
||||
policy_list = {
|
||||
org_policies = {
|
||||
"compute.disableGuestAttributesAccess" = {
|
||||
enforce = true
|
||||
}
|
||||
"constraints/compute.skipDefaultNetworkCreation" = {
|
||||
enforce = true
|
||||
}
|
||||
"iam.disableServiceAccountKeyCreation" = {
|
||||
enforce = true
|
||||
}
|
||||
"iam.disableServiceAccountKeyUpload" = {
|
||||
enforce = false
|
||||
rules = [
|
||||
{
|
||||
condition = {
|
||||
expression = "resource.matchTagId(\"tagKeys/1234\", \"tagValues/1234\")"
|
||||
title = "condition"
|
||||
description = "test condition"
|
||||
location = "somewhere"
|
||||
}
|
||||
enforce = true
|
||||
}
|
||||
]
|
||||
}
|
||||
"constraints/iam.allowedPolicyMemberDomains" = {
|
||||
allow = {
|
||||
values = ["C0xxxxxxx", "C0yyyyyyy"]
|
||||
}
|
||||
}
|
||||
"constraints/compute.trustedImageProjects" = {
|
||||
inherit_from_parent = null
|
||||
suggested_value = null
|
||||
status = true
|
||||
values = ["projects/my-project"]
|
||||
allow = {
|
||||
values = ["projects/my-project"]
|
||||
}
|
||||
}
|
||||
"constraints/compute.vmExternalIpAccess" = {
|
||||
deny = { all = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=6
|
||||
# tftest modules=1 resources=10
|
||||
```
|
||||
|
||||
## Logging Sinks
|
||||
|
@ -349,7 +377,7 @@ output "compute_robot" {
|
|||
| [iam.tf](./iam.tf) | Generic and OSLogin-specific IAM bindings and roles. | <code>google_project_iam_binding</code> · <code>google_project_iam_custom_role</code> · <code>google_project_iam_member</code> |
|
||||
| [logging.tf](./logging.tf) | Log sinks and supporting resources. | <code>google_bigquery_dataset_iam_member</code> · <code>google_logging_project_exclusion</code> · <code>google_logging_project_sink</code> · <code>google_project_iam_member</code> · <code>google_pubsub_topic_iam_member</code> · <code>google_storage_bucket_iam_member</code> |
|
||||
| [main.tf](./main.tf) | Module-level locals and resources. | <code>google_compute_project_metadata_item</code> · <code>google_essential_contacts_contact</code> · <code>google_monitoring_monitored_project</code> · <code>google_project</code> · <code>google_project_service</code> · <code>google_resource_manager_lien</code> |
|
||||
| [organization-policies.tf](./organization-policies.tf) | Project-level organization policies. | <code>google_project_organization_policy</code> |
|
||||
| [organization-policies.tf](./organization-policies.tf) | Project-level organization policies. | <code>google_org_policy_policy</code> |
|
||||
| [outputs.tf](./outputs.tf) | Module outputs. | |
|
||||
| [service-accounts.tf](./service-accounts.tf) | Service identities and supporting resources. | <code>google_kms_crypto_key_iam_member</code> · <code>google_project_default_service_accounts</code> · <code>google_project_iam_member</code> · <code>google_project_service_identity</code> |
|
||||
| [shared-vpc.tf](./shared-vpc.tf) | Shared VPC project-level configuration. | <code>google_compute_shared_vpc_host_project</code> · <code>google_compute_shared_vpc_service_project</code> · <code>google_project_iam_member</code> |
|
||||
|
@ -367,8 +395,8 @@ output "compute_robot" {
|
|||
| [billing_account](variables.tf#L23) | Billing account id. | <code>string</code> | | <code>null</code> |
|
||||
| [contacts](variables.tf#L29) | List of essential contacts for this resource. Must be in the form EMAIL -> [NOTIFICATION_TYPES]. Valid notification types are ALL, SUSPENSION, SECURITY, TECHNICAL, BILLING, LEGAL, PRODUCT_UPDATES. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [custom_roles](variables.tf#L36) | Map of role name => list of permissions to create in this project. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [default_service_account](variables.tf#L49) | Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`. | <code>string</code> | | <code>"keep"</code> |
|
||||
| [descriptive_name](variables.tf#L43) | Name of the project name. Used for project name instead of `name` variable. | <code>string</code> | | <code>null</code> |
|
||||
| [default_service_account](variables.tf#L43) | Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`. | <code>string</code> | | <code>"keep"</code> |
|
||||
| [descriptive_name](variables.tf#L49) | Name of the project name. Used for project name instead of `name` variable. | <code>string</code> | | <code>null</code> |
|
||||
| [group_iam](variables.tf#L55) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam](variables.tf#L62) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [iam_additive](variables.tf#L69) | IAM additive bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
|
@ -378,23 +406,22 @@ output "compute_robot" {
|
|||
| [logging_exclusions](variables.tf#L95) | Logging exclusions for this project in the form {NAME -> FILTER}. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [logging_sinks](variables.tf#L102) | Logging sinks to create for this project. | <code title="map(object({ destination = string type = string filter = string iam = bool unique_writer = bool exclusions = map(string) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [metric_scopes](variables.tf#L124) | List of projects that will act as metric scopes for this project. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [oslogin](variables.tf#L136) | Enable OS Login. | <code>bool</code> | | <code>false</code> |
|
||||
| [oslogin_admins](variables.tf#L142) | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [oslogin_users](variables.tf#L150) | List of IAM-style identities that will be granted roles necessary for OS Login users. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [parent](variables.tf#L157) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | <code>string</code> | | <code>null</code> |
|
||||
| [policy_boolean](variables.tf#L167) | Map of boolean org policies and enforcement value, set value to null for policy restore. | <code>map(bool)</code> | | <code>{}</code> |
|
||||
| [policy_list](variables.tf#L174) | Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny. | <code title="map(object({ inherit_from_parent = bool suggested_value = string status = bool values = list(string) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [prefix](variables.tf#L186) | Prefix used to generate project id and name. | <code>string</code> | | <code>null</code> |
|
||||
| [project_create](variables.tf#L192) | Create project. When set to false, uses a data source to reference existing project. | <code>bool</code> | | <code>true</code> |
|
||||
| [service_config](variables.tf#L198) | Configure service API activation. | <code title="object({ disable_on_destroy = bool disable_dependent_services = bool })">object({…})</code> | | <code title="{ disable_on_destroy = false disable_dependent_services = false }">{…}</code> |
|
||||
| [service_encryption_key_ids](variables.tf#L210) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [service_perimeter_bridges](variables.tf#L217) | Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. | <code>list(string)</code> | | <code>null</code> |
|
||||
| [service_perimeter_standard](variables.tf#L224) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | <code>string</code> | | <code>null</code> |
|
||||
| [services](variables.tf#L230) | Service APIs to enable. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [shared_vpc_host_config](variables.tf#L236) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code title="object({ enabled = bool service_projects = optional(list(string), []) })">object({…})</code> | | <code>null</code> |
|
||||
| [shared_vpc_service_config](variables.tf#L245) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object({ host_project = string service_identity_iam = optional(map(list(string))) })">object({…})</code> | | <code>null</code> |
|
||||
| [skip_delete](variables.tf#L255) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> |
|
||||
| [tag_bindings](variables.tf#L261) | Tag bindings for this project, in key => tag value id format. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [org_policies](variables.tf#L136) | Organization policies applied to this project keyed by policy name. | <code title="map(object({ inherit_from_parent = optional(bool) # for list policies only. reset = optional(bool) allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool, true) # for boolean policies only. rules = optional(list(object({ allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool, true) # for boolean policies only. condition = object({ description = optional(string) expression = optional(string) location = optional(string) title = optional(string) }) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [oslogin](variables.tf#L176) | Enable OS Login. | <code>bool</code> | | <code>false</code> |
|
||||
| [oslogin_admins](variables.tf#L182) | List of IAM-style identities that will be granted roles necessary for OS Login administrators. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [oslogin_users](variables.tf#L190) | List of IAM-style identities that will be granted roles necessary for OS Login users. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [parent](variables.tf#L197) | Parent folder or organization in 'folders/folder_id' or 'organizations/org_id' format. | <code>string</code> | | <code>null</code> |
|
||||
| [prefix](variables.tf#L207) | Prefix used to generate project id and name. | <code>string</code> | | <code>null</code> |
|
||||
| [project_create](variables.tf#L213) | Create project. When set to false, uses a data source to reference existing project. | <code>bool</code> | | <code>true</code> |
|
||||
| [service_config](variables.tf#L219) | Configure service API activation. | <code title="object({ disable_on_destroy = bool disable_dependent_services = bool })">object({…})</code> | | <code title="{ disable_on_destroy = false disable_dependent_services = false }">{…}</code> |
|
||||
| [service_encryption_key_ids](variables.tf#L231) | Cloud KMS encryption key in {SERVICE => [KEY_URL]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||
| [service_perimeter_bridges](variables.tf#L238) | Name of VPC-SC Bridge perimeters to add project into. See comment in the variables file for format. | <code>list(string)</code> | | <code>null</code> |
|
||||
| [service_perimeter_standard](variables.tf#L245) | Name of VPC-SC Standard perimeter to add project into. See comment in the variables file for format. | <code>string</code> | | <code>null</code> |
|
||||
| [services](variables.tf#L251) | Service APIs to enable. | <code>list(string)</code> | | <code>[]</code> |
|
||||
| [shared_vpc_host_config](variables.tf#L257) | Configures this project as a Shared VPC host project (mutually exclusive with shared_vpc_service_project). | <code title="object({ enabled = bool service_projects = optional(list(string), []) })">object({…})</code> | | <code>null</code> |
|
||||
| [shared_vpc_service_config](variables.tf#L266) | Configures this project as a Shared VPC service project (mutually exclusive with shared_vpc_host_config). | <code title="object({ host_project = string service_identity_iam = optional(map(list(string))) })">object({…})</code> | | <code>null</code> |
|
||||
| [skip_delete](variables.tf#L276) | Allows the underlying resources to be destroyed without destroying the project itself. | <code>bool</code> | | <code>false</code> |
|
||||
| [tag_bindings](variables.tf#L282) | Tag bindings for this project, in key => tag value id format. | <code>map(string)</code> | | <code>null</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
@ -402,9 +429,9 @@ output "compute_robot" {
|
|||
|---|---|:---:|
|
||||
| [custom_roles](outputs.tf#L17) | Ids of the created custom roles. | |
|
||||
| [name](outputs.tf#L25) | Project name. | |
|
||||
| [number](outputs.tf#L38) | Project number. | |
|
||||
| [project_id](outputs.tf#L56) | Project id. | |
|
||||
| [service_accounts](outputs.tf#L76) | Product robot service accounts in project. | |
|
||||
| [sink_writer_identities](outputs.tf#L92) | Writer identities created for each sink. | |
|
||||
| [number](outputs.tf#L37) | Project number. | |
|
||||
| [project_id](outputs.tf#L54) | Project id. | |
|
||||
| [service_accounts](outputs.tf#L73) | Product robot service accounts in project. | |
|
||||
| [sink_writer_identities](outputs.tf#L89) | Writer identities created for each sink. | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
|
|
@ -16,75 +16,79 @@
|
|||
|
||||
# tfdoc:file:description Project-level organization policies.
|
||||
|
||||
resource "google_project_organization_policy" "boolean" {
|
||||
for_each = var.policy_boolean
|
||||
project = local.project.project_id
|
||||
constraint = each.key
|
||||
|
||||
dynamic "boolean_policy" {
|
||||
for_each = each.value == null ? [] : [each.value]
|
||||
iterator = policy
|
||||
content {
|
||||
enforced = policy.value
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "restore_policy" {
|
||||
for_each = each.value == null ? [""] : []
|
||||
content {
|
||||
default = true
|
||||
}
|
||||
locals {
|
||||
org_policies = {
|
||||
for k, v in var.org_policies :
|
||||
k => merge(v, {
|
||||
is_boolean_policy = v.allow == null && v.deny == null
|
||||
has_values = (
|
||||
length(coalesce(try(v.allow.values, []), [])) > 0 ||
|
||||
length(coalesce(try(v.deny.values, []), [])) > 0
|
||||
)
|
||||
rules = [
|
||||
for r in v.rules :
|
||||
merge(r, {
|
||||
has_values = (
|
||||
length(coalesce(try(r.allow.values, []), [])) > 0 ||
|
||||
length(coalesce(try(r.deny.values, []), [])) > 0
|
||||
)
|
||||
})
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_project_organization_policy" "list" {
|
||||
for_each = var.policy_list
|
||||
project = local.project.project_id
|
||||
constraint = each.key
|
||||
resource "google_org_policy_policy" "default" {
|
||||
for_each = local.org_policies
|
||||
name = "projects/${local.project.project_id}/policies/${each.key}"
|
||||
parent = "projects/${local.project.project_id}"
|
||||
|
||||
dynamic "list_policy" {
|
||||
for_each = each.value.status == null ? [] : [each.value]
|
||||
iterator = policy
|
||||
content {
|
||||
inherit_from_parent = policy.value.inherit_from_parent
|
||||
suggested_value = policy.value.suggested_value
|
||||
dynamic "allow" {
|
||||
for_each = policy.value.status ? [""] : []
|
||||
spec {
|
||||
inherit_from_parent = each.value.inherit_from_parent
|
||||
reset = each.value.reset
|
||||
|
||||
rules {
|
||||
allow_all = try(each.value.allow.all, null) == true ? "TRUE" : null
|
||||
deny_all = try(each.value.deny.all, null) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
each.value.is_boolean_policy && each.value.enforce != null
|
||||
? upper(tostring(each.value.enforce))
|
||||
: null
|
||||
)
|
||||
dynamic "values" {
|
||||
for_each = each.value.has_values ? [1] : []
|
||||
content {
|
||||
values = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? policy.value.values
|
||||
: null
|
||||
)
|
||||
all = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? null
|
||||
: true
|
||||
)
|
||||
allowed_values = try(each.value.allow.values, null)
|
||||
denied_values = try(each.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
dynamic "deny" {
|
||||
for_each = policy.value.status ? [] : [""]
|
||||
content {
|
||||
values = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? policy.value.values
|
||||
: null
|
||||
)
|
||||
all = (
|
||||
try(length(policy.value.values) > 0, false)
|
||||
? null
|
||||
: true
|
||||
)
|
||||
}
|
||||
|
||||
dynamic "rules" {
|
||||
for_each = each.value.rules
|
||||
iterator = rule
|
||||
content {
|
||||
allow_all = try(rule.value.allow.all, false) == true ? "TRUE" : null
|
||||
deny_all = try(rule.value.deny.all, false) == true ? "TRUE" : null
|
||||
enforce = (
|
||||
each.value.is_boolean_policy && rule.value.enforce != null
|
||||
? upper(tostring(rule.value.enforce))
|
||||
: null
|
||||
)
|
||||
condition {
|
||||
description = rule.value.condition.description
|
||||
expression = rule.value.condition.expression
|
||||
location = rule.value.condition.location
|
||||
title = rule.value.condition.title
|
||||
}
|
||||
dynamic "values" {
|
||||
for_each = rule.value.has_values ? [1] : []
|
||||
content {
|
||||
allowed_values = try(rule.value.allow.values, null)
|
||||
denied_values = try(rule.value.deny.values, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynamic "restore_policy" {
|
||||
for_each = each.value.status == null ? [true] : []
|
||||
content {
|
||||
default = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,7 @@ output "name" {
|
|||
description = "Project name."
|
||||
value = local.project.name
|
||||
depends_on = [
|
||||
google_project_organization_policy.boolean,
|
||||
google_project_organization_policy.list,
|
||||
google_org_policy_policy.default,
|
||||
google_project_service.project_services,
|
||||
google_compute_shared_vpc_service_project.service_projects,
|
||||
google_project_iam_member.shared_vpc_host_robots,
|
||||
|
@ -39,8 +38,7 @@ output "number" {
|
|||
description = "Project number."
|
||||
value = local.project.number
|
||||
depends_on = [
|
||||
google_project_organization_policy.boolean,
|
||||
google_project_organization_policy.list,
|
||||
google_org_policy_policy.default,
|
||||
google_project_service.project_services,
|
||||
google_compute_shared_vpc_host_project.shared_vpc_host,
|
||||
google_compute_shared_vpc_service_project.shared_vpc_service,
|
||||
|
@ -59,8 +57,7 @@ output "project_id" {
|
|||
depends_on = [
|
||||
google_project.project,
|
||||
data.google_project.project,
|
||||
google_project_organization_policy.boolean,
|
||||
google_project_organization_policy.list,
|
||||
google_org_policy_policy.default,
|
||||
google_project_service.project_services,
|
||||
google_compute_shared_vpc_host_project.shared_vpc_host,
|
||||
google_compute_shared_vpc_service_project.shared_vpc_service,
|
||||
|
|
|
@ -40,18 +40,18 @@ variable "custom_roles" {
|
|||
nullable = false
|
||||
}
|
||||
|
||||
variable "descriptive_name" {
|
||||
description = "Name of the project name. Used for project name instead of `name` variable."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "default_service_account" {
|
||||
description = "Project default service account setting: can be one of `delete`, `deprivilege`, `disable`, or `keep`."
|
||||
default = "keep"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "descriptive_name" {
|
||||
description = "Name of the project name. Used for project name instead of `name` variable."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "group_iam" {
|
||||
description = "Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable."
|
||||
type = map(list(string))
|
||||
|
@ -133,6 +133,46 @@ variable "name" {
|
|||
type = string
|
||||
}
|
||||
|
||||
variable "org_policies" {
|
||||
description = "Organization policies applied to this project keyed by policy name."
|
||||
type = map(object({
|
||||
inherit_from_parent = optional(bool) # for list policies only.
|
||||
reset = optional(bool)
|
||||
|
||||
# default (unconditional) values
|
||||
allow = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
deny = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
enforce = optional(bool, true) # for boolean policies only.
|
||||
|
||||
# conditional values
|
||||
rules = optional(list(object({
|
||||
allow = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
deny = optional(object({
|
||||
all = optional(bool)
|
||||
values = optional(list(string))
|
||||
}))
|
||||
enforce = optional(bool, true) # for boolean policies only.
|
||||
condition = object({
|
||||
description = optional(string)
|
||||
expression = optional(string)
|
||||
location = optional(string)
|
||||
title = optional(string)
|
||||
})
|
||||
})), [])
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "oslogin" {
|
||||
description = "Enable OS Login."
|
||||
type = bool
|
||||
|
@ -164,25 +204,6 @@ variable "parent" {
|
|||
}
|
||||
}
|
||||
|
||||
variable "policy_boolean" {
|
||||
description = "Map of boolean org policies and enforcement value, set value to null for policy restore."
|
||||
type = map(bool)
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "policy_list" {
|
||||
description = "Map of list org policies, status is true for allow, false for deny, null for restore. Values can only be used for allow or deny."
|
||||
type = map(object({
|
||||
inherit_from_parent = bool
|
||||
suggested_value = string
|
||||
status = bool
|
||||
values = list(string)
|
||||
}))
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "prefix" {
|
||||
description = "Prefix used to generate project id and name."
|
||||
type = string
|
||||
|
|
|
@ -12,11 +12,9 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import os
|
||||
import pytest
|
||||
|
||||
|
||||
FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture')
|
||||
|
||||
|
||||
|
@ -24,4 +22,4 @@ def test_resources(e2e_plan_runner):
|
|||
"Test that plan works and the numbers of resources is as expected."
|
||||
modules, resources = e2e_plan_runner(FIXTURES_DIR)
|
||||
assert len(modules) == 41
|
||||
assert len(resources) == 314
|
||||
assert len(resources) == 315
|
||||
|
|
|
@ -12,15 +12,14 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import os
|
||||
import pytest
|
||||
|
||||
|
||||
FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture')
|
||||
|
||||
|
||||
def test_resources(e2e_plan_runner):
|
||||
"Test that plan works and the numbers of resources is as expected."
|
||||
modules, resources = e2e_plan_runner(FIXTURES_DIR)
|
||||
assert len(modules) == 7
|
||||
assert len(resources) == 34
|
||||
assert len(resources) == 35
|
||||
|
|
|
@ -24,21 +24,18 @@ resource "google_compute_disk" "default" {
|
|||
}
|
||||
|
||||
module "test" {
|
||||
source = "../../../../modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
location = "europe-west1"
|
||||
name = "test-mig"
|
||||
target_size = 2
|
||||
default_version = {
|
||||
instance_template = "foo-template"
|
||||
name = "foo"
|
||||
}
|
||||
autoscaler_config = var.autoscaler_config
|
||||
health_check_config = var.health_check_config
|
||||
named_ports = var.named_ports
|
||||
regional = var.regional
|
||||
stateful_config = var.stateful_config
|
||||
|
||||
update_policy = var.update_policy
|
||||
versions = var.versions
|
||||
source = "../../../../modules/compute-mig"
|
||||
project_id = "my-project"
|
||||
name = "test-mig"
|
||||
target_size = 2
|
||||
default_version_name = "foo"
|
||||
instance_template = "foo-template"
|
||||
location = var.location
|
||||
autoscaler_config = var.autoscaler_config
|
||||
health_check_config = var.health_check_config
|
||||
named_ports = var.named_ports
|
||||
stateful_config = var.stateful_config
|
||||
stateful_disks = var.stateful_disks
|
||||
update_policy = var.update_policy
|
||||
versions = var.versions
|
||||
}
|
||||
|
|
|
@ -14,101 +14,82 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
variable "autoscaler_config" {
|
||||
type = object({
|
||||
max_replicas = number
|
||||
min_replicas = number
|
||||
cooldown_period = number
|
||||
cpu_utilization_target = number
|
||||
load_balancing_utilization_target = number
|
||||
metric = object({
|
||||
name = string
|
||||
single_instance_assignment = number
|
||||
target = number
|
||||
type = string # GAUGE, DELTA_PER_SECOND, DELTA_PER_MINUTE
|
||||
filter = string
|
||||
})
|
||||
})
|
||||
variable "all_instances_config" {
|
||||
type = any
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "auto_healing_policies" {
|
||||
type = object({
|
||||
health_check = string
|
||||
initial_delay_sec = number
|
||||
})
|
||||
type = any
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "autoscaler_config" {
|
||||
type = any
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "default_version_name" {
|
||||
type = any
|
||||
default = "default"
|
||||
}
|
||||
|
||||
variable "description" {
|
||||
type = any
|
||||
default = "Terraform managed."
|
||||
}
|
||||
|
||||
variable "distribution_policy" {
|
||||
type = any
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "health_check_config" {
|
||||
type = object({
|
||||
type = string # http https tcp ssl http2
|
||||
check = map(any) # actual health check block attributes
|
||||
config = map(number) # interval, thresholds, timeout
|
||||
logging = bool
|
||||
})
|
||||
type = any
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "location" {
|
||||
type = any
|
||||
default = "europe-west1-b"
|
||||
}
|
||||
|
||||
variable "named_ports" {
|
||||
type = map(number)
|
||||
type = any
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "regional" {
|
||||
type = bool
|
||||
default = false
|
||||
variable "stateful_disks" {
|
||||
type = any
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "stateful_config" {
|
||||
description = "Stateful configuration can be done by individual instances or for all instances in the MIG. They key in per_instance_config is the name of the specific instance. The key of the stateful_disks is the 'device_name' field of the resource. Please note that device_name is defined at the OS mount level, unlike the disk name."
|
||||
type = object({
|
||||
per_instance_config = map(object({
|
||||
#name is the key
|
||||
#name = string
|
||||
stateful_disks = map(object({
|
||||
#device_name is the key
|
||||
source = string
|
||||
mode = string # READ_WRITE | READ_ONLY
|
||||
delete_rule = string # NEVER | ON_PERMANENT_INSTANCE_DELETION
|
||||
}))
|
||||
metadata = map(string)
|
||||
update_config = object({
|
||||
minimal_action = string # NONE | REPLACE | RESTART | REFRESH
|
||||
most_disruptive_allowed_action = string # REPLACE | RESTART | REFRESH | NONE
|
||||
remove_instance_state_on_destroy = bool
|
||||
})
|
||||
}))
|
||||
type = any
|
||||
default = {}
|
||||
}
|
||||
|
||||
mig_config = object({
|
||||
stateful_disks = map(object({
|
||||
#device_name is the key
|
||||
delete_rule = string # NEVER | ON_PERMANENT_INSTANCE_DELETION
|
||||
}))
|
||||
})
|
||||
variable "target_pools" {
|
||||
type = any
|
||||
default = []
|
||||
}
|
||||
|
||||
})
|
||||
variable "target_size" {
|
||||
type = any
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "update_policy" {
|
||||
type = object({
|
||||
type = string # OPPORTUNISTIC | PROACTIVE
|
||||
minimal_action = string # REPLACE | RESTART
|
||||
min_ready_sec = number
|
||||
max_surge_type = string # fixed | percent
|
||||
max_surge = number
|
||||
max_unavailable_type = string
|
||||
max_unavailable = number
|
||||
})
|
||||
type = any
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "versions" {
|
||||
type = map(object({
|
||||
instance_template = string
|
||||
target_type = string # fixed | percent
|
||||
target_size = number
|
||||
}))
|
||||
type = any
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "wait_for_instances" {
|
||||
type = any
|
||||
default = null
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
def test_defaults(plan_runner):
|
||||
"Test variable defaults."
|
||||
_, resources = plan_runner()
|
||||
|
@ -21,7 +22,7 @@ def test_defaults(plan_runner):
|
|||
assert mig['type'] == 'google_compute_instance_group_manager'
|
||||
assert mig['values']['target_size'] == 2
|
||||
assert mig['values']['zone']
|
||||
_, resources = plan_runner(regional='true')
|
||||
_, resources = plan_runner(location='"europe-west1"')
|
||||
assert len(resources) == 1
|
||||
mig = resources[0]
|
||||
assert mig['type'] == 'google_compute_region_instance_group_manager'
|
||||
|
@ -31,7 +32,12 @@ def test_defaults(plan_runner):
|
|||
|
||||
def test_health_check(plan_runner):
|
||||
"Test health check resource."
|
||||
health_check_config = '{type="tcp", check={port=80}, config=null, logging=false}'
|
||||
health_check_config = '''{
|
||||
enable_logging = true
|
||||
tcp = {
|
||||
port = 80
|
||||
}
|
||||
}'''
|
||||
_, resources = plan_runner(health_check_config=health_check_config)
|
||||
assert len(resources) == 2
|
||||
assert any(r['type'] == 'google_compute_health_check' for r in resources)
|
||||
|
@ -39,20 +45,26 @@ def test_health_check(plan_runner):
|
|||
|
||||
def test_autoscaler(plan_runner):
|
||||
"Test autoscaler resource."
|
||||
autoscaler_config = (
|
||||
'{'
|
||||
'max_replicas=3, min_replicas=1, cooldown_period=60,'
|
||||
'cpu_utilization_target=65, load_balancing_utilization_target=null,'
|
||||
'metric=null'
|
||||
'}'
|
||||
)
|
||||
autoscaler_config = '''{
|
||||
colldown_period = 60
|
||||
max_replicas = 3
|
||||
min_replicas = 1
|
||||
scaling_signals = {
|
||||
cpu_utilization = {
|
||||
target = 65
|
||||
}
|
||||
}
|
||||
}'''
|
||||
_, resources = plan_runner(autoscaler_config=autoscaler_config)
|
||||
assert len(resources) == 2
|
||||
autoscaler = resources[0]
|
||||
assert autoscaler['type'] == 'google_compute_autoscaler'
|
||||
assert autoscaler['values']['autoscaling_policy'] == [{
|
||||
'cooldown_period': 60,
|
||||
'cpu_utilization': [{'predictive_method': 'NONE', 'target': 65}],
|
||||
'cpu_utilization': [{
|
||||
'predictive_method': 'NONE',
|
||||
'target': 65
|
||||
}],
|
||||
'load_balancing_utilization': [],
|
||||
'max_replicas': 3,
|
||||
'metric': [],
|
||||
|
@ -62,7 +74,7 @@ def test_autoscaler(plan_runner):
|
|||
'scaling_schedules': [],
|
||||
}]
|
||||
_, resources = plan_runner(autoscaler_config=autoscaler_config,
|
||||
regional='true')
|
||||
location='"europe-west1"')
|
||||
assert len(resources) == 2
|
||||
autoscaler = resources[0]
|
||||
assert autoscaler['type'] == 'google_compute_region_autoscaler'
|
||||
|
@ -71,17 +83,10 @@ def test_autoscaler(plan_runner):
|
|||
def test_stateful_mig(plan_runner):
|
||||
"Test stateful instances - mig."
|
||||
|
||||
stateful_config = (
|
||||
'{'
|
||||
'per_instance_config = {},'
|
||||
'mig_config = {'
|
||||
'stateful_disks = {'
|
||||
'persistent-disk-1 = {delete_rule="NEVER"}'
|
||||
'}'
|
||||
'}'
|
||||
'}'
|
||||
)
|
||||
_, resources = plan_runner(stateful_config=stateful_config)
|
||||
stateful_disks = '''{
|
||||
persistent-disk-1 = null
|
||||
}'''
|
||||
_, resources = plan_runner(stateful_disks=stateful_disks)
|
||||
assert len(resources) == 1
|
||||
statefuldisk = resources[0]
|
||||
assert statefuldisk['type'] == 'google_compute_instance_group_manager'
|
||||
|
@ -93,35 +98,19 @@ def test_stateful_mig(plan_runner):
|
|||
|
||||
def test_stateful_instance(plan_runner):
|
||||
"Test stateful instances - instance."
|
||||
stateful_config = (
|
||||
'{'
|
||||
'per_instance_config = {'
|
||||
'instance-1 = {'
|
||||
'stateful_disks = {'
|
||||
'persistent-disk-1 = {'
|
||||
'source = "test-disk",'
|
||||
'mode = "READ_ONLY",'
|
||||
'delete_rule= "NEVER",'
|
||||
'},'
|
||||
'},'
|
||||
'metadata = {'
|
||||
'foo = "bar"'
|
||||
'},'
|
||||
'update_config = {'
|
||||
'minimal_action = "NONE",'
|
||||
'most_disruptive_allowed_action = "REPLACE",'
|
||||
'remove_instance_state_on_destroy = false,'
|
||||
|
||||
'},'
|
||||
'},'
|
||||
'},'
|
||||
'mig_config = {'
|
||||
'stateful_disks = {'
|
||||
'persistent-disk-1 = {delete_rule="NEVER"}'
|
||||
'}'
|
||||
'}'
|
||||
'}'
|
||||
)
|
||||
stateful_config = '''{
|
||||
instance-1 = {
|
||||
most_disruptive_action = "REPLACE",
|
||||
preserved_state = {
|
||||
disks = {
|
||||
persistent-disk-1 = {
|
||||
source = "test-disk"
|
||||
}
|
||||
}
|
||||
metadata = { foo = "bar" }
|
||||
}
|
||||
}
|
||||
}'''
|
||||
_, resources = plan_runner(stateful_config=stateful_config)
|
||||
assert len(resources) == 2
|
||||
instanceconfig = resources[0]
|
||||
|
@ -134,13 +123,12 @@ def test_stateful_instance(plan_runner):
|
|||
'device_name': 'persistent-disk-1',
|
||||
'delete_rule': 'NEVER',
|
||||
'source': 'test-disk',
|
||||
'mode': 'READ_ONLY',
|
||||
'mode': 'READ_WRITE',
|
||||
}],
|
||||
'metadata': {
|
||||
'foo': 'bar'
|
||||
}
|
||||
}]
|
||||
|
||||
assert instanceconfig['values']['minimal_action'] == 'NONE'
|
||||
assert instanceconfig['values']['most_disruptive_allowed_action'] == 'REPLACE'
|
||||
assert instanceconfig['values']['remove_instance_state_on_destroy'] == False
|
||||
|
|
|
@ -22,10 +22,9 @@ module "test" {
|
|||
iam = var.iam
|
||||
iam_additive = var.iam_additive
|
||||
iam_additive_members = var.iam_additive_members
|
||||
policy_boolean = var.policy_boolean
|
||||
policy_list = var.policy_list
|
||||
firewall_policies = var.firewall_policies
|
||||
firewall_policy_association = var.firewall_policy_association
|
||||
logging_sinks = var.logging_sinks
|
||||
logging_exclusions = var.logging_exclusions
|
||||
org_policies = var.org_policies
|
||||
}
|
||||
|
|
|
@ -34,16 +34,6 @@ variable "iam_additive_members" {
|
|||
default = {}
|
||||
}
|
||||
|
||||
variable "policy_boolean" {
|
||||
type = any
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "policy_list" {
|
||||
type = any
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "firewall_policies" {
|
||||
type = any
|
||||
default = {}
|
||||
|
@ -63,3 +53,8 @@ variable "logging_exclusions" {
|
|||
type = any
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "org_policies" {
|
||||
type = any
|
||||
default = {}
|
||||
}
|
||||
|
|
|
@ -12,56 +12,212 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
def test_sink(plan_runner):
|
||||
"Test folder-level sink."
|
||||
policy_boolean = '{policy-a = true, policy-b = false, policy-c = null}'
|
||||
_, resources = plan_runner(policy_boolean=policy_boolean)
|
||||
|
||||
def test_policy_boolean(plan_runner):
|
||||
"Test boolean org policy."
|
||||
policies = '''{
|
||||
"iam.disableServiceAccountKeyCreation" = {
|
||||
enforce = true
|
||||
}
|
||||
"iam.disableServiceAccountKeyUpload" = {
|
||||
enforce = false
|
||||
rules = [
|
||||
{
|
||||
condition = {
|
||||
expression = "resource.matchTagId(\\"tagKeys/1234\\", \\"tagValues/1234\\")"
|
||||
title = "condition"
|
||||
description = "test condition"
|
||||
location = "xxx"
|
||||
}
|
||||
enforce = true
|
||||
}
|
||||
]
|
||||
}
|
||||
}'''
|
||||
_, resources = plan_runner(org_policies=policies)
|
||||
assert len(resources) == 3
|
||||
|
||||
policies = [r for r in resources if r['type'] == 'google_org_policy_policy']
|
||||
assert len(policies) == 2
|
||||
|
||||
p1 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'iam.disableServiceAccountKeyCreation'
|
||||
][0]
|
||||
|
||||
assert p1['inherit_from_parent'] is None
|
||||
assert p1['reset'] is None
|
||||
assert p1['rules'] == [{
|
||||
'allow_all': None,
|
||||
'condition': [],
|
||||
'deny_all': None,
|
||||
'enforce': 'TRUE',
|
||||
'values': []
|
||||
}]
|
||||
|
||||
p2 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'iam.disableServiceAccountKeyUpload'
|
||||
][0]
|
||||
|
||||
assert p2['inherit_from_parent'] is None
|
||||
assert p2['reset'] is None
|
||||
assert len(p2['rules']) == 2
|
||||
assert p2['rules'][0] == {
|
||||
'allow_all': None,
|
||||
'condition': [],
|
||||
'deny_all': None,
|
||||
'enforce': 'FALSE',
|
||||
'values': []
|
||||
}
|
||||
assert p2['rules'][1] == {
|
||||
'allow_all': None,
|
||||
'condition': [{
|
||||
'description': 'test condition',
|
||||
'expression': 'resource.matchTagId("tagKeys/1234", "tagValues/1234")',
|
||||
'location': 'xxx',
|
||||
'title': 'condition'
|
||||
}],
|
||||
'deny_all': None,
|
||||
'enforce': 'TRUE',
|
||||
'values': []
|
||||
}
|
||||
|
||||
|
||||
def test_policy_list(plan_runner):
|
||||
"Test list org policy."
|
||||
policies = '''{
|
||||
"compute.vmExternalIpAccess" = {
|
||||
deny = { all = true }
|
||||
}
|
||||
"iam.allowedPolicyMemberDomains" = {
|
||||
allow = {
|
||||
values = ["C0xxxxxxx", "C0yyyyyyy"]
|
||||
}
|
||||
}
|
||||
"compute.restrictLoadBalancerCreationForTypes" = {
|
||||
deny = { values = ["in:EXTERNAL"] }
|
||||
rules = [
|
||||
{
|
||||
condition = {
|
||||
expression = "resource.matchTagId(\\"tagKeys/1234\\", \\"tagValues/1234\\")"
|
||||
title = "condition"
|
||||
description = "test condition"
|
||||
location = "xxx"
|
||||
}
|
||||
allow = {
|
||||
values = ["EXTERNAL_1"]
|
||||
}
|
||||
},
|
||||
{
|
||||
condition = {
|
||||
expression = "resource.matchTagId(\\"tagKeys/12345\\", \\"tagValues/12345\\")"
|
||||
title = "condition2"
|
||||
description = "test condition2"
|
||||
location = "xxx"
|
||||
}
|
||||
allow = {
|
||||
all = true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}'''
|
||||
_, resources = plan_runner(org_policies=policies)
|
||||
assert len(resources) == 4
|
||||
resources = [r for r in resources if r['type']
|
||||
== 'google_folder_organization_policy']
|
||||
assert sorted([r['index'] for r in resources]) == [
|
||||
'policy-a',
|
||||
'policy-b',
|
||||
'policy-c',
|
||||
]
|
||||
policy_values = []
|
||||
for resource in resources:
|
||||
for policy in ('boolean_policy', 'restore_policy'):
|
||||
value = resource['values'][policy]
|
||||
if value:
|
||||
policy_values.append((resource['index'], policy,) + value[0].popitem())
|
||||
assert sorted(policy_values) == [
|
||||
('policy-a', 'boolean_policy', 'enforced', True),
|
||||
('policy-b', 'boolean_policy', 'enforced', False),
|
||||
('policy-c', 'restore_policy', 'default', True),
|
||||
]
|
||||
|
||||
policies = [r for r in resources if r['type'] == 'google_org_policy_policy']
|
||||
assert len(policies) == 3
|
||||
|
||||
def test_exclussions(plan_runner):
|
||||
"Test folder-level logging exclusions."
|
||||
policy_list = (
|
||||
'{'
|
||||
'policy-a = {inherit_from_parent = true, suggested_value = null, status = true, values = []}, '
|
||||
'policy-b = {inherit_from_parent = null, suggested_value = "foo", status = false, values = ["bar"]}, '
|
||||
'policy-c = {inherit_from_parent = null, suggested_value = true, status = null, values = null}'
|
||||
'}'
|
||||
)
|
||||
_, resources = plan_runner(policy_list=policy_list)
|
||||
assert len(resources) == 4
|
||||
resources = [r for r in resources if r['type']
|
||||
== 'google_folder_organization_policy']
|
||||
assert sorted([r['index'] for r in resources]) == [
|
||||
'policy-a',
|
||||
'policy-b',
|
||||
'policy-c',
|
||||
]
|
||||
values = [r['values'] for r in resources]
|
||||
assert [r['constraint'] for r in values] == [
|
||||
'policy-a', 'policy-b', 'policy-c'
|
||||
]
|
||||
assert values[0]['list_policy'][0]['allow'] == [
|
||||
{'all': True, 'values': None}]
|
||||
assert values[1]['list_policy'][0]['deny'] == [
|
||||
{'all': False, 'values': ["bar"]}]
|
||||
assert values[2]['restore_policy'] == [{'default': True}]
|
||||
p1 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'compute.vmExternalIpAccess'
|
||||
][0]
|
||||
assert p1['inherit_from_parent'] is None
|
||||
assert p1['reset'] is None
|
||||
assert p1['rules'] == [{
|
||||
'allow_all': None,
|
||||
'condition': [],
|
||||
'deny_all': 'TRUE',
|
||||
'enforce': None,
|
||||
'values': []
|
||||
}]
|
||||
|
||||
p2 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'iam.allowedPolicyMemberDomains'
|
||||
][0]
|
||||
assert p2['inherit_from_parent'] is None
|
||||
assert p2['reset'] is None
|
||||
assert p2['rules'] == [{
|
||||
'allow_all':
|
||||
None,
|
||||
'condition': [],
|
||||
'deny_all':
|
||||
None,
|
||||
'enforce':
|
||||
None,
|
||||
'values': [{
|
||||
'allowed_values': [
|
||||
'C0xxxxxxx',
|
||||
'C0yyyyyyy',
|
||||
],
|
||||
'denied_values': None
|
||||
}]
|
||||
}]
|
||||
|
||||
p3 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'compute.restrictLoadBalancerCreationForTypes'
|
||||
][0]
|
||||
assert p3['inherit_from_parent'] is None
|
||||
assert p3['reset'] is None
|
||||
assert len(p3['rules']) == 3
|
||||
assert p3['rules'][0] == {
|
||||
'allow_all': None,
|
||||
'condition': [],
|
||||
'deny_all': None,
|
||||
'enforce': None,
|
||||
'values': [{
|
||||
'allowed_values': None,
|
||||
'denied_values': ['in:EXTERNAL']
|
||||
}]
|
||||
}
|
||||
|
||||
assert p3['rules'][1] == {
|
||||
'allow_all': None,
|
||||
'condition': [{
|
||||
'description': 'test condition',
|
||||
'expression': 'resource.matchTagId("tagKeys/1234", "tagValues/1234")',
|
||||
'location': 'xxx',
|
||||
'title': 'condition'
|
||||
}],
|
||||
'deny_all': None,
|
||||
'enforce': None,
|
||||
'values': [{
|
||||
'allowed_values': ['EXTERNAL_1'],
|
||||
'denied_values': None
|
||||
}]
|
||||
}
|
||||
|
||||
assert p3['rules'][2] == {
|
||||
'allow_all': 'TRUE',
|
||||
'condition': [{
|
||||
'description':
|
||||
'test condition2',
|
||||
'expression':
|
||||
'resource.matchTagId("tagKeys/12345", "tagValues/12345")',
|
||||
'location':
|
||||
'xxx',
|
||||
'title':
|
||||
'condition2'
|
||||
}],
|
||||
'deny_all': None,
|
||||
'enforce': None,
|
||||
'values': []
|
||||
}
|
||||
|
|
|
@ -28,8 +28,7 @@ module "test" {
|
|||
iam_audit_config = var.iam_audit_config
|
||||
logging_sinks = var.logging_sinks
|
||||
logging_exclusions = var.logging_exclusions
|
||||
policy_boolean = var.policy_boolean
|
||||
policy_list = var.policy_list
|
||||
org_policies = var.org_policies
|
||||
tag_bindings = var.tag_bindings
|
||||
tags = var.tags
|
||||
}
|
||||
|
|
|
@ -44,16 +44,6 @@ variable "iam_audit_config" {
|
|||
default = {}
|
||||
}
|
||||
|
||||
variable "policy_boolean" {
|
||||
type = any
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "policy_list" {
|
||||
type = any
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "firewall_policies" {
|
||||
type = any
|
||||
default = {}
|
||||
|
@ -79,6 +69,11 @@ variable "logging_exclusions" {
|
|||
default = {}
|
||||
}
|
||||
|
||||
variable "org_policies" {
|
||||
type = any
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "tag_bindings" {
|
||||
type = any
|
||||
default = null
|
||||
|
|
|
@ -12,13 +12,14 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
def test_audit_config(plan_runner):
|
||||
"Test audit config."
|
||||
iam_audit_config = '{allServices={DATA_READ=[], DATA_WRITE=["user:me@example.org"]}}'
|
||||
_, resources = plan_runner(iam_audit_config=iam_audit_config)
|
||||
assert len(resources) == 1
|
||||
log_types = set(r['log_type']
|
||||
for r in resources[0]['values']['audit_log_config'])
|
||||
log_types = set(
|
||||
r['log_type'] for r in resources[0]['values']['audit_log_config'])
|
||||
assert log_types == set(['DATA_READ', 'DATA_WRITE'])
|
||||
|
||||
|
||||
|
@ -28,21 +29,21 @@ def test_iam(plan_runner):
|
|||
'{'
|
||||
'"owners@example.org" = ["roles/owner", "roles/resourcemanager.folderAdmin"],'
|
||||
'"viewers@example.org" = ["roles/viewer"]'
|
||||
'}'
|
||||
)
|
||||
iam = (
|
||||
'{'
|
||||
'"roles/owner" = ["user:one@example.org", "user:two@example.org"],'
|
||||
'"roles/browser" = ["domain:example.org"]'
|
||||
'}'
|
||||
)
|
||||
'}')
|
||||
iam = ('{'
|
||||
'"roles/owner" = ["user:one@example.org", "user:two@example.org"],'
|
||||
'"roles/browser" = ["domain:example.org"]'
|
||||
'}')
|
||||
_, resources = plan_runner(group_iam=group_iam, iam=iam)
|
||||
roles = sorted([(r['values']['role'], sorted(r['values']['members']))
|
||||
for r in resources if r['type'] == 'google_organization_iam_binding'])
|
||||
for r in resources
|
||||
if r['type'] == 'google_organization_iam_binding'])
|
||||
assert roles == [
|
||||
('roles/browser', ['domain:example.org']),
|
||||
('roles/owner', ['group:owners@example.org', 'user:one@example.org',
|
||||
'user:two@example.org']),
|
||||
('roles/owner', [
|
||||
'group:owners@example.org', 'user:one@example.org',
|
||||
'user:two@example.org'
|
||||
]),
|
||||
('roles/resourcemanager.folderAdmin', ['group:owners@example.org']),
|
||||
('roles/viewer', ['group:viewers@example.org']),
|
||||
]
|
||||
|
@ -50,55 +51,12 @@ def test_iam(plan_runner):
|
|||
|
||||
def test_iam_additive_members(plan_runner):
|
||||
"Test IAM additive members."
|
||||
iam = (
|
||||
'{"user:one@example.org" = ["roles/owner"],'
|
||||
'"user:two@example.org" = ["roles/owner", "roles/editor"]}'
|
||||
)
|
||||
iam = ('{"user:one@example.org" = ["roles/owner"],'
|
||||
'"user:two@example.org" = ["roles/owner", "roles/editor"]}')
|
||||
_, resources = plan_runner(iam_additive_members=iam)
|
||||
roles = set((r['values']['role'], r['values']['member'])
|
||||
for r in resources if r['type'] == 'google_organization_iam_member')
|
||||
assert roles == set([
|
||||
('roles/owner', 'user:one@example.org'),
|
||||
('roles/owner', 'user:two@example.org'),
|
||||
('roles/editor', 'user:two@example.org')
|
||||
])
|
||||
|
||||
|
||||
def test_policy_boolean(plan_runner):
|
||||
"Test boolean org policy."
|
||||
policy_boolean = '{policy-a = true, policy-b = false, policy-c = null}'
|
||||
_, resources = plan_runner(policy_boolean=policy_boolean)
|
||||
assert len(resources) == 3
|
||||
constraints = set(r['values']['constraint'] for r in resources)
|
||||
assert set(constraints) == set(['policy-a', 'policy-b', 'policy-c'])
|
||||
policies = []
|
||||
for resource in resources:
|
||||
for policy in ('boolean_policy', 'restore_policy'):
|
||||
value = resource['values'][policy]
|
||||
if value:
|
||||
policies.append((policy,) + value[0].popitem())
|
||||
assert set(policies) == set([
|
||||
('boolean_policy', 'enforced', True),
|
||||
('boolean_policy', 'enforced', False),
|
||||
('restore_policy', 'default', True)])
|
||||
|
||||
|
||||
def test_policy_list(plan_runner):
|
||||
"Test list org policy."
|
||||
policy_list = (
|
||||
'{'
|
||||
'policy-a = {inherit_from_parent = true, suggested_value = null, status = true, values = []}, '
|
||||
'policy-b = {inherit_from_parent = null, suggested_value = "foo", status = false, values = ["bar"]}, '
|
||||
'policy-c = {inherit_from_parent = null, suggested_value = true, status = null, values = null}'
|
||||
'}'
|
||||
)
|
||||
_, resources = plan_runner(policy_list=policy_list)
|
||||
assert len(resources) == 3
|
||||
values = [r['values'] for r in resources]
|
||||
assert [r['constraint']
|
||||
for r in values] == ['policy-a', 'policy-b', 'policy-c']
|
||||
assert values[0]['list_policy'][0]['allow'] == [
|
||||
{'all': True, 'values': None}]
|
||||
assert values[1]['list_policy'][0]['deny'] == [
|
||||
{'all': False, 'values': ["bar"]}]
|
||||
assert values[2]['restore_policy'] == [{'default': True}]
|
||||
for r in resources
|
||||
if r['type'] == 'google_organization_iam_member')
|
||||
assert roles == set([('roles/owner', 'user:one@example.org'),
|
||||
('roles/owner', 'user:two@example.org'),
|
||||
('roles/editor', 'user:two@example.org')])
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
# 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.
|
||||
|
||||
|
||||
def test_policy_boolean(plan_runner):
|
||||
"Test boolean org policy."
|
||||
policies = '''{
|
||||
"iam.disableServiceAccountKeyCreation" = {
|
||||
enforce = true
|
||||
}
|
||||
"iam.disableServiceAccountKeyUpload" = {
|
||||
enforce = false
|
||||
rules = [
|
||||
{
|
||||
condition = {
|
||||
expression = "resource.matchTagId(\\"tagKeys/1234\\", \\"tagValues/1234\\")"
|
||||
title = "condition"
|
||||
description = "test condition"
|
||||
location = "xxx"
|
||||
}
|
||||
enforce = true
|
||||
}
|
||||
]
|
||||
}
|
||||
}'''
|
||||
_, resources = plan_runner(org_policies=policies)
|
||||
assert len(resources) == 2
|
||||
|
||||
policies = [r for r in resources if r['type'] == 'google_org_policy_policy']
|
||||
assert len(policies) == 2
|
||||
assert all(
|
||||
x['values']['parent'] == 'organizations/1234567890' for x in policies)
|
||||
|
||||
p1 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'iam.disableServiceAccountKeyCreation'
|
||||
][0]
|
||||
|
||||
assert p1['inherit_from_parent'] is None
|
||||
assert p1['reset'] is None
|
||||
assert p1['rules'] == [{
|
||||
'allow_all': None,
|
||||
'condition': [],
|
||||
'deny_all': None,
|
||||
'enforce': 'TRUE',
|
||||
'values': []
|
||||
}]
|
||||
|
||||
p2 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'iam.disableServiceAccountKeyUpload'
|
||||
][0]
|
||||
|
||||
assert p2['inherit_from_parent'] is None
|
||||
assert p2['reset'] is None
|
||||
assert len(p2['rules']) == 2
|
||||
assert p2['rules'][0] == {
|
||||
'allow_all': None,
|
||||
'condition': [],
|
||||
'deny_all': None,
|
||||
'enforce': 'FALSE',
|
||||
'values': []
|
||||
}
|
||||
assert p2['rules'][1] == {
|
||||
'allow_all': None,
|
||||
'condition': [{
|
||||
'description': 'test condition',
|
||||
'expression': 'resource.matchTagId("tagKeys/1234", "tagValues/1234")',
|
||||
'location': 'xxx',
|
||||
'title': 'condition'
|
||||
}],
|
||||
'deny_all': None,
|
||||
'enforce': 'TRUE',
|
||||
'values': []
|
||||
}
|
||||
|
||||
|
||||
def test_policy_list(plan_runner):
|
||||
"Test list org policy."
|
||||
policies = '''{
|
||||
"compute.vmExternalIpAccess" = {
|
||||
deny = { all = true }
|
||||
}
|
||||
"iam.allowedPolicyMemberDomains" = {
|
||||
allow = {
|
||||
values = ["C0xxxxxxx", "C0yyyyyyy"]
|
||||
}
|
||||
}
|
||||
"compute.restrictLoadBalancerCreationForTypes" = {
|
||||
deny = { values = ["in:EXTERNAL"] }
|
||||
rules = [
|
||||
{
|
||||
condition = {
|
||||
expression = "resource.matchTagId(\\"tagKeys/1234\\", \\"tagValues/1234\\")"
|
||||
title = "condition"
|
||||
description = "test condition"
|
||||
location = "xxx"
|
||||
}
|
||||
allow = {
|
||||
values = ["EXTERNAL_1"]
|
||||
}
|
||||
},
|
||||
{
|
||||
condition = {
|
||||
expression = "resource.matchTagId(\\"tagKeys/12345\\", \\"tagValues/12345\\")"
|
||||
title = "condition2"
|
||||
description = "test condition2"
|
||||
location = "xxx"
|
||||
}
|
||||
allow = {
|
||||
all = true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}'''
|
||||
_, resources = plan_runner(org_policies=policies)
|
||||
assert len(resources) == 3
|
||||
|
||||
policies = [r for r in resources if r['type'] == 'google_org_policy_policy']
|
||||
assert len(policies) == 3
|
||||
assert all(
|
||||
x['values']['parent'] == 'organizations/1234567890' for x in policies)
|
||||
|
||||
p1 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'compute.vmExternalIpAccess'
|
||||
][0]
|
||||
assert p1['inherit_from_parent'] is None
|
||||
assert p1['reset'] is None
|
||||
assert p1['rules'] == [{
|
||||
'allow_all': None,
|
||||
'condition': [],
|
||||
'deny_all': 'TRUE',
|
||||
'enforce': None,
|
||||
'values': []
|
||||
}]
|
||||
|
||||
p2 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'iam.allowedPolicyMemberDomains'
|
||||
][0]
|
||||
assert p2['inherit_from_parent'] is None
|
||||
assert p2['reset'] is None
|
||||
assert p2['rules'] == [{
|
||||
'allow_all':
|
||||
None,
|
||||
'condition': [],
|
||||
'deny_all':
|
||||
None,
|
||||
'enforce':
|
||||
None,
|
||||
'values': [{
|
||||
'allowed_values': [
|
||||
'C0xxxxxxx',
|
||||
'C0yyyyyyy',
|
||||
],
|
||||
'denied_values': None
|
||||
}]
|
||||
}]
|
||||
|
||||
p3 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'compute.restrictLoadBalancerCreationForTypes'
|
||||
][0]
|
||||
assert p3['inherit_from_parent'] is None
|
||||
assert p3['reset'] is None
|
||||
assert len(p3['rules']) == 3
|
||||
assert p3['rules'][0] == {
|
||||
'allow_all': None,
|
||||
'condition': [],
|
||||
'deny_all': None,
|
||||
'enforce': None,
|
||||
'values': [{
|
||||
'allowed_values': None,
|
||||
'denied_values': ['in:EXTERNAL']
|
||||
}]
|
||||
}
|
||||
|
||||
assert p3['rules'][1] == {
|
||||
'allow_all': None,
|
||||
'condition': [{
|
||||
'description': 'test condition',
|
||||
'expression': 'resource.matchTagId("tagKeys/1234", "tagValues/1234")',
|
||||
'location': 'xxx',
|
||||
'title': 'condition'
|
||||
}],
|
||||
'deny_all': None,
|
||||
'enforce': None,
|
||||
'values': [{
|
||||
'allowed_values': ['EXTERNAL_1'],
|
||||
'denied_values': None
|
||||
}]
|
||||
}
|
||||
|
||||
assert p3['rules'][2] == {
|
||||
'allow_all': 'TRUE',
|
||||
'condition': [{
|
||||
'description':
|
||||
'test condition2',
|
||||
'expression':
|
||||
'resource.matchTagId("tagKeys/12345", "tagValues/12345")',
|
||||
'location':
|
||||
'xxx',
|
||||
'title':
|
||||
'condition2'
|
||||
}],
|
||||
'deny_all': None,
|
||||
'enforce': None,
|
||||
'values': []
|
||||
}
|
|
@ -25,12 +25,11 @@ module "test" {
|
|||
iam_additive_members = var.iam_additive_members
|
||||
labels = var.labels
|
||||
lien_reason = var.lien_reason
|
||||
org_policies = var.org_policies
|
||||
oslogin = var.oslogin
|
||||
oslogin_admins = var.oslogin_admins
|
||||
oslogin_users = var.oslogin_users
|
||||
parent = var.parent
|
||||
policy_boolean = var.policy_boolean
|
||||
policy_list = var.policy_list
|
||||
prefix = var.prefix
|
||||
service_encryption_key_ids = var.service_encryption_key_ids
|
||||
services = var.services
|
||||
|
@ -63,4 +62,3 @@ module "test-svpc-service" {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,11 @@ variable "lien_reason" {
|
|||
default = ""
|
||||
}
|
||||
|
||||
variable "org_policies" {
|
||||
type = any
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "oslogin" {
|
||||
type = bool
|
||||
default = false
|
||||
|
@ -84,21 +89,6 @@ variable "parent" {
|
|||
default = null
|
||||
}
|
||||
|
||||
variable "policy_boolean" {
|
||||
type = map(bool)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "policy_list" {
|
||||
type = map(object({
|
||||
inherit_from_parent = bool
|
||||
suggested_value = string
|
||||
status = bool
|
||||
values = list(string)
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "prefix" {
|
||||
type = string
|
||||
default = null
|
||||
|
|
|
@ -12,47 +12,214 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
def test_policy_boolean(plan_runner):
|
||||
"Test boolean org policy."
|
||||
policy_boolean = '{policy-a = true, policy-b = false, policy-c = null}'
|
||||
_, resources = plan_runner(policy_boolean=policy_boolean)
|
||||
assert len(resources) == 7
|
||||
resources = [r for r in resources if r['type']
|
||||
== 'google_project_organization_policy']
|
||||
assert sorted([r['index'] for r in resources]) == [
|
||||
'policy-a', 'policy-b', 'policy-c'
|
||||
]
|
||||
policy_values = []
|
||||
for resource in resources:
|
||||
for policy in ('boolean_policy', 'restore_policy'):
|
||||
value = resource['values'][policy]
|
||||
if value:
|
||||
policy_values.append((policy,) + value[0].popitem())
|
||||
assert sorted(policy_values) == [
|
||||
('boolean_policy', 'enforced', False),
|
||||
('boolean_policy', 'enforced', True),
|
||||
('restore_policy', 'default', True)
|
||||
]
|
||||
policies = '''{
|
||||
"iam.disableServiceAccountKeyCreation" = {
|
||||
enforce = true
|
||||
}
|
||||
"iam.disableServiceAccountKeyUpload" = {
|
||||
enforce = false
|
||||
rules = [
|
||||
{
|
||||
condition = {
|
||||
expression = "resource.matchTagId(\\"tagKeys/1234\\", \\"tagValues/1234\\")"
|
||||
title = "condition"
|
||||
description = "test condition"
|
||||
location = "xxx"
|
||||
}
|
||||
enforce = true
|
||||
}
|
||||
]
|
||||
}
|
||||
}'''
|
||||
_, resources = plan_runner(org_policies=policies)
|
||||
assert len(resources) == 6
|
||||
|
||||
policies = [r for r in resources if r['type'] == 'google_org_policy_policy']
|
||||
assert len(policies) == 2
|
||||
assert all(x['values']['parent'] == 'projects/my-project' for x in policies)
|
||||
|
||||
p1 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'iam.disableServiceAccountKeyCreation'
|
||||
][0]
|
||||
|
||||
assert p1['inherit_from_parent'] is None
|
||||
assert p1['reset'] is None
|
||||
assert p1['rules'] == [{
|
||||
'allow_all': None,
|
||||
'condition': [],
|
||||
'deny_all': None,
|
||||
'enforce': 'TRUE',
|
||||
'values': []
|
||||
}]
|
||||
|
||||
p2 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'iam.disableServiceAccountKeyUpload'
|
||||
][0]
|
||||
|
||||
assert p2['inherit_from_parent'] is None
|
||||
assert p2['reset'] is None
|
||||
assert len(p2['rules']) == 2
|
||||
assert p2['rules'][0] == {
|
||||
'allow_all': None,
|
||||
'condition': [],
|
||||
'deny_all': None,
|
||||
'enforce': 'FALSE',
|
||||
'values': []
|
||||
}
|
||||
assert p2['rules'][1] == {
|
||||
'allow_all': None,
|
||||
'condition': [{
|
||||
'description': 'test condition',
|
||||
'expression': 'resource.matchTagId("tagKeys/1234", "tagValues/1234")',
|
||||
'location': 'xxx',
|
||||
'title': 'condition'
|
||||
}],
|
||||
'deny_all': None,
|
||||
'enforce': 'TRUE',
|
||||
'values': []
|
||||
}
|
||||
|
||||
|
||||
def test_policy_list(plan_runner):
|
||||
"Test list org policy."
|
||||
policy_list = (
|
||||
'{'
|
||||
'policy-a = {inherit_from_parent = true, suggested_value = null, status = true, values = []}, '
|
||||
'policy-b = {inherit_from_parent = null, suggested_value = "foo", status = false, values = ["bar"]}, '
|
||||
'policy-c = {inherit_from_parent = null, suggested_value = true, status = null, values = null}'
|
||||
'}'
|
||||
)
|
||||
_, resources = plan_runner(policy_list=policy_list)
|
||||
policies = '''{
|
||||
"compute.vmExternalIpAccess" = {
|
||||
deny = { all = true }
|
||||
}
|
||||
"iam.allowedPolicyMemberDomains" = {
|
||||
allow = {
|
||||
values = ["C0xxxxxxx", "C0yyyyyyy"]
|
||||
}
|
||||
}
|
||||
"compute.restrictLoadBalancerCreationForTypes" = {
|
||||
deny = { values = ["in:EXTERNAL"] }
|
||||
rules = [
|
||||
{
|
||||
condition = {
|
||||
expression = "resource.matchTagId(\\"tagKeys/1234\\", \\"tagValues/1234\\")"
|
||||
title = "condition"
|
||||
description = "test condition"
|
||||
location = "xxx"
|
||||
}
|
||||
allow = {
|
||||
values = ["EXTERNAL_1"]
|
||||
}
|
||||
},
|
||||
{
|
||||
condition = {
|
||||
expression = "resource.matchTagId(\\"tagKeys/12345\\", \\"tagValues/12345\\")"
|
||||
title = "condition2"
|
||||
description = "test condition2"
|
||||
location = "xxx"
|
||||
}
|
||||
allow = {
|
||||
all = true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}'''
|
||||
_, resources = plan_runner(org_policies=policies)
|
||||
assert len(resources) == 7
|
||||
values = [r['values'] for r in resources if r['type']
|
||||
== 'google_project_organization_policy']
|
||||
assert [r['constraint'] for r in values] == [
|
||||
'policy-a', 'policy-b', 'policy-c'
|
||||
]
|
||||
assert values[0]['list_policy'][0]['allow'] == [
|
||||
{'all': True, 'values': None}]
|
||||
assert values[1]['list_policy'][0]['deny'] == [
|
||||
{'all': False, 'values': ["bar"]}]
|
||||
assert values[2]['restore_policy'] == [{'default': True}]
|
||||
|
||||
policies = [r for r in resources if r['type'] == 'google_org_policy_policy']
|
||||
assert len(policies) == 3
|
||||
assert all(x['values']['parent'] == 'projects/my-project' for x in policies)
|
||||
|
||||
p1 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'compute.vmExternalIpAccess'
|
||||
][0]
|
||||
assert p1['inherit_from_parent'] is None
|
||||
assert p1['reset'] is None
|
||||
assert p1['rules'] == [{
|
||||
'allow_all': None,
|
||||
'condition': [],
|
||||
'deny_all': 'TRUE',
|
||||
'enforce': None,
|
||||
'values': []
|
||||
}]
|
||||
|
||||
p2 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'iam.allowedPolicyMemberDomains'
|
||||
][0]
|
||||
assert p2['inherit_from_parent'] is None
|
||||
assert p2['reset'] is None
|
||||
assert p2['rules'] == [{
|
||||
'allow_all':
|
||||
None,
|
||||
'condition': [],
|
||||
'deny_all':
|
||||
None,
|
||||
'enforce':
|
||||
None,
|
||||
'values': [{
|
||||
'allowed_values': [
|
||||
'C0xxxxxxx',
|
||||
'C0yyyyyyy',
|
||||
],
|
||||
'denied_values': None
|
||||
}]
|
||||
}]
|
||||
|
||||
p3 = [
|
||||
r['values']['spec'][0]
|
||||
for r in policies
|
||||
if r['index'] == 'compute.restrictLoadBalancerCreationForTypes'
|
||||
][0]
|
||||
assert p3['inherit_from_parent'] is None
|
||||
assert p3['reset'] is None
|
||||
assert len(p3['rules']) == 3
|
||||
assert p3['rules'][0] == {
|
||||
'allow_all': None,
|
||||
'condition': [],
|
||||
'deny_all': None,
|
||||
'enforce': None,
|
||||
'values': [{
|
||||
'allowed_values': None,
|
||||
'denied_values': ['in:EXTERNAL']
|
||||
}]
|
||||
}
|
||||
|
||||
assert p3['rules'][1] == {
|
||||
'allow_all': None,
|
||||
'condition': [{
|
||||
'description': 'test condition',
|
||||
'expression': 'resource.matchTagId("tagKeys/1234", "tagValues/1234")',
|
||||
'location': 'xxx',
|
||||
'title': 'condition'
|
||||
}],
|
||||
'deny_all': None,
|
||||
'enforce': None,
|
||||
'values': [{
|
||||
'allowed_values': ['EXTERNAL_1'],
|
||||
'denied_values': None
|
||||
}]
|
||||
}
|
||||
|
||||
assert p3['rules'][2] == {
|
||||
'allow_all': 'TRUE',
|
||||
'condition': [{
|
||||
'description':
|
||||
'test condition2',
|
||||
'expression':
|
||||
'resource.matchTagId("tagKeys/12345", "tagValues/12345")',
|
||||
'location':
|
||||
'xxx',
|
||||
'title':
|
||||
'condition2'
|
||||
}],
|
||||
'deny_all': None,
|
||||
'enforce': None,
|
||||
'values': []
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue