From 45fbddc9a07749f9142cfe4b0138ab3731300e91 Mon Sep 17 00:00:00 2001 From: Khuzaima-Shakeel Date: Mon, 17 Nov 2025 19:10:24 +0530 Subject: [PATCH 1/7] added vpc quickstart DA --- solutions/quickstart/README.md | 3 + .../catalogValidationValues.json.template | 11 + solutions/quickstart/main.tf | 67 +++ solutions/quickstart/outputs.tf | 110 +++++ solutions/quickstart/provider.tf | 10 + solutions/quickstart/variables.tf | 456 ++++++++++++++++++ solutions/quickstart/version.tf | 14 + 7 files changed, 671 insertions(+) create mode 100644 solutions/quickstart/README.md create mode 100644 solutions/quickstart/catalogValidationValues.json.template create mode 100644 solutions/quickstart/main.tf create mode 100644 solutions/quickstart/outputs.tf create mode 100644 solutions/quickstart/provider.tf create mode 100644 solutions/quickstart/variables.tf create mode 100644 solutions/quickstart/version.tf diff --git a/solutions/quickstart/README.md b/solutions/quickstart/README.md new file mode 100644 index 00000000..0d9c0bc8 --- /dev/null +++ b/solutions/quickstart/README.md @@ -0,0 +1,3 @@ +# Cloud automation for VPC (Fully configurable) + +:exclamation: **Important:** This solution is not intended to be called by other modules because it contains a provider configuration and is not compatible with the `for_each`, `count`, and `depends_on` arguments. For more information, see [Providers Within Modules](https://developer.hashicorp.com/terraform/language/modules/develop/providers). diff --git a/solutions/quickstart/catalogValidationValues.json.template b/solutions/quickstart/catalogValidationValues.json.template new file mode 100644 index 00000000..92c260d6 --- /dev/null +++ b/solutions/quickstart/catalogValidationValues.json.template @@ -0,0 +1,11 @@ +{ + "ibmcloud_api_key": $VALIDATION_APIKEY, + "region": "us-south", + "resource_tags": $TAGS, + "existing_resource_group_name": "geretain-test-resources", + "prefix": $PREFIX, + "enable_vpc_flow_logs": true, + "kms_encryption_enabled_bucket": true, + "existing_kms_instance_crn": $HPCS_US_SOUTH_CRN, + "existing_cos_instance_crn": $COS_INSTANCE_CRN +} diff --git a/solutions/quickstart/main.tf b/solutions/quickstart/main.tf new file mode 100644 index 00000000..909c6f2e --- /dev/null +++ b/solutions/quickstart/main.tf @@ -0,0 +1,67 @@ +locals { + prefix = var.prefix != null ? (trimspace(var.prefix) != "" ? "${var.prefix}-" : "") : "" +} + +############################################################################## +# Resource Group +############################################################################## + +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.4.0" + existing_resource_group_name = var.existing_resource_group_name +} + + +############################################################################# +# Provision cloud object storage and bucket +############################################################################# + +resource "ibm_resource_instance" "cos_instance" { + name = "${var.prefix}-vpc-logs-cos" + resource_group_id = module.resource_group.resource_group_id + service = "cloud-object-storage" + plan = "standard" + location = "global" +} + +resource "ibm_cos_bucket" "cos_bucket" { + bucket_name = "${var.prefix}-vpc-logs-cos-bucket" + resource_instance_id = ibm_resource_instance.cos_instance.id + region_location = var.region + storage_class = "standard" +} + +############################################################################# +# VPC +############################################################################# + +locals { + # create 'use_public_gateways' object + public_gateway_object = { + for key, value in var.subnets : key => value != null ? length([for sub in value : sub.public_gateway if sub.public_gateway]) > 0 ? [for sub in value : sub.public_gateway if sub.public_gateway][0] : false : false + } +} + +# Create VPC +module "vpc" { + source = "../../" + resource_group_id = module.resource_group.resource_group_id + region = var.region + create_vpc = true + name = var.vpc_name + prefix = local.prefix != "" ? trimspace(var.prefix) : null + tags = var.resource_tags + access_tags = var.access_tags + subnets = var.subnets + network_acls = var.network_acls + security_group_rules = var.security_group_rules + clean_default_sg_acl = var.clean_default_security_group_acl + use_public_gateways = local.public_gateway_object + address_prefixes = var.address_prefixes + routes = var.routes + enable_vpc_flow_logs = var.enable_vpc_flow_logs + create_authorization_policy_vpc_to_cos = !var.skip_vpc_cos_iam_auth_policy + existing_cos_instance_guid = var.enable_vpc_flow_logs ? ibm_resource_instance.cos_instance[0].guid : null + existing_storage_bucket_name = var.enable_vpc_flow_logs ? ibm_cos_bucket.cos_bucket[0].bucket_name : null +} diff --git a/solutions/quickstart/outputs.tf b/solutions/quickstart/outputs.tf new file mode 100644 index 00000000..8532f465 --- /dev/null +++ b/solutions/quickstart/outputs.tf @@ -0,0 +1,110 @@ +############################################################################## +# VPC +############################################################################## + +output "vpc_name" { + description = "Name of the VPC created." + value = module.vpc.vpc_name +} + +output "vpc_id" { + description = "ID of the VPC created." + value = module.vpc.vpc_id +} + +output "vpc_crn" { + description = "CRN of the VPC created." + value = module.vpc.vpc_crn +} + +############################################################################## +# Public Gateways +############################################################################## + +output "public_gateways" { + description = "Map of the public gateways by zone." + value = module.vpc.public_gateways +} + +############################################################################## +# VPC flow logs +############################################################################## + +output "vpc_flow_logs" { + description = "Details of the VPC flow logs collector." + value = module.vpc.vpc_flow_logs +} + +############################################################################## +# Network ACLs +############################################################################## + +output "network_acls" { + description = "List of shortnames and IDs of network ACLs." + value = module.vpc.network_acls +} + +############################################################################## +# Subnet Outputs +############################################################################## + +output "subnet_ids" { + description = "The IDs of the subnets." + value = module.vpc.subnet_ids +} + +output "private_path_subnet_id" { + description = "The IDs of the subnets." + value = length(module.vpc.subnet_ids) > 0 ? module.vpc.subnet_ids[0] : null +} + +output "subnet_detail_list" { + description = "A list of subnets containing names, CIDR blocks, and zones." + value = module.vpc.subnet_detail_list +} + +output "subnet_zone_list" { + description = "A list of subnet IDs and subnet zones." + value = module.vpc.subnet_zone_list +} + +output "subnet_detail_map" { + description = "A map of subnets containing IDs, CIDR blocks, and zones." + value = module.vpc.subnet_detail_map +} + + + +############################################################################## +# Security Group Details +############################################################################## + +output "security_group_details" { + description = "Details of security group." + value = module.vpc.security_group_details +} + +output "next_steps_text" { + value = "Your Virtual Private Cloud is ready." + description = "Next steps text" +} + +output "next_step_primary_label" { + value = "Go to Virtual Private Cloud" + description = "Primary label" +} + +output "next_step_primary_url" { + value = "https://cloud.ibm.com/infrastructure/network/vpc/${var.region}~${module.vpc.vpc_id}/overview" + description = "Primary URL" +} + +output "next_step_secondary_label" { + value = "Virtual Private Cloud overview page" + description = "Secondary label" +} + +output "next_step_secondary_url" { + value = "https://cloud.ibm.com/docs/vpc?topic=vpc-about-vpc" + description = "Secondary URL" +} diff --git a/solutions/quickstart/provider.tf b/solutions/quickstart/provider.tf new file mode 100644 index 00000000..17cfc770 --- /dev/null +++ b/solutions/quickstart/provider.tf @@ -0,0 +1,10 @@ +######################################################################################################################## +# Provider config +######################################################################################################################## + +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region + visibility = var.provider_visibility + private_endpoint_type = (var.provider_visibility == "private" && var.region == "ca-mon") ? "vpe" : null +} diff --git a/solutions/quickstart/variables.tf b/solutions/quickstart/variables.tf new file mode 100644 index 00000000..02715d57 --- /dev/null +++ b/solutions/quickstart/variables.tf @@ -0,0 +1,456 @@ +############################################################################## +# Input Variables +############################################################################## + +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API key to deploy resources." + sensitive = true +} + +variable "provider_visibility" { + description = "Set the visibility value for the IBM terraform provider. Supported values are `public`, `private`, `public-and-private`. [Learn more](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/guides/custom-service-endpoints)." + type = string + default = "private" + + validation { + condition = contains(["public", "private", "public-and-private"], var.provider_visibility) + error_message = "Invalid visibility option. Allowed values are 'public', 'private', or 'public-and-private'." + } +} + +variable "existing_resource_group_name" { + type = string + description = "The name of an existing resource group to provision the resources. [Learn more](https://cloud.ibm.com/docs/account?topic=account-rgs&interface=ui#create_rgs) about how to create a resource group." + default = "Default" +} + +variable "prefix" { + type = string + nullable = true + description = "The prefix to add to all resources that this solution creates (e.g `prod`, `test`, `dev`). To skip using a prefix, set this value to null or an empty string. [Learn more](https://terraform-ibm-modules.github.io/documentation/#/prefix.md)." + + validation { + # - null and empty string is allowed + # - Must not contain consecutive hyphens (--): length(regexall("--", var.prefix)) == 0 + # - Starts with a lowercase letter: [a-z] + # - Contains only lowercase letters (a–z), digits (0–9), and hyphens (-) + # - Must not end with a hyphen (-): [a-z0-9] + condition = (var.prefix == null || var.prefix == "" ? true : + alltrue([ + can(regex("^[a-z][-a-z0-9]*[a-z0-9]$", var.prefix)), + length(regexall("--", var.prefix)) == 0 + ]) + ) + error_message = "Prefix must begin with a lowercase letter and may contain only lowercase letters, digits, and hyphens '-'. It must not end with a hyphen('-'), and cannot contain consecutive hyphens ('--')." + } + + validation { + # must not exceed 16 characters in length + condition = var.prefix == null || var.prefix == "" ? true : length(var.prefix) <= 16 + error_message = "Prefix must not exceed 16 characters." + } +} + +variable "vpc_name" { + default = "vpc" + description = "Name of the VPC. If a prefix input variable is specified, the prefix is added to the name in the `-` format." + type = string +} + +variable "region" { + type = string + description = "The region to provision all resources in. [Learn more](https://terraform-ibm-modules.github.io/documentation/#/region) about how to select different regions for different services." + default = "us-south" +} + +variable "resource_tags" { + type = list(string) + description = "The list of tags to add to the VPC instance." + default = [] +} + +variable "access_tags" { + type = list(string) + description = "The list of access tags to add to the VPC instance." + default = [] +} + +############################################################################## +# Subnets +############################################################################## + +variable "subnets" { + description = "List of subnets for the vpc. For each item in each array, a subnet will be created. Items can be either CIDR blocks or total ipv4 addresses. Public gateways will be enabled only in zones where a gateway has been created. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/blob/main/solutions/fully-configurable/DA-types.md#subnets-)." + type = object({ + zone-1 = list(object({ + name = string + cidr = string + public_gateway = optional(bool) + acl_name = string + no_addr_prefix = optional(bool, false) # do not automatically add address prefix for subnet, overrides other conditions if set to true + subnet_tags = optional(list(string), []) + })) + zone-2 = optional(list(object({ + name = string + cidr = string + public_gateway = optional(bool) + acl_name = string + no_addr_prefix = optional(bool, false) # do not automatically add address prefix for subnet, overrides other conditions if set to true + subnet_tags = optional(list(string), []) + }))) + zone-3 = optional(list(object({ + name = string + cidr = string + public_gateway = optional(bool) + acl_name = string + no_addr_prefix = optional(bool, false) # do not automatically add address prefix for subnet, overrides other conditions if set to true + subnet_tags = optional(list(string), []) + }))) + }) + + default = { + zone-1 = [ + { + name = "subnet-a" + cidr = "10.10.10.0/24" + public_gateway = true + acl_name = "vpc-acl" + no_addr_prefix = false + } + ], + zone-2 = [ + { + name = "subnet-b" + cidr = "10.20.10.0/24" + public_gateway = true + acl_name = "vpc-acl" + no_addr_prefix = false + } + ], + zone-3 = [ + { + name = "subnet-c" + cidr = "10.30.10.0/24" + public_gateway = true + acl_name = "vpc-acl" + no_addr_prefix = false + } + ] + } + + validation { + condition = alltrue([for key, value in var.subnets : value != null ? length([for subnet in value : subnet.public_gateway if subnet.public_gateway]) > 1 ? false : true : true]) + error_message = "var.subnets has more than one public gateway in a zone. Only one public gateway can be attached to a zone for the virtual private cloud." + } +} + +############################################################################## +# Network ACLs +############################################################################## + +variable "network_acls" { + description = "The list of ACLs to create. Provide at least one rule for each ACL. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/blob/main/solutions/fully-configurable/DA-types.md#network-acls-)." + type = list( + object({ + name = string + add_ibm_cloud_internal_rules = optional(bool) + add_vpc_connectivity_rules = optional(bool) + prepend_ibm_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 = "vpc-acl" + add_ibm_cloud_internal_rules = true + add_vpc_connectivity_rules = true + prepend_ibm_rules = true + rules = [ + { + name = "allow-all-443-inbound" + action = "allow" + direction = "inbound" + tcp = { + port_min = 443 + port_max = 443 + source_port_min = 443 + source_port_max = 443 + } + destination = "0.0.0.0/0" + source = "0.0.0.0/0" + }, + { + name = "allow-all-80-inbound" + action = "allow" + direction = "inbound" + tcp = { + port_min = 80 + port_max = 80 + source_port_min = 80 + source_port_max = 80 + } + destination = "0.0.0.0/0" + source = "0.0.0.0/0" + }, + { + name = "allow-all-22-inbound" + action = "allow" + direction = "inbound" + tcp = { + port_min = 22 + port_max = 22 + source_port_min = 22 + source_port_max = 22 + } + 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 = 443 + port_max = 443 + } + destination = "0.0.0.0/0" + source = "0.0.0.0/0" + }, + { + name = "allow-all-80-outbound" + action = "allow" + direction = "outbound" + tcp = { + source_port_min = 80 + source_port_max = 80 + port_min = 80 + port_max = 80 + } + destination = "0.0.0.0/0" + source = "0.0.0.0/0" + }, + { + name = "allow-all-22-outbound" + action = "allow" + direction = "outbound" + tcp = { + source_port_min = 22 + source_port_max = 22 + port_min = 22 + port_max = 22 + } + destination = "0.0.0.0/0" + source = "0.0.0.0/0" + } + ] + } + ] + + validation { + error_message = "ACL rule actions can only be `allow` or `deny`." + condition = length(distinct( + flatten([ + # Check through rules + for rule in flatten([var.network_acls[*].rules]) : + # Return false action is not valid + false if !contains(["allow", "deny"], rule.action) + ]) + )) == 0 + } + + validation { + error_message = "ACL rule direction can only be `inbound` or `outbound`." + condition = length(distinct( + flatten([ + # Check through rules + for rule in flatten([var.network_acls[*].rules]) : + # Return false if direction is not valid + false if !contains(["inbound", "outbound"], rule.direction) + ]) + )) == 0 + } + + validation { + error_message = "ACL rule names must match the regex pattern ^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$." + condition = length(distinct( + flatten([ + # Check through rules + for rule in flatten([var.network_acls[*].rules]) : + # Return false if direction is not valid + false if !can(regex("^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$", rule.name)) + ]) + )) == 0 + } + +} + +############################################################################## +# Default Security Group Rules +############################################################################## + +variable "security_group_rules" { + description = "A list of security group rules to be added to the default vpc security group (default empty). [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/blob/main/solutions/fully-configurable/DA-types.md#security-group-rules-)." + default = [] + type = list( + object({ + name = string + direction = string + remote = optional(string) + local = optional(string) + ip_version = optional(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) + }) + ) + }) + ) + + validation { + error_message = "Security group rule direction can only be `inbound` or `outbound`." + condition = (var.security_group_rules == null || length(var.security_group_rules) == 0) ? true : length(distinct( + flatten([ + # Check through rules + for rule in var.security_group_rules : + # Return false if direction is not valid + false if !contains(["inbound", "outbound"], rule.direction) + ]) + )) == 0 + } + + validation { + error_message = "Security group rule names must match the regex pattern ^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$." + condition = (var.security_group_rules == null || length(var.security_group_rules) == 0) ? true : length(distinct( + flatten([ + # Check through rules + for rule in var.security_group_rules : + # Return false if direction is not valid + false if !can(regex("^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$", rule.name)) + ]) + )) == 0 + } +} + +variable "clean_default_security_group_acl" { + description = "Remove all rules from the default VPC security group and VPC ACL (less permissive)." + type = bool + nullable = false + default = true +} + +############################################################################## +# Address Prefixes +############################################################################## + +variable "address_prefixes" { + description = "The IP range that will be defined for the VPC for a certain location. Use only with manual address prefixes. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/blob/main/solutions/fully-configurable/DA-types.md#address-prefixes-)." + type = object({ + zone-1 = optional(list(string)) + zone-2 = optional(list(string)) + zone-3 = optional(list(string)) + }) + default = { + zone-1 = null + zone-2 = null + zone-3 = null + } + validation { + error_message = "Keys for `use_public_gateways` must be in the order `zone-1`, `zone-2`, `zone-3`." + condition = var.address_prefixes == null ? true : ( + (length(var.address_prefixes) == 1 && keys(var.address_prefixes)[0] == "zone-1") || + (length(var.address_prefixes) == 2 && keys(var.address_prefixes)[0] == "zone-1" && keys(var.address_prefixes)[1] == "zone-2") || + (length(var.address_prefixes) == 3 && keys(var.address_prefixes)[0] == "zone-1" && keys(var.address_prefixes)[1] == "zone-2") && keys(var.address_prefixes)[2] == "zone-3" + ) + } +} + +############################################################################## +# Add routes to VPC +############################################################################## + +variable "routes" { + description = "Allows you to specify the next hop for packets based on their destination address. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/blob/main/solutions/fully-configurable/DA-types.md#routes-)." + type = list( + object({ + name = string + route_direct_link_ingress = optional(bool) + route_transit_gateway_ingress = optional(bool) + route_vpc_zone_ingress = optional(bool) + routes = optional( + list( + object({ + action = optional(string) + zone = number + destination = string + next_hop = string + }) + )) + }) + ) + default = [] +} + +############################################################################## +# VPC Flow Logs +############################################################################## + +variable "enable_vpc_flow_logs" { + description = "To enable VPC Flow logs, set this to true." + type = bool + nullable = false + default = false +} + +variable "skip_vpc_cos_iam_auth_policy" { + description = "To skip creating an IAM authorization policy that allows the VPC to access the Cloud Object Storage, set this variable to `true`. Required only if `enable_vpc_flow_logs` is set to true." + type = bool + nullable = false + default = false +} \ No newline at end of file diff --git a/solutions/quickstart/version.tf b/solutions/quickstart/version.tf new file mode 100644 index 00000000..2b774858 --- /dev/null +++ b/solutions/quickstart/version.tf @@ -0,0 +1,14 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + # Lock DA into an exact provider version - renovate automation will keep it updated + ibm = { + source = "IBM-Cloud/ibm" + version = "1.80.0" + } + time = { + source = "hashicorp/time" + version = "0.13.1" + } + } +} From 6e1016fac07247b794f03c0933bfabd17d8333db Mon Sep 17 00:00:00 2001 From: Khuzaima-Shakeel Date: Sun, 23 Nov 2025 20:33:26 +0530 Subject: [PATCH 2/7] init commit --- ibm_catalog.json | 188 +++++++++ ...deployable-architecture-quickstart-vpc.svg | 4 + solutions/quickstart/README.md | 2 +- .../catalogValidationValues.json.template | 6 +- solutions/quickstart/main.tf | 170 +++++++-- solutions/quickstart/outputs.tf | 34 +- solutions/quickstart/variables.tf | 360 +----------------- solutions/quickstart/version.tf | 4 - tests/pr_test.go | 69 ++++ 9 files changed, 404 insertions(+), 433 deletions(-) create mode 100644 reference-architectures/deployable-architecture-quickstart-vpc.svg diff --git a/ibm_catalog.json b/ibm_catalog.json index 9476b6f5..fa7aab27 100644 --- a/ibm_catalog.json +++ b/ibm_catalog.json @@ -768,6 +768,194 @@ "dependency_version_2": true, "terraform_version": "1.12.2", "ignore_readme": true + }, + { + "label": "QuickStart - Basic and Simple", + "name": "quickstart", + "index": 2, + "install_type": "fullstack", + "working_directory": "solutions/quickstart", + "release_notes_url": "https://cloud.ibm.com/docs/secure-infrastructure-vpc?topic=secure-infrastructure-vpc-secure-infrastructure-vpc-relnotes", + "architecture": { + "features": [ + { + "title": " ", + "description": "Ideal for users new to IBM Cloud or Virtual Private Cloud (VPC) who want to get started without configuring underlying infrastructure." + }, + { + "title": " ", + "description": "A lightweight, experimental configuration for quickly provisioning Virtual Private Cloud (VPC) instances on IBM Cloud." + } + ], + "diagrams": [ + { + "diagram": { + "caption": "Virtual Private Cloud topology - Quickstart (Basic and simple)", + "url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/main/reference-architectures/deployable-architecture-quickstart-vpc.svg", + "type": "image/svg+xml" + }, + "description": "This architecture provisions and configures a Virtual Private Cloud (VPC) environment. This basic configuration creates a VPC with one subnet in each zone (three subnets total) and applies a predefined network ACL." + } + ] + }, + "iam_permissions": [ + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Viewer" + ], + "service_name": "Resource group only", + "notes": "Viewer access is required in the resource group you want to provision in." + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Administrator" + ], + "service_name": "All Account Management services", + "notes": "[Optional] Required to create new resource groups when enabling the Account Configuration integration." + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Administrator" + ], + "service_name": "All Identity and Access enabled services", + "notes": "[Optional] Required to to create trusted profile for App Configuration aggregator which is used for compliance scanning." + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Administrator" + ], + "service_name": "is.vpc", + "notes": "Required to create Virtual Private Cloud(VPC)" + }, + { + "service_name": "cloud-object-storage", + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "notes": "[Optional] Required if VPC Flow Logs are enabled." + } + ], + "configuration": [ + { + "key": "ibmcloud_api_key" + }, + { + "key": "prefix", + "required": true, + "default_value": "dev", + "random_string": { + "length": 4 + }, + "value_constraints": [ + { + "type": "regex", + "description": "Prefix must begin with a lowercase letter and may contain only lowercase letters, digits, and hyphens '-'. It must not end with a hyphen('-'), and cannot contain consecutive hyphens ('--'). It should not exceed 16 characters.", + "value": "^$|^__NULL__$|^[a-z](?!.*--)(?:[a-z0-9-]{0,14}[a-z0-9])?$" + } + ] + }, + { + "key": "region", + "required": true, + "custom_config": { + "config_constraints": { + "generationType": "2" + }, + "grouping": "deployment", + "original_grouping": "deployment", + "type": "vpc_region" + } + }, + { + "key": "vpc_name", + "required": true + }, + { + "key": "existing_resource_group_name", + "display_name": "resource_group", + "custom_config": { + "type": "resource_group", + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "identifier": "rg_name" + } + }, + "default_value": "Default", + "description": "The name of an existing resource group to provision the resources." + }, + { + "key": "skip_vpc_cos_iam_auth_policy" + }, + { + "key": "network_acls", + "description": "Select a predefined ACL profile for the VPC. Options: open (allow all inbound/outbound), common (allow SSH/HTTP/HTTPS with IBM internal + VPC connectivity), ibm-internal (only IBM internal + VPC connectivity), closed (fully restricted).", + "options": [ + { + "displayname": "open", + "value": "open" + }, + { + "displayname": "common", + "value": "common" + }, + { + "displayname": "ibm-internal", + "value": "ibm-internal" + }, + { + "displayname": "closed", + "value": "closed" + } + ] + }, + { + "key": "resource_tags", + "custom_config": { + "type": "array", + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "type": "string" + } + } + }, + { + "key": "access_tags", + "custom_config": { + "type": "array", + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "type": "string" + } + } + }, + { + "key": "enable_vpc_flow_logs" + }, + { + "key": "provider_visibility", + "hidden": true, + "options": [ + { + "displayname": "private", + "value": "private" + }, + { + "displayname": "public", + "value": "public" + }, + { + "displayname": "public-and-private", + "value": "public-and-private" + } + ] + } + ], + "terraform_version": "1.12.2", + "ignore_readme": true } ] } diff --git a/reference-architectures/deployable-architecture-quickstart-vpc.svg b/reference-architectures/deployable-architecture-quickstart-vpc.svg new file mode 100644 index 00000000..cbcb5d32 --- /dev/null +++ b/reference-architectures/deployable-architecture-quickstart-vpc.svg @@ -0,0 +1,4 @@ + + + +
ACL
locked
IBM Cloud
Region
Resource GroupFlow Logs bucket
[Optional] Object Storage 
VPC
Zone 3
Zone 2
Zone 1
ACL
Subnet
\ No newline at end of file diff --git a/solutions/quickstart/README.md b/solutions/quickstart/README.md index 0d9c0bc8..f687026c 100644 --- a/solutions/quickstart/README.md +++ b/solutions/quickstart/README.md @@ -1,3 +1,3 @@ -# Cloud automation for VPC (Fully configurable) +# Cloud automation for VPC (Quickstart) :exclamation: **Important:** This solution is not intended to be called by other modules because it contains a provider configuration and is not compatible with the `for_each`, `count`, and `depends_on` arguments. For more information, see [Providers Within Modules](https://developer.hashicorp.com/terraform/language/modules/develop/providers). diff --git a/solutions/quickstart/catalogValidationValues.json.template b/solutions/quickstart/catalogValidationValues.json.template index 92c260d6..41ce44f9 100644 --- a/solutions/quickstart/catalogValidationValues.json.template +++ b/solutions/quickstart/catalogValidationValues.json.template @@ -3,9 +3,5 @@ "region": "us-south", "resource_tags": $TAGS, "existing_resource_group_name": "geretain-test-resources", - "prefix": $PREFIX, - "enable_vpc_flow_logs": true, - "kms_encryption_enabled_bucket": true, - "existing_kms_instance_crn": $HPCS_US_SOUTH_CRN, - "existing_cos_instance_crn": $COS_INSTANCE_CRN + "prefix": $PREFIX } diff --git a/solutions/quickstart/main.tf b/solutions/quickstart/main.tf index 909c6f2e..da2598b8 100644 --- a/solutions/quickstart/main.tf +++ b/solutions/quickstart/main.tf @@ -17,51 +17,149 @@ module "resource_group" { # Provision cloud object storage and bucket ############################################################################# -resource "ibm_resource_instance" "cos_instance" { - name = "${var.prefix}-vpc-logs-cos" - resource_group_id = module.resource_group.resource_group_id - service = "cloud-object-storage" - plan = "standard" - location = "global" +module "cos" { + count = var.enable_vpc_flow_logs ? 1 : 0 + source = "terraform-ibm-modules/cos/ibm" + version = "10.5.8" + resource_group_id = module.resource_group.resource_group_id + region = var.region + cos_instance_name = "${var.prefix}-cos" + cos_tags = var.resource_tags + bucket_name = "${var.prefix}-bucket" + kms_encryption_enabled = false } -resource "ibm_cos_bucket" "cos_bucket" { - bucket_name = "${var.prefix}-vpc-logs-cos-bucket" - resource_instance_id = ibm_resource_instance.cos_instance.id - region_location = var.region - storage_class = "standard" +########################################################################### +# NETWORK ACL PROFILES +########################################################################### + +locals { + acl_profiles = { + open = [ + { + name = "${local.prefix}acl" + add_ibm_cloud_internal_rules = false + add_vpc_connectivity_rules = false + prepend_ibm_rules = false + rules = [ + { + name = "allow-all-inbound" + action = "allow" + direction = "inbound" + source = "0.0.0.0/0" + destination = "0.0.0.0/0" + }, + { + name = "allow-all-outbound" + action = "allow" + direction = "outbound" + source = "0.0.0.0/0" + destination = "0.0.0.0/0" + } + ] + } + ] + common = [ + { + name = "${local.prefix}acl" + add_ibm_cloud_internal_rules = true + add_vpc_connectivity_rules = true + prepend_ibm_rules = true + rules = [ + { + name = "allow-ssh" + action = "allow" + direction = "inbound" + source = "0.0.0.0/0" + destination = "0.0.0.0/0" + tcp = { port_min = 22, port_max = 22 } + }, + { + name = "allow-https" + action = "allow" + direction = "inbound" + source = "0.0.0.0/0" + destination = "0.0.0.0/0" + tcp = { port_min = 443, port_max = 443 } + }, + { + name = "allow-http" + action = "allow" + direction = "inbound" + source = "0.0.0.0/0" + destination = "0.0.0.0/0" + tcp = { port_min = 80, port_max = 80 } + } + ] + } + ] + ibm-internal = [ + { + name = "${local.prefix}acl" + add_ibm_cloud_internal_rules = true + add_vpc_connectivity_rules = true + prepend_ibm_rules = true + rules = [] + } + ] + closed = [ + { + name = "${local.prefix}acl" + add_ibm_cloud_internal_rules = false + add_vpc_connectivity_rules = false + prepend_ibm_rules = false + rules = [] + } + ] + } + network_acls = lookup(local.acl_profiles, var.network_acls, local.acl_profiles["common"]) } ############################################################################# # VPC ############################################################################# -locals { - # create 'use_public_gateways' object - public_gateway_object = { - for key, value in var.subnets : key => value != null ? length([for sub in value : sub.public_gateway if sub.public_gateway]) > 0 ? [for sub in value : sub.public_gateway if sub.public_gateway][0] : false : false - } -} - -# Create VPC module "vpc" { - source = "../../" - resource_group_id = module.resource_group.resource_group_id - region = var.region - create_vpc = true - name = var.vpc_name - prefix = local.prefix != "" ? trimspace(var.prefix) : null - tags = var.resource_tags - access_tags = var.access_tags - subnets = var.subnets - network_acls = var.network_acls - security_group_rules = var.security_group_rules - clean_default_sg_acl = var.clean_default_security_group_acl - use_public_gateways = local.public_gateway_object - address_prefixes = var.address_prefixes - routes = var.routes + source = "../../" + resource_group_id = module.resource_group.resource_group_id + region = var.region + create_vpc = true + name = var.vpc_name + prefix = local.prefix != "" ? trimspace(var.prefix) : null + tags = var.resource_tags + access_tags = var.access_tags + subnets = { + zone-1 = [ + { + name = "${local.prefix}subnet-a" + cidr = "10.10.10.0/24" + public_gateway = true + acl_name = "${local.prefix}acl" + no_addr_prefix = false + } + ] + zone-2 = [ + { + name = "${local.prefix}subnet-b" + cidr = "10.20.10.0/24" + public_gateway = true + acl_name = "${local.prefix}acl" + no_addr_prefix = false + } + ] + zone-3 = [ + { + name = "${local.prefix}subnet-c" + cidr = "10.30.10.0/24" + public_gateway = true + acl_name = "${local.prefix}acl" + no_addr_prefix = false + } + ] + } + network_acls = local.network_acls enable_vpc_flow_logs = var.enable_vpc_flow_logs create_authorization_policy_vpc_to_cos = !var.skip_vpc_cos_iam_auth_policy - existing_cos_instance_guid = var.enable_vpc_flow_logs ? ibm_resource_instance.cos_instance[0].guid : null - existing_storage_bucket_name = var.enable_vpc_flow_logs ? ibm_cos_bucket.cos_bucket[0].bucket_name : null + existing_cos_instance_guid = var.enable_vpc_flow_logs ? module.cos[0].cos_instance_guid : null + existing_storage_bucket_name = var.enable_vpc_flow_logs ? module.cos[0].bucket_name : null } diff --git a/solutions/quickstart/outputs.tf b/solutions/quickstart/outputs.tf index 8532f465..a4b930a7 100644 --- a/solutions/quickstart/outputs.tf +++ b/solutions/quickstart/outputs.tf @@ -17,31 +17,9 @@ output "vpc_crn" { value = module.vpc.vpc_crn } -############################################################################## -# Public Gateways -############################################################################## - -output "public_gateways" { - description = "Map of the public gateways by zone." - value = module.vpc.public_gateways -} - -############################################################################## -# VPC flow logs -############################################################################## - -output "vpc_flow_logs" { - description = "Details of the VPC flow logs collector." - value = module.vpc.vpc_flow_logs -} - -############################################################################## -# Network ACLs -############################################################################## - output "network_acls" { description = "List of shortnames and IDs of network ACLs." - value = module.vpc.network_acls + value = local.network_acls } ############################################################################## @@ -74,16 +52,6 @@ output "subnet_detail_map" { } - -############################################################################## -# Security Group Details -############################################################################## - -output "security_group_details" { - description = "Details of security group." - value = module.vpc.security_group_details -} - output "next_steps_text" { value = "Your Virtual Private Cloud is ready." description = "Next steps text" diff --git a/solutions/quickstart/variables.tf b/solutions/quickstart/variables.tf index 02715d57..2c9dea1d 100644 --- a/solutions/quickstart/variables.tf +++ b/solutions/quickstart/variables.tf @@ -76,367 +76,19 @@ variable "access_tags" { default = [] } -############################################################################## -# Subnets -############################################################################## - -variable "subnets" { - description = "List of subnets for the vpc. For each item in each array, a subnet will be created. Items can be either CIDR blocks or total ipv4 addresses. Public gateways will be enabled only in zones where a gateway has been created. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/blob/main/solutions/fully-configurable/DA-types.md#subnets-)." - type = object({ - zone-1 = list(object({ - name = string - cidr = string - public_gateway = optional(bool) - acl_name = string - no_addr_prefix = optional(bool, false) # do not automatically add address prefix for subnet, overrides other conditions if set to true - subnet_tags = optional(list(string), []) - })) - zone-2 = optional(list(object({ - name = string - cidr = string - public_gateway = optional(bool) - acl_name = string - no_addr_prefix = optional(bool, false) # do not automatically add address prefix for subnet, overrides other conditions if set to true - subnet_tags = optional(list(string), []) - }))) - zone-3 = optional(list(object({ - name = string - cidr = string - public_gateway = optional(bool) - acl_name = string - no_addr_prefix = optional(bool, false) # do not automatically add address prefix for subnet, overrides other conditions if set to true - subnet_tags = optional(list(string), []) - }))) - }) - - default = { - zone-1 = [ - { - name = "subnet-a" - cidr = "10.10.10.0/24" - public_gateway = true - acl_name = "vpc-acl" - no_addr_prefix = false - } - ], - zone-2 = [ - { - name = "subnet-b" - cidr = "10.20.10.0/24" - public_gateway = true - acl_name = "vpc-acl" - no_addr_prefix = false - } - ], - zone-3 = [ - { - name = "subnet-c" - cidr = "10.30.10.0/24" - public_gateway = true - acl_name = "vpc-acl" - no_addr_prefix = false - } - ] - } - - validation { - condition = alltrue([for key, value in var.subnets : value != null ? length([for subnet in value : subnet.public_gateway if subnet.public_gateway]) > 1 ? false : true : true]) - error_message = "var.subnets has more than one public gateway in a zone. Only one public gateway can be attached to a zone for the virtual private cloud." - } -} - ############################################################################## # Network ACLs ############################################################################## variable "network_acls" { - description = "The list of ACLs to create. Provide at least one rule for each ACL. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/blob/main/solutions/fully-configurable/DA-types.md#network-acls-)." - type = list( - object({ - name = string - add_ibm_cloud_internal_rules = optional(bool) - add_vpc_connectivity_rules = optional(bool) - prepend_ibm_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 = "vpc-acl" - add_ibm_cloud_internal_rules = true - add_vpc_connectivity_rules = true - prepend_ibm_rules = true - rules = [ - { - name = "allow-all-443-inbound" - action = "allow" - direction = "inbound" - tcp = { - port_min = 443 - port_max = 443 - source_port_min = 443 - source_port_max = 443 - } - destination = "0.0.0.0/0" - source = "0.0.0.0/0" - }, - { - name = "allow-all-80-inbound" - action = "allow" - direction = "inbound" - tcp = { - port_min = 80 - port_max = 80 - source_port_min = 80 - source_port_max = 80 - } - destination = "0.0.0.0/0" - source = "0.0.0.0/0" - }, - { - name = "allow-all-22-inbound" - action = "allow" - direction = "inbound" - tcp = { - port_min = 22 - port_max = 22 - source_port_min = 22 - source_port_max = 22 - } - 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 = 443 - port_max = 443 - } - destination = "0.0.0.0/0" - source = "0.0.0.0/0" - }, - { - name = "allow-all-80-outbound" - action = "allow" - direction = "outbound" - tcp = { - source_port_min = 80 - source_port_max = 80 - port_min = 80 - port_max = 80 - } - destination = "0.0.0.0/0" - source = "0.0.0.0/0" - }, - { - name = "allow-all-22-outbound" - action = "allow" - direction = "outbound" - tcp = { - source_port_min = 22 - source_port_max = 22 - port_min = 22 - port_max = 22 - } - destination = "0.0.0.0/0" - source = "0.0.0.0/0" - } - ] - } - ] - - validation { - error_message = "ACL rule actions can only be `allow` or `deny`." - condition = length(distinct( - flatten([ - # Check through rules - for rule in flatten([var.network_acls[*].rules]) : - # Return false action is not valid - false if !contains(["allow", "deny"], rule.action) - ]) - )) == 0 - } - - validation { - error_message = "ACL rule direction can only be `inbound` or `outbound`." - condition = length(distinct( - flatten([ - # Check through rules - for rule in flatten([var.network_acls[*].rules]) : - # Return false if direction is not valid - false if !contains(["inbound", "outbound"], rule.direction) - ]) - )) == 0 - } - - validation { - error_message = "ACL rule names must match the regex pattern ^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$." - condition = length(distinct( - flatten([ - # Check through rules - for rule in flatten([var.network_acls[*].rules]) : - # Return false if direction is not valid - false if !can(regex("^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$", rule.name)) - ]) - )) == 0 - } - -} - -############################################################################## -# Default Security Group Rules -############################################################################## - -variable "security_group_rules" { - description = "A list of security group rules to be added to the default vpc security group (default empty). [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/blob/main/solutions/fully-configurable/DA-types.md#security-group-rules-)." - default = [] - type = list( - object({ - name = string - direction = string - remote = optional(string) - local = optional(string) - ip_version = optional(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) - }) - ) - }) - ) - - validation { - error_message = "Security group rule direction can only be `inbound` or `outbound`." - condition = (var.security_group_rules == null || length(var.security_group_rules) == 0) ? true : length(distinct( - flatten([ - # Check through rules - for rule in var.security_group_rules : - # Return false if direction is not valid - false if !contains(["inbound", "outbound"], rule.direction) - ]) - )) == 0 - } - - validation { - error_message = "Security group rule names must match the regex pattern ^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$." - condition = (var.security_group_rules == null || length(var.security_group_rules) == 0) ? true : length(distinct( - flatten([ - # Check through rules - for rule in var.security_group_rules : - # Return false if direction is not valid - false if !can(regex("^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$", rule.name)) - ]) - )) == 0 - } -} - -variable "clean_default_security_group_acl" { - description = "Remove all rules from the default VPC security group and VPC ACL (less permissive)." - type = bool - nullable = false - default = true -} - -############################################################################## -# Address Prefixes -############################################################################## - -variable "address_prefixes" { - description = "The IP range that will be defined for the VPC for a certain location. Use only with manual address prefixes. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/blob/main/solutions/fully-configurable/DA-types.md#address-prefixes-)." - type = object({ - zone-1 = optional(list(string)) - zone-2 = optional(list(string)) - zone-3 = optional(list(string)) - }) - default = { - zone-1 = null - zone-2 = null - zone-3 = null - } + description = "Predefined ACL profile options: open (allow all inbound and outbound traffic), common (allow SSH 22, HTTP 80, HTTPS 443, and includes IBM internal + VPC connectivity rules), ibm-internal (only IBM internal and VPC connectivity rules; no customer inbound traffic), closed (fully restricted; no inbound or outbound traffic)." + type = string + default = "common" validation { - error_message = "Keys for `use_public_gateways` must be in the order `zone-1`, `zone-2`, `zone-3`." - condition = var.address_prefixes == null ? true : ( - (length(var.address_prefixes) == 1 && keys(var.address_prefixes)[0] == "zone-1") || - (length(var.address_prefixes) == 2 && keys(var.address_prefixes)[0] == "zone-1" && keys(var.address_prefixes)[1] == "zone-2") || - (length(var.address_prefixes) == 3 && keys(var.address_prefixes)[0] == "zone-1" && keys(var.address_prefixes)[1] == "zone-2") && keys(var.address_prefixes)[2] == "zone-3" - ) + condition = contains(["open", "common", "ibm-internal", "closed"], var.network_acls) + error_message = "Valid values: open, common, ibm-internal, closed." } } - -############################################################################## -# Add routes to VPC -############################################################################## - -variable "routes" { - description = "Allows you to specify the next hop for packets based on their destination address. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/blob/main/solutions/fully-configurable/DA-types.md#routes-)." - type = list( - object({ - name = string - route_direct_link_ingress = optional(bool) - route_transit_gateway_ingress = optional(bool) - route_vpc_zone_ingress = optional(bool) - routes = optional( - list( - object({ - action = optional(string) - zone = number - destination = string - next_hop = string - }) - )) - }) - ) - default = [] -} - ############################################################################## # VPC Flow Logs ############################################################################## @@ -453,4 +105,4 @@ variable "skip_vpc_cos_iam_auth_policy" { type = bool nullable = false default = false -} \ No newline at end of file +} diff --git a/solutions/quickstart/version.tf b/solutions/quickstart/version.tf index 2b774858..0d77e06a 100644 --- a/solutions/quickstart/version.tf +++ b/solutions/quickstart/version.tf @@ -6,9 +6,5 @@ terraform { source = "IBM-Cloud/ibm" version = "1.80.0" } - time = { - source = "hashicorp/time" - version = "0.13.1" - } } } diff --git a/tests/pr_test.go b/tests/pr_test.go index f850375f..6b31a1ae 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -35,6 +35,7 @@ const specificZoneExampleTerraformDir = "examples/specific-zone-only" const noprefixExampleTerraformDir = "examples/no-prefix" const vpcWithDnsExampleTerraformDir = "examples/vpc-with-dns" const fullyConfigFlavorDir = "solutions/fully-configurable" +const quickStartFlavorDir = "solutions/quickstart" const resourceGroup = "geretain-test-resources" const yamlLocation = "../common-dev-assets/common-go-assets/common-permanent-resources.yaml" const terraformVersion = "terraform_v1.12.2" // This should match the version in the ibm_catalog.json @@ -238,6 +239,74 @@ func TestFullyConfigurable(t *testing.T) { assert.Nil(t, err, "This should not have errored") } +func TestQuickstartDefaultConfigSchematics(t *testing.T) { + t.Parallel() + + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Region: "eu-de", + Prefix: "vpc-qs", + TarIncludePatterns: []string{ + "*.tf", + "dynamic_values/*.tf", + "dynamic_values/config_modules/*/*.tf", + quickStartFlavorDir + "/*.tf", + }, + TemplateFolder: quickStartFlavorDir, + Tags: []string{"vpc-qs-test"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 120, + TerraformVersion: terraformVersion, + }) + + options.TerraformVars = []testschematic.TestSchematicTerraformVar{ + {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, + {Name: "existing_resource_group_name", Value: resourceGroup, DataType: "string"}, + {Name: "region", Value: options.Region, DataType: "string"}, + {Name: "resource_tags", Value: options.Tags, DataType: "list(string)"}, + {Name: "access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, + {Name: "prefix", Value: options.Prefix, DataType: "string"}, + } + + err := options.RunSchematicTest() + assert.Nil(t, err, "This should not have errored") +} + +func TestQuickstartDefaultConfigUpgradeSchematics(t *testing.T) { + t.Parallel() + + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Region: "us-south", + Prefix: "vpc-qs", + TarIncludePatterns: []string{ + "*.tf", + "dynamic_values/*.tf", + "dynamic_values/config_modules/*/*.tf", + quickStartFlavorDir + "/*.tf", + }, + TemplateFolder: quickStartFlavorDir, + Tags: []string{"vpc-qs-test"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 120, + TerraformVersion: terraformVersion, + }) + + options.TerraformVars = []testschematic.TestSchematicTerraformVar{ + {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, + {Name: "existing_resource_group_name", Value: resourceGroup, DataType: "string"}, + {Name: "region", Value: options.Region, DataType: "string"}, + {Name: "resource_tags", Value: options.Tags, DataType: "list(string)"}, + {Name: "access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, + {Name: "prefix", Value: options.Prefix, DataType: "string"}, + } + + err := options.RunSchematicUpgradeTest() + if !options.UpgradeTestSkipped { + assert.Nil(t, err, "This should not have errored") + } +} + func validateEnvVariable(t *testing.T, varName string) string { val, present := os.LookupEnv(varName) require.True(t, present, "%s environment variable not set", varName) From 7252064bc4233a934721f1891e9774fc65739730 Mon Sep 17 00:00:00 2001 From: Khuzaima-Shakeel Date: Sun, 23 Nov 2025 21:42:49 +0530 Subject: [PATCH 3/7] SKIP UPGRADE TEST: upgrade test failing due to breaking change From eea457955cd60790c3a2a9eaae8f3e256cbd1dee Mon Sep 17 00:00:00 2001 From: Khuzaima-Shakeel Date: Thu, 27 Nov 2025 19:23:17 +0530 Subject: [PATCH 4/7] resolve review comments --- ibm_catalog.json | 5 +++-- solutions/quickstart/README.md | 2 +- solutions/quickstart/variables.tf | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ibm_catalog.json b/ibm_catalog.json index 4cae41e7..fb077b3a 100644 --- a/ibm_catalog.json +++ b/ibm_catalog.json @@ -776,7 +776,7 @@ "ignore_readme": true }, { - "label": "QuickStart - Basic and Simple", + "label": "QuickStart - Basic and simple", "name": "quickstart", "index": 2, "install_type": "fullstack", @@ -892,7 +892,8 @@ "description": "The name of an existing resource group to provision the resources." }, { - "key": "skip_vpc_cos_iam_auth_policy" + "key": "skip_vpc_cos_iam_auth_policy", + "hidden": true }, { "key": "network_acls", diff --git a/solutions/quickstart/README.md b/solutions/quickstart/README.md index f687026c..334b1b52 100644 --- a/solutions/quickstart/README.md +++ b/solutions/quickstart/README.md @@ -1,3 +1,3 @@ -# Cloud automation for VPC (Quickstart) +# Cloud foundation for VPC (Quickstart) :exclamation: **Important:** This solution is not intended to be called by other modules because it contains a provider configuration and is not compatible with the `for_each`, `count`, and `depends_on` arguments. For more information, see [Providers Within Modules](https://developer.hashicorp.com/terraform/language/modules/develop/providers). diff --git a/solutions/quickstart/variables.tf b/solutions/quickstart/variables.tf index 2c9dea1d..56a24afc 100644 --- a/solutions/quickstart/variables.tf +++ b/solutions/quickstart/variables.tf @@ -60,7 +60,7 @@ variable "vpc_name" { variable "region" { type = string - description = "The region to provision all resources in. [Learn more](https://terraform-ibm-modules.github.io/documentation/#/region) about how to select different regions for different services." + description = "The region to provision all resources in." default = "us-south" } From f3531a4b7bcc760b24d3afcdd15aaff95dbc8d1d Mon Sep 17 00:00:00 2001 From: Khuzaima-Shakeel Date: Tue, 2 Dec 2025 18:33:33 +0530 Subject: [PATCH 5/7] Updated PR as per discussion --- .catalog-onboard-pipeline.yaml | 3 ++ ibm_catalog.json | 82 ++++++++++++++++++++----------- solutions/quickstart/main.tf | 18 +++++-- solutions/quickstart/outputs.tf | 18 +++++++ solutions/quickstart/variables.tf | 4 +- 5 files changed, 89 insertions(+), 36 deletions(-) diff --git a/.catalog-onboard-pipeline.yaml b/.catalog-onboard-pipeline.yaml index 1d84f733..f796bf85 100644 --- a/.catalog-onboard-pipeline.yaml +++ b/.catalog-onboard-pipeline.yaml @@ -13,3 +13,6 @@ offerings: instance_id: 1c7d5f78-9262-44c3-b779-b28fe4d88c37 region: us-south scope_resource_group_var_name: existing_resource_group_name + - name: quickstart + mark_ready: true + install_type: fullstack diff --git a/ibm_catalog.json b/ibm_catalog.json index fb077b3a..279b5911 100644 --- a/ibm_catalog.json +++ b/ibm_catalog.json @@ -63,6 +63,7 @@ "flavors": [ { "label": "Standard - Integrated setup with configurable services", + "short_description": "Provides a flexible yet reliable starting point, offering full control over VPC architecture parameters with well-chosen defaults so users can deploy a functional VPC environment without manual configuration.", "name": "fully-configurable", "index": 1, "install_type": "fullstack", @@ -777,6 +778,7 @@ }, { "label": "QuickStart - Basic and simple", + "short_description": "Ideal for users new to IBM Cloud or Virtual Private Cloud (VPC) who want to get started without configuring underlying infrastructure.", "name": "quickstart", "index": 2, "install_type": "fullstack", @@ -846,6 +848,20 @@ { "key": "ibmcloud_api_key" }, + { + "key": "existing_resource_group_name", + "display_name": "resource_group", + "custom_config": { + "type": "resource_group", + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "identifier": "rg_name" + } + }, + "default_value": "Default", + "description": "The name of an existing resource group to provision the resources." + }, { "key": "prefix", "required": true, @@ -878,42 +894,32 @@ "required": true }, { - "key": "existing_resource_group_name", - "display_name": "resource_group", - "custom_config": { - "type": "resource_group", - "grouping": "deployment", - "original_grouping": "deployment", - "config_constraints": { - "identifier": "rg_name" - } - }, - "default_value": "Default", - "description": "The name of an existing resource group to provision the resources." - }, - { - "key": "skip_vpc_cos_iam_auth_policy", - "hidden": true - }, - { - "key": "network_acls", - "description": "Select a predefined ACL profile for the VPC. Options: open (allow all inbound/outbound), common (allow SSH/HTTP/HTTPS with IBM internal + VPC connectivity), ibm-internal (only IBM internal + VPC connectivity), closed (fully restricted).", + "key": "network_profile", + "type": "string", + "displayname": "Network Profile", + "required": true, + "default_value": "common", + "description": "Select the predefined network profile that controls allowed traffic and security posture for the VPC.", "options": [ { - "displayname": "open", - "value": "open" + "displayname": "Open", + "value": "open", + "description": "Allows all inbound and outbound traffic. Suitable for testing or unrestricted workloads." }, { - "displayname": "common", - "value": "common" + "displayname": "Standard", + "value": "common", + "description": "Allows standard access (SSH, HTTP, HTTPS) and includes IBM internal + VPC connectivity rules. Recommended default for most workloads." }, { - "displayname": "ibm-internal", - "value": "ibm-internal" + "displayname": "IBM Internal", + "value": "ibm-internal", + "description": "Blocks all customer inbound traffic. Only IBM internal and VPC connectivity traffic is allowed." }, { - "displayname": "closed", - "value": "closed" + "displayname": "Closed", + "value": "closed", + "description": "Fully restricted network profile. No inbound or outbound traffic allowed." } ] }, @@ -940,7 +946,25 @@ } }, { - "key": "enable_vpc_flow_logs" + "key": "enable_vpc_flow_logs", + "type": "boolean", + "type_metadata": "boolean", + "options": [ + { + "description": "Do not collect VPC network traffic metadata.", + "displayname": "False", + "value": false + }, + { + "description": "Collect and store IP traffic metadata from VPC network interfaces to Cloud Object Storage for monitoring, security analysis, and troubleshooting.", + "displayname": "True", + "value": true + } + ] + }, + { + "key": "skip_vpc_cos_iam_auth_policy", + "hidden": true }, { "key": "provider_visibility", diff --git a/solutions/quickstart/main.tf b/solutions/quickstart/main.tf index da2598b8..564daff1 100644 --- a/solutions/quickstart/main.tf +++ b/solutions/quickstart/main.tf @@ -112,7 +112,9 @@ locals { } ] } - network_acls = lookup(local.acl_profiles, var.network_acls, local.acl_profiles["common"]) + network_acls = lookup(local.acl_profiles, var.network_profile, local.acl_profiles["common"]) + clean_default_sg_acl = contains(["ibm-internal", "closed"], var.network_profile) + allow_public_gateway = contains(["open", "common"], var.network_profile) } ############################################################################# @@ -133,7 +135,7 @@ module "vpc" { { name = "${local.prefix}subnet-a" cidr = "10.10.10.0/24" - public_gateway = true + public_gateway = local.allow_public_gateway acl_name = "${local.prefix}acl" no_addr_prefix = false } @@ -142,7 +144,7 @@ module "vpc" { { name = "${local.prefix}subnet-b" cidr = "10.20.10.0/24" - public_gateway = true + public_gateway = local.allow_public_gateway acl_name = "${local.prefix}acl" no_addr_prefix = false } @@ -151,13 +153,19 @@ module "vpc" { { name = "${local.prefix}subnet-c" cidr = "10.30.10.0/24" - public_gateway = true + public_gateway = local.allow_public_gateway acl_name = "${local.prefix}acl" no_addr_prefix = false } ] } - network_acls = local.network_acls + network_acls = local.network_acls + clean_default_sg_acl = local.clean_default_sg_acl + use_public_gateways = { + zone-1 = local.allow_public_gateway + zone-2 = local.allow_public_gateway + zone-3 = local.allow_public_gateway + } enable_vpc_flow_logs = var.enable_vpc_flow_logs create_authorization_policy_vpc_to_cos = !var.skip_vpc_cos_iam_auth_policy existing_cos_instance_guid = var.enable_vpc_flow_logs ? module.cos[0].cos_instance_guid : null diff --git a/solutions/quickstart/outputs.tf b/solutions/quickstart/outputs.tf index a4b930a7..2280b4a4 100644 --- a/solutions/quickstart/outputs.tf +++ b/solutions/quickstart/outputs.tf @@ -51,6 +51,24 @@ output "subnet_detail_map" { value = module.vpc.subnet_detail_map } +############################################################################## +# Public Gateways +############################################################################## + +output "public_gateways" { + description = "Map of the public gateways by zone." + value = module.vpc.public_gateways +} + + +############################################################################## +# Security Group Details +############################################################################## + +output "security_group_details" { + description = "Details of security group." + value = module.vpc.security_group_details +} output "next_steps_text" { value = "Your Virtual Private Cloud is ready." diff --git a/solutions/quickstart/variables.tf b/solutions/quickstart/variables.tf index 56a24afc..008d6ac5 100644 --- a/solutions/quickstart/variables.tf +++ b/solutions/quickstart/variables.tf @@ -80,12 +80,12 @@ variable "access_tags" { # Network ACLs ############################################################################## -variable "network_acls" { +variable "network_profile" { description = "Predefined ACL profile options: open (allow all inbound and outbound traffic), common (allow SSH 22, HTTP 80, HTTPS 443, and includes IBM internal + VPC connectivity rules), ibm-internal (only IBM internal and VPC connectivity rules; no customer inbound traffic), closed (fully restricted; no inbound or outbound traffic)." type = string default = "common" validation { - condition = contains(["open", "common", "ibm-internal", "closed"], var.network_acls) + condition = contains(["open", "common", "ibm-internal", "closed"], var.network_profile) error_message = "Valid values: open, common, ibm-internal, closed." } } From 76df8dd0c3b64bddf339038c4d230e70004b5973 Mon Sep 17 00:00:00 2001 From: Khuzaima-Shakeel Date: Tue, 2 Dec 2025 19:07:44 +0530 Subject: [PATCH 6/7] added readme.md --- .../deploy-arch-ibm-vpc-quickstart-vpc.md | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 reference-architectures/deploy-arch-ibm-vpc-quickstart-vpc.md diff --git a/reference-architectures/deploy-arch-ibm-vpc-quickstart-vpc.md b/reference-architectures/deploy-arch-ibm-vpc-quickstart-vpc.md new file mode 100644 index 00000000..b02ad809 --- /dev/null +++ b/reference-architectures/deploy-arch-ibm-vpc-quickstart-vpc.md @@ -0,0 +1,148 @@ +--- + +copyright: + years: 2025 +lastupdated: "2025-12-02" + +keywords: + +subcollection: deployable-reference-architectures + +authors: + - name: "Khuzaima Shakeel" + +# The release that the reference architecture describes +version: 1.0.0 + +# Whether the reference architecture is published to Cloud Docs production. +# When set to false, the file is available only in staging. Default is false. +production: true + +# Use if the reference architecture has deployable code. +# Value is the URL to land the user in the IBM Cloud catalog details page +# for the deployable architecture. +# See https://test.cloud.ibm.com/docs/get-coding?topic=get-coding-deploy-button +deployment-url: https://cloud.ibm.com/catalog/architecture/deploy-arch-ibm-slz-vpc-9fc0fa64-27af-4fed-9dce-47b3640ba739-global + +docs: https://cloud.ibm.com/docs/secure-infrastructure-vpc + +image_source: https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone-vpc/blob/main/reference-architectures/deployable-architecture-quickstart-vpc.svg + +related_links: + - title: "Cloud foundation for VPC (Standard - Integrated setup with configurable services)" + url: "https://cloud.ibm.com/docs/deployable-reference-architectures?topic=deployable-reference-architectures-vpc-fully-configurable" + description: "A deployable architecture that provides full control over VPC networking, security, and connectivity components." + - title: "Cloud foundation for VPC (Standard - Financial Services edition)" + url: "https://cloud.ibm.com/docs/deployable-reference-architectures?topic=deployable-reference-architectures-vpc-ra" + description: "A VPC architecture based on IBM Cloud for Financial Services controls." + +use-case: Foundational Infrastructure +compliance: None + +content-type: reference-architecture + +--- + +{{site.data.keyword.attribute-definition-list}} + +# Cloud foundation for VPC – QuickStart (Basic and simple) +{: #vpc-quickstart-ra} +{: toc-content-type="reference-architecture"} +{: toc-industry="CrossIndustry"} +{: toc-use-case="Foundational Infrastructure"} +{: toc-version="1.0.0"} + +The QuickStart variation provides a **simple and beginner-friendly** Virtual Private Cloud (VPC) deployment that requires minimal configuration. It helps users rapidly create a functional network environment on IBM Cloud without needing to define subnets, ACL rules, security groups, or connectivity patterns manually. +This variation is ideal for users who want a **basic VPC setup**, lightweight networking defaults, and the option to enable VPC Flow Logs. + +Unlike the fully configurable architecture, QuickStart intentionally limits complexity and deploys a clean, minimal network footprint that can be used as a stepping stone toward more advanced architectures. + +--- + +## Architecture diagram +{: #ra-vpc-quickstart-architecture} + +![Architecture diagram for the QuickStart variation of Cloud foundation for VPC](deployable-architecture-quickstart-vpc.svg "QuickStart VPC architecture"){: caption="QuickStart VPC architecture" caption-side="bottom"}{: external download="deployable-architecture-quickstart-vpc.svg"} + +--- + +## Design requirements +{: #ra-vpc-quickstart-design-requirements} + +![Design requirements for QuickStart VPC](heat-map-deploy-arch-slz-vpc-quickstart.svg "Design requirements"){: caption="Scope of the QuickStart design requirements" caption-side="bottom"} + +--- + +## Components +{: #ra-vpc-quickstart-components} + +### VPC architecture decisions +{: #ra-vpc-quickstart-components-arch} + +| Requirement | Component | Reasons for choice | Alternative | +|------------|-----------|--------------------|-------------| +| *Provide a basic, ready-to-use VPC with minimal inputs* | Predefined VPC | Deploys a VPC quickly without requiring the user to design the network | Use the fully configurable variation for deeper customization | +| *Create availability-zone redundancy* | Fixed three-zone subnets | One subnet per zone to ensure multi-AZ coverage | Single-zone deployment (not recommended) | +| *Basic traffic governance* | Network profile selector (open, common, ibm-internal, closed) | Users can choose the desired security posture without written ACL rules | Manually writing custom ACLs | + +{: caption="VPC architecture decisions" caption-side="bottom"} + +--- + +### Networking and connectivity decisions +{: #ra-vpc-quickstart-components-connectivity} + +| Requirement | Component | Reason | Alternative | +|------------|-----------|--------|-------------| +| *Optional access to the internet* | Public gateways per zone (automatic) | Enabled only for `open` and `common` profiles | No public gateways for locked-down profiles | +| *Subnet-level traffic control* | Network ACL profiles | Simplifies security without requiring rule definitions | Fully customizable ACLs (in advanced variation) | +| *Instance-level default security* | Default VPC security group | Automatically cleaned when selecting restrictive profiles (`ibm-internal`, `closed`) | Custom security group rules | + +{: caption="Networking and connectivity decisions" caption-side="bottom"} + +--- + +### Simplicity and user experience decisions +{: #ra-vpc-quickstart-components-simplicity} + +| Requirement | Component | Reasons | Alternative | +|------------|-----------|---------|-------------| +| *Zero-effort deployment* | Predefined subnets + ACL mapping | Users only pick prefix, region, and profile | Manual subnet planning | +| *Clear security posture options* | User-friendly “Network Profile” widget | Shows descriptions and recommendations | Plain text input | +| *Simple observability integration* | Optional VPC Flow Logs → COS | Enabled via toggle | External log collectors | + +{: caption="Simplicity decision points" caption-side="bottom"} + +--- + +## Key features +{: #ra-vpc-quickstart-features} + +### Core VPC Setup +- Automatically creates a new VPC with IBM-recommended defaults +- Deploys **one subnet per zone** (three total) + +### Built-in Network Profiles +- **Open** – Unrestricted +- **Standard (Common)** – SSH/HTTP/HTTPS + IBM internal rules +- **IBM Internal** – No inbound customer traffic +- **Closed** – Fully restricted + +### Security & Network Defaults +- ACLs applied according to selected network profile +- Security group automatically cleaned for restrictive profiles +- Public gateways created only when allowed by the profile + +### Optional Flow Logs +- Enable VPC Flow Logs to create a COS instance and bucket automatically + + +--- + + From 996d5b6f90aa3b2044e6e3bd55dad5a989209c06 Mon Sep 17 00:00:00 2001 From: Khuzaima-Shakeel Date: Tue, 2 Dec 2025 21:38:44 +0530 Subject: [PATCH 7/7] fix pre-commit --- .../deploy-arch-ibm-vpc-quickstart-vpc.md | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/reference-architectures/deploy-arch-ibm-vpc-quickstart-vpc.md b/reference-architectures/deploy-arch-ibm-vpc-quickstart-vpc.md index b02ad809..a91505b6 100644 --- a/reference-architectures/deploy-arch-ibm-vpc-quickstart-vpc.md +++ b/reference-architectures/deploy-arch-ibm-vpc-quickstart-vpc.md @@ -52,7 +52,7 @@ content-type: reference-architecture {: toc-use-case="Foundational Infrastructure"} {: toc-version="1.0.0"} -The QuickStart variation provides a **simple and beginner-friendly** Virtual Private Cloud (VPC) deployment that requires minimal configuration. It helps users rapidly create a functional network environment on IBM Cloud without needing to define subnets, ACL rules, security groups, or connectivity patterns manually. +The QuickStart variation provides a **simple and beginner-friendly** Virtual Private Cloud (VPC) deployment that requires minimal configuration. It helps users rapidly create a functional network environment on IBM Cloud without needing to define subnets, ACL rules, security groups, or connectivity patterns manually. This variation is ideal for users who want a **basic VPC setup**, lightweight networking defaults, and the option to enable VPC Flow Logs. Unlike the fully configurable architecture, QuickStart intentionally limits complexity and deploys a clean, minimal network footprint that can be used as a stepping stone toward more advanced architectures. @@ -119,22 +119,22 @@ Unlike the fully configurable architecture, QuickStart intentionally limits comp {: #ra-vpc-quickstart-features} ### Core VPC Setup -- Automatically creates a new VPC with IBM-recommended defaults +- Automatically creates a new VPC with IBM-recommended defaults - Deploys **one subnet per zone** (three total) ### Built-in Network Profiles -- **Open** – Unrestricted -- **Standard (Common)** – SSH/HTTP/HTTPS + IBM internal rules -- **IBM Internal** – No inbound customer traffic -- **Closed** – Fully restricted +- **Open** – Unrestricted +- **Standard (Common)** – SSH/HTTP/HTTPS + IBM internal rules +- **IBM Internal** – No inbound customer traffic +- **Closed** – Fully restricted ### Security & Network Defaults -- ACLs applied according to selected network profile -- Security group automatically cleaned for restrictive profiles -- Public gateways created only when allowed by the profile +- ACLs applied according to selected network profile +- Security group automatically cleaned for restrictive profiles +- Public gateways created only when allowed by the profile ### Optional Flow Logs -- Enable VPC Flow Logs to create a COS instance and bucket automatically +- Enable VPC Flow Logs to create a COS instance and bucket automatically --- @@ -145,4 +145,3 @@ Unlike the fully configurable architecture, QuickStart intentionally limits comp TODO: Decide what next steps to list, if any Optional section. Include links to your deployment guide or next steps to get started with the architecture. --> -