Merge branch 'master' into gleichda-net-dashboard
This commit is contained in:
commit
db60b0eda4
|
@ -1,55 +0,0 @@
|
|||
# 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.
|
||||
|
||||
name: "FAST Tests"
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- fast-dev
|
||||
- fast-dev-gke
|
||||
- master
|
||||
# paths:
|
||||
# - 'modules/**'
|
||||
# - 'fast/stages/**'
|
||||
# - 'tests/fast/**'
|
||||
tags:
|
||||
- ci
|
||||
- test
|
||||
|
||||
env:
|
||||
TF_PLUGIN_CACHE_DIR: "/home/runner/.terraform.d/plugin-cache"
|
||||
GOOGLE_APPLICATION_CREDENTIALS: "/home/runner/credentials.json"
|
||||
PYTEST_ADDOPTS: "--color=yes"
|
||||
|
||||
jobs:
|
||||
all-fast-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Config auth
|
||||
run: |
|
||||
echo '{"type": "service_account", "project_id": "test-only"}' \
|
||||
| tee -a $GOOGLE_APPLICATION_CREDENTIALS
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "3.9"
|
||||
|
||||
- name: Run tests on FAST stages
|
||||
run: |
|
||||
mkdir -p ${{ env.TF_PLUGIN_CACHE_DIR }}
|
||||
pip install -r tests/requirements.txt
|
||||
pytest -vv tests/fast
|
|
@ -26,9 +26,11 @@ on:
|
|||
- test
|
||||
|
||||
env:
|
||||
TF_PLUGIN_CACHE_DIR: "/home/runner/.terraform.d/plugin-cache"
|
||||
GOOGLE_APPLICATION_CREDENTIALS: "/home/runner/credentials.json"
|
||||
PYTEST_ADDOPTS: "--color=yes"
|
||||
PYTHON_VERSION: 3.9
|
||||
TF_PLUGIN_CACHE_DIR: "/home/runner/.terraform.d/plugin-cache"
|
||||
TF_VERSION: 1.1.8
|
||||
|
||||
jobs:
|
||||
doc-examples:
|
||||
|
@ -44,9 +46,10 @@ jobs:
|
|||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "3.9"
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Run tests on documentation examples
|
||||
id: pytest
|
||||
run: |
|
||||
mkdir -p ${{ env.TF_PLUGIN_CACHE_DIR }}
|
||||
pip install -r tests/requirements.txt
|
||||
|
@ -65,15 +68,16 @@ jobs:
|
|||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "3.9"
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Set up Terraform
|
||||
uses: hashicorp/setup-terraform@v1
|
||||
with:
|
||||
terraform_version: 1.1.4
|
||||
terraform_version: ${{ env.TF_VERSION }}
|
||||
terraform_wrapper: false
|
||||
|
||||
- name: Run tests environments
|
||||
id: pytest
|
||||
run: |
|
||||
mkdir -p ${{ env.TF_PLUGIN_CACHE_DIR }}
|
||||
pip install -r tests/requirements.txt
|
||||
|
@ -92,16 +96,45 @@ jobs:
|
|||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: "3.9"
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Set up Terraform
|
||||
uses: hashicorp/setup-terraform@v1
|
||||
with:
|
||||
terraform_version: 1.1.4
|
||||
terraform_version: ${{ env.TF_VERSION }}
|
||||
terraform_wrapper: false
|
||||
|
||||
- name: Run tests modules
|
||||
id: pytest
|
||||
run: |
|
||||
mkdir -p ${{ env.TF_PLUGIN_CACHE_DIR }}
|
||||
pip install -r tests/requirements.txt
|
||||
pytest -vv tests/modules
|
||||
|
||||
fast:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Config auth
|
||||
run: |
|
||||
echo '{"type": "service_account", "project_id": "test-only"}' \
|
||||
| tee -a $GOOGLE_APPLICATION_CREDENTIALS
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
|
||||
- name: Set up Terraform
|
||||
uses: hashicorp/setup-terraform@v1
|
||||
with:
|
||||
terraform_version: ${{ env.TF_VERSION }}
|
||||
terraform_wrapper: false
|
||||
|
||||
- name: Run tests on FAST stages
|
||||
id: pytest
|
||||
run: |
|
||||
mkdir -p ${{ env.TF_PLUGIN_CACHE_DIR }}
|
||||
pip install -r tests/requirements.txt
|
||||
pytest -vv tests/fast
|
||||
|
|
|
@ -27,3 +27,4 @@ fast/stages/**/terraform.tfvars.json
|
|||
fast/stages/**/terraform-*.auto.tfvars.json
|
||||
fast/stages/**/0*.auto.tfvars*
|
||||
**/node_modules
|
||||
fast/stages/**/globals.auto.tfvars.json
|
||||
|
|
|
@ -183,20 +183,21 @@ Due to its simplicity, this stage lends itself easily to customizations: adding
|
|||
| [groups](variables.tf#L118) | Group names to grant organization-level permissions. | <code>map(string)</code> | | <code title="{ gcp-billing-admins = "gcp-billing-admins", gcp-devops = "gcp-devops", gcp-network-admins = "gcp-network-admins" gcp-organization-admins = "gcp-organization-admins" gcp-security-admins = "gcp-security-admins" gcp-support = "gcp-support" }">{…}</code> | <code>00-bootstrap</code> |
|
||||
| [organization_policy_configs](variables.tf#L143) | Organization policies customization. | <code title="object({ allowed_policy_member_domains = list(string) })">object({…})</code> | | <code>null</code> | |
|
||||
| [outputs_location](variables.tf#L151) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable | <code>string</code> | | <code>null</code> | |
|
||||
| [team_folders](variables.tf#L168) | Team folders to be created. Format is described in a code comment. | <code title="map(object({ descriptive_name = string group_iam = map(list(string)) impersonation_groups = list(string) }))">map(object({…}))</code> | | <code>null</code> | |
|
||||
| [tag_names](variables.tf#L168) | Customized names for resource management tags. | <code title="object({ context = string environment = string })">object({…})</code> | | <code title="{ context = "context" environment = "environment" }">{…}</code> | |
|
||||
| [team_folders](variables.tf#L185) | Team folders to be created. Format is described in a code comment. | <code title="map(object({ descriptive_name = string group_iam = map(list(string)) impersonation_groups = list(string) }))">map(object({…}))</code> | | <code>null</code> | |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive | consumers |
|
||||
|---|---|:---:|---|
|
||||
| [cicd_repositories](outputs.tf#L156) | WIF configuration for CI/CD repositories. | | |
|
||||
| [dataplatform](outputs.tf#L168) | Data for the Data Platform stage. | | |
|
||||
| [networking](outputs.tf#L184) | Data for the networking stage. | | |
|
||||
| [project_factories](outputs.tf#L193) | Data for the project factories stage. | | |
|
||||
| [providers](outputs.tf#L209) | Terraform provider files for this stage and dependent stages. | ✓ | <code>02-networking</code> · <code>02-security</code> · <code>03-dataplatform</code> · <code>xx-sandbox</code> · <code>xx-teams</code> |
|
||||
| [sandbox](outputs.tf#L216) | Data for the sandbox stage. | | <code>xx-sandbox</code> |
|
||||
| [security](outputs.tf#L226) | Data for the networking stage. | | <code>02-security</code> |
|
||||
| [teams](outputs.tf#L236) | Data for the teams stage. | | |
|
||||
| [tfvars](outputs.tf#L249) | Terraform variable files for the following stages. | ✓ | |
|
||||
| [cicd_repositories](outputs.tf#L157) | WIF configuration for CI/CD repositories. | | |
|
||||
| [dataplatform](outputs.tf#L169) | Data for the Data Platform stage. | | |
|
||||
| [networking](outputs.tf#L185) | Data for the networking stage. | | |
|
||||
| [project_factories](outputs.tf#L194) | Data for the project factories stage. | | |
|
||||
| [providers](outputs.tf#L210) | Terraform provider files for this stage and dependent stages. | ✓ | <code>02-networking</code> · <code>02-security</code> · <code>03-dataplatform</code> · <code>xx-sandbox</code> · <code>xx-teams</code> |
|
||||
| [sandbox](outputs.tf#L217) | Data for the sandbox stage. | | <code>xx-sandbox</code> |
|
||||
| [security](outputs.tf#L227) | Data for the networking stage. | | <code>02-security</code> |
|
||||
| [teams](outputs.tf#L237) | Data for the teams stage. | | |
|
||||
| [tfvars](outputs.tf#L250) | Terraform variable files for the following stages. | ✓ | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
|
|
@ -21,7 +21,9 @@ module "branch-dp-folder" {
|
|||
parent = "organizations/${var.organization.id}"
|
||||
name = "Data Platform"
|
||||
tag_bindings = {
|
||||
context = try(module.organization.tag_values["context/data"].id, null)
|
||||
context = try(
|
||||
module.organization.tag_values["${var.tag_names.context}/data"].id, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +41,9 @@ module "branch-dp-dev-folder" {
|
|||
"roles/resourcemanager.projectCreator" = [module.branch-dp-dev-sa.iam_email]
|
||||
}
|
||||
tag_bindings = {
|
||||
context = try(module.organization.tag_values["environment/development"].id, null)
|
||||
context = try(
|
||||
module.organization.tag_values["${var.tag_names.environment}/development"].id, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,7 +61,9 @@ module "branch-dp-prod-folder" {
|
|||
"roles/resourcemanager.projectCreator" = [module.branch-dp-prod-sa.iam_email]
|
||||
}
|
||||
tag_bindings = {
|
||||
context = try(module.organization.tag_values["environment/production"].id, null)
|
||||
context = try(
|
||||
module.organization.tag_values["${var.tag_names.environment}/production"].id, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,9 @@ module "branch-network-folder" {
|
|||
"roles/compute.xpnAdmin" = [module.branch-network-sa.iam_email]
|
||||
}
|
||||
tag_bindings = {
|
||||
context = try(module.organization.tag_values["context/networking"].id, null)
|
||||
context = try(
|
||||
module.organization.tag_values["${var.tag_names.context}/networking"].id, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +56,9 @@ module "branch-network-prod-folder" {
|
|||
]
|
||||
}
|
||||
tag_bindings = {
|
||||
environment = try(module.organization.tag_values["environment/production"].id, null)
|
||||
environment = try(
|
||||
module.organization.tag_values["${var.tag_names.environment}/production"].id, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,7 +73,9 @@ module "branch-network-dev-folder" {
|
|||
]
|
||||
}
|
||||
tag_bindings = {
|
||||
environment = try(module.organization.tag_values["environment/development"].id, null)
|
||||
environment = try(
|
||||
module.organization.tag_values["${var.tag_names.environment}/development"].id, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,9 @@ module "branch-sandbox-folder" {
|
|||
}
|
||||
}
|
||||
tag_bindings = {
|
||||
context = try(module.organization.tag_values["context/sandbox"].id, null)
|
||||
context = try(
|
||||
module.organization.tag_values["${var.tag_names.context}/sandbox"].id, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,9 @@ module "branch-security-folder" {
|
|||
"roles/resourcemanager.projectCreator" = [module.branch-security-sa.iam_email]
|
||||
}
|
||||
tag_bindings = {
|
||||
context = try(module.organization.tag_values["context/security"].id, null)
|
||||
context = try(
|
||||
module.organization.tag_values["${var.tag_names.context}/security"].id, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,9 @@ module "branch-teams-folder" {
|
|||
parent = "organizations/${var.organization.id}"
|
||||
name = "Teams"
|
||||
tag_bindings = {
|
||||
context = try(module.organization.tag_values["context/teams"].id, null)
|
||||
context = try(
|
||||
module.organization.tag_values["${var.tag_names.context}/teams"].id, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +92,9 @@ module "branch-teams-team-dev-folder" {
|
|||
"roles/resourcemanager.projectCreator" = [module.branch-teams-dev-pf-sa.iam_email]
|
||||
}
|
||||
tag_bindings = {
|
||||
environment = try(module.organization.tag_values["environment/development"].id, null)
|
||||
environment = try(
|
||||
module.organization.tag_values["${var.tag_names.environment}/development"].id, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,7 +115,9 @@ module "branch-teams-team-prod-folder" {
|
|||
"roles/resourcemanager.projectCreator" = [module.branch-teams-prod-pf-sa.iam_email]
|
||||
}
|
||||
tag_bindings = {
|
||||
environment = try(module.organization.tag_values["environment/production"].id, null)
|
||||
environment = try(
|
||||
module.organization.tag_values["${var.tag_names.environment}/production"].id, null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
/home/ludomagno/Desktop/dev/tf-playground/config/tfvars/globals.auto.tfvars.json
|
|
@ -151,7 +151,7 @@ module "organization" {
|
|||
# )
|
||||
}
|
||||
tags = {
|
||||
context = {
|
||||
(var.tag_names.context) = {
|
||||
description = "Resource management context."
|
||||
iam = {}
|
||||
values = {
|
||||
|
@ -163,7 +163,7 @@ module "organization" {
|
|||
teams = null
|
||||
}
|
||||
}
|
||||
environment = {
|
||||
(var.tag_names.environment) = {
|
||||
description = "Environment definition."
|
||||
iam = {}
|
||||
values = {
|
||||
|
@ -190,9 +190,9 @@ resource "google_organization_iam_member" "org_policy_admin" {
|
|||
title = "org_policy_tag_scoped"
|
||||
description = "Org policy tag scoped grant for ${each.value.0}/${each.value.1}."
|
||||
expression = <<-END
|
||||
resource.matchTag('${var.organization.id}/context', '${each.value.0}')
|
||||
resource.matchTag('${var.organization.id}/${var.tag_names.context}', '${each.value.0}')
|
||||
&&
|
||||
resource.matchTag('${var.organization.id}/environment', '${each.value.1}')
|
||||
resource.matchTag('${var.organization.id}/${var.tag_names.environment}', '${each.value.1}')
|
||||
END
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,6 +150,7 @@ locals {
|
|||
tfvars = {
|
||||
folder_ids = local.folder_ids
|
||||
service_accounts = local.service_accounts
|
||||
tag_names = var.tag_names
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -165,6 +165,23 @@ variable "prefix" {
|
|||
}
|
||||
}
|
||||
|
||||
variable "tag_names" {
|
||||
description = "Customized names for resource management tags."
|
||||
type = object({
|
||||
context = string
|
||||
environment = string
|
||||
})
|
||||
default = {
|
||||
context = "context"
|
||||
environment = "environment"
|
||||
}
|
||||
nullable = false
|
||||
validation {
|
||||
condition = alltrue([for k, v in var.tag_names : v != null])
|
||||
error_message = "Tag names cannot be null."
|
||||
}
|
||||
}
|
||||
|
||||
variable "team_folders" {
|
||||
description = "Team folders to be created. Format is described in a code comment."
|
||||
type = map(object({
|
||||
|
|
|
@ -56,8 +56,8 @@ module "db" {
|
|||
tier = "db-g1-small"
|
||||
|
||||
replicas = {
|
||||
replica1 = "europe-west3"
|
||||
replica2 = "us-central1"
|
||||
replica1 = { region = "europe-west3", encryption_key_name = null }
|
||||
replica2 = { region = "us-central1", encryption_key_name = null }
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=3
|
||||
|
@ -93,6 +93,53 @@ module "db" {
|
|||
}
|
||||
# tftest modules=1 resources=6
|
||||
```
|
||||
|
||||
### CMEK encryption
|
||||
```hcl
|
||||
|
||||
module "project" {
|
||||
source = "./modules/project"
|
||||
billing_account = var.billing_account_id
|
||||
parent = var.organization_id
|
||||
name = "my-db-project"
|
||||
services = [
|
||||
"servicenetworking.googleapis.com",
|
||||
"sqladmin.googleapis.com",
|
||||
]
|
||||
}
|
||||
|
||||
module "kms" {
|
||||
source = "./modules/kms"
|
||||
project_id = module.project.project_id
|
||||
keyring = {
|
||||
name = "keyring"
|
||||
location = var.region
|
||||
}
|
||||
keys = {
|
||||
key-sql = null
|
||||
}
|
||||
key_iam = {
|
||||
key-sql = {
|
||||
"roles/cloudkms.cryptoKeyEncrypterDecrypter" = [
|
||||
"serviceAccount:${module.project.service_accounts.robots.sqladmin}"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "db" {
|
||||
source = "./modules/cloudsql-instance"
|
||||
project_id = module.project.project_id
|
||||
encryption_key_name = module.kms.keys["key-sql"].id
|
||||
network = var.vpc.self_link
|
||||
name = "db"
|
||||
region = var.region
|
||||
database_version = "POSTGRES_13"
|
||||
tier = "db-g1-small"
|
||||
}
|
||||
|
||||
# tftest modules=3 resources=10
|
||||
```
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Variables
|
||||
|
@ -100,11 +147,11 @@ module "db" {
|
|||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [database_version](variables.tf#L50) | Database type and version to create. | <code>string</code> | ✓ | |
|
||||
| [name](variables.tf#L91) | Name of primary replica. | <code>string</code> | ✓ | |
|
||||
| [network](variables.tf#L96) | VPC self link where the instances will be deployed. Private Service Networking must be enabled and configured in this VPC. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L107) | The ID of the project where this instances will be created. | <code>string</code> | ✓ | |
|
||||
| [region](variables.tf#L112) | Region of the primary replica. | <code>string</code> | ✓ | |
|
||||
| [tier](variables.tf#L123) | The machine type to use for the instances. | <code>string</code> | ✓ | |
|
||||
| [name](variables.tf#L97) | Name of primary instance. | <code>string</code> | ✓ | |
|
||||
| [network](variables.tf#L102) | VPC self link where the instances will be deployed. Private Service Networking must be enabled and configured in this VPC. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L113) | The ID of the project where this instances will be created. | <code>string</code> | ✓ | |
|
||||
| [region](variables.tf#L118) | Region of the primary instance. | <code>string</code> | ✓ | |
|
||||
| [tier](variables.tf#L132) | The machine type to use for the instances. | <code>string</code> | ✓ | |
|
||||
| [authorized_networks](variables.tf#L17) | Map of NAME=>CIDR_RANGE to allow to connect to the database(s). | <code>map(string)</code> | | <code>null</code> |
|
||||
| [availability_type](variables.tf#L23) | Availability type for the primary replica. Either `ZONAL` or `REGIONAL`. | <code>string</code> | | <code>"ZONAL"</code> |
|
||||
| [backup_configuration](variables.tf#L29) | Backup settings for primary instance. Will be automatically enabled if using MySQL with one or more replicas. | <code title="object({ enabled = bool binary_log_enabled = bool start_time = string location = string log_retention_days = number retention_count = number })">object({…})</code> | | <code title="{ enabled = false binary_log_enabled = false start_time = "23:00" location = null log_retention_days = 7 retention_count = 7 }">{…}</code> |
|
||||
|
@ -112,11 +159,12 @@ module "db" {
|
|||
| [deletion_protection](variables.tf#L61) | Allow terraform to delete instances. | <code>bool</code> | | <code>false</code> |
|
||||
| [disk_size](variables.tf#L67) | Disk size in GB. Set to null to enable autoresize. | <code>number</code> | | <code>null</code> |
|
||||
| [disk_type](variables.tf#L73) | The type of data disk: `PD_SSD` or `PD_HDD`. | <code>string</code> | | <code>"PD_SSD"</code> |
|
||||
| [flags](variables.tf#L79) | Map FLAG_NAME=>VALUE for database-specific tuning. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [labels](variables.tf#L85) | Labels to be attached to all instances. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [prefix](variables.tf#L101) | Prefix used to generate instance names. | <code>string</code> | | <code>null</code> |
|
||||
| [replicas](variables.tf#L117) | Map of NAME=>REGION for additional read replicas. Set to null to disable replica creation. | <code>map(any)</code> | | <code>null</code> |
|
||||
| [users](variables.tf#L128) | Map of users to create in the primary instance (and replicated to other replicas) in the format USER=>PASSWORD. For MySQL, anything afterr the first `@` (if persent) will be used as the user's host. Set PASSWORD to null if you want to get an autogenerated password. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [encryption_key_name](variables.tf#L79) | The full path to the encryption key used for the CMEK disk encryption of the primary instance. | <code>string</code> | | <code>null</code> |
|
||||
| [flags](variables.tf#L85) | Map FLAG_NAME=>VALUE for database-specific tuning. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [labels](variables.tf#L91) | Labels to be attached to all instances. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [prefix](variables.tf#L107) | Prefix used to generate instance names. | <code>string</code> | | <code>null</code> |
|
||||
| [replicas](variables.tf#L123) | Map of NAME=> {REGION, KMS_KEY} for additional read replicas. Set to null to disable replica creation. | <code title="map(object({ region = string encryption_key_name = string }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [users](variables.tf#L137) | Map of users to create in the primary instance (and replicated to other replicas) in the format USER=>PASSWORD. For MySQL, anything afterr the first `@` (if persent) will be used as the user's host. Set PASSWORD to null if you want to get an autogenerated password. | <code>map(string)</code> | | <code>null</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
|
@ -129,8 +177,10 @@ module "db" {
|
|||
| [instances](outputs.tf#L50) | Cloud SQL instance resources. | ✓ |
|
||||
| [ip](outputs.tf#L56) | IP address of the primary instance. | |
|
||||
| [ips](outputs.tf#L61) | IP addresses of all instances. | |
|
||||
| [self_link](outputs.tf#L69) | Self link of the primary instance. | |
|
||||
| [self_links](outputs.tf#L74) | Self links of all instances. | |
|
||||
| [user_passwords](outputs.tf#L82) | Map of containing the password of all users created through terraform. | ✓ |
|
||||
| [name](outputs.tf#L69) | Name of the primary instance. | |
|
||||
| [names](outputs.tf#L74) | Names of all instances. | |
|
||||
| [self_link](outputs.tf#L82) | Self link of the primary instance. | |
|
||||
| [self_links](outputs.tf#L87) | Self links of all instances. | |
|
||||
| [user_passwords](outputs.tf#L95) | Map of containing the password of all users created through terraform. | ✓ |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
|
|
@ -39,10 +39,12 @@ locals {
|
|||
}
|
||||
|
||||
resource "google_sql_database_instance" "primary" {
|
||||
project = var.project_id
|
||||
name = "${local.prefix}${var.name}"
|
||||
region = var.region
|
||||
database_version = var.database_version
|
||||
provider = google-beta
|
||||
project = var.project_id
|
||||
name = "${local.prefix}${var.name}"
|
||||
region = var.region
|
||||
database_version = var.database_version
|
||||
encryption_key_name = var.encryption_key_name
|
||||
|
||||
settings {
|
||||
tier = var.tier
|
||||
|
@ -99,11 +101,13 @@ resource "google_sql_database_instance" "primary" {
|
|||
}
|
||||
|
||||
resource "google_sql_database_instance" "replicas" {
|
||||
provider = google-beta
|
||||
for_each = local.has_replicas ? var.replicas : {}
|
||||
project = var.project_id
|
||||
name = "${local.prefix}${each.key}"
|
||||
region = each.value
|
||||
region = each.value.region
|
||||
database_version = var.database_version
|
||||
encryption_key_name = each.value.encryption_key_name
|
||||
master_instance_name = google_sql_database_instance.primary.name
|
||||
|
||||
settings {
|
||||
|
|
|
@ -66,6 +66,19 @@ output "ips" {
|
|||
}
|
||||
}
|
||||
|
||||
output "name" {
|
||||
description = "Name of the primary instance."
|
||||
value = google_sql_database_instance.primary.name
|
||||
}
|
||||
|
||||
output "names" {
|
||||
description = "Names of all instances."
|
||||
value = {
|
||||
for id, instance in local._all_intances :
|
||||
id => instance.name
|
||||
}
|
||||
}
|
||||
|
||||
output "self_link" {
|
||||
description = "Self link of the primary instance."
|
||||
value = google_sql_database_instance.primary.self_link
|
||||
|
|
|
@ -76,6 +76,12 @@ variable "disk_type" {
|
|||
default = "PD_SSD"
|
||||
}
|
||||
|
||||
variable "encryption_key_name" {
|
||||
description = "The full path to the encryption key used for the CMEK disk encryption of the primary instance."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "flags" {
|
||||
description = "Map FLAG_NAME=>VALUE for database-specific tuning."
|
||||
type = map(string)
|
||||
|
@ -89,7 +95,7 @@ variable "labels" {
|
|||
}
|
||||
|
||||
variable "name" {
|
||||
description = "Name of primary replica."
|
||||
description = "Name of primary instance."
|
||||
type = string
|
||||
}
|
||||
|
||||
|
@ -110,14 +116,17 @@ variable "project_id" {
|
|||
}
|
||||
|
||||
variable "region" {
|
||||
description = "Region of the primary replica."
|
||||
description = "Region of the primary instance."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "replicas" {
|
||||
description = "Map of NAME=>REGION for additional read replicas. Set to null to disable replica creation."
|
||||
type = map(any)
|
||||
default = null
|
||||
description = "Map of NAME=> {REGION, KMS_KEY} for additional read replicas. Set to null to disable replica creation."
|
||||
type = map(object({
|
||||
region = string
|
||||
encryption_key_name = string
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "tier" {
|
||||
|
|
|
@ -1,8 +1,19 @@
|
|||
# Project Module
|
||||
|
||||
## Examples
|
||||
This module implements the creation and management of one GCP project including IAM, organization policies, Shared VPC host or service attachment, service API activation, and tag attachment. It also offers a convenient way to refer to managed service identities (aka robot service accounts) for APIs.
|
||||
|
||||
### Minimal example with IAM
|
||||
## IAM Examples
|
||||
|
||||
IAM is managed via several variables that implement different levels of control:
|
||||
|
||||
- `group_iam` and `iam` configure authoritative bindings that manage individual roles exclusively, mapping to the [`google_project_iam_binding`](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam#google_project_iam_binding) resource
|
||||
- `iam_additive` and `iam_additive_members` configure additive bindings that only manage individual role/member pairs, mapping to the [`google_project_iam_member`](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_project_iam#google_project_iam_member) resource
|
||||
|
||||
Be mindful about service identity roles when using authoritative IAM, as you might inadvertently remove a role from a [service identity](https://cloud.google.com/iam/docs/service-accounts#google-managed) or default service account. For example, using `roles/editor` with `iam` or `group_iam` will remove the default permissions for the Cloud Services identity. A simple workaround for these scenarios is described below.
|
||||
|
||||
### Authoritative IAM
|
||||
|
||||
The `iam` variable is based on role keys and is typically used for service accounts, or where member values can be dynamic and would create potential problems in the underlying `for_each` cycle.
|
||||
|
||||
```hcl
|
||||
locals {
|
||||
|
@ -28,16 +39,43 @@ module "project" {
|
|||
# tftest modules=1 resources=4
|
||||
```
|
||||
|
||||
### Minimal example with IAM additive roles
|
||||
The `group_iam` variable uses group email addresses as keys and is a convenient way to assign roles to humans following Google's best practices. The end result is readable code that also serves as documentation.
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
source = "./modules/project"
|
||||
billing_account = "123456-123456-123456"
|
||||
name = "project-example"
|
||||
parent = "folders/1234567890"
|
||||
prefix = "foo"
|
||||
services = [
|
||||
"container.googleapis.com",
|
||||
"stackdriver.googleapis.com"
|
||||
]
|
||||
group_iam = {
|
||||
"gcp-security-admins@example.com" = [
|
||||
"roles/cloudasset.owner",
|
||||
"roles/cloudsupport.techSupportEditor",
|
||||
"roles/iam.securityReviewer",
|
||||
"roles/logging.admin",
|
||||
]
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=7
|
||||
```
|
||||
|
||||
### Additive IAM
|
||||
|
||||
Additive IAM is typically used where bindings for specific roles are controlled by different modules or in different Terraform stages. One example is when the project is created by one team but a different team manages service account creation for the project, and some of the project-level roles overlap in the two configurations.
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
source = "./modules/project"
|
||||
name = "project-example"
|
||||
|
||||
iam_additive = {
|
||||
"roles/viewer" = [
|
||||
"group:one@example.org", "group:two@xample.org"
|
||||
"group:one@example.org",
|
||||
"group:two@xample.org"
|
||||
],
|
||||
"roles/storage.objectAdmin" = [
|
||||
"group:two@example.org"
|
||||
|
@ -50,13 +88,54 @@ module "project" {
|
|||
# tftest modules=1 resources=5
|
||||
```
|
||||
|
||||
### Shared VPC service
|
||||
### Service Identities and authoritative IAM
|
||||
|
||||
As mentioned above, there are cases where authoritative management of specific IAM roles results in removal of default bindings from service identities. One example is outlined below, with a simple workaround leveraging the `service_accounts` output to identify the service identity. A full list of service identities and their roles can be found [here](https://cloud.google.com/iam/docs/service-agents).
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
source = "./modules/project"
|
||||
name = "project-example"
|
||||
group_iam = {
|
||||
"roles/editor" = [
|
||||
"group:foo@example.com"
|
||||
]
|
||||
}
|
||||
iam = {
|
||||
"roles/editor" = [
|
||||
"serviceAccount:${module.project.service_accounts.cloud_services}"
|
||||
]
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=3
|
||||
```
|
||||
|
||||
## Shared VPC service
|
||||
|
||||
The module allows managing Shared VPC status for both hosts and service projects, and includes a simple way of assigning Shared VPC roles to service identities.
|
||||
|
||||
### Host project
|
||||
|
||||
You can enable Shared VPC Host at the project level and manage project service association independently.
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
source = "./modules/project"
|
||||
name = "project-example"
|
||||
shared_vpc_host_config = {
|
||||
enabled = true
|
||||
service_projects = []
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=2
|
||||
```
|
||||
|
||||
### Service project
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
source = "./modules/project"
|
||||
name = "project-example"
|
||||
shared_vpc_service_config = {
|
||||
attach = true
|
||||
host_project = "my-host-project"
|
||||
|
@ -76,7 +155,7 @@ module "project" {
|
|||
# tftest modules=1 resources=6
|
||||
```
|
||||
|
||||
### Organization policies
|
||||
## Organization policies
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
|
@ -184,6 +263,8 @@ module "project-host" {
|
|||
|
||||
## Cloud KMS encryption keys
|
||||
|
||||
The module offers a simple, centralized way to assign `roles/cloudkms.cryptoKeyEncrypterDecrypter` to service identities.
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
source = "./modules/project"
|
||||
|
@ -238,6 +319,27 @@ module "project" {
|
|||
# tftest modules=2 resources=6
|
||||
```
|
||||
|
||||
## Outputs
|
||||
|
||||
Most of this module's outputs depend on its resources, to allow Terraform to compute all dependencies required for the project to be correctly configured. This allows you to reference outputs like `project_id` in other modules or resources without having to worry about setting `depends_on` blocks manually.
|
||||
|
||||
One non-obvious output is `service_accounts`, which offers a simple way to discover service identities and default service accounts, and guarantees that service identities that require an API call to trigger creation (like GCS or BigQuery) exist before use.
|
||||
|
||||
```hcl
|
||||
module "project" {
|
||||
source = "./modules/project"
|
||||
name = "project-example"
|
||||
services = [
|
||||
"compute.googleapis.com"
|
||||
]
|
||||
}
|
||||
|
||||
output "compute_robot" {
|
||||
value = module.project.service_accounts.robots.compute
|
||||
}
|
||||
# tftest modules=1 resources=2
|
||||
```
|
||||
|
||||
<!-- TFDOC OPTS files:1 -->
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
|
|
|
@ -42,7 +42,9 @@ locals {
|
|||
gcf = "service-%s@gcf-admin-robot"
|
||||
pubsub = "service-%s@gcp-sa-pubsub"
|
||||
secretmanager = "service-%s@gcp-sa-secretmanager"
|
||||
sql = "service-%s@gcp-sa-cloud-sql"
|
||||
storage = "service-%s@gs-project-accounts"
|
||||
sqladmin = "service-%s@gcp-sa-cloud-sql"
|
||||
}
|
||||
service_accounts_default = {
|
||||
compute = "${local.project.number}-compute@developer.gserviceaccount.com"
|
||||
|
@ -56,9 +58,10 @@ locals {
|
|||
k => "${format(v, local.project.number)}.iam.gserviceaccount.com"
|
||||
}
|
||||
service_accounts_jit_services = [
|
||||
"secretmanager.googleapis.com",
|
||||
"cloudasset.googleapis.com",
|
||||
"pubsub.googleapis.com",
|
||||
"cloudasset.googleapis.com"
|
||||
"secretmanager.googleapis.com",
|
||||
"sqladmin.googleapis.com"
|
||||
]
|
||||
service_accounts_cmek_service_keys = distinct(flatten([
|
||||
for s in keys(var.service_encryption_key_ids) : [
|
||||
|
|
|
@ -94,7 +94,7 @@ variable "region" {
|
|||
}
|
||||
|
||||
variable "replicas" {
|
||||
type = map(any)
|
||||
type = any
|
||||
default = null
|
||||
}
|
||||
|
||||
|
|
|
@ -35,8 +35,8 @@ def test_prefix(plan_runner):
|
|||
assert r['values']['name'] == 'prefix-db'
|
||||
|
||||
replicas = """{
|
||||
replica1 = "europe-west3"
|
||||
replica2 = "us-central1"
|
||||
replica1 = { region = "europe-west3", encryption_key_name = null }
|
||||
replica2 = { region = "us-central1", encryption_key_name = null }
|
||||
}"""
|
||||
|
||||
_, resources = plan_runner(prefix="prefix")
|
||||
|
@ -49,8 +49,8 @@ def test_replicas(plan_runner):
|
|||
"Test replicated instance."
|
||||
|
||||
replicas = """{
|
||||
replica1 = "europe-west3"
|
||||
replica2 = "us-central1"
|
||||
replica1 = { region = "europe-west3", encryption_key_name = null }
|
||||
replica2 = { region = "us-central1", encryption_key_name = null }
|
||||
}"""
|
||||
|
||||
_, resources = plan_runner(replicas=replicas, prefix="prefix")
|
||||
|
@ -80,10 +80,9 @@ def test_mysql_replicas_enables_backup(plan_runner):
|
|||
"Test MySQL backup setup with replicas."
|
||||
|
||||
replicas = """{
|
||||
replica1 = "europe-west3"
|
||||
replica1 = { region = "europe-west3", encryption_key_name = null }
|
||||
}"""
|
||||
_, resources = plan_runner(replicas=replicas,
|
||||
database_version="MYSQL_8_0")
|
||||
_, resources = plan_runner(replicas=replicas, database_version="MYSQL_8_0")
|
||||
assert len(resources) == 2
|
||||
primary = [r for r in resources if r['name'] == 'primary'][0]
|
||||
backup_config = primary['values']['settings'][0]['backup_configuration'][0]
|
||||
|
|
Loading…
Reference in New Issue