diff --git a/.cz.toml b/.cz.toml index 088e162..7866eab 100644 --- a/.cz.toml +++ b/.cz.toml @@ -1,7 +1,6 @@ [tool.commitizen] name = "cz_conventional_commits" version_provider = "scm" -version_files = ["galaxy.yml:^version"] update_changelog_on_bump = true major_version_zero = true tag_format = "v$version" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0cd8e68 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log + +# Exclude all .tfvars files, which are likely to contain sentitive data, such as +# password, private keys, and other secrets. These should not be part of version +# control as they are data points which are potentially sensitive and subject +# to change depending on the environment. +# +#*.tfvars + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# +# !example_override.tf + +# Ignore CLI configuration files +.terraformrc +terraform.rc + +# Credentials files +credentials.auto.tfvars + +# Local plan files +plan.out +**temp.txt diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl new file mode 100644 index 0000000..59aa238 --- /dev/null +++ b/.terraform.lock.hcl @@ -0,0 +1,41 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/hashicorp/vault" { + version = "4.4.0" + hashes = [ + "h1:s0t6P9ZfUQnHLxtUcnpPWpME68KwO/OxZqHAKSIvOoo=", + "zh:0309ea8f81386e17ab13c06c5991ca959708c55c815b0cfba2bbcd865e0d606e", + "zh:40e56199ccd266bffa216e8ebbcdc2e29b6ef5145b39377be766e763cac759c8", + "zh:6fad1f073bd2e53e34736e000f98db581137e153ac80bbb5c4f1a1e38b46a1d2", + "zh:74564fd4759decccf7f3c952aa2feba1012f103a66ec354aa3b3292a2f1b2412", + "zh:7aae012c1a43e6e5dae6f608ec0f08cdb3f95fa121a32e413fe7ee37cb99947f", + "zh:7c83f508e164844b1dd9bafe9de0fe60c7be7b55a02e704a6e2f50cff38b7d96", + "zh:873a42322b68d9fba4a38217b97ee04a1eb617e811d7f9954016f5c3eb6cb0bc", + "zh:9db2b13472cf91a5f18f0a7c6ae532277c05b0980d87f492341426b981679f7b", + "zh:ac1cbd2926265db80efe3f1814bed82901f7d8a7d4e5b1e22592e1eef234b1c7", + "zh:f465a955cc96f640e7426a648ba672c169a4a2959bad6146fe61583d67642561", + ] +} + +provider "registry.opentofu.org/terraform-provider-openstack/openstack" { + version = "2.1.0" + constraints = ">= 1.54.0, ~> 2.1.0" + hashes = [ + "h1:2TcmfEzBOGQPALErrXTaL6v+k/WAL40adao4izRYmdw=", + "zh:113661750398bf21c8fe36aade9fb6f5eb82b5bcd3bcd30bd37ac805d83398f4", + "zh:1b3c26347b9cd61e413ee93c2f422cc3278a77f55fd3516eaabb3e2a85f65281", + "zh:1b751bbf1e4152829a643b532fd3f5967a2e89a41fac381257e0b41665be3306", + "zh:1b967bbfd9b344419c0e0df0c3a15fcbd731e91f19a18955a55aace8d9ec039a", + "zh:1bc0fc7c0a21e568db043b654501ce668ba19bf7628d37a7d2aaa512fd6e5aeb", + "zh:425cbf61757d4b503e7bf0f409ea59835ca3afbd2432d56ad552c2e5d234a572", + "zh:67d4f059cb4d73bf6c060313ec32962c4e5bd8dc7be2542a6f2098ab32575cd9", + "zh:7fe841ac5b68a4f52fb3cf45070828f3845de44746679d434e4349f3c23e3ef2", + "zh:ac1ed4c6ef0b6a3410568a05d3f9933d184497f065988503c43da0b2f0786ab2", + "zh:c5c0d14c86fabd9ab6a5d555e6a8d511942665fb5fa948dd452b0d1934068344", + "zh:c9ae5c210192275185d6823566a9421983e8e64c2665a4cae00b92dd0706bd19", + "zh:ee9865ccc053e7f345e532654fb628d1cf1e81cd2e929643c1691bebffcf7b98", + "zh:f3416d2f666095e740522c4964e436470bb9ec17bd53aaae8169ad93297d07bd", + "zh:fbca85457dd49e17168989d64f7cfc4a519d55ef4e00e89cea2859e87ad87f83", + ] +} diff --git a/README.md b/README.md index e2f58b0..7b8a6b3 100644 --- a/README.md +++ b/README.md @@ -43,20 +43,25 @@ No modules. | [openstack_networking_subnet_v2.backend](https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/resources/networking_subnet_v2) | resource | | [openstack_networking_subnet_v2.database](https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/resources/networking_subnet_v2) | resource | | [openstack_networking_subnet_v2.frontend](https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/resources/networking_subnet_v2) | resource | -| [openstack_networking_subnetpool_v2.this](https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/resources/networking_subnetpool_v2) | resource | +| [openstack_networking_subnetpool_v2.apps](https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/resources/networking_subnetpool_v2) | resource | +| [openstack_networking_subnetpool_v2.database](https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/resources/networking_subnetpool_v2) | resource | | [openstack_identity_project_v3.this](https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs/data-sources/identity_project_v3) | data source | ### Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [application_subnetpool_cidr_blocks](#input_application_subnetpool_cidr_blocks) | The CIDR blocks for the application subnet pool | `list(string)` |
[| no | +| [application_subnetpool_id](#input_application_subnetpool_id) | The id of the subnetpool to create the public (first 2 tier) networks from.
"192.168.0.0/21"
]
[| no | | [database_subnetpool_id](#input_database_subnetpool_id) | The id of the subnetpool to create the databse network from.
"192.168.8.0/23"
]
[| no | ### Outputs | Name | Description | |------|-------------| +| [apps_subnetpool](#output_apps_subnetpool) | The application subnetpool object (as a list), if created | | [backend_network](#output_backend_network) | The backend network object (as a list), if created | | [backend_secgroups](#output_backend_secgroups) | The backend security group objects (as a list), if created | | [backend_subnet](#output_backend_subnet) | The backend subnet object (as a list), if created | | [database_network](#output_database_network) | The database network object (as a list), if created | | [database_secgroups](#output_database_secgroups) | The database security group objects (as a list), if created | | [database_subnet](#output_database_subnet) | The database subnet object (as a list), if created | +| [database_subnetpool](#output_database_subnetpool) | The database subnetpool object (as a list), if created | | [frontend_network](#output_frontend_network) | The frontend network object (as a list), if created | | [frontend_secgroups](#output_frontend_secgroups) | The frontend security group objects (as a list), if created | | [frontend_subnet](#output_frontend_subnet) | The frontend subnet object (as a list), if created | | [router](#output_router) | The entire router object (as a list), if created | -| [subnetpool](#output_subnetpool) | The subnetpool object (as a list), if created | diff --git a/main.tf b/main.tf index e331bf0..aec813d 100644 --- a/main.tf +++ b/main.tf @@ -11,6 +11,10 @@ terraform { } } +locals { + resource_prefix = lower(var.project_name) +} + #! data sources data "openstack_identity_project_v3" "this" { name = var.project_name @@ -18,18 +22,26 @@ data "openstack_identity_project_v3" "this" { } #! subnetpools -resource "openstack_networking_subnetpool_v2" "this" { - count = var.create_subnetpool ? 1 : 0 - name = "${var.project_name}-subnetpool" +resource "openstack_networking_subnetpool_v2" "apps" { + count = var.create_application_subnetpool ? 1 : 0 + name = "${local.resource_prefix}-application-subnetpool" is_default = false ip_version = 4 - prefixes = var.subnetpool_cidr_blocks + prefixes = var.application_subnetpool_cidr_blocks +} + +resource "openstack_networking_subnetpool_v2" "database" { + count = var.create_database_subnetpool ? 1 : 0 + name = "${local.resource_prefix}-database-subnetpool" + is_default = false + ip_version = 4 + prefixes = var.database_subnetpool_cidr_blocks } #! networks & subnets resource "openstack_networking_network_v2" "frontend" { count = var.architecture_tiers > 0 ? 1 : 0 - name = "${var.project_name}-frontend-network" + name = "${local.resource_prefix}-frontend-network" description = "Terraform managed." tenant_id = data.openstack_identity_project_v3.this.id shared = false @@ -39,7 +51,7 @@ resource "openstack_networking_network_v2" "frontend" { resource "openstack_networking_network_v2" "backend" { count = var.architecture_tiers > 1 ? 1 : 0 - name = "${var.project_name}-backend-network" + name = "${local.resource_prefix}-backend-network" description = "Terraform managed." tenant_id = data.openstack_identity_project_v3.this.id shared = false @@ -49,7 +61,7 @@ resource "openstack_networking_network_v2" "backend" { resource "openstack_networking_network_v2" "database" { count = var.architecture_tiers == 3 ? 1 : 0 - name = "${var.project_name}-database-network" + name = "${local.resource_prefix}-database-network" description = "Terraform managed." tenant_id = data.openstack_identity_project_v3.this.id shared = false @@ -59,44 +71,44 @@ resource "openstack_networking_network_v2" "database" { resource "openstack_networking_subnet_v2" "frontend" { count = var.architecture_tiers > 0 ? 1 : 0 - name = "${var.project_name}-frontend-subnet-${count.index + 1}" + name = "${local.resource_prefix}-frontend-subnet-${count.index + 1}" description = "Terraform managed." tenant_id = data.openstack_identity_project_v3.this.id network_id = openstack_networking_network_v2.frontend[0].id prefix_length = var.frontend_subnet_prefix_len ip_version = 4 - subnetpool_id = var.create_subnetpool ? openstack_networking_subnetpool_v2.this[0].id : var.public_subnetpool_id + subnetpool_id = var.create_application_subnetpool ? openstack_networking_subnetpool_v2.apps[0].id : var.application_subnetpool_id dns_nameservers = var.public_nameservers } resource "openstack_networking_subnet_v2" "backend" { count = var.architecture_tiers > 1 ? 1 : 0 - name = "${var.project_name}-backend-subnet-${count.index + 1}" + name = "${local.resource_prefix}-backend-subnet-${count.index + 1}" description = "Terraform managed." tenant_id = data.openstack_identity_project_v3.this.id network_id = openstack_networking_network_v2.backend[0].id prefix_length = var.backend_subnet_prefix_len ip_version = 4 - subnetpool_id = var.create_subnetpool ? openstack_networking_subnetpool_v2.this[0].id : var.public_subnetpool_id + subnetpool_id = var.create_application_subnetpool ? openstack_networking_subnetpool_v2.apps[0].id : var.application_subnetpool_id dns_nameservers = var.public_nameservers } resource "openstack_networking_subnet_v2" "database" { count = var.architecture_tiers == 3 ? 1 : 0 - name = "${var.project_name}-database-subnet-${count.index + 1}" + name = "${local.resource_prefix}-database-subnet-${count.index + 1}" description = "Terraform managed." tenant_id = data.openstack_identity_project_v3.this.id network_id = openstack_networking_network_v2.database[0].id prefix_length = var.database_subnet_prefix_len ip_version = 4 - subnetpool_id = var.create_subnetpool ? openstack_networking_subnetpool_v2.this[0].id : var.database_subnetpool_id + subnetpool_id = var.create_application_subnetpool ? openstack_networking_subnetpool_v2.database[0].id : var.database_subnetpool_id dns_nameservers = var.public_nameservers } #! router resource "openstack_networking_router_v2" "this" { count = var.architecture_tiers > 0 ? 1 : 0 - name = "${var.project_name}-main-${count.index + 1}" + name = "${local.resource_prefix}-main-${count.index + 1}" description = "Terraform managed." tenant_id = data.openstack_identity_project_v3.this.id external_network_id = var.attach_to_external ? var.external_network_id : null @@ -128,7 +140,7 @@ resource "openstack_networking_secgroup_v2" "frontend" { var.create_default_secgroups ) ? 1 : 0 - name = "${var.project_name}-frontend-secgroup" + name = "${local.resource_prefix}-frontend-secgroup" description = "Terraform managed." tenant_id = data.openstack_identity_project_v3.this.id delete_default_rules = true @@ -163,7 +175,7 @@ resource "openstack_networking_secgroup_v2" "backend" { var.architecture_tiers > 1 && var.create_default_secgroups ) ? 1 : 0 - name = "${var.project_name}-backend-secgroup" + name = "${local.resource_prefix}-backend-secgroup" description = "Terraform managed." tenant_id = data.openstack_identity_project_v3.this.id delete_default_rules = true @@ -198,7 +210,7 @@ resource "openstack_networking_secgroup_v2" "database" { var.architecture_tiers == 3 && var.create_default_secgroups ) ? length(local.db_secgroups) : 0 - name = "${var.project_name}-database-${local.db_secgroups[count.index].type}-secgroup" + name = "${local.resource_prefix}-database-${local.db_secgroups[count.index].type}-secgroup" description = "Terraform managed." tenant_id = data.openstack_identity_project_v3.this.id delete_default_rules = true diff --git a/outputs.tf b/outputs.tf index 7c9a7f3..ff82575 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,7 +1,14 @@ -output "subnetpool" { - value = length(openstack_networking_subnetpool_v2.this) > 0 ? openstack_networking_subnetpool_v2.this.* : null +output "apps_subnetpool" { + value = length(openstack_networking_subnetpool_v2.apps) > 0 ? openstack_networking_subnetpool_v2.apps.* : null sensitive = false - description = "The subnetpool object (as a list), if created" + description = "The application subnetpool object (as a list), if created" + depends_on = [] +} + +output "database_subnetpool" { + value = length(openstack_networking_subnetpool_v2.database) > 0 ? openstack_networking_subnetpool_v2.database.* : null + sensitive = false + description = "The database subnetpool object (as a list), if created" depends_on = [] } diff --git a/tests/module/.terraform.lock.hcl b/tests/module/.terraform.lock.hcl new file mode 100644 index 0000000..59aa238 --- /dev/null +++ b/tests/module/.terraform.lock.hcl @@ -0,0 +1,41 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/hashicorp/vault" { + version = "4.4.0" + hashes = [ + "h1:s0t6P9ZfUQnHLxtUcnpPWpME68KwO/OxZqHAKSIvOoo=", + "zh:0309ea8f81386e17ab13c06c5991ca959708c55c815b0cfba2bbcd865e0d606e", + "zh:40e56199ccd266bffa216e8ebbcdc2e29b6ef5145b39377be766e763cac759c8", + "zh:6fad1f073bd2e53e34736e000f98db581137e153ac80bbb5c4f1a1e38b46a1d2", + "zh:74564fd4759decccf7f3c952aa2feba1012f103a66ec354aa3b3292a2f1b2412", + "zh:7aae012c1a43e6e5dae6f608ec0f08cdb3f95fa121a32e413fe7ee37cb99947f", + "zh:7c83f508e164844b1dd9bafe9de0fe60c7be7b55a02e704a6e2f50cff38b7d96", + "zh:873a42322b68d9fba4a38217b97ee04a1eb617e811d7f9954016f5c3eb6cb0bc", + "zh:9db2b13472cf91a5f18f0a7c6ae532277c05b0980d87f492341426b981679f7b", + "zh:ac1cbd2926265db80efe3f1814bed82901f7d8a7d4e5b1e22592e1eef234b1c7", + "zh:f465a955cc96f640e7426a648ba672c169a4a2959bad6146fe61583d67642561", + ] +} + +provider "registry.opentofu.org/terraform-provider-openstack/openstack" { + version = "2.1.0" + constraints = ">= 1.54.0, ~> 2.1.0" + hashes = [ + "h1:2TcmfEzBOGQPALErrXTaL6v+k/WAL40adao4izRYmdw=", + "zh:113661750398bf21c8fe36aade9fb6f5eb82b5bcd3bcd30bd37ac805d83398f4", + "zh:1b3c26347b9cd61e413ee93c2f422cc3278a77f55fd3516eaabb3e2a85f65281", + "zh:1b751bbf1e4152829a643b532fd3f5967a2e89a41fac381257e0b41665be3306", + "zh:1b967bbfd9b344419c0e0df0c3a15fcbd731e91f19a18955a55aace8d9ec039a", + "zh:1bc0fc7c0a21e568db043b654501ce668ba19bf7628d37a7d2aaa512fd6e5aeb", + "zh:425cbf61757d4b503e7bf0f409ea59835ca3afbd2432d56ad552c2e5d234a572", + "zh:67d4f059cb4d73bf6c060313ec32962c4e5bd8dc7be2542a6f2098ab32575cd9", + "zh:7fe841ac5b68a4f52fb3cf45070828f3845de44746679d434e4349f3c23e3ef2", + "zh:ac1ed4c6ef0b6a3410568a05d3f9933d184497f065988503c43da0b2f0786ab2", + "zh:c5c0d14c86fabd9ab6a5d555e6a8d511942665fb5fa948dd452b0d1934068344", + "zh:c9ae5c210192275185d6823566a9421983e8e64c2665a4cae00b92dd0706bd19", + "zh:ee9865ccc053e7f345e532654fb628d1cf1e81cd2e929643c1691bebffcf7b98", + "zh:f3416d2f666095e740522c4964e436470bb9ec17bd53aaae8169ad93297d07bd", + "zh:fbca85457dd49e17168989d64f7cfc4a519d55ef4e00e89cea2859e87ad87f83", + ] +} diff --git a/tests/module/locals.tf b/tests/module/locals.tf new file mode 100644 index 0000000..41b3be0 --- /dev/null +++ b/tests/module/locals.tf @@ -0,0 +1,18 @@ +locals { + project_name = data.vault_kv_secret_v2.openstack_provider_project_info.data["tenant_name"] + project_domain = var.project_domain + project_tags = var.project_tags + architecture_tiers = var.architecture_tiers + create_application_subnetpool = var.create_application_subnetpool + application_subnetpool_cidr_blocks = var.application_subnetpool_cidr_blocks + create_database_subnetpool = var.create_database_subnetpool + database_subnetpool_cidr_blocks = var.database_subnetpool_cidr_blocks + frontend_subnet_prefix_len = var.frontend_subnet_prefix_len + backend_subnet_prefix_len = var.backend_subnet_prefix_len + database_subnet_prefix_len = var.database_subnet_prefix_len + public_nameservers = var.public_nameservers + create_default_secgroups = var.create_default_secgroups + database_secgroup_strict = var.database_secgroup_strict + attach_to_external = var.attach_to_external + external_network_id = data.openstack_networking_network_v2.ext_floating.id +} diff --git a/tests/module/main.tf b/tests/module/main.tf new file mode 100644 index 0000000..d1c5af4 --- /dev/null +++ b/tests/module/main.tf @@ -0,0 +1,67 @@ +terraform { + required_version = ">= 1.0.0" + required_providers { + vault = { + source = "hashicorp/vault" + } + openstack = { + source = "terraform-provider-openstack/openstack" + version = "~> 2.1.0" + } + } +} + +provider "vault" { + address = "https://active.vault.service.consul:8200" + skip_tls_verify = true +} + +provider "openstack" { + auth_url = data.vault_kv_secret_v2.openstack_provider_region.data["auth_url"] + region = data.vault_kv_secret_v2.openstack_provider_region.data["region_name"] + tenant_name = data.vault_kv_secret_v2.openstack_provider_project_info.data["tenant_name"] + application_credential_id = data.vault_kv_secret_v2.openstack_provider_project_user.data["application_credential_id"] + application_credential_secret = data.vault_kv_secret_v2.openstack_provider_project_user.data["application_credential_secret"] +} + +data "vault_kv_secret_v2" "openstack_provider_region" { + mount = "kv" + name = "infrastructure/openstack/regions/dc3_a" +} + +data "vault_kv_secret_v2" "openstack_provider_project_user" { + mount = "kv" + name = "infrastructure/openstack/projects/pcp_dawxdax/users/tofu" +} + +data "vault_kv_secret_v2" "openstack_provider_project_info" { + mount = "kv" + name = "infrastructure/openstack/projects/pcp_dawxdax/info" +} + +data "openstack_networking_network_v2" "ext_floating" { + name = "ext-floating1" +} + +module "landing_zone" { + source = "../../" + + project_name = local.project_name + project_domain = local.project_domain + project_tags = local.project_tags + + architecture_tiers = local.architecture_tiers + create_application_subnetpool = local.create_application_subnetpool + application_subnetpool_cidr_blocks = local.application_subnetpool_cidr_blocks + create_database_subnetpool = local.create_database_subnetpool + database_subnetpool_cidr_blocks = local.database_subnetpool_cidr_blocks + frontend_subnet_prefix_len = local.frontend_subnet_prefix_len + backend_subnet_prefix_len = local.backend_subnet_prefix_len + database_subnet_prefix_len = local.database_subnet_prefix_len + public_nameservers = local.public_nameservers + + create_default_secgroups = local.create_default_secgroups + + attach_to_external = local.attach_to_external + external_network_id = local.external_network_id +} diff --git a/tests/module/variables.tf b/tests/module/variables.tf new file mode 100644 index 0000000..bdb6bc1 --- /dev/null +++ b/tests/module/variables.tf @@ -0,0 +1,170 @@ +#! global variables +variable "project_domain" { + type = string + description = "The domain where this project will be created" + default = "default" +} + +variable "project_tags" { + type = list(string) + description = "The tags to append to this project" + default = [] +} + +#! architecture tiering variables +variable "architecture_tiers" { + type = number + description = <<-EOT + The type of architecture. + Can be either 0, 1, 2 or 3. + Tier 0 will not create any subnets or networks. + Tier 1 will only create a single frontend subnet. + Tier 2 will create a frontend and backend subnet. + Tier 3 will create a frontend, backend and database subnet. + EOT + default = 1 + validation { + condition = ( + var.architecture_tiers > 0 && + var.architecture_tiers <= 3 + ) + error_message = "The architecture_tiers must be between 0 and 3." + } +} + +#! subnetpools creation +variable "create_application_subnetpool" { + type = bool + description = "Whether the module should create an application subnet pool for this project, or use an existing one." + default = true +} + +variable "application_subnetpool_cidr_blocks" { + type = list(string) + description = "The CIDR blocks for the application subnet pool" + default = ["192.168.0.0/21"] + validation { + condition = alltrue([ + for i in var.application_subnetpool_cidr_blocks : can(cidrhost(i, 0)) + ]) + error_message = "The application_subnetpool_cidr_blocks must be a valid IPv4 CIDR" + } +} + +variable "create_database_subnetpool" { + type = bool + description = "Whether the module should create a database subnet pool for this project, or use an existing one." + default = true +} + +variable "database_subnetpool_cidr_blocks" { + type = list(string) + description = "The CIDR blocks for the database subnet pool" + default = ["192.168.8.0/23"] + validation { + condition = alltrue([ + for i in var.database_subnetpool_cidr_blocks : can(cidrhost(i, 0)) + ]) + error_message = "The database_subnetpool_cidr_blocks must be a valid IPv4 CIDR" + } +} + +#! networking variables +variable "frontend_subnet_prefix_len" { + type = number + description = "The prefix length of the frontend subnet. Must be between 20 and 32." + default = 24 + validation { + condition = ( + var.frontend_subnet_prefix_len >= 20 && + var.frontend_subnet_prefix_len <= 32 + ) + error_message = "The prefix length must be between 20 and 32." + } +} + +variable "backend_subnet_prefix_len" { + type = number + description = "The prefix length of the backend subnet. Must be between 20 and 32." + default = 24 + validation { + condition = ( + var.backend_subnet_prefix_len >= 20 && + var.backend_subnet_prefix_len <= 32 + ) + error_message = "The prefix length must be between 20 and 32." + } +} + +variable "database_subnet_prefix_len" { + type = number + description = "The prefix length of the database subnet. Must be between 24 and 32." + default = 24 + validation { + condition = ( + var.database_subnet_prefix_len >= 24 && + var.database_subnet_prefix_len <= 32 + ) + error_message = "The prefix length must be between 24 and 32." + } +} + +variable "public_nameservers" { + type = list(string) + description = <<-EOT + A list of public DNS servers to upstreams requests to in your subnets. + This is not necessary if your openstack deployment already has configured default upstreams for neutron. + EOT + default = [] +} + +#! security variables +variable "create_default_secgroups" { + type = bool + description = <<-EOT + Whether to create default security groups or not. + Depending on your choice of architecture tiering, will create security groups so that each tier can connect to the one below. + Security groups for the database tier will be created for mariadb, postgresql and redis. + A default security group allowing ssh connection will also be created. + EOT + default = false +} + +variable "database_secgroup_strict" { + type = bool + description = <<-EOT + Defines whether the security groups for the database network should be strict. + In strict mode, egress is only allowed to the backend network. + EOT + default = false +} + +#! subnetpool variables & validation +variable "application_subnetpool_id" { + type = string + description = <<-EOT + The id of the subnetpool to create the public (first 2 tier) networks from. + Since this module can route private subnets to the backbone, it needs to make sure it's not creating overlapping subnets. + EOT + default = null +} + +variable "database_subnetpool_id" { + type = string + description = <<-EOT + The id of the subnetpool to create the databse network from. + Since this module can route private subnets to the backbone, it needs to make sure it's not creating overlapping subnets. + EOT + default = null +} + +#! public network attachement variables +variable "attach_to_external" { + type = bool + description = <<-EOT + Whether the frontend subnet should be routed or not to the external LAN. + This options implies that you have sufficient permissions to configure static route on the backbone infrastructure. + This will create an static route entry in the route table of the backbone router, in order to make your project available from the outside. + EOT + default = false +} diff --git a/tests/one_tier.tftest.hcl b/tests/one_tier.tftest.hcl new file mode 100644 index 0000000..73961d8 --- /dev/null +++ b/tests/one_tier.tftest.hcl @@ -0,0 +1,59 @@ +run "two_tier" { + module { + source = "./tests/module" + } + + variables { + project_domain = "default" + project_tags = ["cloud", "test", "tofu"] + architecture_tiers = 1 + create_application_subnetpool = true + application_subnetpool_cidr_blocks = ["10.10.128.0/17"] + create_database_subnetpool = false + frontend_subnet_prefix_len = 24 + public_nameservers = ["9.9.9.9", "9.9.9.10"] + create_default_secgroups = true + database_secgroup_strict = false + attach_to_external = true + } + + assert { + condition = alltrue([for i in module.landing_zone.apps_subnetpool[0].prefixes : can(cidrhost(i, 0))]) + error_message = "The application subnetpool does not seem correct" + } + + assert { + condition = module.landing_zone.database_subnetpool == null + error_message = "The database subnetpool is not null" + } + + assert { + condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", module.landing_zone.frontend_network[0].id)) + error_message = "The frontend network was not created successfully" + } + + assert { + condition = module.landing_zone.backend_network == null + error_message = "The backend network is not null" + } + + assert { + condition = module.landing_zone.database_network == null + error_message = "The database network is not null" + } + + assert { + condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", module.landing_zone.frontend_subnet[0].id)) + error_message = "The frontend subnet was not created successfully" + } + + assert { + condition = module.landing_zone.backend_subnet == null + error_message = "The backend subnet is not null" + } + + assert { + condition = module.landing_zone.database_subnet == null + error_message = "The database subnet is not null" + } +} diff --git a/tests/three_tier.tftest.hcl b/tests/three_tier.tftest.hcl new file mode 100644 index 0000000..6eba221 --- /dev/null +++ b/tests/three_tier.tftest.hcl @@ -0,0 +1,62 @@ +run "three_tier" { + module { + source = "./tests/module" + } + + variables { + project_domain = "default" + project_tags = ["cloud", "test", "tofu"] + architecture_tiers = 3 + create_application_subnetpool = true + application_subnetpool_cidr_blocks = ["10.10.128.0/17"] + create_database_subnetpool = true + database_subnetpool_cidr_blocks = ["192.168.0.0/16"] + frontend_subnet_prefix_len = 24 + backend_subnet_prefix_len = 24 + database_subnet_prefix_len = 24 + public_nameservers = ["9.9.9.9", "9.9.9.10"] + create_default_secgroups = true + database_secgroup_strict = false + attach_to_external = true + } + + assert { + condition = alltrue([for i in module.landing_zone.apps_subnetpool[0].prefixes : can(cidrhost(i, 0))]) + error_message = "The application subnetpool was not created successfully" + } + + assert { + condition = alltrue([for i in module.landing_zone.database_subnetpool[0].prefixes : can(cidrhost(i, 0))]) + error_message = "The database subnetpool was not created successfully" + } + + assert { + condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", module.landing_zone.frontend_network[0].id)) + error_message = "The frontend network was not created successfully" + } + + assert { + condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", module.landing_zone.backend_network[0].id)) + error_message = "The backend network was not created successfully" + } + + assert { + condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", module.landing_zone.database_network[0].id)) + error_message = "The database network was not created successfully" + } + + assert { + condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", module.landing_zone.frontend_subnet[0].id)) + error_message = "The frontend subnet was not created successfully" + } + + assert { + condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", module.landing_zone.backend_subnet[0].id)) + error_message = "The backend subnet was not created successfully" + } + + assert { + condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", module.landing_zone.database_subnet[0].id)) + error_message = "The database subnet was not created successfully" + } +} diff --git a/tests/two_tier.tftest.hcl b/tests/two_tier.tftest.hcl new file mode 100644 index 0000000..d71b500 --- /dev/null +++ b/tests/two_tier.tftest.hcl @@ -0,0 +1,60 @@ +run "two_tier" { + module { + source = "./tests/module" + } + + variables { + project_domain = "default" + project_tags = ["cloud", "test", "tofu"] + architecture_tiers = 2 + create_application_subnetpool = true + application_subnetpool_cidr_blocks = ["10.10.128.0/17"] + create_database_subnetpool = false + frontend_subnet_prefix_len = 24 + backend_subnet_prefix_len = 24 + public_nameservers = ["9.9.9.9", "9.9.9.10"] + create_default_secgroups = true + database_secgroup_strict = false + attach_to_external = true + } + + assert { + condition = alltrue([for i in module.landing_zone.apps_subnetpool[0].prefixes : can(cidrhost(i, 0))]) + error_message = "The application subnetpool does not seem correct" + } + + assert { + condition = module.landing_zone.database_subnetpool == null + error_message = "The database subnetpool does not seem correct" + } + + assert { + condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", module.landing_zone.frontend_network[0].id)) + error_message = "The frontend network was not created successfully" + } + + assert { + condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", module.landing_zone.backend_network[0].id)) + error_message = "The backend network was not created successfully" + } + + assert { + condition = module.landing_zone.database_network == null + error_message = "The database network is not null" + } + + assert { + condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", module.landing_zone.frontend_subnet[0].id)) + error_message = "The frontend subnet was not created successfully" + } + + assert { + condition = can(regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", module.landing_zone.backend_subnet[0].id)) + error_message = "The backend subnet was not created successfully" + } + + assert { + condition = module.landing_zone.database_subnet == null + error_message = "The database subnet is not null" + } +} diff --git a/variables.tf b/variables.tf index f5da099..6a3fddf 100644 --- a/variables.tf +++ b/variables.tf @@ -41,22 +41,40 @@ variable "architecture_tiers" { } } -#! subnetpool creation -variable "create_subnetpool" { +#! subnetpools creation +variable "create_application_subnetpool" { type = bool - description = "Whether the module should create a subnet pool for this project, or use an existing one." + description = "Whether the module should create an application subnet pool for this project, or use an existing one." default = true } -variable "subnetpool_cidr_blocks" { +variable "application_subnetpool_cidr_blocks" { type = list(string) - description = "The CIDR block for the subnet pool" + description = "The CIDR blocks for the application subnet pool" default = ["192.168.0.0/21"] validation { condition = alltrue([ - for i in var.subnetpool_cidr_blocks : can(cidrhost(i, 0)) + for i in var.application_subnetpool_cidr_blocks : can(cidrhost(i, 0)) ]) - error_message = "The subnetpool_cidr_blocks must be a valid IPv4 CIDR" + error_message = "The application_subnetpool_cidr_blocks must be a valid IPv4 CIDR" + } +} + +variable "create_database_subnetpool" { + type = bool + description = "Whether the module should create a database subnet pool for this project, or use an existing one." + default = true +} + +variable "database_subnetpool_cidr_blocks" { + type = list(string) + description = "The CIDR blocks for the database subnet pool" + default = ["192.168.8.0/23"] + validation { + condition = alltrue([ + for i in var.database_subnetpool_cidr_blocks : can(cidrhost(i, 0)) + ]) + error_message = "The database_subnetpool_cidr_blocks must be a valid IPv4 CIDR" } } @@ -152,7 +170,7 @@ locals { } #! subnetpool variables & validation -variable "public_subnetpool_id" { +variable "application_subnetpool_id" { type = string description = <<-EOT The id of the subnetpool to create the public (first 2 tier) networks from. @@ -171,14 +189,14 @@ variable "database_subnetpool_id" { } locals { - validate_public_subnetpool_ids = ( + validate_application_subnetpool_ids = ( var.architecture_tiers > 0 && - var.create_subnetpool == false && - var.public_subnetpool_id == null + var.create_application_subnetpool == false && + var.application_subnetpool_id == null ) ? tobool("You have to either create or specify an existing subnetpool to create the public subnets from") : true validate_database_subnetpool_ids = ( var.architecture_tiers > 2 && - var.create_subnetpool == false && + var.create_database_subnetpool == false && var.database_subnetpool_id == null ) ? tobool("You have to either create or specify an existing subnetpool to create the database subnets from") : true }
"192.168.0.0/21"
]