Merge pull request #549 from GoogleCloudPlatform/fast/var-contracts

Fast stage contracts via vars
This commit is contained in:
Julio Castillo 2022-02-16 11:09:00 +01:00 committed by GitHub
commit 50e18454a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 461 additions and 362 deletions

3
.gitignore vendored
View File

@ -21,7 +21,8 @@ bundle.zip
**/*.pkrvars.hcl **/*.pkrvars.hcl
fixture_* fixture_*
fast/configs fast/configs
fast/stages/**/providers.tf fast/stages/**/*providers.tf
fast/stages/**/terraform.tfvars fast/stages/**/terraform.tfvars
fast/stages/**/terraform.tfvars.json fast/stages/**/terraform.tfvars.json
fast/stages/**/terraform-*.auto.tfvars.json fast/stages/**/terraform-*.auto.tfvars.json
fast/stages/**/0*.auto.tfvars*

View File

@ -192,7 +192,7 @@ outputs_location = "../../fast-config"
### Output files and cross-stage variables ### Output files and cross-stage variables
At any time during the life of this stage, you can configure it to automatically generate provider configurations and variable files for the following, to simplify exchanging inputs and outputs between stages and avoid having to edit files manually. At any time during the life of this stage, you can configure it to automatically generate provider configurations and variable files consumed by the following stages, to simplify passing outputs to input variables by not having to edit files manually.
Automatic generation of files is disabled by default. To enable the mechanism, set the `outputs_location` variable to a valid path on a local filesystem, e.g. Automatic generation of files is disabled by default. To enable the mechanism, set the `outputs_location` variable to a valid path on a local filesystem, e.g.
@ -202,27 +202,23 @@ outputs_location = "../../config"
Once the variable is set, `apply` will generate and manage providers and variables files, including the initial one used for this stage after the first run. You can then link these files in the relevant stages, instead of manually transfering outputs from one stage, to Terraform variables in another. Once the variable is set, `apply` will generate and manage providers and variables files, including the initial one used for this stage after the first run. You can then link these files in the relevant stages, instead of manually transfering outputs from one stage, to Terraform variables in another.
Below is the outline of the output files generated by this stage: Below is the outline of the output files generated by all stages:
```bash ```bash
[path specified in outputs_location] [path specified in outputs_location]
├── 00-bootstrap ├── providers
│   ├── providers.tf │   ├── 00-bootstrap-providers.tf
├── 01-resman │   ├── 01-resman-providers.tf
│   ├── providers.tf │   ├── 02-networking-providers.tf
│   ├── terraform-bootstrap.auto.tfvars.json │   ├── 02-security-providers.tf
├── 02-networking │   ├── 03-project-factory-dev-providers.tf
│   ├── terraform-bootstrap.auto.tfvars.json │   ├── 03-project-factory-prod-providers.tf
├── 02-security │   └── 99-sandbox-providers.tf
│   ├── terraform-bootstrap.auto.tfvars.json └── tfvars
├── 03-gke-multitenant-dev ├── 00-bootstrap.auto.tfvars.json
│   └── terraform-bootstrap.auto.tfvars.json ├── 01-resman.auto.tfvars.json
├── 03-gke-multitenant-prod ├── 02-networking.auto.tfvars.json
│   └── terraform-bootstrap.auto.tfvars.json └── 02-security.auto.tfvars.json
├── 03-project-factory-dev
│   └── terraform-bootstrap.auto.tfvars.json
├── 03-project-factory-prod
│   └── terraform-bootstrap.auto.tfvars.json
``` ```
### Running the stage ### Running the stage
@ -241,7 +237,7 @@ Once the initial `apply` completes successfully, configure a remote backend usin
```bash ```bash
# if using output files via the outputs_location and set to `../../config` # if using output files via the outputs_location and set to `../../config`
ln -s ../../config/00-bootstrap/* ./ ln -s ../../config/providers/00-bootstrap* ./
# or from outputs if not using output files # or from outputs if not using output files
terraform output -json providers | jq -r '.["00-bootstrap"]' \ terraform output -json providers | jq -r '.["00-bootstrap"]' \
> providers.tf > providers.tf
@ -350,9 +346,10 @@ Names used in internal references (e.g. `module.foo-prod.id`) are only used by T
| name | description | sensitive | consumers | | name | description | sensitive | consumers |
|---|---|:---:|---| |---|---|:---:|---|
| [billing_dataset](outputs.tf#L89) | BigQuery dataset prepared for billing export. | | | | [billing_dataset](outputs.tf#L58) | BigQuery dataset prepared for billing export. | | |
| [project_ids](outputs.tf#L94) | Projects created by this stage. | | | | [custom_roles](outputs.tf#L63) | Organization-level custom roles. | | |
| [providers](outputs.tf#L105) | Terraform provider files for this stage and dependent stages. | ✓ | <code>stage-01</code> | | [project_ids](outputs.tf#L68) | Projects created by this stage. | | |
| [tfvars](outputs.tf#L114) | Terraform variable files for the following stages. | ✓ | | | [providers](outputs.tf#L79) | Terraform provider files for this stage and dependent stages. | ✓ | <code>stage-01</code> |
| [tfvars](outputs.tf#L88) | Terraform variable files for the following stages. | ✓ | |
<!-- END TFDOC --> <!-- END TFDOC -->

View File

@ -15,7 +15,7 @@
*/ */
locals { locals {
_custom_roles = { custom_roles = {
for k, v in var.custom_role_names : for k, v in var.custom_role_names :
k => module.organization.custom_role_id[v] k => module.organization.custom_role_id[v]
} }
@ -32,56 +32,25 @@ locals {
}) })
} }
tfvars = { tfvars = {
"01-resman" = jsonencode({ automation_project_id = module.automation-project.project_id
automation_project_id = module.automation-project.project_id custom_roles = local.custom_roles
billing_account = var.billing_account
custom_roles = local._custom_roles
groups = var.groups
organization = var.organization
prefix = var.prefix
})
"02-networking" = jsonencode({
billing_account_id = var.billing_account.id
custom_roles = local._custom_roles
organization = var.organization
prefix = var.prefix
})
"02-security" = jsonencode({
billing_account_id = var.billing_account.id
organization = var.organization
prefix = var.prefix
})
"03-gke-multitenant-dev" = jsonencode({
billing_account_id = var.billing_account.id
prefix = var.prefix
})
"03-gke-multitenant-prod" = jsonencode({
billing_account_id = var.billing_account.id
prefix = var.prefix
})
"03-project-factory-dev" = jsonencode({
billing_account_id = var.billing_account.id
prefix = var.prefix
})
"03-project-factory-prod" = jsonencode({
billing_account_id = var.billing_account.id
prefix = var.prefix
})
} }
} }
# optionally generate providers and tfvars files for subsequent stages # optionally generate providers and tfvars files for subsequent stages
resource "local_file" "providers" { resource "local_file" "providers" {
for_each = var.outputs_location == null ? {} : local.providers for_each = var.outputs_location == null ? {} : local.providers
filename = "${pathexpand(var.outputs_location)}/${each.key}/providers.tf" file_permission = "0644"
content = each.value filename = "${pathexpand(var.outputs_location)}/providers/${each.key}-providers.tf"
content = each.value
} }
resource "local_file" "tfvars" { resource "local_file" "tfvars" {
for_each = var.outputs_location == null ? {} : local.tfvars for_each = var.outputs_location == null ? {} : { 1 = 1 }
filename = "${pathexpand(var.outputs_location)}/${each.key}/terraform-bootstrap.auto.tfvars.json" file_permission = "0644"
content = each.value filename = "${pathexpand(var.outputs_location)}/tfvars/00-bootstrap.auto.tfvars.json"
content = jsonencode(local.tfvars)
} }
# outputs # outputs
@ -91,6 +60,11 @@ output "billing_dataset" {
value = try(module.billing-export-dataset.0.id, null) value = try(module.billing-export-dataset.0.id, null)
} }
output "custom_roles" {
description = "Organization-level custom roles."
value = local.custom_roles
}
output "project_ids" { output "project_ids" {
description = "Projects created by this stage." description = "Projects created by this stage."
value = { value = {

View File

@ -50,11 +50,11 @@ The default way of making sure you have the right permissions, is to use the ide
To simplify setup, the previous stage pre-configures a valid providers file in its output, and optionally writes it to a local file if the `outputs_location` variable is set to a valid path. To simplify setup, the previous stage pre-configures a valid providers file in its output, and optionally writes it to a local file if the `outputs_location` variable is set to a valid path.
If you have set a valid value for `outputs_location` in the bootstrap stage, simply link the relevant `providers.tf` file from this stage's folder in the path you specified: If you have set a valid value for `outputs_location` in the bootstrap stage (see the [bootstrap stage README](../00-bootstrap/#output-files-and-cross-stage-variables) for more details), simply link the relevant `providers.tf` file from this stage's folder in the path you specified:
```bash ```bash
# `outputs_location` is set to `../../config` # `outputs_location` is set to `~/config`
ln -s ../../config/01-resman/providers.tf ln -s ~/config/providers/01-resman* ./
``` ```
If you have not configured `outputs_location` in bootstrap, you can derive the providers file from that stage's outputs: If you have not configured `outputs_location` in bootstrap, you can derive the providers file from that stage's outputs:
@ -76,14 +76,16 @@ There are two broad sets of variables you will need to fill in:
To avoid the tedious job of filling in the first group of variable with values derived from other stages' outputs, the same mechanism used above for the provider configuration can be used to leverage pre-configured `.tfvars` files. To avoid the tedious job of filling in the first group of variable with values derived from other stages' outputs, the same mechanism used above for the provider configuration can be used to leverage pre-configured `.tfvars` files.
If you configured a valid path for `outputs_location` in the bootstrap stage, simply link the relevant `terraform-*.auto.tfvars.json` files from this stage's outputs folder (under the path you specified), where the `*` above is set to the name of the stage that produced it. For this stage, a single `.tfvars` file is avalaible: If you configured a valid path for `outputs_location` in the bootstrap stage, simply link the relevant `terraform-*.auto.tfvars.json` files from this stage's outputs folder. For this stage, you need the `.tfvars` file compiled manually for the bootstrap stage, and the one generated by it:
```bash ```bash
# `outputs_location` is set to `../../config` # `outputs_location` is set to `~/config`
ln -s ../../config/01-resman/terraform-bootstrap.auto.tfvars.json ln -s ../../config/tfvars/00*.json ./
# also copy the tfvars file used for the bootstrap stage
cp ../00-bootstrap/terraform.tfvars ./
``` ```
A second set of variables is specific to this stage, they are all optional so if you need to customize them, create an extra `terraform.tfvars` file. A second set of variables is specific to this stage, they are all optional so if you need to customize them, create an extra `terraform.tfvars` file or add them to the file copied from bootstrap.
Refer to the [Variables](#variables) table at the bottom of this document, for a full list of variables, their origin (e.g. a stage or specific to this one), and descriptions explaining their meaning. The sections below also describe some of the possible customizations. For billing configurations, refer to the [Bootstrap documentation on billing](../00-bootstrap/README.md#billing-account) as the `billing_account` variable is identical across all stages. Refer to the [Variables](#variables) table at the bottom of this document, for a full list of variables, their origin (e.g. a stage or specific to this one), and descriptions explaining their meaning. The sections below also describe some of the possible customizations. For billing configurations, refer to the [Bootstrap documentation on billing](../00-bootstrap/README.md#billing-account) as the `billing_account` variable is identical across all stages.
@ -163,8 +165,8 @@ Due to its simplicity, this stage lends itself easily to customizations: adding
| name | description | type | required | default | producer | | name | description | type | required | default | producer |
|---|---|:---:|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|:---:|
| [automation_project_id](variables.tf#L29) | Project id for the automation project created by the bootstrap stage. | <code>string</code> | ✓ | | <code>00-bootstrap</code> | | [automation_project_id](variables.tf#L20) | Project id for the automation project created by the bootstrap stage. | <code>string</code> | ✓ | | <code>00-bootstrap</code> |
| [billing_account](variables.tf#L20) | Billing account id and organization id ('nnnnnnnn' or null). | <code title="object&#40;&#123;&#10; id &#61; string&#10; organization_id &#61; number&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>00-bootstrap</code> | | [billing_account](variables.tf#L26) | Billing account id and organization id ('nnnnnnnn' or null). | <code title="object&#40;&#123;&#10; id &#61; string&#10; organization_id &#61; number&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>00-bootstrap</code> |
| [organization](variables.tf#L57) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>00-bootstrap</code> | | [organization](variables.tf#L57) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>00-bootstrap</code> |
| [prefix](variables.tf#L81) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>00-bootstrap</code> | | [prefix](variables.tf#L81) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>00-bootstrap</code> |
| [custom_roles](variables.tf#L35) | Custom roles defined at the org level, in key => id format. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>00-bootstrap</code> | | [custom_roles](variables.tf#L35) | Custom roles defined at the org level, in key => id format. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>00-bootstrap</code> |
@ -177,12 +179,12 @@ Due to its simplicity, this stage lends itself easily to customizations: adding
| name | description | sensitive | consumers | | name | description | sensitive | consumers |
|---|---|:---:|---| |---|---|:---:|---|
| [networking](outputs.tf#L83) | Data for the networking stage. | | <code>02-networking</code> | | [networking](outputs.tf#L101) | Data for the networking stage. | | |
| [project_factories](outputs.tf#L93) | Data for the project factories stage. | | <code>xx-teams</code> | | [project_factories](outputs.tf#L110) | Data for the project factories stage. | | |
| [providers](outputs.tf#L110) | Terraform provider files for this stage and dependent stages. | ✓ | <code>02-networking</code> · <code>02-security</code> · <code>xx-sandbox</code> · <code>xx-teams</code> | | [providers](outputs.tf#L126) | Terraform provider files for this stage and dependent stages. | ✓ | <code>02-networking</code> · <code>02-security</code> · <code>xx-sandbox</code> · <code>xx-teams</code> |
| [sandbox](outputs.tf#L117) | Data for the sandbox stage. | | <code>xx-sandbox</code> | | [sandbox](outputs.tf#L133) | Data for the sandbox stage. | | <code>xx-sandbox</code> |
| [security](outputs.tf#L127) | Data for the networking stage. | | <code>02-security</code> | | [security](outputs.tf#L143) | Data for the networking stage. | | <code>02-security</code> |
| [teams](outputs.tf#L137) | Data for the teams stage. | | | | [teams](outputs.tf#L153) | Data for the teams stage. | | |
| [tfvars](outputs.tf#L150) | Terraform variable files for the following stages. | ✓ | | | [tfvars](outputs.tf#L166) | Terraform variable files for the following stages. | ✓ | |
<!-- END TFDOC --> <!-- END TFDOC -->

View File

@ -15,10 +15,25 @@
*/ */
locals { locals {
_project_factory_sas = { folder_ids = merge(
dev = module.branch-teams-dev-projectfactory-sa.iam_email {
prod = module.branch-teams-prod-projectfactory-sa.iam_email networking = module.branch-network-folder.id
} networking-dev = module.branch-network-dev-folder.id
networking-prod = module.branch-network-prod-folder.id
sandbox = module.branch-sandbox-folder.id
security = module.branch-security-folder.id
teams = module.branch-teams-folder.id
},
{
for k, v in module.branch-teams-team-folder : "team-${k}" => v.id
},
{
for k, v in module.branch-teams-team-dev-folder : "team-${k}-dev" => v.id
},
{
for k, v in module.branch-teams-team-prod-folder : "team-${k}-prod" => v.id
}
)
providers = { providers = {
"02-networking" = templatefile("${path.module}/../../assets/templates/providers.tpl", { "02-networking" = templatefile("${path.module}/../../assets/templates/providers.tpl", {
bucket = module.branch-network-gcs.name bucket = module.branch-network-gcs.name
@ -46,42 +61,44 @@ locals {
sa = module.branch-sandbox-sa.email sa = module.branch-sandbox-sa.email
}) })
} }
service_accounts = merge(
{
networking = module.branch-network-sa.email
project-factory-dev = module.branch-teams-dev-projectfactory-sa.email
project-factory-prod = module.branch-teams-prod-projectfactory-sa.email
sandbox = module.branch-sandbox-sa.email
security = module.branch-security-sa.email
teams = module.branch-teams-prod-sa.email
},
{
for k, v in module.branch-teams-team-sa : "team-${k}" => v.email
},
)
tfvars = { tfvars = {
"02-networking" = jsonencode({ folder_ids = local.folder_ids
folder_ids = { service_accounts = local.service_accounts
networking = module.branch-network-folder.id
networking-dev = module.branch-network-dev-folder.id
networking-prod = module.branch-network-prod-folder.id
}
project_factory_sa = local._project_factory_sas
})
"02-security" = jsonencode({
folder_id = module.branch-security-folder.id
kms_restricted_admins = {
for k, v in local._project_factory_sas : k => [v]
}
})
} }
} }
# optionally generate providers and tfvars files for subsequent stages # optionally generate providers and tfvars files for subsequent stages
resource "local_file" "providers" { resource "local_file" "providers" {
for_each = var.outputs_location == null ? {} : local.providers for_each = var.outputs_location == null ? {} : local.providers
filename = "${pathexpand(var.outputs_location)}/${each.key}/providers.tf" file_permission = "0644"
content = each.value filename = "${pathexpand(var.outputs_location)}/providers/${each.key}-providers.tf"
content = each.value
} }
resource "local_file" "tfvars" { resource "local_file" "tfvars" {
for_each = var.outputs_location == null ? {} : local.tfvars for_each = var.outputs_location == null ? {} : { 1 = 1 }
filename = "${pathexpand(var.outputs_location)}/${each.key}/terraform-resman.auto.tfvars.json" file_permission = "0644"
content = each.value filename = "${pathexpand(var.outputs_location)}/tfvars/01-resman.auto.tfvars.json"
content = jsonencode(local.tfvars)
} }
# outputs # outputs
output "networking" { output "networking" {
# tfdoc:output:consumers 02-networking
description = "Data for the networking stage." description = "Data for the networking stage."
value = { value = {
folder = module.branch-network-folder.id folder = module.branch-network-folder.id
@ -91,7 +108,6 @@ output "networking" {
} }
output "project_factories" { output "project_factories" {
# tfdoc:output:consumers xx-teams
description = "Data for the project factories stage." description = "Data for the project factories stage."
value = { value = {
dev = { dev = {

View File

@ -17,6 +17,12 @@
# defaults for variables marked with global tfdoc annotations, can be set via # defaults for variables marked with global tfdoc annotations, can be set via
# the tfvars file generated in stage 00 and stored in its outputs # the tfvars file generated in stage 00 and stored in its outputs
variable "automation_project_id" {
# tfdoc:variable:source 00-bootstrap
description = "Project id for the automation project created by the bootstrap stage."
type = string
}
variable "billing_account" { variable "billing_account" {
# tfdoc:variable:source 00-bootstrap # tfdoc:variable:source 00-bootstrap
description = "Billing account id and organization id ('nnnnnnnn' or null)." description = "Billing account id and organization id ('nnnnnnnn' or null)."
@ -26,12 +32,6 @@ variable "billing_account" {
}) })
} }
variable "automation_project_id" {
# tfdoc:variable:source 00-bootstrap
description = "Project id for the automation project created by the bootstrap stage."
type = string
}
variable "custom_roles" { variable "custom_roles" {
# tfdoc:variable:source 00-bootstrap # tfdoc:variable:source 00-bootstrap
description = "Custom roles defined at the org level, in key => id format." description = "Custom roles defined at the org level, in key => id format."

View File

@ -363,30 +363,30 @@ Don't forget to add a peering zone in the landing project and point it to the ne
| name | description | type | required | default | producer | | name | description | type | required | default | producer |
|---|---|:---:|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|:---:|
| [billing_account_id](variables.tf#L17) | Billing account id. | <code>string</code> | ✓ | | <code>00-bootstrap</code> | | [billing_account](variables.tf#L17) | Billing account id and organization id ('nnnnnnnn' or null). | <code title="object&#40;&#123;&#10; id &#61; string&#10; organization_id &#61; number&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>00-bootstrap</code> |
| [folder_ids](variables.tf#L59) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | <code>map&#40;string&#41;</code> | ✓ | | <code>01-resman</code> | | [folder_ids](variables.tf#L71) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | <code title="object&#40;&#123;&#10; networking &#61; string&#10; networking-dev &#61; string&#10; networking-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>01-resman</code> |
| [organization](variables.tf#L91) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>00-bootstrap</code> | | [organization](variables.tf#L107) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>00-bootstrap</code> |
| [prefix](variables.tf#L107) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>00-bootstrap</code> | | [prefix](variables.tf#L123) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>00-bootstrap</code> |
| [custom_adv](variables.tf#L23) | Custom advertisement definitions in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; cloud_dns &#61; &#34;35.199.192.0&#47;19&#34;&#10; gcp_all &#61; &#34;10.128.0.0&#47;16&#34;&#10; gcp_dev_ew1 &#61; &#34;10.128.128.0&#47;19&#34;&#10; gcp_dev_ew4 &#61; &#34;10.128.160.0&#47;19&#34;&#10; gcp_landing_trusted_ew1 &#61; &#34;10.128.64.0&#47;19&#34;&#10; gcp_landing_trusted_ew4 &#61; &#34;10.128.96.0&#47;19&#34;&#10; gcp_landing_untrusted_ew1 &#61; &#34;10.128.0.0&#47;19&#34;&#10; gcp_landing_untrusted_ew4 &#61; &#34;10.128.32.0&#47;19&#34;&#10; gcp_prod_ew1 &#61; &#34;10.128.192.0&#47;19&#34;&#10; gcp_prod_ew4 &#61; &#34;10.128.224.0&#47;19&#34;&#10; googleapis_private &#61; &#34;199.36.153.8&#47;30&#34;&#10; googleapis_restricted &#61; &#34;199.36.153.4&#47;30&#34;&#10; rfc_1918_10 &#61; &#34;10.0.0.0&#47;8&#34;&#10; rfc_1918_172 &#61; &#34;172.16.0.0&#47;12&#34;&#10; rfc_1918_192 &#61; &#34;192.168.0.0&#47;16&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [custom_adv](variables.tf#L26) | Custom advertisement definitions in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; cloud_dns &#61; &#34;35.199.192.0&#47;19&#34;&#10; gcp_all &#61; &#34;10.128.0.0&#47;16&#34;&#10; gcp_dev_ew1 &#61; &#34;10.128.128.0&#47;19&#34;&#10; gcp_dev_ew4 &#61; &#34;10.128.160.0&#47;19&#34;&#10; gcp_landing_trusted_ew1 &#61; &#34;10.128.64.0&#47;19&#34;&#10; gcp_landing_trusted_ew4 &#61; &#34;10.128.96.0&#47;19&#34;&#10; gcp_landing_untrusted_ew1 &#61; &#34;10.128.0.0&#47;19&#34;&#10; gcp_landing_untrusted_ew4 &#61; &#34;10.128.32.0&#47;19&#34;&#10; gcp_prod_ew1 &#61; &#34;10.128.192.0&#47;19&#34;&#10; gcp_prod_ew4 &#61; &#34;10.128.224.0&#47;19&#34;&#10; googleapis_private &#61; &#34;199.36.153.8&#47;30&#34;&#10; googleapis_restricted &#61; &#34;199.36.153.4&#47;30&#34;&#10; rfc_1918_10 &#61; &#34;10.0.0.0&#47;8&#34;&#10; rfc_1918_172 &#61; &#34;172.16.0.0&#47;12&#34;&#10; rfc_1918_192 &#61; &#34;192.168.0.0&#47;16&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [data_dir](variables.tf#L45) | Relative path for the folder storing configuration data for network resources. | <code>string</code> | | <code>&#34;data&#34;</code> | | | [custom_roles](variables.tf#L48) | Custom roles defined at the org level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>00-bootstrap</code> |
| [dns](variables.tf#L51) | Onprem DNS resolvers | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; onprem &#61; &#91;&#34;10.0.200.3&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [data_dir](variables.tf#L57) | Relative path for the folder storing configuration data for network resources. | <code>string</code> | | <code>&#34;data&#34;</code> | |
| [l7ilb_subnets](variables.tf#L65) | Subnets used for L7 ILBs. | <code title="map&#40;list&#40;object&#40;&#123;&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10;&#125;&#41;&#41;&#41;">map&#40;list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;&#41;</code> | | <code title="&#123;&#10; dev &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.159.0&#47;24&#34;, region &#61; &#34;europe-west1&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.191.0&#47;24&#34;, region &#61; &#34;europe-west4&#34; &#125;&#10; &#93;&#10; prod &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.223.0&#47;24&#34;, region &#61; &#34;europe-west1&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.255.0&#47;24&#34;, region &#61; &#34;europe-west4&#34; &#125;&#10; &#93;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [dns](variables.tf#L63) | Onprem DNS resolvers | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; onprem &#61; &#91;&#34;10.0.200.3&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [onprem_cidr](variables.tf#L83) | Onprem addresses in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; main &#61; &#34;10.0.0.0&#47;24&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [l7ilb_subnets](variables.tf#L81) | Subnets used for L7 ILBs. | <code title="map&#40;list&#40;object&#40;&#123;&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10;&#125;&#41;&#41;&#41;">map&#40;list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;&#41;</code> | | <code title="&#123;&#10; dev &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.159.0&#47;24&#34;, region &#61; &#34;europe-west1&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.191.0&#47;24&#34;, region &#61; &#34;europe-west4&#34; &#125;&#10; &#93;&#10; prod &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.223.0&#47;24&#34;, region &#61; &#34;europe-west1&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.255.0&#47;24&#34;, region &#61; &#34;europe-west4&#34; &#125;&#10; &#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L101) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | | | [onprem_cidr](variables.tf#L99) | Onprem addresses in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; main &#61; &#34;10.0.0.0&#47;24&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [project_factory_sa](variables.tf#L118) | IAM emails for project factory service accounts | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>01-resman</code> | | [outputs_location](variables.tf#L117) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [psa_ranges](variables.tf#L125) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code>map&#40;map&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; dev &#61; &#123;&#10; cloudsql-mysql-ew1 &#61; &#34;10.128.157.0&#47;24&#34;&#10; cloudsql-mysql-ew4 &#61; &#34;10.128.189.0&#47;24&#34;&#10; cloudsql-sqlserver-ew1 &#61; &#34;10.128.158.0&#47;24&#34;&#10; cloudsql-sqlserver-ew4 &#61; &#34;10.128.190.0&#47;24&#34;&#10; &#125;&#10; prod &#61; &#123;&#10; cloudsql-mysql-ew1 &#61; &#34;10.128.221.0&#47;24&#34;&#10; cloudsql-mysql-ew4 &#61; &#34;10.128.253.0&#47;24&#34;&#10; cloudsql-sqlserver-ew1 &#61; &#34;10.128.222.0&#47;24&#34;&#10; cloudsql-sqlserver-ew4 &#61; &#34;10.128.254.0&#47;24&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [psa_ranges](variables.tf#L134) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code>map&#40;map&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; dev &#61; &#123;&#10; cloudsql-mysql-ew1 &#61; &#34;10.128.157.0&#47;24&#34;&#10; cloudsql-mysql-ew4 &#61; &#34;10.128.189.0&#47;24&#34;&#10; cloudsql-sqlserver-ew1 &#61; &#34;10.128.158.0&#47;24&#34;&#10; cloudsql-sqlserver-ew4 &#61; &#34;10.128.190.0&#47;24&#34;&#10; &#125;&#10; prod &#61; &#123;&#10; cloudsql-mysql-ew1 &#61; &#34;10.128.221.0&#47;24&#34;&#10; cloudsql-mysql-ew4 &#61; &#34;10.128.253.0&#47;24&#34;&#10; cloudsql-sqlserver-ew1 &#61; &#34;10.128.222.0&#47;24&#34;&#10; cloudsql-sqlserver-ew4 &#61; &#34;10.128.254.0&#47;24&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_configs](variables.tf#L144) | Configurations for CRs and onprem routers. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-trusted-ew1 &#61; &#123;&#10; asn &#61; &#34;64512&#34;&#10; adv &#61; null&#10; &#125;&#10; landing-trusted-ew4 &#61; &#123;&#10; asn &#61; &#34;64512&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [router_configs](variables.tf#L153) | Configurations for CRs and onprem routers. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-trusted-ew1 &#61; &#123;&#10; asn &#61; &#34;64512&#34;&#10; adv &#61; null&#10; &#125;&#10; landing-trusted-ew4 &#61; &#123;&#10; asn &#61; &#34;64512&#34;&#10; adv &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [vpn_onprem_configs](variables.tf#L167) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;object&#40;&#123;&#10; id &#61; number&#10; ip_address &#61; string&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-trusted-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#10; &#123; id &#61; 0, ip_address &#61; &#34;8.8.8.8&#34; &#125;,&#10; &#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10; landing-trusted-ew4 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#10; &#123; id &#61; 0, ip_address &#61; &#34;8.8.8.8&#34; &#125;,&#10; &#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [service_accounts](variables.tf#L176) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>01-resman</code> |
| [vpn_onprem_configs](variables.tf#L186) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;object&#40;&#123;&#10; id &#61; number&#10; ip_address &#61; string&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-trusted-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#10; &#123; id &#61; 0, ip_address &#61; &#34;8.8.8.8&#34; &#125;,&#10; &#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10; landing-trusted-ew4 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#10; &#123; id &#61; 0, ip_address &#61; &#34;8.8.8.8&#34; &#125;,&#10; &#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
## Outputs ## Outputs
| name | description | sensitive | consumers | | name | description | sensitive | consumers |
|---|---|:---:|---| |---|---|:---:|---|
| [project_ids](outputs.tf#L42) | Network project ids. | | | | [host_project_ids](outputs.tf#L52) | Network project ids. | | |
| [project_numbers](outputs.tf#L51) | Network project numbers. | | | | [host_project_numbers](outputs.tf#L57) | Network project numbers. | | |
| [shared_vpc_host_projects](outputs.tf#L60) | Shared VPC host projects. | | | | [shared_vpc_self_links](outputs.tf#L62) | Shared VPC host projects. | | |
| [shared_vpc_self_links](outputs.tf#L69) | Shared VPC host projects. | | | | [tfvars](outputs.tf#L81) | Terraform variables file for the following stages. | ✓ | |
| [tfvars](outputs.tf#L93) | Network-related variables used in other stages. | ✓ | | | [vpn_gateway_endpoints](outputs.tf#L67) | External IP Addresses for the GCP VPN gateways. | | |
| [vpn_gateway_endpoints](outputs.tf#L79) | External IP Addresses for the GCP VPN gateways. | | |
<!-- END TFDOC --> <!-- END TFDOC -->

View File

@ -18,7 +18,7 @@
module "landing-project" { module "landing-project" {
source = "../../../modules/project" source = "../../../modules/project"
billing_account = var.billing_account_id billing_account = var.billing_account.id
name = "prod-net-landing-0" name = "prod-net-landing-0"
parent = var.folder_ids.networking-prod parent = var.folder_ids.networking-prod
prefix = var.prefix prefix = var.prefix
@ -37,6 +37,13 @@ module "landing-project" {
enabled = true enabled = true
service_projects = [] service_projects = []
} }
metric_scopes = [module.landing-project.project_id]
iam = {
"roles/dns.admin" = [local.service_accounts.project-factory-prod]
(local.custom_roles.service_project_network_admin) = [
local.service_accounts.project-factory-prod
]
}
} }
# Untrusted VPC # Untrusted VPC

View File

@ -17,12 +17,16 @@
# tfdoc:file:description Networking folder and hierarchical policy. # tfdoc:file:description Networking folder and hierarchical policy.
locals { locals {
custom_roles = coalesce(var.custom_roles, {})
l7ilb_subnets = { for env, v in var.l7ilb_subnets : env => [ l7ilb_subnets = { for env, v in var.l7ilb_subnets : env => [
for s in v : merge(s, { for s in v : merge(s, {
active = true active = true
name = "${env}-l7ilb-${s.region}" name = "${env}-l7ilb-${s.region}"
})] })]
} }
service_accounts = {
for k, v in coalesce(var.service_accounts, {}) : k => "serviceAccount:${v}"
}
} }
module "folder" { module "folder" {

View File

@ -14,66 +14,54 @@
* limitations under the License. * limitations under the License.
*/ */
# Optionally, generate providers and tfvars files for subsequent stages
locals { locals {
host_project_ids = {
dev-spoke-0 = module.dev-spoke-project.project_id
prod-landing = module.landing-project.project_id
prod-spoke-0 = module.prod-spoke-project.project_id
}
host_project_numbers = {
dev-spoke-0 = module.dev-spoke-project.number
prod-landing = module.landing-project.number
prod-spoke-0 = module.prod-spoke-project.number
}
tfvars = { tfvars = {
"03-project-factory-dev" = jsonencode({ host_project_ids = local.host_project_ids
environment_dns_zone = module.dev-dns-private-zone.domain host_project_numbers = local.host_project_numbers
shared_vpc_self_link = module.dev-spoke-vpc.self_link vpc_self_links = local.vpc_self_links
vpc_host_project = module.dev-spoke-project.project_id }
}) vpc_self_links = {
"03-project-factory-prod" = jsonencode({ prod-landing-trusted = module.landing-trusted-vpc.self_link
environment_dns_zone = module.prod-dns-private-zone.domain prod-landing-untrusted = module.landing-untrusted-vpc.self_link
shared_vpc_self_link = module.prod-spoke-vpc.self_link dev-spoke-0 = module.dev-spoke-vpc.self_link
vpc_host_project = module.prod-spoke-project.project_id prod-spoke-0 = module.prod-spoke-vpc.self_link
})
} }
} }
# optionally generate tfvars file for subsequent stages
resource "local_file" "tfvars" { resource "local_file" "tfvars" {
for_each = var.outputs_location == null ? {} : local.tfvars for_each = var.outputs_location == null ? {} : { 1 = 1 }
filename = "${pathexpand(var.outputs_location)}/${each.key}/terraform-networking.auto.tfvars.json" file_permission = "0644"
content = each.value filename = "${pathexpand(var.outputs_location)}/tfvars/02-networking.auto.tfvars.json"
content = jsonencode(local.tfvars)
} }
# Outputs # outputs
output "project_ids" { output "host_project_ids" {
description = "Network project ids." description = "Network project ids."
value = { value = local.host_project_ids
dev = module.dev-spoke-project.project_id
landing = module.landing-project.project_id
prod = module.prod-spoke-project.project_id
}
} }
output "project_numbers" { output "host_project_numbers" {
description = "Network project numbers." description = "Network project numbers."
value = { value = local.host_project_numbers
dev = "projects/${module.dev-spoke-project.number}"
landing = "projects/${module.landing-project.number}"
prod = "projects/${module.prod-spoke-project.number}"
}
}
output "shared_vpc_host_projects" {
description = "Shared VPC host projects."
value = {
dev = module.dev-spoke-project.project_id
landing = module.landing-project.project_id
prod = module.prod-spoke-project.project_id
}
} }
output "shared_vpc_self_links" { output "shared_vpc_self_links" {
description = "Shared VPC host projects." description = "Shared VPC host projects."
value = { value = local.vpc_self_links
dev = module.dev-spoke-vpc.self_link
landing-trusted = module.landing-trusted-vpc.self_link
landing-untrusted = module.landing-untrusted-vpc.self_link
prod = module.prod-spoke-vpc.self_link
}
} }
output "vpn_gateway_endpoints" { output "vpn_gateway_endpoints" {
@ -91,7 +79,7 @@ output "vpn_gateway_endpoints" {
} }
output "tfvars" { output "tfvars" {
description = "Network-related variables used in other stages." description = "Terraform variables file for the following stages."
sensitive = true sensitive = true
value = local.tfvars value = local.tfvars
} }

View File

@ -18,7 +18,7 @@
module "dev-spoke-project" { module "dev-spoke-project" {
source = "../../../modules/project" source = "../../../modules/project"
billing_account = var.billing_account_id billing_account = var.billing_account.id
name = "dev-net-spoke-0" name = "dev-net-spoke-0"
parent = var.folder_ids.networking-dev parent = var.folder_ids.networking-dev
prefix = var.prefix prefix = var.prefix
@ -39,7 +39,10 @@ module "dev-spoke-project" {
} }
metric_scopes = [module.landing-project.project_id] metric_scopes = [module.landing-project.project_id]
iam = { iam = {
"roles/dns.admin" = [var.project_factory_sa.dev] "roles/dns.admin" = [local.service_accounts.project-factory-dev]
(local.custom_roles.service_project_network_admin) = [
local.service_accounts.project-factory-dev
]
} }
} }

View File

@ -18,7 +18,7 @@
module "prod-spoke-project" { module "prod-spoke-project" {
source = "../../../modules/project" source = "../../../modules/project"
billing_account = var.billing_account_id billing_account = var.billing_account.id
name = "prod-net-spoke-0" name = "prod-net-spoke-0"
parent = var.folder_ids.networking-prod parent = var.folder_ids.networking-prod
prefix = var.prefix prefix = var.prefix
@ -39,7 +39,10 @@ module "prod-spoke-project" {
} }
metric_scopes = [module.landing-project.project_id] metric_scopes = [module.landing-project.project_id]
iam = { iam = {
"roles/dns.admin" = [var.project_factory_sa.prod] "roles/dns.admin" = [local.service_accounts.project-factory-prod]
(local.custom_roles.service_project_network_admin) = [
local.service_accounts.project-factory-prod
]
} }
} }

View File

@ -14,10 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
variable "billing_account_id" { variable "billing_account" {
# tfdoc:variable:source 00-bootstrap # tfdoc:variable:source 00-bootstrap
description = "Billing account id." description = "Billing account id and organization id ('nnnnnnnn' or null)."
type = string type = object({
id = string
organization_id = number
})
} }
variable "custom_adv" { variable "custom_adv" {
@ -42,6 +45,15 @@ variable "custom_adv" {
} }
} }
variable "custom_roles" {
# tfdoc:variable:source 00-bootstrap
description = "Custom roles defined at the org level, in key => id format."
type = object({
service_project_network_admin = string
})
default = null
}
variable "data_dir" { variable "data_dir" {
description = "Relative path for the folder storing configuration data for network resources." description = "Relative path for the folder storing configuration data for network resources."
type = string type = string
@ -59,7 +71,11 @@ variable "dns" {
variable "folder_ids" { variable "folder_ids" {
# tfdoc:variable:source 01-resman # tfdoc:variable:source 01-resman
description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created." description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created."
type = map(string) type = object({
networking = string
networking-dev = string
networking-prod = string
})
} }
variable "l7ilb_subnets" { variable "l7ilb_subnets" {
@ -115,13 +131,6 @@ variable "prefix" {
} }
} }
variable "project_factory_sa" {
# tfdoc:variable:source 01-resman
description = "IAM emails for project factory service accounts"
type = map(string)
default = {}
}
variable "psa_ranges" { variable "psa_ranges" {
description = "IP ranges used for Private Service Access (e.g. CloudSQL)." description = "IP ranges used for Private Service Access (e.g. CloudSQL)."
type = map(map(string)) type = map(map(string))
@ -164,6 +173,16 @@ variable "router_configs" {
} }
} }
variable "service_accounts" {
# tfdoc:variable:source 01-resman
description = "Automation service accounts in name => email format."
type = object({
project-factory-dev = string
project-factory-prod = string
})
default = null
}
variable "vpn_onprem_configs" { variable "vpn_onprem_configs" {
description = "VPN gateway configuration for onprem interconnection." description = "VPN gateway configuration for onprem interconnection."
type = map(object({ type = map(object({

View File

@ -308,32 +308,31 @@ DNS configurations are centralised in the `dns.tf` file. Spokes delegate DNS res
| name | description | type | required | default | producer | | name | description | type | required | default | producer |
|---|---|:---:|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|:---:|
| [billing_account_id](variables.tf#L17) | Billing account id. | <code>string</code> | ✓ | | <code>00-bootstrap</code> | | [billing_account](variables.tf#L17) | Billing account id and organization id ('nnnnnnnn' or null). | <code title="object&#40;&#123;&#10; id &#61; string&#10; organization_id &#61; number&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>00-bootstrap</code> |
| [folder_ids](variables.tf#L61) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | <code>map&#40;string&#41;</code> | ✓ | | <code>01-resman</code> | | [folder_ids](variables.tf#L66) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | <code title="object&#40;&#123;&#10; networking &#61; string&#10; networking-dev &#61; string&#10; networking-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>01-resman</code> |
| [organization](variables.tf#L85) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>00-bootstrap</code> | | [organization](variables.tf#L94) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>00-bootstrap</code> |
| [prefix](variables.tf#L101) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>00-bootstrap</code> | | [prefix](variables.tf#L110) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>00-bootstrap</code> |
| [custom_adv](variables.tf#L23) | Custom advertisement definitions in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; cloud_dns &#61; &#34;35.199.192.0&#47;19&#34;&#10; gcp_all &#61; &#34;10.128.0.0&#47;16&#34;&#10; gcp_dev &#61; &#34;10.128.32.0&#47;19&#34;&#10; gcp_landing &#61; &#34;10.128.0.0&#47;19&#34;&#10; gcp_prod &#61; &#34;10.128.64.0&#47;19&#34;&#10; googleapis_private &#61; &#34;199.36.153.8&#47;30&#34;&#10; googleapis_restricted &#61; &#34;199.36.153.4&#47;30&#34;&#10; rfc_1918_10 &#61; &#34;10.0.0.0&#47;8&#34;&#10; rfc_1918_172 &#61; &#34;172.16.0.0&#47;12&#34;&#10; rfc_1918_192 &#61; &#34;192.168.0.0&#47;16&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [custom_adv](variables.tf#L26) | Custom advertisement definitions in name => range format. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; cloud_dns &#61; &#34;35.199.192.0&#47;19&#34;&#10; gcp_all &#61; &#34;10.128.0.0&#47;16&#34;&#10; gcp_dev &#61; &#34;10.128.32.0&#47;19&#34;&#10; gcp_landing &#61; &#34;10.128.0.0&#47;19&#34;&#10; gcp_prod &#61; &#34;10.128.64.0&#47;19&#34;&#10; googleapis_private &#61; &#34;199.36.153.8&#47;30&#34;&#10; googleapis_restricted &#61; &#34;199.36.153.4&#47;30&#34;&#10; rfc_1918_10 &#61; &#34;10.0.0.0&#47;8&#34;&#10; rfc_1918_172 &#61; &#34;172.16.0.0&#47;12&#34;&#10; rfc_1918_192 &#61; &#34;192.168.0.0&#47;16&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [custom_roles](variables.tf#L40) | Custom roles defined at the org level, in key => id format. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>00-bootstrap</code> | | [custom_roles](variables.tf#L43) | Custom roles defined at the org level, in key => id format. | <code title="object&#40;&#123;&#10; service_project_network_admin &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>00-bootstrap</code> |
| [data_dir](variables.tf#L47) | Relative path for the folder storing configuration data for network resources. | <code>string</code> | | <code>&#34;data&#34;</code> | | | [data_dir](variables.tf#L52) | Relative path for the folder storing configuration data for network resources. | <code>string</code> | | <code>&#34;data&#34;</code> | |
| [dns](variables.tf#L53) | Onprem DNS resolvers. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; onprem &#61; &#91;&#34;10.0.200.3&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [dns](variables.tf#L58) | Onprem DNS resolvers. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; onprem &#61; &#91;&#34;10.0.200.3&#34;&#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [l7ilb_subnets](variables.tf#L67) | Subnets used for L7 ILBs. | <code title="map&#40;list&#40;object&#40;&#123;&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10;&#125;&#41;&#41;&#41;">map&#40;list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;&#41;</code> | | <code title="&#123;&#10; prod &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.92.0&#47;24&#34;, region &#61; &#34;europe-west1&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.93.0&#47;24&#34;, region &#61; &#34;europe-west4&#34; &#125;&#10; &#93;&#10; dev &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.60.0&#47;24&#34;, region &#61; &#34;europe-west1&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.61.0&#47;24&#34;, region &#61; &#34;europe-west4&#34; &#125;&#10; &#93;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [l7ilb_subnets](variables.tf#L76) | Subnets used for L7 ILBs. | <code title="map&#40;list&#40;object&#40;&#123;&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10;&#125;&#41;&#41;&#41;">map&#40;list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;&#41;</code> | | <code title="&#123;&#10; prod &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.92.0&#47;24&#34;, region &#61; &#34;europe-west1&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.93.0&#47;24&#34;, region &#61; &#34;europe-west4&#34; &#125;&#10; &#93;&#10; dev &#61; &#91;&#10; &#123; ip_cidr_range &#61; &#34;10.128.60.0&#47;24&#34;, region &#61; &#34;europe-west1&#34; &#125;,&#10; &#123; ip_cidr_range &#61; &#34;10.128.61.0&#47;24&#34;, region &#61; &#34;europe-west4&#34; &#125;&#10; &#93;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [outputs_location](variables.tf#L95) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | | | [outputs_location](variables.tf#L104) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [project_factory_sa](variables.tf#L112) | IAM emails for project factory service accounts. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> | <code>01-resman</code> | | [psa_ranges](variables.tf#L121) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code>map&#40;map&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; prod &#61; &#123;&#10; cloudsql-mysql &#61; &#34;10.128.94.0&#47;24&#34;&#10; cloudsql-sqlserver &#61; &#34;10.128.95.0&#47;24&#34;&#10; &#125;&#10; dev &#61; &#123;&#10; cloudsql-mysql &#61; &#34;10.128.62.0&#47;24&#34;&#10; cloudsql-sqlserver &#61; &#34;10.128.63.0&#47;24&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [psa_ranges](variables.tf#L119) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code>map&#40;map&#40;string&#41;&#41;</code> | | <code title="&#123;&#10; prod &#61; &#123;&#10; cloudsql-mysql &#61; &#34;10.128.94.0&#47;24&#34;&#10; cloudsql-sqlserver &#61; &#34;10.128.95.0&#47;24&#34;&#10; &#125;&#10; dev &#61; &#123;&#10; cloudsql-mysql &#61; &#34;10.128.62.0&#47;24&#34;&#10; cloudsql-sqlserver &#61; &#34;10.128.63.0&#47;24&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [router_configs](variables.tf#L136) | Configurations for CRs and onprem routers. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; onprem-ew1 &#61; &#123;&#10; asn &#61; &#34;65534&#34;&#10; adv &#61; null&#10; &#125;&#10; landing-ew1 &#61; &#123; asn &#61; &#34;64512&#34;, adv &#61; null &#125;&#10; landing-ew4 &#61; &#123; asn &#61; &#34;64512&#34;, adv &#61; null &#125;&#10; spoke-dev-ew1 &#61; &#123; asn &#61; &#34;64513&#34;, adv &#61; null &#125;&#10; spoke-dev-ew4 &#61; &#123; asn &#61; &#34;64513&#34;, adv &#61; null &#125;&#10; spoke-prod-ew1 &#61; &#123; asn &#61; &#34;64514&#34;, adv &#61; null &#125;&#10; spoke-prod-ew4 &#61; &#123; asn &#61; &#34;64514&#34;, adv &#61; null &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [router_configs](variables.tf#L134) | Configurations for CRs and onprem routers. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; custom &#61; list&#40;string&#41;&#10; default &#61; bool&#10; &#125;&#41;&#10; asn &#61; number&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; onprem-ew1 &#61; &#123;&#10; asn &#61; &#34;65534&#34;&#10; adv &#61; null&#10; &#125;&#10; landing-ew1 &#61; &#123; asn &#61; &#34;64512&#34;, adv &#61; null &#125;&#10; landing-ew4 &#61; &#123; asn &#61; &#34;64512&#34;, adv &#61; null &#125;&#10; spoke-dev-ew1 &#61; &#123; asn &#61; &#34;64513&#34;, adv &#61; null &#125;&#10; spoke-dev-ew4 &#61; &#123; asn &#61; &#34;64513&#34;, adv &#61; null &#125;&#10; spoke-prod-ew1 &#61; &#123; asn &#61; &#34;64514&#34;, adv &#61; null &#125;&#10; spoke-prod-ew4 &#61; &#123; asn &#61; &#34;64514&#34;, adv &#61; null &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [service_accounts](variables.tf#L160) | Automation service accounts in name => email format. | <code title="object&#40;&#123;&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>01-resman</code> |
| [vpn_onprem_configs](variables.tf#L158) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;object&#40;&#123;&#10; id &#61; number&#10; ip_address &#61; string&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#10; &#123; id &#61; 0, ip_address &#61; &#34;8.8.8.8&#34; &#125;,&#10; &#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [vpn_onprem_configs](variables.tf#L170) | VPN gateway configuration for onprem interconnection. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; peer_external_gateway &#61; object&#40;&#123;&#10; redundancy_type &#61; string&#10; interfaces &#61; list&#40;object&#40;&#123;&#10; id &#61; number&#10; ip_address &#61; string&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#10; tunnels &#61; list&#40;object&#40;&#123;&#10; peer_asn &#61; number&#10; peer_external_gateway_interface &#61; number&#10; secret &#61; string&#10; session_range &#61; string&#10; vpn_gateway_interface &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#10; &#34;cloud_dns&#34;, &#34;googleapis_private&#34;, &#34;googleapis_restricted&#34;, &#34;gcp_all&#34;&#10; &#93;&#10; &#125;&#10; peer_external_gateway &#61; &#123;&#10; redundancy_type &#61; &#34;SINGLE_IP_INTERNALLY_REDUNDANT&#34;&#10; interfaces &#61; &#91;&#10; &#123; id &#61; 0, ip_address &#61; &#34;8.8.8.8&#34; &#125;,&#10; &#93;&#10; &#125;&#10; tunnels &#61; &#91;&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.0&#47;30&#34;&#10; vpn_gateway_interface &#61; 0&#10; &#125;,&#10; &#123;&#10; peer_asn &#61; 65534&#10; peer_external_gateway_interface &#61; 0&#10; secret &#61; &#34;foobar&#34;&#10; session_range &#61; &#34;169.254.1.4&#47;30&#34;&#10; vpn_gateway_interface &#61; 1&#10; &#125;&#10; &#93;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [vpn_spoke_configs](variables.tf#L214) | VPN gateway configuration for spokes. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; session_range &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;rfc_1918_10&#34;, &#34;rfc_1918_172&#34;, &#34;rfc_1918_192&#34;&#93;&#10; &#125;&#10; session_range &#61; null&#10; &#125;&#10; landing-ew4 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;rfc_1918_10&#34;, &#34;rfc_1918_172&#34;, &#34;rfc_1918_192&#34;&#93;&#10; &#125;&#10; session_range &#61; null&#10; &#125;&#10; dev-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;gcp_dev&#34;&#93;&#10; &#125;&#10; session_range &#61; &#34;169.254.0.0&#47;27&#34;&#10; &#125;&#10; prod-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;gcp_prod&#34;&#93;&#10; &#125;&#10; session_range &#61; &#34;169.254.0.64&#47;27&#34;&#10; &#125;&#10; prod-ew4 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;gcp_prod&#34;&#93;&#10; &#125;&#10; session_range &#61; &#34;169.254.0.96&#47;27&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [vpn_spoke_configs](variables.tf#L226) | VPN gateway configuration for spokes. | <code title="map&#40;object&#40;&#123;&#10; adv &#61; object&#40;&#123;&#10; default &#61; bool&#10; custom &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; session_range &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; landing-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;rfc_1918_10&#34;, &#34;rfc_1918_172&#34;, &#34;rfc_1918_192&#34;&#93;&#10; &#125;&#10; session_range &#61; null&#10; &#125;&#10; landing-ew4 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;rfc_1918_10&#34;, &#34;rfc_1918_172&#34;, &#34;rfc_1918_192&#34;&#93;&#10; &#125;&#10; session_range &#61; null&#10; &#125;&#10; dev-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;gcp_dev&#34;&#93;&#10; &#125;&#10; session_range &#61; &#34;169.254.0.0&#47;27&#34;&#10; &#125;&#10; prod-ew1 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;gcp_prod&#34;&#93;&#10; &#125;&#10; session_range &#61; &#34;169.254.0.64&#47;27&#34;&#10; &#125;&#10; prod-ew4 &#61; &#123;&#10; adv &#61; &#123;&#10; default &#61; false&#10; custom &#61; &#91;&#34;gcp_prod&#34;&#93;&#10; &#125;&#10; session_range &#61; &#34;169.254.0.96&#47;27&#34;&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> | |
## Outputs ## Outputs
| name | description | sensitive | consumers | | name | description | sensitive | consumers |
|---|---|:---:|---| |---|---|:---:|---|
| [cloud_dns_inbound_policy](outputs.tf#L41) | IP Addresses for Cloud DNS inbound policy. | | | | [cloud_dns_inbound_policy](outputs.tf#L51) | IP Addresses for Cloud DNS inbound policy. | | |
| [project_ids](outputs.tf#L46) | Network project ids. | | | | [host_project_ids](outputs.tf#L56) | Network project ids. | | |
| [project_numbers](outputs.tf#L55) | Network project numbers. | | | | [host_project_numbers](outputs.tf#L61) | Network project numbers. | | |
| [shared_vpc_host_projects](outputs.tf#L64) | Shared VPC host projects. | | | | [shared_vpc_self_links](outputs.tf#L66) | Shared VPC host projects. | | |
| [shared_vpc_self_links](outputs.tf#L74) | Shared VPC host projects. | | | | [tfvars](outputs.tf#L81) | Terraform variables file for the following stages. | ✓ | |
| [tfvars](outputs.tf#L91) | Network-related variables used in other stages. | ✓ | | | [vpn_gateway_endpoints](outputs.tf#L71) | External IP Addresses for the GCP VPN gateways. | | |
| [vpn_gateway_endpoints](outputs.tf#L84) | External IP Addresses for the GCP VPN gateways. | | |
<!-- END TFDOC --> <!-- END TFDOC -->

View File

@ -18,7 +18,7 @@
module "landing-project" { module "landing-project" {
source = "../../../modules/project" source = "../../../modules/project"
billing_account = var.billing_account_id billing_account = var.billing_account.id
name = "prod-net-landing-0" name = "prod-net-landing-0"
parent = var.folder_ids.networking-prod parent = var.folder_ids.networking-prod
prefix = var.prefix prefix = var.prefix
@ -37,6 +37,13 @@ module "landing-project" {
enabled = true enabled = true
service_projects = [] service_projects = []
} }
metric_scopes = [module.landing-project.project_id]
iam = {
"roles/dns.admin" = [local.service_accounts.project-factory-prod]
(local.custom_roles.service_project_network_admin) = [
local.service_accounts.project-factory-prod
]
}
} }
module "landing-vpc" { module "landing-vpc" {

View File

@ -30,6 +30,7 @@ locals {
route_priority = null route_priority = null
} }
} }
custom_roles = coalesce(var.custom_roles, {})
l7ilb_subnets = { l7ilb_subnets = {
for env, v in var.l7ilb_subnets : env => [ for env, v in var.l7ilb_subnets : env => [
for s in v : merge(s, { for s in v : merge(s, {
@ -47,6 +48,9 @@ locals {
"roles/container.hostServiceAgentUser", "roles/container.hostServiceAgentUser",
"roles/vpcaccess.user", "roles/vpcaccess.user",
] ]
service_accounts = {
for k, v in coalesce(var.service_accounts, {}) : k => "serviceAccount:${v}"
}
} }
module "folder" { module "folder" {

View File

@ -13,27 +13,37 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
# optionally generate providers and tfvars files for subsequent stages
locals { locals {
host_project_ids = {
dev-spoke-0 = module.dev-spoke-project.project_id
prod-landing = module.landing-project.project_id
prod-spoke-0 = module.prod-spoke-project.project_id
}
host_project_numbers = {
dev-spoke-0 = module.dev-spoke-project.number
prod-landing = module.landing-project.number
prod-spoke-0 = module.prod-spoke-project.number
}
tfvars = { tfvars = {
"03-project-factory-dev" = jsonencode({ host_project_ids = local.host_project_ids
environment_dns_zone = module.dev-dns-private-zone.domain host_project_numbers = local.host_project_numbers
shared_vpc_self_link = module.dev-spoke-vpc.self_link vpc_self_links = local.vpc_self_links
vpc_host_project = module.dev-spoke-project.project_id }
}) vpc_self_links = {
"03-project-factory-prod" = jsonencode({ prod-landing = module.landing-vpc.self_link
environment_dns_zone = module.prod-dns-private-zone.domain dev-spoke-0 = module.dev-spoke-vpc.self_link
shared_vpc_self_link = module.prod-spoke-vpc.self_link prod-spoke-0 = module.prod-spoke-vpc.self_link
vpc_host_project = module.prod-spoke-project.project_id
})
} }
} }
# optionally generate tfvars file for subsequent stages
resource "local_file" "tfvars" { resource "local_file" "tfvars" {
for_each = var.outputs_location == null ? {} : local.tfvars for_each = var.outputs_location == null ? {} : { 1 = 1 }
filename = "${pathexpand(var.outputs_location)}/${each.key}/terraform-networking.auto.tfvars.json" file_permission = "0644"
content = each.value filename = "${pathexpand(var.outputs_location)}/tfvars/02-networking.auto.tfvars.json"
content = jsonencode(loca.tfvars)
} }
# outputs # outputs
@ -43,53 +53,33 @@ output "cloud_dns_inbound_policy" {
value = [for s in module.landing-vpc.subnets : cidrhost(s.ip_cidr_range, 2)] value = [for s in module.landing-vpc.subnets : cidrhost(s.ip_cidr_range, 2)]
} }
output "project_ids" { output "host_project_ids" {
description = "Network project ids." description = "Network project ids."
value = { value = local.host_project_ids
dev = module.dev-spoke-project.project_id
landing = module.landing-project.project_id
prod = module.prod-spoke-project.project_id
}
} }
output "project_numbers" { output "host_project_numbers" {
description = "Network project numbers." description = "Network project numbers."
value = { value = local.host_project_numbers
dev = "projects/${module.dev-spoke-project.number}"
landing = "projects/${module.landing-project.number}"
prod = "projects/${module.prod-spoke-project.number}"
}
} }
output "shared_vpc_host_projects" {
description = "Shared VPC host projects."
value = {
landing = module.landing-project.project_id
dev = module.dev-spoke-project.project_id
prod = module.prod-spoke-project.project_id
}
}
output "shared_vpc_self_links" { output "shared_vpc_self_links" {
description = "Shared VPC host projects." description = "Shared VPC host projects."
value = { value = local.vpc_self_links
landing = module.landing-vpc.self_link
dev = module.dev-spoke-vpc.self_link
prod = module.prod-spoke-vpc.self_link
}
} }
output "vpn_gateway_endpoints" { output "vpn_gateway_endpoints" {
description = "External IP Addresses for the GCP VPN gateways." description = "External IP Addresses for the GCP VPN gateways."
value = { value = {
onprem-ew1 = { for v in module.landing-to-onprem-ew1-vpn.gateway.vpn_interfaces : v.id => v.ip_address } onprem-ew1 = {
for v in module.landing-to-onprem-ew1-vpn.gateway.vpn_interfaces :
v.id => v.ip_address
}
} }
} }
output "tfvars" { output "tfvars" {
description = "Network-related variables used in other stages." description = "Terraform variables file for the following stages."
sensitive = true sensitive = true
value = local.tfvars value = local.tfvars
} }

View File

@ -18,7 +18,7 @@
module "dev-spoke-project" { module "dev-spoke-project" {
source = "../../../modules/project" source = "../../../modules/project"
billing_account = var.billing_account_id billing_account = var.billing_account.id
name = "dev-net-spoke-0" name = "dev-net-spoke-0"
parent = var.folder_ids.networking-dev parent = var.folder_ids.networking-dev
prefix = var.prefix prefix = var.prefix
@ -39,9 +39,9 @@ module "dev-spoke-project" {
} }
metric_scopes = [module.landing-project.project_id] metric_scopes = [module.landing-project.project_id]
iam = { iam = {
"roles/dns.admin" = [var.project_factory_sa.dev] "roles/dns.admin" = [local.service_accounts.project-factory-dev]
(var.custom_roles.service_project_network_admin) = [ (local.custom_roles.service_project_network_admin) = [
var.project_factory_sa.prod local.service_accounts.project-factory-dev
] ]
} }
} }
@ -102,7 +102,7 @@ resource "google_project_iam_binding" "dev_spoke_project_iam_delegated" {
project = module.dev-spoke-project.project_id project = module.dev-spoke-project.project_id
role = "roles/resourcemanager.projectIamAdmin" role = "roles/resourcemanager.projectIamAdmin"
members = [ members = [
var.project_factory_sa.dev local.service_accounts.project-factory-dev
] ]
condition { condition {
title = "dev_stage3_sa_delegated_grants" title = "dev_stage3_sa_delegated_grants"

View File

@ -18,7 +18,7 @@
module "prod-spoke-project" { module "prod-spoke-project" {
source = "../../../modules/project" source = "../../../modules/project"
billing_account = var.billing_account_id billing_account = var.billing_account.id
name = "prod-net-spoke-0" name = "prod-net-spoke-0"
parent = var.folder_ids.networking-prod parent = var.folder_ids.networking-prod
prefix = var.prefix prefix = var.prefix
@ -39,9 +39,9 @@ module "prod-spoke-project" {
} }
metric_scopes = [module.landing-project.project_id] metric_scopes = [module.landing-project.project_id]
iam = { iam = {
"roles/dns.admin" = [var.project_factory_sa.prod] "roles/dns.admin" = [local.service_accounts.project-factory-prod]
(var.custom_roles.service_project_network_admin) = [ (local.custom_roles.service_project_network_admin) = [
var.project_factory_sa.prod local.service_accounts.project-factory-prod
] ]
} }
} }
@ -102,7 +102,7 @@ resource "google_project_iam_binding" "prod_spoke_project_iam_delegated" {
project = module.prod-spoke-project.project_id project = module.prod-spoke-project.project_id
role = "roles/resourcemanager.projectIamAdmin" role = "roles/resourcemanager.projectIamAdmin"
members = [ members = [
var.project_factory_sa.prod local.service_accounts.project-factory-prod
] ]
condition { condition {
title = "prod_stage3_sa_delegated_grants" title = "prod_stage3_sa_delegated_grants"

View File

@ -14,10 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
variable "billing_account_id" { variable "billing_account" {
# tfdoc:variable:source 00-bootstrap # tfdoc:variable:source 00-bootstrap
description = "Billing account id." description = "Billing account id and organization id ('nnnnnnnn' or null)."
type = string type = object({
id = string
organization_id = number
})
} }
variable "custom_adv" { variable "custom_adv" {
@ -40,8 +43,10 @@ variable "custom_adv" {
variable "custom_roles" { variable "custom_roles" {
# tfdoc:variable:source 00-bootstrap # tfdoc:variable:source 00-bootstrap
description = "Custom roles defined at the org level, in key => id format." description = "Custom roles defined at the org level, in key => id format."
type = map(string) type = object({
default = {} service_project_network_admin = string
})
default = null
} }
variable "data_dir" { variable "data_dir" {
@ -61,7 +66,11 @@ variable "dns" {
variable "folder_ids" { variable "folder_ids" {
# tfdoc:variable:source 01-resman # tfdoc:variable:source 01-resman
description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created." description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created."
type = map(string) type = object({
networking = string
networking-dev = string
networking-prod = string
})
} }
variable "l7ilb_subnets" { variable "l7ilb_subnets" {
@ -109,13 +118,6 @@ variable "prefix" {
} }
} }
variable "project_factory_sa" {
# tfdoc:variable:source 01-resman
description = "IAM emails for project factory service accounts."
type = map(string)
default = {}
}
variable "psa_ranges" { variable "psa_ranges" {
description = "IP ranges used for Private Service Access (e.g. CloudSQL)." description = "IP ranges used for Private Service Access (e.g. CloudSQL)."
type = map(map(string)) type = map(map(string))
@ -155,6 +157,16 @@ variable "router_configs" {
} }
} }
variable "service_accounts" {
# tfdoc:variable:source 01-resman
description = "Automation service accounts in name => email format."
type = object({
project-factory-dev = string
project-factory-prod = string
})
default = null
}
variable "vpn_onprem_configs" { variable "vpn_onprem_configs" {
description = "VPN gateway configuration for onprem interconnection." description = "VPN gateway configuration for onprem interconnection."
type = map(object({ type = map(object({

View File

@ -285,27 +285,29 @@ Some references that might be useful in setting up this stage:
| name | description | type | required | default | producer | | name | description | type | required | default | producer |
|---|---|:---:|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|:---:|
| [billing_account_id](variables.tf#L17) | Billing account id. | <code>string</code> | ✓ | | <code>bootstrap</code> | | [billing_account](variables.tf#L17) | Billing account id and organization id ('nnnnnnnn' or null). | <code title="object&#40;&#123;&#10; id &#61; string&#10; organization_id &#61; number&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>00-bootstrap</code> |
| [folder_id](variables.tf#L23) | Folder to be used for the networking resources in folders/nnnn format. | <code>string</code> | ✓ | | <code>resman</code> | | [folder_ids](variables.tf#L26) | Folder name => id mappings, the 'security' folder name must exist. | <code title="object&#40;&#123;&#10; security &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>01-resman</code> |
| [organization](variables.tf#L73) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>bootstrap</code> | | [organization](variables.tf#L81) | Organization details. | <code title="object&#40;&#123;&#10; domain &#61; string&#10; id &#61; number&#10; customer_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>00-bootstrap</code> |
| [prefix](variables.tf#L89) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>00-bootstrap</code> | | [prefix](variables.tf#L97) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>00-bootstrap</code> |
| [groups](variables.tf#L29) | Group names to grant organization-level permissions. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; gcp-billing-admins &#61; &#34;gcp-billing-admins&#34;,&#10; gcp-devops &#61; &#34;gcp-devops&#34;,&#10; gcp-network-admins &#61; &#34;gcp-network-admins&#34;&#10; gcp-organization-admins &#61; &#34;gcp-organization-admins&#34;&#10; gcp-security-admins &#61; &#34;gcp-security-admins&#34;&#10; gcp-support &#61; &#34;gcp-support&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | <code>bootstrap</code> | | [service_accounts](variables.tf#L72) | Automation service accounts that can assign the encrypt/decrypt roles on keys. | <code title="object&#40;&#123;&#10; project-factory-dev &#61; string&#10; project-factory-prod &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>01-resman</code> |
| [kms_defaults](variables.tf#L44) | Defaults used for KMS keys. | <code title="object&#40;&#123;&#10; locations &#61; list&#40;string&#41;&#10; rotation_period &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; locations &#61; &#91;&#34;europe&#34;, &#34;europe-west1&#34;, &#34;europe-west3&#34;, &#34;global&#34;&#93;&#10; rotation_period &#61; &#34;7776000s&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [groups](variables.tf#L34) | Group names to grant organization-level permissions. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; gcp-billing-admins &#61; &#34;gcp-billing-admins&#34;,&#10; gcp-devops &#61; &#34;gcp-devops&#34;,&#10; gcp-network-admins &#61; &#34;gcp-network-admins&#34;&#10; gcp-organization-admins &#61; &#34;gcp-organization-admins&#34;&#10; gcp-security-admins &#61; &#34;gcp-security-admins&#34;&#10; gcp-support &#61; &#34;gcp-support&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | <code>00-bootstrap</code> |
| [kms_keys](variables.tf#L56) | KMS keys to create, keyed by name. Null attributes will be interpolated with defaults. | <code title="map&#40;object&#40;&#123;&#10; iam &#61; map&#40;list&#40;string&#41;&#41;&#10; labels &#61; map&#40;string&#41;&#10; locations &#61; list&#40;string&#41;&#10; rotation_period &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | | [kms_defaults](variables.tf#L49) | Defaults used for KMS keys. | <code title="object&#40;&#123;&#10; locations &#61; list&#40;string&#41;&#10; rotation_period &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; locations &#61; &#91;&#34;europe&#34;, &#34;europe-west1&#34;, &#34;europe-west3&#34;, &#34;global&#34;&#93;&#10; rotation_period &#61; &#34;7776000s&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [kms_restricted_admins](variables.tf#L67) | Map of environment => [identities] who can assign the encrypt/decrypt roles on keys. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> | | | [kms_keys](variables.tf#L61) | KMS keys to create, keyed by name. Null attributes will be interpolated with defaults. | <code title="map&#40;object&#40;&#123;&#10; iam &#61; map&#40;list&#40;string&#41;&#41;&#10; labels &#61; map&#40;string&#41;&#10; locations &#61; list&#40;string&#41;&#10; rotation_period &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [outputs_location](variables.tf#L83) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | | | [outputs_location](variables.tf#L91) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [vpc_sc_access_levels](variables.tf#L100) | VPC SC access level definitions. | <code title="map&#40;object&#40;&#123;&#10; combining_function &#61; string&#10; conditions &#61; list&#40;object&#40;&#123;&#10; ip_subnetworks &#61; list&#40;string&#41;&#10; members &#61; list&#40;string&#41;&#10; negate &#61; bool&#10; regions &#61; list&#40;string&#41;&#10; required_access_levels &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | | [vpc_sc_access_levels](variables.tf#L108) | VPC SC access level definitions. | <code title="map&#40;object&#40;&#123;&#10; combining_function &#61; string&#10; conditions &#61; list&#40;object&#40;&#123;&#10; ip_subnetworks &#61; list&#40;string&#41;&#10; members &#61; list&#40;string&#41;&#10; negate &#61; bool&#10; regions &#61; list&#40;string&#41;&#10; required_access_levels &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [vpc_sc_egress_policies](variables.tf#L115) | VPC SC egress policy defnitions. | <code title="map&#40;object&#40;&#123;&#10; egress_from &#61; object&#40;&#123;&#10; identity_type &#61; string&#10; identities &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; egress_to &#61; object&#40;&#123;&#10; operations &#61; list&#40;object&#40;&#123;&#10; method_selectors &#61; list&#40;string&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;&#10; resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | | [vpc_sc_egress_policies](variables.tf#L123) | VPC SC egress policy defnitions. | <code title="map&#40;object&#40;&#123;&#10; egress_from &#61; object&#40;&#123;&#10; identity_type &#61; string&#10; identities &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; egress_to &#61; object&#40;&#123;&#10; operations &#61; list&#40;object&#40;&#123;&#10; method_selectors &#61; list&#40;string&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;&#10; resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [vpc_sc_ingress_policies](variables.tf#L133) | VPC SC ingress policy defnitions. | <code title="map&#40;object&#40;&#123;&#10; ingress_from &#61; object&#40;&#123;&#10; identity_type &#61; string&#10; identities &#61; list&#40;string&#41;&#10; source_access_levels &#61; list&#40;string&#41;&#10; source_resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; ingress_to &#61; object&#40;&#123;&#10; operations &#61; list&#40;object&#40;&#123;&#10; method_selectors &#61; list&#40;string&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;&#10; resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | | [vpc_sc_ingress_policies](variables.tf#L141) | VPC SC ingress policy defnitions. | <code title="map&#40;object&#40;&#123;&#10; ingress_from &#61; object&#40;&#123;&#10; identity_type &#61; string&#10; identities &#61; list&#40;string&#41;&#10; source_access_levels &#61; list&#40;string&#41;&#10; source_resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; ingress_to &#61; object&#40;&#123;&#10; operations &#61; list&#40;object&#40;&#123;&#10; method_selectors &#61; list&#40;string&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;&#10; resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [vpc_sc_perimeter_access_levels](variables.tf#L153) | VPC SC perimeter access_levels. | <code title="object&#40;&#123;&#10; dev &#61; list&#40;string&#41;&#10; landing &#61; list&#40;string&#41;&#10; prod &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | | | [vpc_sc_perimeter_access_levels](variables.tf#L161) | VPC SC perimeter access_levels. | <code title="object&#40;&#123;&#10; dev &#61; list&#40;string&#41;&#10; landing &#61; list&#40;string&#41;&#10; prod &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [vpc_sc_perimeter_egress_policies](variables.tf#L163) | VPC SC egress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | <code title="object&#40;&#123;&#10; dev &#61; list&#40;string&#41;&#10; landing &#61; list&#40;string&#41;&#10; prod &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | | | [vpc_sc_perimeter_egress_policies](variables.tf#L171) | VPC SC egress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | <code title="object&#40;&#123;&#10; dev &#61; list&#40;string&#41;&#10; landing &#61; list&#40;string&#41;&#10; prod &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [vpc_sc_perimeter_ingress_policies](variables.tf#L173) | VPC SC ingress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | <code title="object&#40;&#123;&#10; dev &#61; list&#40;string&#41;&#10; landing &#61; list&#40;string&#41;&#10; prod &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | | | [vpc_sc_perimeter_ingress_policies](variables.tf#L181) | VPC SC ingress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | <code title="object&#40;&#123;&#10; dev &#61; list&#40;string&#41;&#10; landing &#61; list&#40;string&#41;&#10; prod &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [vpc_sc_perimeter_projects](variables.tf#L183) | VPC SC perimeter resources. | <code title="object&#40;&#123;&#10; dev &#61; list&#40;string&#41;&#10; landing &#61; list&#40;string&#41;&#10; prod &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | | | [vpc_sc_perimeter_projects](variables.tf#L191) | VPC SC perimeter resources. | <code title="object&#40;&#123;&#10; dev &#61; list&#40;string&#41;&#10; landing &#61; list&#40;string&#41;&#10; prod &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
## Outputs ## Outputs
| name | description | sensitive | consumers | | name | description | sensitive | consumers |
|---|---|:---:|---| |---|---|:---:|---|
| [stage_perimeter_projects](outputs.tf#L37) | Security project numbers. They can be added to perimeter resources. | | | | [kms_keys](outputs.tf#L53) | KMS key ids. | | |
| [stage_perimeter_projects](outputs.tf#L58) | Security project numbers. They can be added to perimeter resources. | | |
| [tfvars](outputs.tf#L68) | Terraform variable files for the following stages. | ✓ | |
<!-- END TFDOC --> <!-- END TFDOC -->

View File

@ -14,14 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
locals {
dev_kms_restricted_admins = [
"serviceAccount:${var.service_accounts.project-factory-dev}"
]
}
module "dev-sec-project" { module "dev-sec-project" {
source = "../../../modules/project" source = "../../../modules/project"
name = "dev-sec-core-0" name = "dev-sec-core-0"
parent = var.folder_id parent = var.folder_ids.security
prefix = var.prefix prefix = var.prefix
billing_account = var.billing_account_id billing_account = var.billing_account.id
iam = { iam = {
"roles/cloudkms.viewer" = try(var.kms_restricted_admins.dev, []) "roles/cloudkms.viewer" = local.dev_kms_restricted_admins
} }
labels = { environment = "dev", team = "security" } labels = { environment = "dev", team = "security" }
services = local.project_services services = local.project_services
@ -46,7 +52,7 @@ module "dev-sec-kms" {
# TODO(ludo): grant delegated role at key instead of project level # TODO(ludo): grant delegated role at key instead of project level
resource "google_project_iam_member" "dev_key_admin_delegated" { resource "google_project_iam_member" "dev_key_admin_delegated" {
for_each = toset(try(var.kms_restricted_admins.dev, [])) for_each = toset(local.dev_kms_restricted_admins)
project = module.dev-sec-project.project_id project = module.dev-sec-project.project_id
role = "roles/cloudkms.admin" role = "roles/cloudkms.admin"
member = each.key member = each.key

View File

@ -14,14 +14,20 @@
* limitations under the License. * limitations under the License.
*/ */
locals {
prod_kms_restricted_admins = [
"serviceAccount:${var.service_accounts.project-factory-prod}"
]
}
module "prod-sec-project" { module "prod-sec-project" {
source = "../../../modules/project" source = "../../../modules/project"
name = "prod-sec-core-0" name = "prod-sec-core-0"
parent = var.folder_id parent = var.folder_ids.security
prefix = var.prefix prefix = var.prefix
billing_account = var.billing_account_id billing_account = var.billing_account.id
iam = { iam = {
"roles/cloudkms.viewer" = try(var.kms_restricted_admins.prod, []) "roles/cloudkms.viewer" = local.prod_kms_restricted_admins
} }
labels = { environment = "prod", team = "security" } labels = { environment = "prod", team = "security" }
services = local.project_services services = local.project_services
@ -45,7 +51,7 @@ module "prod-sec-kms" {
# TODO(ludo): add support for conditions to Fabric modules # TODO(ludo): add support for conditions to Fabric modules
resource "google_project_iam_member" "prod_key_admin_delegated" { resource "google_project_iam_member" "prod_key_admin_delegated" {
for_each = toset(try(var.kms_restricted_admins.prod, [])) for_each = toset(local.prod_kms_restricted_admins)
project = module.prod-sec-project.project_id project = module.prod-sec-project.project_id
role = "roles/cloudkms.admin" role = "roles/cloudkms.admin"
member = each.key member = each.key

View File

@ -14,26 +14,47 @@
* limitations under the License. * limitations under the License.
*/ */
# optionally generate files for subsequent stages locals {
_output_kms_keys = concat(
resource "local_file" "dev_sec_kms" { flatten([
for_each = var.outputs_location == null ? {} : { 1 = 1 } for location, mod in module.dev-sec-kms : [
filename = "${pathexpand(var.outputs_location)}/yamls/02-security-kms-dev-keys.yaml" for name, id in mod.key_ids : {
content = yamlencode({ key = "dev-${name}:${location}"
for k, m in module.dev-sec-kms : k => m.key_ids id = id
}) }
]
]),
flatten([
for location, mod in module.prod-sec-kms : [
for name, id in mod.key_ids : {
key = "prod-${name}:${location}"
id = id
}
]
])
)
output_kms_keys = { for k in local._output_kms_keys : k.key => k.id }
tfvars = {
kms_keys = local.output_kms_keys
}
} }
resource "local_file" "prod_sec_kms" { # optionally generate files for subsequent stages
for_each = var.outputs_location == null ? {} : { 1 = 1 }
filename = "${pathexpand(var.outputs_location)}/yamls/02-security-kms-prod-keys.yaml" resource "local_file" "tfvars" {
content = yamlencode({ for_each = var.outputs_location == null ? {} : { 1 = 1 }
for k, m in module.prod-sec-kms : k => m.key_ids file_permission = "0644"
}) filename = "${pathexpand(var.outputs_location)}/tfvars/02-security.auto.tfvars.json"
content = jsonencode(local.tfvars)
} }
# outputs # outputs
output "kms_keys" {
description = "KMS key ids."
value = local.output_kms_keys
}
output "stage_perimeter_projects" { output "stage_perimeter_projects" {
description = "Security project numbers. They can be added to perimeter resources." description = "Security project numbers. They can be added to perimeter resources."
value = { value = {
@ -41,3 +62,11 @@ output "stage_perimeter_projects" {
prod = ["projects/${module.prod-sec-project.number}"] prod = ["projects/${module.prod-sec-project.number}"]
} }
} }
# ready to use variable values for subsequent stages
output "tfvars" {
description = "Terraform variable files for the following stages."
sensitive = true
value = local.tfvars
}

View File

@ -14,20 +14,25 @@
* limitations under the License. * limitations under the License.
*/ */
variable "billing_account_id" { variable "billing_account" {
# tfdoc:variable:source bootstrap # tfdoc:variable:source 00-bootstrap
description = "Billing account id." description = "Billing account id and organization id ('nnnnnnnn' or null)."
type = string type = object({
id = string
organization_id = number
})
} }
variable "folder_id" { variable "folder_ids" {
# tfdoc:variable:source resman # tfdoc:variable:source 01-resman
description = "Folder to be used for the networking resources in folders/nnnn format." description = "Folder name => id mappings, the 'security' folder name must exist."
type = string type = object({
security = string
})
} }
variable "groups" { variable "groups" {
# tfdoc:variable:source bootstrap # tfdoc:variable:source 00-bootstrap
description = "Group names to grant organization-level permissions." description = "Group names to grant organization-level permissions."
type = map(string) type = map(string)
# https://cloud.google.com/docs/enterprise/setup-checklist # https://cloud.google.com/docs/enterprise/setup-checklist
@ -64,14 +69,17 @@ variable "kms_keys" {
default = {} default = {}
} }
variable "kms_restricted_admins" { variable "service_accounts" {
description = "Map of environment => [identities] who can assign the encrypt/decrypt roles on keys." # tfdoc:variable:source 01-resman
type = map(list(string)) description = "Automation service accounts that can assign the encrypt/decrypt roles on keys."
default = {} type = object({
project-factory-dev = string
project-factory-prod = string
})
} }
variable "organization" { variable "organization" {
# tfdoc:variable:source bootstrap # tfdoc:variable:source 00-bootstrap
description = "Organization details." description = "Organization details."
type = object({ type = object({
domain = string domain = string

View File

@ -107,13 +107,13 @@ terraform apply
| name | description | type | required | default | producer | | name | description | type | required | default | producer |
|---|---|:---:|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|:---:|
| [billing_account_id](variables.tf#L19) | Billing account id. | <code>string</code> | ✓ | | <code>00-bootstrap</code> | | [billing_account](variables.tf#L19) | Billing account id and organization id ('nnnnnnnn' or null). | <code title="object&#40;&#123;&#10; id &#61; string&#10; organization_id &#61; number&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | | <code>00-bootstrap</code> |
| [prefix](variables.tf#L44) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>00-bootstrap</code> | | [prefix](variables.tf#L47) | Prefix used for resources that need unique names. Use 9 characters or less. | <code>string</code> | ✓ | | <code>00-bootstrap</code> |
| [data_dir](variables.tf#L25) | Relative path for the folder storing configuration data. | <code>string</code> | | <code>&#34;data&#47;projects&#34;</code> | | | [data_dir](variables.tf#L28) | Relative path for the folder storing configuration data. | <code>string</code> | | <code>&#34;data&#47;projects&#34;</code> | |
| [defaults_file](variables.tf#L38) | Relative path for the file storing the project factory configuration. | <code>string</code> | | <code>&#34;data&#47;defaults.yaml&#34;</code> | | | [defaults_file](variables.tf#L41) | Relative path for the file storing the project factory configuration. | <code>string</code> | | <code>&#34;data&#47;defaults.yaml&#34;</code> | |
| [environment_dns_zone](variables.tf#L31) | DNS zone suffix for environment. | <code>string</code> | | <code>null</code> | <code>02-networking</code> | | [environment_dns_zone](variables.tf#L34) | DNS zone suffix for environment. | <code>string</code> | | <code>null</code> | <code>02-networking</code> |
| [shared_vpc_self_link](variables.tf#L55) | Self link for the shared VPC. | <code>string</code> | | <code>null</code> | <code>02-networking</code> | | [shared_vpc_self_links](variables.tf#L58) | Self link for the shared VPC. | <code title="object&#40;&#123;&#10; dev-spoke-0 &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>02-networking</code> |
| [vpc_host_project](variables.tf#L62) | Host project for the shared VPC. | <code>string</code> | | <code>null</code> | <code>02-networking</code> | | [vpc_host_project_ids](variables.tf#L67) | Host project for the shared VPC. | <code title="object&#40;&#123;&#10; dev-spoke-0 &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | <code>02-networking</code> |
## Outputs ## Outputs

View File

@ -20,10 +20,10 @@
locals { locals {
_defaults = yamldecode(file(var.defaults_file)) _defaults = yamldecode(file(var.defaults_file))
_defaults_net = { _defaults_net = {
billing_account_id = var.billing_account_id billing_account_id = var.billing_account.id
environment_dns_zone = var.environment_dns_zone environment_dns_zone = var.environment_dns_zone
shared_vpc_self_link = var.shared_vpc_self_link shared_vpc_self_link = try(var.shared_vpc_self_links["dev:spoke-0"], null)
vpc_host_project = var.vpc_host_project vpc_host_project = try(var.vpc_host_project_ids["dev:spoke-0"], null)
} }
defaults = merge(local._defaults, local._defaults_net) defaults = merge(local._defaults, local._defaults_net)
projects = { projects = {

View File

@ -16,10 +16,13 @@
#TODO: tfdoc annotations #TODO: tfdoc annotations
variable "billing_account_id" { variable "billing_account" {
# tfdoc:variable:source 00-bootstrap # tfdoc:variable:source 00-bootstrap
description = "Billing account id." description = "Billing account id and organization id ('nnnnnnnn' or null)."
type = string type = object({
id = string
organization_id = number
})
} }
variable "data_dir" { variable "data_dir" {
@ -52,16 +55,20 @@ variable "prefix" {
} }
} }
variable "shared_vpc_self_link" { variable "shared_vpc_self_links" {
# tfdoc:variable:source 02-networking # tfdoc:variable:source 02-networking
description = "Self link for the shared VPC." description = "Self link for the shared VPC."
type = string type = object({
default = null dev-spoke-0 = string
})
default = null
} }
variable "vpc_host_project" { variable "vpc_host_project_ids" {
# tfdoc:variable:source 02-networking # tfdoc:variable:source 02-networking
description = "Host project for the shared VPC." description = "Host project for the shared VPC."
type = string type = object({
default = null dev-spoke-0 = string
})
default = null
} }

View File

@ -2,23 +2,38 @@
Each of the folders contained here is a separate "stage", or Terraform root module. Each of the folders contained here is a separate "stage", or Terraform root module.
They are designed to be combined together, each stage leveraging the previous stage's resources and providing outputs to the following stages, but they can also be run in isolation if their specific functionality is all that is needed (e.g. only bring up a hub and spoke VPC in an existing environment). Each stage can be run in isolation (for example to only bring up a hub and spoke VPC in an existing environment), but when combined together they form a modular setup that allows top-down configuration of a whole GCP organization.
When combined together, each stage is designed to leverage the previous stage's resources and to provide outputs to the following stages via predefined contracts, that regulate what is exchanged.
This has two important consequences
- any stage can be swapped out and replaced by different code as long as it respects the contract by providing a predefined set of outputs and optionally accepting a predefined set of variables
- data flow between stages can be partially automated, reducing the effort and pain required to compile variables by hand
One important assumption is that the flow of data is always forward looking, so no stage should depend on outputs generated further down the chain. This greatly simplifies both the logic and the implementation, and allows stages to be effectively independent.
To achieve this, we rely on specific GCP functionality like [delegated role grants](https://medium.com/google-cloud/managing-gcp-service-usage-through-delegated-role-grants-a843610f2226) that allow controlled delegation of responsibilities, for example to allow managing IAM bindings at the organization level only for specific roles.
Refer to each stage's documentation for a detailed description of its purpose, the architectural choices made in its design, and how it can be configured and wired together to terraform a whole GCP organization. The following is a brief overview of each stage. Refer to each stage's documentation for a detailed description of its purpose, the architectural choices made in its design, and how it can be configured and wired together to terraform a whole GCP organization. The following is a brief overview of each stage.
## Organizational level (00-01) ## Organizational level (00-01)
- [Bootstrap](00-bootstrap/README.md) - [Bootstrap](00-bootstrap/README.md)
Enables critical organization-level functionality that depends on broad permissions. It has two primary purposes. The first is to bootstrap the resources needed for automation of this and the following stages (service accounts, GCS buckets). And secondly, it applies the minimum amount of configuration needed at the organization level, to avoid the need of broad permissions later on, and to implement a minimum of security features like sinks and exports from the start. Enables critical organization-level functionality that depends on broad permissions. It has two primary purposes. The first is to bootstrap the resources needed for automation of this and the following stages (service accounts, GCS buckets). And secondly, it applies the minimum amount of configuration needed at the organization level, to avoid the need of broad permissions later on, and to implement a minimum of security features like sinks and exports from the start.\
Exports: automation project id, organization-level custom roles
- [Resource Management](01-resman/README.md) - [Resource Management](01-resman/README.md)
Creates the base resource hierarchy (folders) and the automation resources required later to delegate deployment of each part of the hierarchy to separate stages. This stage also configures organization-level policies and any exceptions needed by different branches of the resource hierarchy. Creates the base resource hierarchy (folders) and the automation resources required later to delegate deployment of each part of the hierarchy to separate stages. This stage also configures organization-level policies and any exceptions needed by different branches of the resource hierarchy.\
Exports: folder ids, automation service account emails
## Shared resources (02) ## Shared resources (02)
- [Security](02-security/README.md) - [Security](02-security/README.md)
Manages centralized security configurations in a separate stage, and is typically owned by the security team. This stage implements VPC Security Controls via separate perimeters for environments and central services, and creates projects to host centralized KMS keys used by the whole organization. It's meant to be easily extended to include other security-related resources which are required, like Secret Manager. Manages centralized security configurations in a separate stage, and is typically owned by the security team. This stage implements VPC Security Controls via separate perimeters for environments and central services, and creates projects to host centralized KMS keys used by the whole organization. It's meant to be easily extended to include other security-related resources which are required, like Secret Manager.\
Exports: KMS key ids
- Networking ([VPN](02-networking-vpn/README.md)/[NVA](02-networking-nva/README.md)) - Networking ([VPN](02-networking-vpn/README.md)/[NVA](02-networking-nva/README.md))
Manages centralized network resources in a separate stage, and is typically owned by the networking team. This stage implements a hub-and-spoke design, and includes connectivity via VPN to on-premises, and YAML-based factories for firewall rules (hierarchical and VPC-level) and subnets. It's currently available in two versions: [spokes connected via VPN](02-networking-vpn/README.md), [and spokes connected via appliances](02-networking-nva/README.md). Manages centralized network resources in a separate stage, and is typically owned by the networking team. This stage implements a hub-and-spoke design, and includes connectivity via VPN to on-premises, and YAML-based factories for firewall rules (hierarchical and VPC-level) and subnets. It's currently available in two versions: [spokes connected via VPN](02-networking-vpn/README.md), [and spokes connected via appliances](02-networking-nva/README.md).\
Exports: host project ids and numbers, vpc self links
## Environment-level resources (03) ## Environment-level resources (03)