diff --git a/README.md b/README.md index 5158b493..7211b802 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,11 @@ This module creates the following IBM Cloud® Virtual Private Cloud (VPC) net ![vpc-module](./.docs/vpc-module.png) +## Presets + +In addition to this root module, this repository provides two submodules that call the root module with presets and defaults that are aligned with the general [Framework for Financial Services](https://cloud.ibm.com/docs/framework-financial-services?topic=framework-financial-services-vpc-architecture-about) management and workload VPC topologies. See the [landing-zone-submodules](/landing-zone-submodule/) for details. + + ## Usage ```terraform module vpc { @@ -62,6 +67,7 @@ You need the following permissions to run this module. ## Examples - [ Default Example](examples/default) +- [ Landing Zone example](examples/landing_zone) --- diff --git a/common-dev-assets b/common-dev-assets index a08e843a..51f43cbd 160000 --- a/common-dev-assets +++ b/common-dev-assets @@ -1 +1 @@ -Subproject commit a08e843ad9560c02a043996162f86af254faf268 +Subproject commit 51f43cbdef7595410d450dbed9837a686ad2fc57 diff --git a/cra-tf-validate-ignore-goals.json b/cra-tf-validate-ignore-goals.json index f2560700..b7242036 100644 --- a/cra-tf-validate-ignore-goals.json +++ b/cra-tf-validate-ignore-goals.json @@ -35,6 +35,18 @@ "description:": "Check whether Cloud Object Storage bucket resiliency is set to cross region", "ignore_reason": "This module does not create any Cloud object storage bucket and it is used in an example for testing purpose.", "is_valid": false + }, + { + "scc_goal_id": "3000451", + "description:": "Check whether Virtual Private Cloud (VPC) network access control lists don't allow ingress from 0.0.0.0/0 to any port", + "ignore_reason": "False positive introduced in CRA 1.2.1. There is no such allow ingress ACL created by the module. There is a deny rule on 0.0.0.0/0 to any port.", + "is_valid": false + }, + { + "scc_goal_id": "3000452", + "description:": "Check whether Virtual Private Cloud (VPC) network access control lists don't allow egress from 0.0.0.0/0 to any port", + "ignore_reason": "False positive introduced in CRA 1.2.1. There is no such allow egress ACL created by the module. There is a deny rule on 0.0.0.0/0 to any port.", + "is_valid": false } ] } diff --git a/examples/landing_zone/README.md b/examples/landing_zone/README.md new file mode 100644 index 00000000..9ceb5c18 --- /dev/null +++ b/examples/landing_zone/README.md @@ -0,0 +1,10 @@ +# Landing Zone example + +This example demonstrates how to use the management and workload VPC [modules](../../landing-zone-submodule/) to create a network VPC topology that is aligned with the network segregation key principles of the IBM Cloud [Framework for Financial Services](https://cloud.ibm.com/docs/framework-financial-services?topic=framework-financial-services-vpc-architecture-connectivity-overview). + +The example shows how to use the base modules to create the following topology: +- A management VPC +- A workload VPC +- A transit gateway that connects the two VPCs + +:exclamation: **Important:** The topology created in this example does not meet all compliance controls for the IBM Cloud Framework for Financial Services. Use the [terraform-ibm-landing-zone](https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone) module to create a fully compliant stack. diff --git a/examples/landing_zone/main.tf b/examples/landing_zone/main.tf new file mode 100644 index 00000000..984097d7 --- /dev/null +++ b/examples/landing_zone/main.tf @@ -0,0 +1,68 @@ +############################################################################## +# Resource Group +############################################################################## + +module "resource_group" { + source = "git::https://github.com/terraform-ibm-modules/terraform-ibm-resource-group.git?ref=v1.0.5" + # if an existing resource group is not set (null) create a new one using prefix + resource_group_name = var.resource_group == null ? "${var.prefix}-resource-group" : null + existing_resource_group_name = var.resource_group +} + +############################################################################# +# Provision cloud object storage and bucket +############################################################################# + +module "cos_bucket" { + count = var.enable_vpc_flow_logs ? 1 : 0 + source = "git::https://github.com/terraform-ibm-modules/terraform-ibm-cos.git?ref=v6.0.0" + resource_group_id = module.resource_group.resource_group_id + region = var.region + cross_region_location = null + cos_instance_name = "${var.prefix}-vpc-logs-cos" + cos_tags = var.resource_tags + bucket_name = "${var.prefix}-vpc-logs-cos-bucket" + encryption_enabled = false + retention_enabled = false +} + +############################################################################# +# Provision VPC +############################################################################# + +module "workload_vpc" { + source = "../../landing-zone-submodule/workload-vpc/" + resource_group_id = module.resource_group.resource_group_id + region = var.region + prefix = var.prefix + tags = var.resource_tags + enable_vpc_flow_logs = var.enable_vpc_flow_logs + create_authorization_policy_vpc_to_cos = var.create_authorization_policy_vpc_to_cos + existing_cos_instance_guid = module.cos_bucket[0].cos_instance_guid + existing_cos_bucket_name = module.cos_bucket[0].bucket_name[0] +} + + +module "management_vpc" { + source = "../../landing-zone-submodule/management-vpc/" + resource_group_id = module.resource_group.resource_group_id + region = var.region + prefix = var.prefix + tags = var.resource_tags +} + + +############################################################################## +# Transit Gateway connects the 2 VPCs +############################################################################## + +module "tg_gateway_connection" { + source = "git::https://github.com/terraform-ibm-modules/terraform-ibm-transit-gateway.git?ref=v2.0.2" + transit_gateway_name = "${var.prefix}-tg" + region = var.region + global_routing = false + resource_tags = var.resource_tags + resource_group_id = module.resource_group.resource_group_id + vpc_connections = [module.workload_vpc.vpc_crn, module.management_vpc.vpc_crn] + classic_connections_count = 0 +} diff --git a/examples/landing_zone/outputs.tf b/examples/landing_zone/outputs.tf new file mode 100644 index 00000000..d01d452b --- /dev/null +++ b/examples/landing_zone/outputs.tf @@ -0,0 +1,33 @@ +############################################################################## +# Outputs +############################################################################## + +output "workload_vpc_name" { + description = "VPC name" + value = module.workload_vpc.vpc_name +} + +output "workload_vpc_id" { + description = "ID of VPC created" + value = module.workload_vpc.vpc_id +} + +output "workload_vpc_crn" { + description = "CRN of VPC created" + value = module.workload_vpc.vpc_crn +} + +output "management_vpc_name" { + description = "VPC name" + value = module.management_vpc.vpc_name +} + +output "management_vpc_id" { + description = "ID of VPC created" + value = module.management_vpc.vpc_id +} + +output "management_vpc_crn" { + description = "CRN of VPC created" + value = module.management_vpc.vpc_crn +} diff --git a/examples/landing_zone/provider.tf b/examples/landing_zone/provider.tf new file mode 100644 index 00000000..df45ef50 --- /dev/null +++ b/examples/landing_zone/provider.tf @@ -0,0 +1,4 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region +} diff --git a/examples/landing_zone/variables.tf b/examples/landing_zone/variables.tf new file mode 100644 index 00000000..ebd6d41e --- /dev/null +++ b/examples/landing_zone/variables.tf @@ -0,0 +1,46 @@ +variable "ibmcloud_api_key" { + description = "APIkey that's associated with the account to provision resources to" + type = string + sensitive = true +} + +variable "region" { + description = "The region to which to deploy the VPC" + type = string + default = "us-south" +} + +variable "prefix" { + description = "The prefix that you would like to append to your resources" + type = string + default = "test-landing-zone" +} + +variable "resource_group" { + type = string + description = "An existing resource group name to use for this example, if unset a new resource group will be created" + default = null +} + +variable "resource_tags" { + description = "List of Tags for the resource created" + type = list(string) + default = null +} + + +############################################################################## +# VPC flow logs variables +############################################################################## + +variable "enable_vpc_flow_logs" { + type = bool + description = "Enable VPC Flow Logs, it will create Flow logs collector if set to true" + default = true +} + +variable "create_authorization_policy_vpc_to_cos" { + description = "Set it to true if authorization policy is required for VPC to access COS" + type = bool + default = true +} diff --git a/examples/landing_zone/version.tf b/examples/landing_zone/version.tf new file mode 100644 index 00000000..38d0fca8 --- /dev/null +++ b/examples/landing_zone/version.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.3.0" + required_providers { + # Pin to the lowest provider version of the range defined in the main module's version.tf to ensure lowest version still works + ibm = { + source = "IBM-Cloud/ibm" + version = "1.51.0" + } + } +} diff --git a/landing-zone-submodule/management-vpc/README.md b/landing-zone-submodule/management-vpc/README.md new file mode 100644 index 00000000..b2cbd9fd --- /dev/null +++ b/landing-zone-submodule/management-vpc/README.md @@ -0,0 +1,57 @@ +# Landing Zone management VPC (standalone) + +This specialized submodule calls the root [landing-zone-vpc module](../..) with a preset configuration that results in a management VPC with a topology that is identical to the management VPC that is created by the [terraform-ibm-landing-zone module](https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone/tree/main). + +You can use this submodule when you need more modularity to create your topology than the terraform-ibm-landing-zone module provides. This submodule provides one of the building blocks for this topology. + +See the [Landing Zone example](../../examples/landing_zone/) for runnable code. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [management\_vpc](#module\_management\_vpc) | ../../ | n/a | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [address\_prefixes](#input\_address\_prefixes) | Use `address_prefixes` only if `use_manual_address_prefixes` is true otherwise prefixes will not be created. Use only if you need to manage prefixes manually. |
object({
zone-1 = optional(list(string))
zone-2 = optional(list(string))
zone-3 = optional(list(string))
})
| `null` | no | +| [classic\_access](#input\_classic\_access) | Optionally allow VPC to access classic infrastructure network | `bool` | `null` | no | +| [create\_authorization\_policy\_vpc\_to\_cos](#input\_create\_authorization\_policy\_vpc\_to\_cos) | Set it to true if authorization policy is required for VPC to access COS | `bool` | `false` | no | +| [default\_network\_acl\_name](#input\_default\_network\_acl\_name) | Override default ACL name | `string` | `null` | no | +| [default\_routing\_table\_name](#input\_default\_routing\_table\_name) | Override default VPC routing table name | `string` | `null` | no | +| [default\_security\_group\_name](#input\_default\_security\_group\_name) | Override default VPC security group name | `string` | `null` | no | +| [default\_security\_group\_rules](#input\_default\_security\_group\_rules) | Override default security group rules |
list(
object({
name = string
direction = string
remote = string
tcp = optional(
object({
port_max = optional(number)
port_min = optional(number)
})
)
udp = optional(
object({
port_max = optional(number)
port_min = optional(number)
})
)
icmp = optional(
object({
type = optional(number)
code = optional(number)
})
)
})
)
| `[]` | no | +| [enable\_vpc\_flow\_logs](#input\_enable\_vpc\_flow\_logs) | Enable VPC Flow Logs, it will create Flow logs collector if set to true | `bool` | `false` | no | +| [existing\_cos\_bucket\_name](#input\_existing\_cos\_bucket\_name) | Name of the COS bucket to collect VPC flow logs | `string` | `null` | no | +| [existing\_cos\_instance\_guid](#input\_existing\_cos\_instance\_guid) | GUID of the COS instance to create Flow log collector | `string` | `null` | no | +| [network\_acls](#input\_network\_acls) | List of network ACLs to create with VPC |
list(
object({
name = string
add_cluster_rules = optional(bool)
rules = list(
object({
name = string
action = string
destination = string
direction = string
source = string
tcp = optional(
object({
port_max = optional(number)
port_min = optional(number)
source_port_max = optional(number)
source_port_min = optional(number)
})
)
udp = optional(
object({
port_max = optional(number)
port_min = optional(number)
source_port_max = optional(number)
source_port_min = optional(number)
})
)
icmp = optional(
object({
type = optional(number)
code = optional(number)
})
)
})
)
})
)
|
[
{
"add_ibm_cloud_internal_rules": true,
"add_vpc_connectivity_rules": true,
"name": "management-acl",
"prepend_ibm_rules": true,
"rules": []
}
]
| no | +| [network\_cidr](#input\_network\_cidr) | Network CIDR for the VPC. This is used to manage network ACL rules for cluster provisioning. | `string` | `"10.0.0.0/8"` | no | +| [prefix](#input\_prefix) | The prefix that you would like to append to your resources | `string` | `"management"` | no | +| [region](#input\_region) | The region to which to deploy the VPC | `string` | `"au-syd"` | no | +| [resource\_group\_id](#input\_resource\_group\_id) | The resource group ID where the VPC to be created | `string` | n/a | yes | +| [subnets](#input\_subnets) | Object for subnets to be created in each zone, each zone can have any number of subnets |
object({
zone-1 = list(object({
name = string
cidr = string
public_gateway = optional(bool)
acl_name = string
}))
zone-2 = list(object({
name = string
cidr = string
public_gateway = optional(bool)
acl_name = string
}))
zone-3 = list(object({
name = string
cidr = string
public_gateway = optional(bool)
acl_name = string
}))
})
|
{
"zone-1": [
{
"acl_name": "management-acl",
"cidr": "10.10.10.0/24",
"name": "vsi-zone-1",
"public_gateway": false
},
{
"acl_name": "management-acl",
"cidr": "10.10.20.0/24",
"name": "vpe-zone-1",
"public_gateway": false
},
{
"acl_name": "management-acl",
"cidr": "10.10.30.0/24",
"name": "vpn-zone-1",
"public_gateway": false
}
],
"zone-2": [
{
"acl_name": "management-acl",
"cidr": "10.20.10.0/24",
"name": "vsi-zone-2",
"public_gateway": false
},
{
"acl_name": "management-acl",
"cidr": "10.20.20.0/24",
"name": "vpe-zone-2",
"public_gateway": false
}
],
"zone-3": [
{
"acl_name": "management-acl",
"cidr": "10.30.10.0/24",
"name": "vsi-zone-3",
"public_gateway": false
},
{
"acl_name": "management-acl",
"cidr": "10.30.20.0/24",
"name": "vpe-zone-3",
"public_gateway": false
}
]
}
| no | +| [tags](#input\_tags) | List of tags to apply to resources created by this module. | `list(string)` | `[]` | no | +| [use\_manual\_address\_prefixes](#input\_use\_manual\_address\_prefixes) | Optionally assign prefixes to VPC manually. By default this is false, and prefixes will be created along with subnets | `bool` | `true` | no | +| [use\_public\_gateways](#input\_use\_public\_gateways) | For each `zone` that is set to `true`, a public gateway will be created in that zone |
object({
zone-1 = optional(bool)
zone-2 = optional(bool)
zone-3 = optional(bool)
})
|
{
"zone-1": false,
"zone-2": false,
"zone-3": false
}
| no | + +## Outputs + +| Name | Description | +|------|-------------| +| [vpc\_crn](#output\_vpc\_crn) | CRN of VPC created | +| [vpc\_id](#output\_vpc\_id) | ID of VPC created | +| [vpc\_name](#output\_vpc\_name) | VPC name | + diff --git a/landing-zone-submodule/management-vpc/main.tf b/landing-zone-submodule/management-vpc/main.tf new file mode 100644 index 00000000..73d15764 --- /dev/null +++ b/landing-zone-submodule/management-vpc/main.tf @@ -0,0 +1,27 @@ +############################################################################# +# Provision VPC +############################################################################# + +module "management_vpc" { + source = "../../" + name = "management" + tags = var.tags + resource_group_id = var.resource_group_id + region = var.region + prefix = var.prefix + network_cidr = var.network_cidr + classic_access = var.classic_access + use_manual_address_prefixes = var.use_manual_address_prefixes + default_network_acl_name = var.default_network_acl_name + default_security_group_name = var.default_security_group_name + security_group_rules = var.default_security_group_rules == null ? [] : var.default_security_group_rules + default_routing_table_name = var.default_routing_table_name + address_prefixes = var.address_prefixes + network_acls = var.network_acls + use_public_gateways = var.use_public_gateways + subnets = var.subnets + enable_vpc_flow_logs = var.enable_vpc_flow_logs + create_authorization_policy_vpc_to_cos = var.create_authorization_policy_vpc_to_cos + existing_cos_instance_guid = var.existing_cos_instance_guid + existing_storage_bucket_name = var.existing_cos_bucket_name +} diff --git a/landing-zone-submodule/management-vpc/outputs.tf b/landing-zone-submodule/management-vpc/outputs.tf new file mode 100644 index 00000000..f425cb29 --- /dev/null +++ b/landing-zone-submodule/management-vpc/outputs.tf @@ -0,0 +1,18 @@ +############################################################################## +# Outputs +############################################################################## + +output "vpc_name" { + description = "VPC name" + value = module.management_vpc.vpc_name +} + +output "vpc_id" { + description = "ID of VPC created" + value = module.management_vpc.vpc_id +} + +output "vpc_crn" { + description = "CRN of VPC created" + value = module.management_vpc.vpc_crn +} diff --git a/landing-zone-submodule/management-vpc/variables.tf b/landing-zone-submodule/management-vpc/variables.tf new file mode 100644 index 00000000..bd86fb6e --- /dev/null +++ b/landing-zone-submodule/management-vpc/variables.tf @@ -0,0 +1,303 @@ +variable "region" { + description = "The region to which to deploy the VPC" + type = string + default = "au-syd" +} + +variable "prefix" { + description = "The prefix that you would like to append to your resources" + type = string + default = "management" +} + +variable "resource_group_id" { + description = "The resource group ID where the VPC to be created" + type = string +} + +variable "tags" { + description = "List of tags to apply to resources created by this module." + type = list(string) + default = [] +} + + +############################################################################# +# VPC variables +############################################################################# + +variable "network_cidr" { + description = "Network CIDR for the VPC. This is used to manage network ACL rules for cluster provisioning." + type = string + default = "10.0.0.0/8" +} + +variable "classic_access" { + description = "Optionally allow VPC to access classic infrastructure network" + type = bool + default = null +} + +variable "use_manual_address_prefixes" { + description = "Optionally assign prefixes to VPC manually. By default this is false, and prefixes will be created along with subnets" + type = bool + default = true +} + +variable "default_network_acl_name" { + description = "Override default ACL name" + type = string + default = null +} + +variable "default_security_group_name" { + description = "Override default VPC security group name" + type = string + default = null +} + +variable "default_routing_table_name" { + description = "Override default VPC routing table name" + type = string + default = null +} + +variable "default_security_group_rules" { + description = "Override default security group rules" + type = list( + object({ + name = string + direction = string + remote = string + tcp = optional( + object({ + port_max = optional(number) + port_min = optional(number) + }) + ) + udp = optional( + object({ + port_max = optional(number) + port_min = optional(number) + }) + ) + icmp = optional( + object({ + type = optional(number) + code = optional(number) + }) + ) + }) + ) + + default = [] +} + +variable "address_prefixes" { + description = "Use `address_prefixes` only if `use_manual_address_prefixes` is true otherwise prefixes will not be created. Use only if you need to manage prefixes manually." + type = object({ + zone-1 = optional(list(string)) + zone-2 = optional(list(string)) + zone-3 = optional(list(string)) + }) + + default = null +} + +variable "network_acls" { + description = "List of network ACLs to create with VPC" + type = list( + object({ + name = string + add_cluster_rules = optional(bool) + rules = list( + object({ + name = string + action = string + destination = string + direction = string + source = string + tcp = optional( + object({ + port_max = optional(number) + port_min = optional(number) + source_port_max = optional(number) + source_port_min = optional(number) + }) + ) + udp = optional( + object({ + port_max = optional(number) + port_min = optional(number) + source_port_max = optional(number) + source_port_min = optional(number) + }) + ) + icmp = optional( + object({ + type = optional(number) + code = optional(number) + }) + ) + }) + ) + }) + ) + default = [ + { + name = "management-acl" + add_ibm_cloud_internal_rules = true + add_vpc_connectivity_rules = true + prepend_ibm_rules = true + rules = [ + ## The below rules may be added to easily provide network connectivity for a loadbalancer + ## Note that opening 0.0.0.0/0 is not FsCloud compliant + # { + # name = "allow-all-443-inbound" + # action = "allow" + # direction = "inbound" + # tcp = { + + # port_min = 443 + # port_max = 443 + # source_port_min = 1024 + # source_port_max = 65535 + # } + # destination = "0.0.0.0/0" + # source = "0.0.0.0/0" + # }, + # { + # name = "allow-all-443-outbound" + # action = "allow" + # direction = "outbound" + # tcp = { + # source_port_min = 443 + # source_port_max = 443 + # port_min = 1024 + # port_max = 65535 + # } + # destination = "0.0.0.0/0" + # source = "0.0.0.0/0" + # } + ] + } + ] +} + +variable "use_public_gateways" { + description = "For each `zone` that is set to `true`, a public gateway will be created in that zone" + type = object({ + zone-1 = optional(bool) + zone-2 = optional(bool) + zone-3 = optional(bool) + }) + default = { + zone-1 = false + zone-2 = false + zone-3 = false + } +} + + +variable "subnets" { + description = "Object for subnets to be created in each zone, each zone can have any number of subnets" + type = object({ + zone-1 = list(object({ + name = string + cidr = string + public_gateway = optional(bool) + acl_name = string + })) + zone-2 = list(object({ + name = string + cidr = string + public_gateway = optional(bool) + acl_name = string + })) + zone-3 = list(object({ + name = string + cidr = string + public_gateway = optional(bool) + acl_name = string + })) + }) + default = { + "zone-1" : [ + { + "acl_name" : "management-acl", + "cidr" : "10.10.10.0/24", + "name" : "vsi-zone-1", + "public_gateway" : false + }, + { + "acl_name" : "management-acl", + "cidr" : "10.10.20.0/24", + "name" : "vpe-zone-1", + "public_gateway" : false + }, + { + "acl_name" : "management-acl", + "cidr" : "10.10.30.0/24", + "name" : "vpn-zone-1", + "public_gateway" : false + } + ], + "zone-2" : [ + { + "acl_name" : "management-acl", + "cidr" : "10.20.10.0/24", + "name" : "vsi-zone-2", + "public_gateway" : false + }, + { + "acl_name" : "management-acl", + "cidr" : "10.20.20.0/24", + "name" : "vpe-zone-2", + "public_gateway" : false + } + ], + "zone-3" : [ + { + "acl_name" : "management-acl", + "cidr" : "10.30.10.0/24", + "name" : "vsi-zone-3", + "public_gateway" : false + }, + { + "acl_name" : "management-acl", + "cidr" : "10.30.20.0/24", + "name" : "vpe-zone-3", + "public_gateway" : false + } + ] + } +} + + +############################################################################# +# Variables for COS and Flow Logs +############################################################################# + +variable "enable_vpc_flow_logs" { + type = bool + description = "Enable VPC Flow Logs, it will create Flow logs collector if set to true" + default = false +} + +variable "create_authorization_policy_vpc_to_cos" { + description = "Set it to true if authorization policy is required for VPC to access COS" + type = bool + default = false +} + +variable "existing_cos_instance_guid" { + description = "GUID of the COS instance to create Flow log collector" + type = string + default = null +} + +variable "existing_cos_bucket_name" { + description = "Name of the COS bucket to collect VPC flow logs" + type = string + default = null +} diff --git a/landing-zone-submodule/management-vpc/version.tf b/landing-zone-submodule/management-vpc/version.tf new file mode 100644 index 00000000..12ad22ab --- /dev/null +++ b/landing-zone-submodule/management-vpc/version.tf @@ -0,0 +1,3 @@ +terraform { + required_version = ">= 1.3.0" +} diff --git a/landing-zone-submodule/workload-vpc/README.md b/landing-zone-submodule/workload-vpc/README.md new file mode 100644 index 00000000..69f8eb1e --- /dev/null +++ b/landing-zone-submodule/workload-vpc/README.md @@ -0,0 +1,58 @@ +# Landing Zone workload VPC (standalone) + +This specialized submodule calls the root [landing-zone-vpc module](../..) with a preset configuration that results in a workload VPC with a topology that is identical to the workload VPC that is created by the [terraform-ibm-landing-zone module](https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone/tree/main). + +You can use this submodule when you need more modularity to create your topology than the terraform-ibm-landing-zone module provides. This submodule provides one of the building blocks for this topology. + +See the [Landing Zone example](../../examples/landing_zone/) for runnable code. + + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.3.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [workload\_vpc](#module\_workload\_vpc) | ../../ | n/a | + +## Resources + +No resources. + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [address\_prefixes](#input\_address\_prefixes) | Use `address_prefixes` only if `use_manual_address_prefixes` is true otherwise prefixes will not be created. Use only if you need to manage prefixes manually. |
object({
zone-1 = optional(list(string))
zone-2 = optional(list(string))
zone-3 = optional(list(string))
})
| `null` | no | +| [classic\_access](#input\_classic\_access) | Optionally allow VPC to access classic infrastructure network | `bool` | `null` | no | +| [create\_authorization\_policy\_vpc\_to\_cos](#input\_create\_authorization\_policy\_vpc\_to\_cos) | Set it to true if authorization policy is required for VPC to access COS | `bool` | `false` | no | +| [default\_network\_acl\_name](#input\_default\_network\_acl\_name) | Override default ACL name | `string` | `null` | no | +| [default\_routing\_table\_name](#input\_default\_routing\_table\_name) | Override default VPC routing table name | `string` | `null` | no | +| [default\_security\_group\_name](#input\_default\_security\_group\_name) | Override default VPC security group name | `string` | `null` | no | +| [default\_security\_group\_rules](#input\_default\_security\_group\_rules) | Override default security group rules |
list(
object({
name = string
direction = string
remote = string
tcp = optional(
object({
port_max = optional(number)
port_min = optional(number)
})
)
udp = optional(
object({
port_max = optional(number)
port_min = optional(number)
})
)
icmp = optional(
object({
type = optional(number)
code = optional(number)
})
)
})
)
| `[]` | no | +| [enable\_vpc\_flow\_logs](#input\_enable\_vpc\_flow\_logs) | Enable VPC Flow Logs, it will create Flow logs collector if set to true | `bool` | `false` | no | +| [existing\_cos\_bucket\_name](#input\_existing\_cos\_bucket\_name) | Name of the COS bucket to collect VPC flow logs | `string` | `null` | no | +| [existing\_cos\_instance\_guid](#input\_existing\_cos\_instance\_guid) | GUID of the COS instance to create Flow log collector | `string` | `null` | no | +| [network\_acls](#input\_network\_acls) | List of network ACLs to create with VPC |
list(
object({
name = string
add_cluster_rules = optional(bool)
rules = list(
object({
name = string
action = string
destination = string
direction = string
source = string
tcp = optional(
object({
port_max = optional(number)
port_min = optional(number)
source_port_max = optional(number)
source_port_min = optional(number)
})
)
udp = optional(
object({
port_max = optional(number)
port_min = optional(number)
source_port_max = optional(number)
source_port_min = optional(number)
})
)
icmp = optional(
object({
type = optional(number)
code = optional(number)
})
)
})
)
})
)
|
[
{
"add_ibm_cloud_internal_rules": true,
"add_vpc_connectivity_rules": true,
"name": "workload-acl",
"prepend_ibm_rules": true,
"rules": []
}
]
| no | +| [network\_cidr](#input\_network\_cidr) | Network CIDR for the VPC. This is used to manage network ACL rules for cluster provisioning. | `string` | `"10.0.0.0/8"` | no | +| [prefix](#input\_prefix) | The prefix that you would like to append to your resources | `string` | `"workload"` | no | +| [region](#input\_region) | The region to which to deploy the VPC | `string` | `"au-syd"` | no | +| [resource\_group\_id](#input\_resource\_group\_id) | The resource group ID where the VPC to be created | `string` | n/a | yes | +| [subnets](#input\_subnets) | Object for subnets to be created in each zone, each zone can have any number of subnets |
object({
zone-1 = list(object({
name = string
cidr = string
public_gateway = optional(bool)
acl_name = string
}))
zone-2 = list(object({
name = string
cidr = string
public_gateway = optional(bool)
acl_name = string
}))
zone-3 = list(object({
name = string
cidr = string
public_gateway = optional(bool)
acl_name = string
}))
})
|
{
"zone-1": [
{
"acl_name": "workload-acl",
"cidr": "10.40.10.0/24",
"name": "vsi-zone-1",
"public_gateway": false
},
{
"acl_name": "workload-acl",
"cidr": "10.40.20.0/24",
"name": "vpe-zone-1",
"public_gateway": false
}
],
"zone-2": [
{
"acl_name": "workload-acl",
"cidr": "10.50.10.0/24",
"name": "vsi-zone-2",
"public_gateway": false
},
{
"acl_name": "workload-acl",
"cidr": "10.50.20.0/24",
"name": "vpe-zone-2",
"public_gateway": false
}
],
"zone-3": [
{
"acl_name": "workload-acl",
"cidr": "10.60.10.0/24",
"name": "vsi-zone-3",
"public_gateway": false
},
{
"acl_name": "workload-acl",
"cidr": "10.60.20.0/24",
"name": "vpe-zone-3",
"public_gateway": false
}
]
}
| no | +| [tags](#input\_tags) | List of tags to apply to resources created by this module. | `list(string)` | `[]` | no | +| [use\_manual\_address\_prefixes](#input\_use\_manual\_address\_prefixes) | Optionally assign prefixes to VPC manually. By default this is false, and prefixes will be created along with subnets | `bool` | `null` | no | +| [use\_public\_gateways](#input\_use\_public\_gateways) | For each `zone` that is set to `true`, a public gateway will be created in that zone |
object({
zone-1 = optional(bool)
zone-2 = optional(bool)
zone-3 = optional(bool)
})
|
{
"zone-1": false,
"zone-2": false,
"zone-3": false
}
| no | + +## Outputs + +| Name | Description | +|------|-------------| +| [vpc\_crn](#output\_vpc\_crn) | CRN of VPC created | +| [vpc\_id](#output\_vpc\_id) | ID of VPC created | +| [vpc\_name](#output\_vpc\_name) | VPC name | + diff --git a/landing-zone-submodule/workload-vpc/main.tf b/landing-zone-submodule/workload-vpc/main.tf new file mode 100644 index 00000000..efedbe2b --- /dev/null +++ b/landing-zone-submodule/workload-vpc/main.tf @@ -0,0 +1,27 @@ +############################################################################# +# Provision VPC +############################################################################# + +module "workload_vpc" { + source = "../../" + name = "workload" + tags = var.tags + resource_group_id = var.resource_group_id + region = var.region + prefix = var.prefix + network_cidr = var.network_cidr + classic_access = var.classic_access + use_manual_address_prefixes = var.use_manual_address_prefixes + default_network_acl_name = var.default_network_acl_name + default_security_group_name = var.default_security_group_name + security_group_rules = var.default_security_group_rules == null ? [] : var.default_security_group_rules + default_routing_table_name = var.default_routing_table_name + address_prefixes = var.address_prefixes + network_acls = var.network_acls + use_public_gateways = var.use_public_gateways + subnets = var.subnets + enable_vpc_flow_logs = var.enable_vpc_flow_logs + create_authorization_policy_vpc_to_cos = var.create_authorization_policy_vpc_to_cos + existing_cos_instance_guid = var.existing_cos_instance_guid + existing_storage_bucket_name = var.existing_cos_bucket_name +} diff --git a/landing-zone-submodule/workload-vpc/outputs.tf b/landing-zone-submodule/workload-vpc/outputs.tf new file mode 100644 index 00000000..f52079f3 --- /dev/null +++ b/landing-zone-submodule/workload-vpc/outputs.tf @@ -0,0 +1,18 @@ +############################################################################## +# Outputs +############################################################################## + +output "vpc_name" { + description = "VPC name" + value = module.workload_vpc.vpc_name +} + +output "vpc_id" { + description = "ID of VPC created" + value = module.workload_vpc.vpc_id +} + +output "vpc_crn" { + description = "CRN of VPC created" + value = module.workload_vpc.vpc_crn +} diff --git a/landing-zone-submodule/workload-vpc/variables.tf b/landing-zone-submodule/workload-vpc/variables.tf new file mode 100644 index 00000000..4d74bf46 --- /dev/null +++ b/landing-zone-submodule/workload-vpc/variables.tf @@ -0,0 +1,295 @@ +variable "region" { + description = "The region to which to deploy the VPC" + type = string + default = "au-syd" +} + +variable "prefix" { + description = "The prefix that you would like to append to your resources" + type = string + default = "workload" +} + +variable "resource_group_id" { + description = "The resource group ID where the VPC to be created" + type = string +} + +variable "tags" { + description = "List of tags to apply to resources created by this module." + type = list(string) + default = [] +} + +############################################################################# +# VPC variables +############################################################################# + +variable "network_cidr" { + description = "Network CIDR for the VPC. This is used to manage network ACL rules for cluster provisioning." + type = string + default = "10.0.0.0/8" +} + +variable "classic_access" { + description = "Optionally allow VPC to access classic infrastructure network" + type = bool + default = null +} + +variable "use_manual_address_prefixes" { + description = "Optionally assign prefixes to VPC manually. By default this is false, and prefixes will be created along with subnets" + type = bool + default = null +} + +variable "default_network_acl_name" { + description = "Override default ACL name" + type = string + default = null +} + +variable "default_security_group_name" { + description = "Override default VPC security group name" + type = string + default = null +} + +variable "default_routing_table_name" { + description = "Override default VPC routing table name" + type = string + default = null +} + +variable "default_security_group_rules" { + description = "Override default security group rules" + type = list( + object({ + name = string + direction = string + remote = string + tcp = optional( + object({ + port_max = optional(number) + port_min = optional(number) + }) + ) + udp = optional( + object({ + port_max = optional(number) + port_min = optional(number) + }) + ) + icmp = optional( + object({ + type = optional(number) + code = optional(number) + }) + ) + }) + ) + + default = [] +} + +variable "address_prefixes" { + description = "Use `address_prefixes` only if `use_manual_address_prefixes` is true otherwise prefixes will not be created. Use only if you need to manage prefixes manually." + type = object({ + zone-1 = optional(list(string)) + zone-2 = optional(list(string)) + zone-3 = optional(list(string)) + }) + + default = null +} + +variable "network_acls" { + description = "List of network ACLs to create with VPC" + type = list( + object({ + name = string + add_cluster_rules = optional(bool) + rules = list( + object({ + name = string + action = string + destination = string + direction = string + source = string + tcp = optional( + object({ + port_max = optional(number) + port_min = optional(number) + source_port_max = optional(number) + source_port_min = optional(number) + }) + ) + udp = optional( + object({ + port_max = optional(number) + port_min = optional(number) + source_port_max = optional(number) + source_port_min = optional(number) + }) + ) + icmp = optional( + object({ + type = optional(number) + code = optional(number) + }) + ) + }) + ) + }) + ) + default = [ + { + name = "workload-acl" + add_ibm_cloud_internal_rules = true + add_vpc_connectivity_rules = true + prepend_ibm_rules = true + rules = [ + ## The below rules may be added to easily provide network connectivity for a loadbalancer + ## Note that opening 0.0.0.0/0 is not FsCloud compliant + # { + # name = "allow-all-443-inbound" + # action = "allow" + # direction = "inbound" + # tcp = { + + # port_min = 443 + # port_max = 443 + # source_port_min = 1024 + # source_port_max = 65535 + # } + # destination = "0.0.0.0/0" + # source = "0.0.0.0/0" + # }, + # { + # name = "allow-all-443-outbound" + # action = "allow" + # direction = "outbound" + # tcp = { + # source_port_min = 443 + # source_port_max = 443 + # port_min = 1024 + # port_max = 65535 + # } + # destination = "0.0.0.0/0" + # source = "0.0.0.0/0" + # } + ] + } + ] +} + +variable "use_public_gateways" { + description = "For each `zone` that is set to `true`, a public gateway will be created in that zone" + type = object({ + zone-1 = optional(bool) + zone-2 = optional(bool) + zone-3 = optional(bool) + }) + default = { + zone-1 = false + zone-2 = false + zone-3 = false + } +} + + +variable "subnets" { + description = "Object for subnets to be created in each zone, each zone can have any number of subnets" + type = object({ + zone-1 = list(object({ + name = string + cidr = string + public_gateway = optional(bool) + acl_name = string + })) + zone-2 = list(object({ + name = string + cidr = string + public_gateway = optional(bool) + acl_name = string + })) + zone-3 = list(object({ + name = string + cidr = string + public_gateway = optional(bool) + acl_name = string + })) + }) + default = { + "zone-1" : [ + { + "acl_name" : "workload-acl", + "cidr" : "10.40.10.0/24", + "name" : "vsi-zone-1", + "public_gateway" : false + }, + { + "acl_name" : "workload-acl", + "cidr" : "10.40.20.0/24", + "name" : "vpe-zone-1", + "public_gateway" : false + } + ], + "zone-2" : [ + { + "acl_name" : "workload-acl", + "cidr" : "10.50.10.0/24", + "name" : "vsi-zone-2", + "public_gateway" : false + }, + { + "acl_name" : "workload-acl", + "cidr" : "10.50.20.0/24", + "name" : "vpe-zone-2", + "public_gateway" : false + } + ], + "zone-3" : [ + { + "acl_name" : "workload-acl", + "cidr" : "10.60.10.0/24", + "name" : "vsi-zone-3", + "public_gateway" : false + }, + { + "acl_name" : "workload-acl", + "cidr" : "10.60.20.0/24", + "name" : "vpe-zone-3", + "public_gateway" : false + } + ] + } +} + +############################################################################# +# Variables for COS and Flow Logs +############################################################################# + +variable "enable_vpc_flow_logs" { + type = bool + description = "Enable VPC Flow Logs, it will create Flow logs collector if set to true" + default = false +} + +variable "create_authorization_policy_vpc_to_cos" { + description = "Set it to true if authorization policy is required for VPC to access COS" + type = bool + default = false +} + +variable "existing_cos_instance_guid" { + description = "GUID of the COS instance to create Flow log collector" + type = string + default = null +} + +variable "existing_cos_bucket_name" { + description = "Name of the COS bucket to collect VPC flow logs" + type = string + default = null +} diff --git a/landing-zone-submodule/workload-vpc/version.tf b/landing-zone-submodule/workload-vpc/version.tf new file mode 100644 index 00000000..12ad22ab --- /dev/null +++ b/landing-zone-submodule/workload-vpc/version.tf @@ -0,0 +1,3 @@ +terraform { + required_version = ">= 1.3.0" +} diff --git a/tests/pr_test.go b/tests/pr_test.go index 3071759d..ff6ef615 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -8,15 +8,16 @@ import ( ) const defaultExampleTerraformDir = "examples/default" +const landingZoneExampleTerraformDir = "examples/landing_zone" const resourceGroup = "geretain-test-resources" // The ACL ignores can be removed once we merge this PR (https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/pull/471) var ignoreUpdates = []string{"module.slz_vpc.ibm_is_network_acl.network_acl[\"vpc-acl\"]"} -func setupOptions(t *testing.T, prefix string) *testhelper.TestOptions { +func setupOptions(t *testing.T, prefix string, terraformDir string) *testhelper.TestOptions { options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ Testing: t, - TerraformDir: defaultExampleTerraformDir, + TerraformDir: terraformDir, Prefix: prefix, ResourceGroup: resourceGroup, IgnoreUpdates: testhelper.Exemptions{ @@ -30,7 +31,17 @@ func setupOptions(t *testing.T, prefix string) *testhelper.TestOptions { func TestRunBasicExample(t *testing.T) { t.Parallel() - options := setupOptions(t, "slz-vpc") + options := setupOptions(t, "slz-vpc", defaultExampleTerraformDir) + + output, err := options.RunTestConsistency() + assert.Nil(t, err, "This should not have errored") + assert.NotNil(t, output, "Expected some output") +} + +func TestRunLandingZoneExample(t *testing.T) { + t.Parallel() + + options := setupOptions(t, "slz", landingZoneExampleTerraformDir) output, err := options.RunTestConsistency() assert.Nil(t, err, "This should not have errored") @@ -38,11 +49,10 @@ func TestRunBasicExample(t *testing.T) { } func TestRunUpgradeBasicExample(t *testing.T) { - // Breaking change in this PR leading to next major version - skip upgrade test - t.Skip() + t.Parallel() - options := setupOptions(t, "slz-vpc-upg") + options := setupOptions(t, "slz-vpc-upg", defaultExampleTerraformDir) output, err := options.RunTestUpgrade() if !options.UpgradeTestSkipped {