Merge pull request #549 from GoogleCloudPlatform/fast/var-contracts
Fast stage contracts via vars
This commit is contained in:
commit
50e18454a8
|
@ -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*
|
||||||
|
|
|
@ -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 -->
|
||||||
|
|
|
@ -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,41 +32,8 @@ locals {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
tfvars = {
|
tfvars = {
|
||||||
"01-resman" = jsonencode({
|
|
||||||
automation_project_id = module.automation-project.project_id
|
automation_project_id = module.automation-project.project_id
|
||||||
billing_account = var.billing_account
|
custom_roles = local.custom_roles
|
||||||
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
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,14 +41,16 @@ locals {
|
||||||
|
|
||||||
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"
|
||||||
|
filename = "${pathexpand(var.outputs_location)}/providers/${each.key}-providers.tf"
|
||||||
content = each.value
|
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 = {
|
||||||
|
|
|
@ -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({ id = string organization_id = number })">object({…})</code> | ✓ | | <code>00-bootstrap</code> |
|
| [billing_account](variables.tf#L26) | Billing account id and organization id ('nnnnnnnn' or null). | <code title="object({ id = string organization_id = number })">object({…})</code> | ✓ | | <code>00-bootstrap</code> |
|
||||||
| [organization](variables.tf#L57) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</code> | ✓ | | <code>00-bootstrap</code> |
|
| [organization](variables.tf#L57) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</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(string)</code> | | <code>{}</code> | <code>00-bootstrap</code> |
|
| [custom_roles](variables.tf#L35) | Custom roles defined at the org level, in key => id format. | <code>map(string)</code> | | <code>{}</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 -->
|
||||||
|
|
|
@ -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,21 +61,22 @@ 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]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,20 +84,21 @@ locals {
|
||||||
|
|
||||||
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"
|
||||||
|
filename = "${pathexpand(var.outputs_location)}/providers/${each.key}-providers.tf"
|
||||||
content = each.value
|
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 = {
|
||||||
|
|
|
@ -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."
|
||||||
|
|
|
@ -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({ id = string organization_id = number })">object({…})</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(string)</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({ networking = string networking-dev = string networking-prod = string })">object({…})</code> | ✓ | | <code>01-resman</code> |
|
||||||
| [organization](variables.tf#L91) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</code> | ✓ | | <code>00-bootstrap</code> |
|
| [organization](variables.tf#L107) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</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(string)</code> | | <code title="{ cloud_dns = "35.199.192.0/19" gcp_all = "10.128.0.0/16" gcp_dev_ew1 = "10.128.128.0/19" gcp_dev_ew4 = "10.128.160.0/19" gcp_landing_trusted_ew1 = "10.128.64.0/19" gcp_landing_trusted_ew4 = "10.128.96.0/19" gcp_landing_untrusted_ew1 = "10.128.0.0/19" gcp_landing_untrusted_ew4 = "10.128.32.0/19" gcp_prod_ew1 = "10.128.192.0/19" gcp_prod_ew4 = "10.128.224.0/19" googleapis_private = "199.36.153.8/30" googleapis_restricted = "199.36.153.4/30" rfc_1918_10 = "10.0.0.0/8" rfc_1918_172 = "172.16.0.0/12" rfc_1918_192 = "192.168.0.0/16" }">{…}</code> | |
|
| [custom_adv](variables.tf#L26) | Custom advertisement definitions in name => range format. | <code>map(string)</code> | | <code title="{ cloud_dns = "35.199.192.0/19" gcp_all = "10.128.0.0/16" gcp_dev_ew1 = "10.128.128.0/19" gcp_dev_ew4 = "10.128.160.0/19" gcp_landing_trusted_ew1 = "10.128.64.0/19" gcp_landing_trusted_ew4 = "10.128.96.0/19" gcp_landing_untrusted_ew1 = "10.128.0.0/19" gcp_landing_untrusted_ew4 = "10.128.32.0/19" gcp_prod_ew1 = "10.128.192.0/19" gcp_prod_ew4 = "10.128.224.0/19" googleapis_private = "199.36.153.8/30" googleapis_restricted = "199.36.153.4/30" rfc_1918_10 = "10.0.0.0/8" rfc_1918_172 = "172.16.0.0/12" rfc_1918_192 = "192.168.0.0/16" }">{…}</code> | |
|
||||||
| [data_dir](variables.tf#L45) | Relative path for the folder storing configuration data for network resources. | <code>string</code> | | <code>"data"</code> | |
|
| [custom_roles](variables.tf#L48) | Custom roles defined at the org level, in key => id format. | <code title="object({ service_project_network_admin = string })">object({…})</code> | | <code>null</code> | <code>00-bootstrap</code> |
|
||||||
| [dns](variables.tf#L51) | Onprem DNS resolvers | <code>map(list(string))</code> | | <code title="{ onprem = ["10.0.200.3"] }">{…}</code> | |
|
| [data_dir](variables.tf#L57) | Relative path for the folder storing configuration data for network resources. | <code>string</code> | | <code>"data"</code> | |
|
||||||
| [l7ilb_subnets](variables.tf#L65) | Subnets used for L7 ILBs. | <code title="map(list(object({ ip_cidr_range = string region = string })))">map(list(object({…})))</code> | | <code title="{ dev = [ { ip_cidr_range = "10.128.159.0/24", region = "europe-west1" }, { ip_cidr_range = "10.128.191.0/24", region = "europe-west4" } ] prod = [ { ip_cidr_range = "10.128.223.0/24", region = "europe-west1" }, { ip_cidr_range = "10.128.255.0/24", region = "europe-west4" } ] }">{…}</code> | |
|
| [dns](variables.tf#L63) | Onprem DNS resolvers | <code>map(list(string))</code> | | <code title="{ onprem = ["10.0.200.3"] }">{…}</code> | |
|
||||||
| [onprem_cidr](variables.tf#L83) | Onprem addresses in name => range format. | <code>map(string)</code> | | <code title="{ main = "10.0.0.0/24" }">{…}</code> | |
|
| [l7ilb_subnets](variables.tf#L81) | Subnets used for L7 ILBs. | <code title="map(list(object({ ip_cidr_range = string region = string })))">map(list(object({…})))</code> | | <code title="{ dev = [ { ip_cidr_range = "10.128.159.0/24", region = "europe-west1" }, { ip_cidr_range = "10.128.191.0/24", region = "europe-west4" } ] prod = [ { ip_cidr_range = "10.128.223.0/24", region = "europe-west1" }, { ip_cidr_range = "10.128.255.0/24", region = "europe-west4" } ] }">{…}</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(string)</code> | | <code title="{ main = "10.0.0.0/24" }">{…}</code> | |
|
||||||
| [project_factory_sa](variables.tf#L118) | IAM emails for project factory service accounts | <code>map(string)</code> | | <code>{}</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(map(string))</code> | | <code title="{ dev = { cloudsql-mysql-ew1 = "10.128.157.0/24" cloudsql-mysql-ew4 = "10.128.189.0/24" cloudsql-sqlserver-ew1 = "10.128.158.0/24" cloudsql-sqlserver-ew4 = "10.128.190.0/24" } prod = { cloudsql-mysql-ew1 = "10.128.221.0/24" cloudsql-mysql-ew4 = "10.128.253.0/24" cloudsql-sqlserver-ew1 = "10.128.222.0/24" cloudsql-sqlserver-ew4 = "10.128.254.0/24" } }">{…}</code> | |
|
| [psa_ranges](variables.tf#L134) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code>map(map(string))</code> | | <code title="{ dev = { cloudsql-mysql-ew1 = "10.128.157.0/24" cloudsql-mysql-ew4 = "10.128.189.0/24" cloudsql-sqlserver-ew1 = "10.128.158.0/24" cloudsql-sqlserver-ew4 = "10.128.190.0/24" } prod = { cloudsql-mysql-ew1 = "10.128.221.0/24" cloudsql-mysql-ew4 = "10.128.253.0/24" cloudsql-sqlserver-ew1 = "10.128.222.0/24" cloudsql-sqlserver-ew4 = "10.128.254.0/24" } }">{…}</code> | |
|
||||||
| [router_configs](variables.tf#L144) | Configurations for CRs and onprem routers. | <code title="map(object({ adv = object({ custom = list(string) default = bool }) asn = number }))">map(object({…}))</code> | | <code title="{ landing-trusted-ew1 = { asn = "64512" adv = null } landing-trusted-ew4 = { asn = "64512" adv = null } }">{…}</code> | |
|
| [router_configs](variables.tf#L153) | Configurations for CRs and onprem routers. | <code title="map(object({ adv = object({ custom = list(string) default = bool }) asn = number }))">map(object({…}))</code> | | <code title="{ landing-trusted-ew1 = { asn = "64512" adv = null } landing-trusted-ew4 = { asn = "64512" adv = null } }">{…}</code> | |
|
||||||
| [vpn_onprem_configs](variables.tf#L167) | VPN gateway configuration for onprem interconnection. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) peer_external_gateway = object({ redundancy_type = string interfaces = list(object({ id = number ip_address = string })) }) tunnels = list(object({ peer_asn = number peer_external_gateway_interface = number secret = string session_range = string vpn_gateway_interface = number })) }))">map(object({…}))</code> | | <code title="{ landing-trusted-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = [ { id = 0, ip_address = "8.8.8.8" }, ] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } landing-trusted-ew4 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = [ { id = 0, ip_address = "8.8.8.8" }, ] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } }">{…}</code> | |
|
| [service_accounts](variables.tf#L176) | Automation service accounts in name => email format. | <code title="object({ project-factory-dev = string project-factory-prod = string })">object({…})</code> | | <code>null</code> | <code>01-resman</code> |
|
||||||
|
| [vpn_onprem_configs](variables.tf#L186) | VPN gateway configuration for onprem interconnection. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) peer_external_gateway = object({ redundancy_type = string interfaces = list(object({ id = number ip_address = string })) }) tunnels = list(object({ peer_asn = number peer_external_gateway_interface = number secret = string session_range = string vpn_gateway_interface = number })) }))">map(object({…}))</code> | | <code title="{ landing-trusted-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = [ { id = 0, ip_address = "8.8.8.8" }, ] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } landing-trusted-ew4 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = [ { id = 0, ip_address = "8.8.8.8" }, ] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } }">{…}</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 -->
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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" {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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({ id = string organization_id = number })">object({…})</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(string)</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({ networking = string networking-dev = string networking-prod = string })">object({…})</code> | ✓ | | <code>01-resman</code> |
|
||||||
| [organization](variables.tf#L85) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</code> | ✓ | | <code>00-bootstrap</code> |
|
| [organization](variables.tf#L94) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</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(string)</code> | | <code title="{ cloud_dns = "35.199.192.0/19" gcp_all = "10.128.0.0/16" gcp_dev = "10.128.32.0/19" gcp_landing = "10.128.0.0/19" gcp_prod = "10.128.64.0/19" googleapis_private = "199.36.153.8/30" googleapis_restricted = "199.36.153.4/30" rfc_1918_10 = "10.0.0.0/8" rfc_1918_172 = "172.16.0.0/12" rfc_1918_192 = "192.168.0.0/16" }">{…}</code> | |
|
| [custom_adv](variables.tf#L26) | Custom advertisement definitions in name => range format. | <code>map(string)</code> | | <code title="{ cloud_dns = "35.199.192.0/19" gcp_all = "10.128.0.0/16" gcp_dev = "10.128.32.0/19" gcp_landing = "10.128.0.0/19" gcp_prod = "10.128.64.0/19" googleapis_private = "199.36.153.8/30" googleapis_restricted = "199.36.153.4/30" rfc_1918_10 = "10.0.0.0/8" rfc_1918_172 = "172.16.0.0/12" rfc_1918_192 = "192.168.0.0/16" }">{…}</code> | |
|
||||||
| [custom_roles](variables.tf#L40) | Custom roles defined at the org level, in key => id format. | <code>map(string)</code> | | <code>{}</code> | <code>00-bootstrap</code> |
|
| [custom_roles](variables.tf#L43) | Custom roles defined at the org level, in key => id format. | <code title="object({ service_project_network_admin = string })">object({…})</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>"data"</code> | |
|
| [data_dir](variables.tf#L52) | Relative path for the folder storing configuration data for network resources. | <code>string</code> | | <code>"data"</code> | |
|
||||||
| [dns](variables.tf#L53) | Onprem DNS resolvers. | <code>map(list(string))</code> | | <code title="{ onprem = ["10.0.200.3"] }">{…}</code> | |
|
| [dns](variables.tf#L58) | Onprem DNS resolvers. | <code>map(list(string))</code> | | <code title="{ onprem = ["10.0.200.3"] }">{…}</code> | |
|
||||||
| [l7ilb_subnets](variables.tf#L67) | Subnets used for L7 ILBs. | <code title="map(list(object({ ip_cidr_range = string region = string })))">map(list(object({…})))</code> | | <code title="{ prod = [ { ip_cidr_range = "10.128.92.0/24", region = "europe-west1" }, { ip_cidr_range = "10.128.93.0/24", region = "europe-west4" } ] dev = [ { ip_cidr_range = "10.128.60.0/24", region = "europe-west1" }, { ip_cidr_range = "10.128.61.0/24", region = "europe-west4" } ] }">{…}</code> | |
|
| [l7ilb_subnets](variables.tf#L76) | Subnets used for L7 ILBs. | <code title="map(list(object({ ip_cidr_range = string region = string })))">map(list(object({…})))</code> | | <code title="{ prod = [ { ip_cidr_range = "10.128.92.0/24", region = "europe-west1" }, { ip_cidr_range = "10.128.93.0/24", region = "europe-west4" } ] dev = [ { ip_cidr_range = "10.128.60.0/24", region = "europe-west1" }, { ip_cidr_range = "10.128.61.0/24", region = "europe-west4" } ] }">{…}</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(string)</code> | | <code>{}</code> | <code>01-resman</code> |
|
| [psa_ranges](variables.tf#L121) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code>map(map(string))</code> | | <code title="{ prod = { cloudsql-mysql = "10.128.94.0/24" cloudsql-sqlserver = "10.128.95.0/24" } dev = { cloudsql-mysql = "10.128.62.0/24" cloudsql-sqlserver = "10.128.63.0/24" } }">{…}</code> | |
|
||||||
| [psa_ranges](variables.tf#L119) | IP ranges used for Private Service Access (e.g. CloudSQL). | <code>map(map(string))</code> | | <code title="{ prod = { cloudsql-mysql = "10.128.94.0/24" cloudsql-sqlserver = "10.128.95.0/24" } dev = { cloudsql-mysql = "10.128.62.0/24" cloudsql-sqlserver = "10.128.63.0/24" } }">{…}</code> | |
|
| [router_configs](variables.tf#L136) | Configurations for CRs and onprem routers. | <code title="map(object({ adv = object({ custom = list(string) default = bool }) asn = number }))">map(object({…}))</code> | | <code title="{ onprem-ew1 = { asn = "65534" adv = null } landing-ew1 = { asn = "64512", adv = null } landing-ew4 = { asn = "64512", adv = null } spoke-dev-ew1 = { asn = "64513", adv = null } spoke-dev-ew4 = { asn = "64513", adv = null } spoke-prod-ew1 = { asn = "64514", adv = null } spoke-prod-ew4 = { asn = "64514", adv = null } }">{…}</code> | |
|
||||||
| [router_configs](variables.tf#L134) | Configurations for CRs and onprem routers. | <code title="map(object({ adv = object({ custom = list(string) default = bool }) asn = number }))">map(object({…}))</code> | | <code title="{ onprem-ew1 = { asn = "65534" adv = null } landing-ew1 = { asn = "64512", adv = null } landing-ew4 = { asn = "64512", adv = null } spoke-dev-ew1 = { asn = "64513", adv = null } spoke-dev-ew4 = { asn = "64513", adv = null } spoke-prod-ew1 = { asn = "64514", adv = null } spoke-prod-ew4 = { asn = "64514", adv = null } }">{…}</code> | |
|
| [service_accounts](variables.tf#L160) | Automation service accounts in name => email format. | <code title="object({ project-factory-dev = string project-factory-prod = string })">object({…})</code> | | <code>null</code> | <code>01-resman</code> |
|
||||||
| [vpn_onprem_configs](variables.tf#L158) | VPN gateway configuration for onprem interconnection. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) peer_external_gateway = object({ redundancy_type = string interfaces = list(object({ id = number ip_address = string })) }) tunnels = list(object({ peer_asn = number peer_external_gateway_interface = number secret = string session_range = string vpn_gateway_interface = number })) }))">map(object({…}))</code> | | <code title="{ landing-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = [ { id = 0, ip_address = "8.8.8.8" }, ] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } }">{…}</code> | |
|
| [vpn_onprem_configs](variables.tf#L170) | VPN gateway configuration for onprem interconnection. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) peer_external_gateway = object({ redundancy_type = string interfaces = list(object({ id = number ip_address = string })) }) tunnels = list(object({ peer_asn = number peer_external_gateway_interface = number secret = string session_range = string vpn_gateway_interface = number })) }))">map(object({…}))</code> | | <code title="{ landing-ew1 = { adv = { default = false custom = [ "cloud_dns", "googleapis_private", "googleapis_restricted", "gcp_all" ] } peer_external_gateway = { redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" interfaces = [ { id = 0, ip_address = "8.8.8.8" }, ] } tunnels = [ { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.0/30" vpn_gateway_interface = 0 }, { peer_asn = 65534 peer_external_gateway_interface = 0 secret = "foobar" session_range = "169.254.1.4/30" vpn_gateway_interface = 1 } ] } }">{…}</code> | |
|
||||||
| [vpn_spoke_configs](variables.tf#L214) | VPN gateway configuration for spokes. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) session_range = string }))">map(object({…}))</code> | | <code title="{ landing-ew1 = { adv = { default = false custom = ["rfc_1918_10", "rfc_1918_172", "rfc_1918_192"] } session_range = null } landing-ew4 = { adv = { default = false custom = ["rfc_1918_10", "rfc_1918_172", "rfc_1918_192"] } session_range = null } dev-ew1 = { adv = { default = false custom = ["gcp_dev"] } session_range = "169.254.0.0/27" } prod-ew1 = { adv = { default = false custom = ["gcp_prod"] } session_range = "169.254.0.64/27" } prod-ew4 = { adv = { default = false custom = ["gcp_prod"] } session_range = "169.254.0.96/27" } }">{…}</code> | |
|
| [vpn_spoke_configs](variables.tf#L226) | VPN gateway configuration for spokes. | <code title="map(object({ adv = object({ default = bool custom = list(string) }) session_range = string }))">map(object({…}))</code> | | <code title="{ landing-ew1 = { adv = { default = false custom = ["rfc_1918_10", "rfc_1918_172", "rfc_1918_192"] } session_range = null } landing-ew4 = { adv = { default = false custom = ["rfc_1918_10", "rfc_1918_172", "rfc_1918_192"] } session_range = null } dev-ew1 = { adv = { default = false custom = ["gcp_dev"] } session_range = "169.254.0.0/27" } prod-ew1 = { adv = { default = false custom = ["gcp_prod"] } session_range = "169.254.0.64/27" } prod-ew4 = { adv = { default = false custom = ["gcp_prod"] } session_range = "169.254.0.96/27" } }">{…}</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 -->
|
||||||
|
|
|
@ -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" {
|
||||||
|
|
|
@ -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" {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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({ id = string organization_id = number })">object({…})</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({ security = string })">object({…})</code> | ✓ | | <code>01-resman</code> |
|
||||||
| [organization](variables.tf#L73) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</code> | ✓ | | <code>bootstrap</code> |
|
| [organization](variables.tf#L81) | Organization details. | <code title="object({ domain = string id = number customer_id = string })">object({…})</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(string)</code> | | <code title="{ gcp-billing-admins = "gcp-billing-admins", gcp-devops = "gcp-devops", gcp-network-admins = "gcp-network-admins" gcp-organization-admins = "gcp-organization-admins" gcp-security-admins = "gcp-security-admins" gcp-support = "gcp-support" }">{…}</code> | <code>bootstrap</code> |
|
| [service_accounts](variables.tf#L72) | Automation service accounts that can assign the encrypt/decrypt roles on keys. | <code title="object({ project-factory-dev = string project-factory-prod = string })">object({…})</code> | ✓ | | <code>01-resman</code> |
|
||||||
| [kms_defaults](variables.tf#L44) | Defaults used for KMS keys. | <code title="object({ locations = list(string) rotation_period = string })">object({…})</code> | | <code title="{ locations = ["europe", "europe-west1", "europe-west3", "global"] rotation_period = "7776000s" }">{…}</code> | |
|
| [groups](variables.tf#L34) | Group names to grant organization-level permissions. | <code>map(string)</code> | | <code title="{ gcp-billing-admins = "gcp-billing-admins", gcp-devops = "gcp-devops", gcp-network-admins = "gcp-network-admins" gcp-organization-admins = "gcp-organization-admins" gcp-security-admins = "gcp-security-admins" gcp-support = "gcp-support" }">{…}</code> | <code>00-bootstrap</code> |
|
||||||
| [kms_keys](variables.tf#L56) | KMS keys to create, keyed by name. Null attributes will be interpolated with defaults. | <code title="map(object({ iam = map(list(string)) labels = map(string) locations = list(string) rotation_period = string }))">map(object({…}))</code> | | <code>{}</code> | |
|
| [kms_defaults](variables.tf#L49) | Defaults used for KMS keys. | <code title="object({ locations = list(string) rotation_period = string })">object({…})</code> | | <code title="{ locations = ["europe", "europe-west1", "europe-west3", "global"] rotation_period = "7776000s" }">{…}</code> | |
|
||||||
| [kms_restricted_admins](variables.tf#L67) | Map of environment => [identities] who can assign the encrypt/decrypt roles on keys. | <code>map(list(string))</code> | | <code>{}</code> | |
|
| [kms_keys](variables.tf#L61) | KMS keys to create, keyed by name. Null attributes will be interpolated with defaults. | <code title="map(object({ iam = map(list(string)) labels = map(string) locations = list(string) rotation_period = string }))">map(object({…}))</code> | | <code>{}</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(object({ combining_function = string conditions = list(object({ ip_subnetworks = list(string) members = list(string) negate = bool regions = list(string) required_access_levels = list(string) })) }))">map(object({…}))</code> | | <code>{}</code> | |
|
| [vpc_sc_access_levels](variables.tf#L108) | VPC SC access level definitions. | <code title="map(object({ combining_function = string conditions = list(object({ ip_subnetworks = list(string) members = list(string) negate = bool regions = list(string) required_access_levels = list(string) })) }))">map(object({…}))</code> | | <code>{}</code> | |
|
||||||
| [vpc_sc_egress_policies](variables.tf#L115) | VPC SC egress policy defnitions. | <code title="map(object({ egress_from = object({ identity_type = string identities = list(string) }) egress_to = object({ operations = list(object({ method_selectors = list(string) service_name = string })) resources = list(string) }) }))">map(object({…}))</code> | | <code>{}</code> | |
|
| [vpc_sc_egress_policies](variables.tf#L123) | VPC SC egress policy defnitions. | <code title="map(object({ egress_from = object({ identity_type = string identities = list(string) }) egress_to = object({ operations = list(object({ method_selectors = list(string) service_name = string })) resources = list(string) }) }))">map(object({…}))</code> | | <code>{}</code> | |
|
||||||
| [vpc_sc_ingress_policies](variables.tf#L133) | VPC SC ingress policy defnitions. | <code title="map(object({ ingress_from = object({ identity_type = string identities = list(string) source_access_levels = list(string) source_resources = list(string) }) ingress_to = object({ operations = list(object({ method_selectors = list(string) service_name = string })) resources = list(string) }) }))">map(object({…}))</code> | | <code>{}</code> | |
|
| [vpc_sc_ingress_policies](variables.tf#L141) | VPC SC ingress policy defnitions. | <code title="map(object({ ingress_from = object({ identity_type = string identities = list(string) source_access_levels = list(string) source_resources = list(string) }) ingress_to = object({ operations = list(object({ method_selectors = list(string) service_name = string })) resources = list(string) }) }))">map(object({…}))</code> | | <code>{}</code> | |
|
||||||
| [vpc_sc_perimeter_access_levels](variables.tf#L153) | VPC SC perimeter access_levels. | <code title="object({ dev = list(string) landing = list(string) prod = list(string) })">object({…})</code> | | <code>null</code> | |
|
| [vpc_sc_perimeter_access_levels](variables.tf#L161) | VPC SC perimeter access_levels. | <code title="object({ dev = list(string) landing = list(string) prod = list(string) })">object({…})</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({ dev = list(string) landing = list(string) prod = list(string) })">object({…})</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({ dev = list(string) landing = list(string) prod = list(string) })">object({…})</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({ dev = list(string) landing = list(string) prod = list(string) })">object({…})</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({ dev = list(string) landing = list(string) prod = list(string) })">object({…})</code> | | <code>null</code> | |
|
||||||
| [vpc_sc_perimeter_projects](variables.tf#L183) | VPC SC perimeter resources. | <code title="object({ dev = list(string) landing = list(string) prod = list(string) })">object({…})</code> | | <code>null</code> | |
|
| [vpc_sc_perimeter_projects](variables.tf#L191) | VPC SC perimeter resources. | <code title="object({ dev = list(string) landing = list(string) prod = list(string) })">object({…})</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 -->
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
resource "local_file" "tfvars" {
|
||||||
for_each = var.outputs_location == null ? {} : { 1 = 1 }
|
for_each = var.outputs_location == null ? {} : { 1 = 1 }
|
||||||
filename = "${pathexpand(var.outputs_location)}/yamls/02-security-kms-prod-keys.yaml"
|
file_permission = "0644"
|
||||||
content = yamlencode({
|
filename = "${pathexpand(var.outputs_location)}/tfvars/02-security.auto.tfvars.json"
|
||||||
for k, m in module.prod-sec-kms : k => m.key_ids
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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({ id = string organization_id = number })">object({…})</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>"data/projects"</code> | |
|
| [data_dir](variables.tf#L28) | Relative path for the folder storing configuration data. | <code>string</code> | | <code>"data/projects"</code> | |
|
||||||
| [defaults_file](variables.tf#L38) | Relative path for the file storing the project factory configuration. | <code>string</code> | | <code>"data/defaults.yaml"</code> | |
|
| [defaults_file](variables.tf#L41) | Relative path for the file storing the project factory configuration. | <code>string</code> | | <code>"data/defaults.yaml"</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({ dev-spoke-0 = string })">object({…})</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({ dev-spoke-0 = string })">object({…})</code> | | <code>null</code> | <code>02-networking</code> |
|
||||||
|
|
||||||
## Outputs
|
## Outputs
|
||||||
|
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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({
|
||||||
|
dev-spoke-0 = string
|
||||||
|
})
|
||||||
default = null
|
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({
|
||||||
|
dev-spoke-0 = string
|
||||||
|
})
|
||||||
default = null
|
default = null
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue