diff --git a/README.md b/README.md index 07dc843..c8cfbd2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # VPC Private-path External Connectivity -[![Incubating (Not yet consumable)](https://img.shields.io/badge/status-Incubating%20(Not%20yet%20consumable)-red)](https://terraform-ibm-modules.github.io/documentation/#/badge-status) +[![Graduated (Supported)](https://img.shields.io/badge/Status-Graduated%20(Supported)-brightgreen)](https://terraform-ibm-modules.github.io/documentation/#/badge-status) [![latest release](https://img.shields.io/github/v/release/terraform-ibm-modules/sample-deployable-architectures?logo=GitHub&sort=semver)](https://github.com/terraform-ibm-modules/sample-deployable-architectures/releases/latest) [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) [![Renovate enabled](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://renovatebot.com/) diff --git a/ibm_catalog.json b/ibm_catalog.json index d9f7973..4208c17 100644 --- a/ibm_catalog.json +++ b/ibm_catalog.json @@ -65,6 +65,7 @@ "name": "fully-configurable", "index": 1, "install_type": "fullstack", + "short_description": "Ideal for users who want flexibility with a reliable starting point.", "working_directory": "solutions/fully-configurable", "iam_permissions": [ { @@ -142,6 +143,14 @@ ], "service_name": "atracker", "notes": "[Optional] Required when enabling the Activity Tracker Event Routing." + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Administrator", + "crn:v1:bluemix:public:iam::::serviceRole:Manager" + ], + "service_name": "secrets-manager", + "notes": "[optional] Required for creating an Secrets Manager instance." } ], "configuration": [ @@ -149,6 +158,9 @@ "key": "ibmcloud_api_key" }, { + "key": "region", + "required": true, + "type": "string", "custom_config": { "config_constraints": { "generationType": "2" @@ -156,9 +168,7 @@ "grouping": "deployment", "original_grouping": "deployment", "type": "vpc_region" - }, - "key": "region", - "required": true + } }, { "key": "prefix", @@ -176,43 +186,38 @@ ] }, { - "key": "enable_platform_metrics", - "type": "string", - "default_value": true, - "description": "When set to `true`, the IBM Cloud Monitoring instance will be configured to collect platform metrics from the provided region. You can configure 1 instance only of the IBM Cloud Monitoring service per region to collect platform metrics in that location. Check with the account or service administrator if another monitoring instance has already been configured. You may not have permissions to see all monitoring instances in the region. [Learn more](https://cloud.ibm.com/docs/monitoring?topic=monitoring-platform_metrics_enabling).", - "required": true, - "virtual": true - }, - { - "key": "logs_routing_tenant_regions", - "type": "list(string)", - "default_value": [], - "description": "To manage platform logs that are generated by IBM Cloud services in a region of IBM Cloud, you must create a tenant in each region that you operate. Pass a list of regions to create a tenant in. [Learn more](https://cloud.ibm.com/docs/logs-router?topic=logs-router-about-platform-logs).", - "required": true, - "virtual": true, + "key": "existing_resource_group_name", + "display_name": "resource_group", "custom_config": { + "type": "resource_group", "grouping": "deployment", "original_grouping": "deployment", "config_constraints": { - "type": "string" + "identifier": "rg_name" } } }, { - "key": "existing_resource_group_name", - "display_name": "resource_group", + "key": "existing_vpc_crn", + "display_name": "existing_vpc", + "value_constraints": [ + { + "type": "regex", + "description": "The value provided for 'existing_vpc_crn' is not valid.", + "value": "^crn:(.*:){3}is:(.*:){2}:vpc:[0-9a-z]{4}-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" + } + ], "custom_config": { - "type": "resource_group", + "type": "platform_resource", "grouping": "deployment", "original_grouping": "deployment", "config_constraints": { - "identifier": "rg_name" + "resourceType": "vpc", + "selection": "single_select", + "valueType": "crn" } } }, - { - "key": "existing_vpc_id" - }, { "key": "existing_subnet_id", "required": true, @@ -291,7 +296,15 @@ "key": "network_loadbalancer_name" }, { - "key": "application_loadbalancer_listener_certificate_instance" + "key": "application_loadbalancer_listener_certificate_crn", + "required": true, + "value_constraints": [ + { + "type": "regex", + "description": "The value provided for 'application_loadbalancer_listener_certificate_crn' is not valid.", + "value": "^__NULL__$|^crn:(.*:){3}secrets-manager:(.*:){2}[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}:secret:[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}$" + } + ] }, { "key": "application_loadbalancer_listener_idle_timeout" @@ -300,10 +313,34 @@ "key": "application_loadbalancer_listener_port" }, { - "key": "application_loadbalancer_listener_protocol" + "key": "application_loadbalancer_listener_protocol", + "options": [ + { + "displayname": "HTTPS", + "value": "https" + }, + { + "displayname": "TCP", + "value": "tcp" + }, + { + "displayname": "UDP", + "value": "udp" + } + ] }, { - "key": "application_loadbalancer_pool_algorithm" + "key": "application_loadbalancer_pool_algorithm", + "options": [ + { + "displayname": "Round Robin", + "value": "round_robin" + }, + { + "displayname": "Weighted Round Robin", + "value": "weighted_round_robin" + } + ] }, { "key": "application_loadbalancer_pool_health_delay" @@ -315,7 +352,17 @@ "key": "application_loadbalancer_pool_health_timeout" }, { - "key": "application_loadbalancer_pool_health_type" + "key": "application_loadbalancer_pool_health_type", + "options": [ + { + "displayname": "HTTPS", + "value": "https" + }, + { + "displayname": "TCP", + "value": "tcp" + } + ] }, { "key": "application_loadbalancer_pool_member_ip_address" @@ -324,7 +371,21 @@ "key": "application_loadbalancer_pool_member_port" }, { - "key": "application_loadbalancer_pool_protocol" + "key": "application_loadbalancer_pool_protocol", + "options": [ + { + "displayname": "HTTPS", + "value": "https" + }, + { + "displayname": "TCP", + "value": "tcp" + }, + { + "displayname": "UDP", + "value": "udp" + } + ] }, { "key": "application_loadbalancer_type", @@ -364,11 +425,107 @@ "key": "network_loadbalancer_pool_health_timeout" }, { - "key": "network_loadbalancer_pool_health_type" + "key": "network_loadbalancer_pool_health_type", + "options": [ + { + "displayname": "HTTPS", + "value": "https" + }, + { + "displayname": "TCP", + "value": "tcp" + } + ] }, { "key": "network_loadbalancer_pool_member_port" }, + { + "key": "secrets_manager_service_plan", + "type_metadata": "string", + "virtual": true, + "default_value": "standard", + "options": [ + { + "description": "Centrally manage secrets in a dedicated instance. Unlimited Secrets Manager instances per IBM Cloud account. Unlimited access to all service capabilities.", + "displayname": "Standard", + "value": "standard" + }, + { + "description": "Choose this option only if your account does not already have a Trial plan instance. Only one Trial plan instance is allowed per account. After it expires, upgrade to the Standard plan to keep using Secrets Manager.", + "displayname": "Trial (30 days)", + "value": "trial" + } + ] + }, + { + "key": "existing_secrets_manager_instance_crn", + "required": false, + "value_constraints": [ + { + "type": "regex", + "description": "The value provided for 'existing_secrets_manager_instance_crn' is not valid.", + "value": "^crn:(.*:){3}secrets-manager:(.*:){2}[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}::$" + } + ] + }, + { + "key": "existing_secrets_manager_secret_group_id" + }, + { + "key": "private_cert_engine_config_root_ca_common_name" + }, + { + "key": "private_cert_engine_config_template_name" + }, + { + "key": "root_ca_name", + "type": "string", + "required": false, + "virtual": true, + "default_value": "root-ca", + "description": "The name of the Root Certificate Authority you want to create." + }, + { + "key": "root_ca_common_name", + "type": "string", + "required": false, + "virtual": true, + "default_value": "example-root.com", + "description": "The common name of the Root Certificate Authority you want to create." + }, + { + "key": "intermediate_ca_name", + "type": "string", + "required": false, + "virtual": true, + "default_value": "intermediate-ca", + "description": "The name of the Intermediate Certificate Authority you want to create." + }, + { + "key": "intermediate_ca_common_name", + "type": "string", + "required": false, + "virtual": true, + "default_value": "example-int.com", + "description": "The common name of the Intermediate Certificate Authority you want to create." + }, + { + "key": "certificate_template_name", + "type": "string", + "required": false, + "virtual": true, + "default_value": "template", + "description": "The name of the Certificate Template you want to create." + }, + { + "key": "template_max_ttl", + "type": "string", + "required": false, + "virtual": true, + "default_value": "8760h", + "description": "Max TTL for the certificate template you want to create." + }, { "key": "provider_visibility", "hidden": true, @@ -386,6 +543,43 @@ "value": "public-and-private" } ] + }, + { + "key": "subnets", + "default_value": "{\n zone-1 = [\n {\n name = \"subnet-a\"\n cidr = \"10.10.10.0/24\"\n public_gateway = true\n acl_name = \"vpc-acl\"\n no_addr_prefix = false\n }\n ],\n zone-2 = [\n {\n name = \"subnet-b\"\n cidr = \"10.20.10.0/24\"\n public_gateway = true\n acl_name = \"vpc-acl\"\n no_addr_prefix = false\n }\n ],\n zone-3 = [\n {\n name = \"subnet-c\"\n cidr = \"10.30.10.0/24\"\n public_gateway = true\n acl_name = \"vpc-acl\"\n no_addr_prefix = false\n }\n ]\n }", + "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-).", + "required": false, + "virtual": true, + "hidden": true, + "custom_config": { + "grouping": "deployment", + "original_grouping": "deployment", + "type": "code_editor", + "config_constraints": { + "supportedLanguages": [ + "hcl" + ] + } + } + }, + { + "key": "network_acls", + "type": "list(object)", + "default_value": "[\n { name = \"vpc-acl\"\n add_ibm_cloud_internal_rules = true\n add_vpc_connectivity_rules = true\n prepend_ibm_rules = true\n rules = [\n {\n name = \"allow-443-inbound-source\"\n action = \"allow\"\n direction = \"inbound\"\n tcp = {\n source_port_min = 443\n source_port_max = 443\n }\n destination = \"0.0.0.0/0\"\n source = \"0.0.0.0/0\"\n },\n {\n name = \"allow-443-inbound-dest\"\n action = \"allow\"\n direction = \"inbound\"\n tcp = {\n port_max = 443\n port_min = 443\n }\n destination = \"0.0.0.0/0\"\n source = \"0.0.0.0/0\"\n },\n {\n name = \"allow-all-80-inbound\"\n action = \"allow\"\n direction = \"inbound\"\n tcp = {\n source_port_min = 80\n source_port_max = 80\n }\n destination = \"0.0.0.0/0\"\n source = \"0.0.0.0/0\"\n },\n {\n name = \"allow-all-ingress-inbound\"\n action = \"allow\"\n direction = \"inbound\"\n tcp = {\n source_port_min = 30000\n source_port_max = 32767\n }\n destination = \"0.0.0.0/0\"\n source = \"0.0.0.0/0\"\n },\n {\n name = \"allow-443-outbound-source\"\n action = \"allow\"\n direction = \"outbound\"\n tcp = {\n source_port_min = 443\n source_port_max = 443\n }\n destination = \"0.0.0.0/0\"\n source = \"0.0.0.0/0\"\n },\n {\n name = \"allow-443-outbound-dest\"\n action = \"allow\"\n direction = \"outbound\"\n tcp = {\n port_min = 443\n port_max = 443\n }\n destination = \"0.0.0.0/0\"\n source = \"0.0.0.0/0\"\n },\n {\n name = \"allow-all-80-outbound\"\n action = \"allow\"\n direction = \"outbound\"\n tcp = {\n port_min = 80\n port_max = 80\n }\n destination = \"0.0.0.0/0\"\n source = \"0.0.0.0/0\"\n },\n {\n name = \"allow-all-ingress-outbound\"\n action = \"allow\"\n direction = \"outbound\"\n tcp = {\n port_min = 30000\n port_max = 32767\n }\n destination = \"0.0.0.0/0\"\n source = \"0.0.0.0/0\"\n }\n ]\n }\n]", + "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-).", + "required": false, + "virtual": true, + "hidden": true, + "custom_config": { + "type": "code_editor", + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "supportedLanguages": [ + "hcl" + ] + } + } } ], "architecture": { @@ -408,15 +602,16 @@ }, "dependencies": [ { - "name": "deploy-arch-ibm-account-infra-base", - "description": "Cloud automation for Account Configuration organizes your IBM Cloud account with a ready-made set of resource groups by default and when you enable the \"with Account Settings\" option, it also applies baseline security and governance settings. ", - "catalog_id": "7a4d68b4-cf8b-40cd-a3d1-f49aff526eb3", + "name": "deploy-arch-ibm-slz-vpc", + "description": "Configure the VPC and subnets required to deploy VPC private path.", + "id": "9fc0fa64-27af-4fed-9dce-47b3640ba739-global", + "version": "v8.8.3", "flavors": [ - "resource-group-only", - "resource-groups-with-account-settings" + "fully-configurable" ], - "default_flavor": "resource-group-only", - "id": "63641cec-6093-4b4f-b7b0-98d2f4185cd6-global", + "catalog_id": "1082e7d2-5e2f-0a11-a3bc-f88a8e1931fc", + "optional": true, + "on_by_default": false, "input_mapping": [ { "dependency_input": "prefix", @@ -424,22 +619,49 @@ "reference_version": true }, { - "dependency_output": "workload_resource_group_name", - "version_input": "existing_resource_group_name" + "dependency_input": "existing_resource_group_name", + "version_input": "existing_resource_group_name", + "reference_version": true + }, + { + "dependency_input": "region", + "version_input": "region", + "reference_version": true + }, + { + "dependency_output": "private_path_subnet_id", + "version_input": "existing_subnet_id" + }, + { + "dependency_output": "vpc_crn", + "version_input": "existing_vpc_crn" + }, + { + "dependency_input": "subnets", + "version_input": "subnets", + "reference_version": true + }, + { + "dependency_input": "network_acls", + "version_input": "network_acls", + "reference_version": true } - ], - "optional": true, - "on_by_default": false, - "version": "v3.0.7" + ] }, { - "name": "deploy-arch-ibm-vpc", - "description": "Configures the VPC network infrastructure required to deploy Private Path service.", - "catalog_id": "7a4d68b4-cf8b-40cd-a3d1-f49aff526eb3", + "name": "deploy-arch-secrets-manager-private-cert", + "description": "Configures secrets manager instance, private certificate engine and ALB listener certificate. Client certificate will not get created and will have to be created manually.", + "id": "422283a7-9cb2-4149-8093-a36a799e1d27-global", + "version": "v1.7.4", "flavors": [ "fully-configurable" ], - "id": "2af61763-f8ef-4527-a815-b92166f29bc8-global", + "catalog_id": "7a4d68b4-cf8b-40cd-a3d1-f49aff526eb3", + "optional": true, + "on_by_default": true, + "ignore_auto_referencing": [ + "*" + ], "input_mapping": [ { "dependency_input": "prefix", @@ -447,33 +669,63 @@ "reference_version": true }, { - "dependency_input": "existing_resource_group_name", - "version_input": "existing_resource_group_name", + "dependency_output": "secrets_manager_crn", + "version_input": "existing_secrets_manager_instance_crn" + }, + { + "dependency_input": "prefix", + "version_input": "prefix", "reference_version": true }, { - "dependency_input": "region", + "dependency_input": "secrets_manager_region", "version_input": "region", "reference_version": true }, { - "dependency_output": "vpc_id", - "version_input": "existing_vpc_id" + "dependency_input": "secrets_manager_service_plan", + "version_input": "secrets_manager_service_plan", + "reference_version": true + }, + { + "dependency_input": "root_ca_name", + "version_input": "root_ca_name", + "reference_version": true + }, + { + "dependency_input": "root_ca_common_name", + "version_input": "root_ca_common_name", + "reference_version": true + }, + { + "dependency_input": "intermediate_ca_name", + "version_input": "intermediate_ca_name", + "reference_version": true + }, + { + "dependency_input": "intermediate_ca_common_name", + "version_input": "intermediate_ca_common_name", + "reference_version": true }, { - "dependency_input": "enable_platform_metrics", - "version_input": "enable_platform_metrics", + "dependency_input": "certificate_template_name", + "version_input": "certificate_template_name", "reference_version": true }, { - "dependency_input": "logs_routing_tenant_regions", - "version_input": "logs_routing_tenant_regions", + "dependency_input": "template_max_ttl", + "version_input": "template_max_ttl", "reference_version": true + }, + { + "version_input": "enable_certificate_auth", + "value": false + }, + { + "dependency_output": "secret_crn", + "version_input": "application_loadbalancer_listener_certificate_crn" } - ], - "optional": true, - "on_by_default": true, - "version": "v7.24.0" + ] } ], "dependency_version_2": true, diff --git a/reference-architectures/deployable-architecture-private-path-external-connectivity.svg b/reference-architectures/deployable-architecture-private-path-external-connectivity.svg index 1d9d825..c521e04 100644 --- a/reference-architectures/deployable-architecture-private-path-external-connectivity.svg +++ b/reference-architectures/deployable-architecture-private-path-external-connectivity.svg @@ -1,4 +1,4 @@ -On-Premisesworkload / service
IBM Cloud
IBM Cloud
Region
Region
Resource GroupVPCSubnetApplicationLoadBalancerPrivate PathNLB
Private Path service
Private Path service
Flow Logs bucket
[Optional] Object Storage 
[Optional] Object Storage 
Cloud logsCloud MonitoringActivity Tracker Event Routing
Observability
Observability
[Optional]
[Option...
SCC Workload Protection
SCC Workload Pr...
App Configuration
App Config...
[Optional] Key Protect
[Optional] Key Protect
Key Ring
Key Ring
Key For Flow Logs Bucket
Text is not SVG - cannot display
\ No newline at end of file +On-Premisesworkload / service
IBM Cloud
Region
Resource GroupVPCSubnet load-balancer--application ApplicationLoadBalancer load-balancer--network Private PathNLB
Private Path service
Flow Logs bucket
[Optional] Object Storage 
Cloud logsCloud MonitoringActivity Tracker Event Routing
Observability
[Optional]
SCC Workload Protection
App Configuration
[Optional] Key Protect
Key Ring
Key For Flow Logs Bucket
Secrets Manager
Private Certificate
\ No newline at end of file diff --git a/solutions/fully-configurable/catalogValidationValues.json.template b/solutions/fully-configurable/catalogValidationValues.json.template index e7afba3..48e3fb5 100644 --- a/solutions/fully-configurable/catalogValidationValues.json.template +++ b/solutions/fully-configurable/catalogValidationValues.json.template @@ -3,5 +3,6 @@ "region": "us-south", "prefix": $PREFIX, "private_path_name": $PREFIX, - "private_path_service_endpoints": ["vpc-pps.dev.internal"] + "private_path_service_endpoints": ["vpc-pps.dev.internal"], + "existing_secrets_manager_instance_crn": $SM_CRN } diff --git a/solutions/fully-configurable/main.tf b/solutions/fully-configurable/main.tf index 84f553c..960797f 100644 --- a/solutions/fully-configurable/main.tf +++ b/solutions/fully-configurable/main.tf @@ -8,27 +8,90 @@ module "resource_group" { existing_resource_group_name = var.existing_resource_group_name } +######################################################################################################################## +# Secrets Manager resources +######################################################################################################################## +locals { + secrets_manager_cert_crn = var.application_loadbalancer_listener_certificate_crn != null ? var.application_loadbalancer_listener_certificate_crn : module.secrets_manager_private_certificate[0].secret_crn + secrets_manager_secret_group_id = var.application_loadbalancer_listener_certificate_crn != null ? null : var.existing_secrets_manager_secret_group_id != null ? var.existing_secrets_manager_secret_group_id : module.secrets_manager_secret_group[0].secret_group_id +} +module "existing_sm_crn_parser" { + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.2.0" + crn = var.existing_secrets_manager_instance_crn +} + +module "application_loadbalancer_listener_certificate_crn_parser" { + count = var.application_loadbalancer_listener_certificate_crn != null ? 1 : 0 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.2.0" + crn = var.application_loadbalancer_listener_certificate_crn +} + +# Create a secret group to place the certificate if provisioning a new certificate +module "secrets_manager_secret_group" { + count = var.application_loadbalancer_listener_certificate_crn == null && var.existing_secrets_manager_secret_group_id == null ? 1 : 0 + source = "terraform-ibm-modules/secrets-manager-secret-group/ibm" + version = "1.3.15" + region = module.existing_sm_crn_parser.region + secrets_manager_guid = module.existing_sm_crn_parser.service_instance + secret_group_name = (var.prefix != null && var.prefix != "") ? "${var.prefix}-cert-secret-group" : "cert-secret-group" + secret_group_description = "secret group used for private certificates" + providers = { + ibm = ibm.ibm-sm + } +} + +# Create private certificate to use for VPN server +module "secrets_manager_private_certificate" { + count = var.application_loadbalancer_listener_certificate_crn == null ? 1 : 0 + source = "terraform-ibm-modules/secrets-manager-private-cert/ibm" + version = "1.7.0" + cert_name = (var.prefix != null && var.prefix != "") ? "${var.prefix}-cts-vpn-private-cert" : "cts-vpn-private-cert" + cert_description = "private certificate for client to site VPN connection" + cert_template = var.private_cert_engine_config_template_name + cert_secrets_group_id = local.secrets_manager_secret_group_id + cert_common_name = var.private_cert_engine_config_root_ca_common_name + secrets_manager_guid = module.existing_sm_crn_parser.service_instance + secrets_manager_region = module.existing_sm_crn_parser.region + providers = { + ibm = ibm.ibm-sm + } +} + +######################################################################################################################## +# Application Load balancer resources +######################################################################################################################## + +module "existing_vpc_crn_parser" { + count = var.existing_vpc_crn != null ? 1 : 0 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.2.0" + crn = var.existing_vpc_crn +} + locals { prefix = var.prefix != null ? trimspace(var.prefix) != "" ? "${var.prefix}-" : "" : "" network_loadbalancer_name = "${local.prefix}${var.network_loadbalancer_name}" private_path_name = "${local.prefix}${var.private_path_name}" subnet_id = var.existing_subnet_id != null ? var.existing_subnet_id : data.ibm_is_vpc.vpc[0].subnets[0].id + existing_vpc_id = var.existing_vpc_crn != null ? module.existing_vpc_crn_parser[0].resource : null } data "ibm_is_vpc" "vpc" { - count = var.existing_vpc_id != null && var.existing_subnet_id == null ? 1 : 0 - identifier = var.existing_vpc_id + count = var.existing_vpc_crn != null && var.existing_subnet_id == null ? 1 : 0 + identifier = local.existing_vpc_id } resource "ibm_is_lb" "alb" { - name = "${local.prefix}-alb" + name = "${local.prefix}alb" resource_group = module.resource_group.resource_group_id subnets = [local.subnet_id] type = var.application_loadbalancer_type } resource "ibm_is_lb_pool" "alb_backend_pool" { - name = "${local.prefix}-alb-pool" + name = "${local.prefix}alb-pool" lb = ibm_is_lb.alb.id algorithm = var.application_loadbalancer_pool_algorithm protocol = var.application_loadbalancer_pool_protocol @@ -51,13 +114,17 @@ resource "ibm_is_lb_listener" "alb_frontend_listener" { port = var.application_loadbalancer_listener_port protocol = var.application_loadbalancer_listener_protocol idle_connection_timeout = var.application_loadbalancer_listener_idle_timeout - certificate_instance = var.application_loadbalancer_listener_certificate_instance + certificate_instance = local.secrets_manager_cert_crn default_pool = ibm_is_lb_pool.alb_backend_pool.pool_id } +######################################################################################################################## +# Private path resources +######################################################################################################################## + module "private_path" { source = "terraform-ibm-modules/vpc-private-path/ibm" - version = "1.3.2" + version = "1.5.0" resource_group_id = module.resource_group.resource_group_id subnet_id = local.subnet_id tags = var.private_path_tags diff --git a/solutions/fully-configurable/outputs.tf b/solutions/fully-configurable/outputs.tf index d4550c7..efbfebe 100644 --- a/solutions/fully-configurable/outputs.tf +++ b/solutions/fully-configurable/outputs.tf @@ -38,6 +38,11 @@ output "alb_listener_id" { value = ibm_is_lb_listener.alb_frontend_listener.id } +output "alb_listener_certificate_secret_id" { + description = "ID of the client to site vpn server certificate secret stored in Secrets Manager" + value = var.application_loadbalancer_listener_certificate_crn == null ? module.secrets_manager_private_certificate[0].secret_id : module.application_loadbalancer_listener_certificate_crn_parser[0].service_instance +} + ## Private Path output "nlb_crn" { description = "The CRN for this load balancer." diff --git a/solutions/fully-configurable/provider.tf b/solutions/fully-configurable/provider.tf index 17cfc77..fa3d678 100644 --- a/solutions/fully-configurable/provider.tf +++ b/solutions/fully-configurable/provider.tf @@ -8,3 +8,11 @@ provider "ibm" { visibility = var.provider_visibility private_endpoint_type = (var.provider_visibility == "private" && var.region == "ca-mon") ? "vpe" : null } + +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = module.existing_sm_crn_parser.region + alias = "ibm-sm" + visibility = var.provider_visibility + private_endpoint_type = (var.provider_visibility == "private" && var.region == "ca-mon") ? "vpe" : null +} diff --git a/solutions/fully-configurable/variables.tf b/solutions/fully-configurable/variables.tf index ac85529..1730a74 100644 --- a/solutions/fully-configurable/variables.tf +++ b/solutions/fully-configurable/variables.tf @@ -74,13 +74,13 @@ variable "private_path_access_tags" { # VPC Variables ############################################################################## -variable "existing_vpc_id" { - description = "The ID of an existing VPC. If the user provides only the `existing_vpc_id`, the Private Path service will be provisioned in the first subnet." +variable "existing_vpc_crn" { type = string + description = "CRN of the existing VPC in which the VPN infrastructure will be created. If the user provides only the `existing_vpc_crn`, the Private Path service will be provisioned in the first subnet." default = null validation { - condition = var.existing_vpc_id == null && var.existing_subnet_id == null ? false : true - error_message = "A value for either `existing_vpc_id` or `existing_subnet_id` should be passed." + condition = var.existing_vpc_crn == null && var.existing_subnet_id == null ? false : true + error_message = "A value for either `existing_vpc_crn` or `existing_subnet_id` should be passed." } } @@ -104,18 +104,27 @@ variable "application_loadbalancer_type" { type = string default = "private" description = "The type of the application load balancer. Supported values are `private`, `public`." + validation { + condition = contains(["private", "public"], var.application_loadbalancer_type) + error_message = "Invalid type of application load balancer. Allowed values are 'private', 'public'." + } } variable "application_loadbalancer_pool_algorithm" { type = string description = "The load-balancing algorithm for Private Path network load balancer pool members. Supported values are `round_robin` or `weighted_round_robin`." default = "round_robin" + + validation { + condition = contains(["round_robin", "weighted_round_robin"], var.application_loadbalancer_pool_algorithm) + error_message = "Invalid load-balancing algorithm. Allowed values are 'round_robin', 'weighted_round_robin'." + } } variable "application_loadbalancer_pool_member_port" { type = number description = "The port where traffic is sent to the instance." - default = 80 + default = 443 } variable "application_loadbalancer_pool_health_delay" { @@ -138,45 +147,88 @@ variable "application_loadbalancer_pool_health_timeout" { variable "application_loadbalancer_pool_health_type" { type = string - description = "The protocol used to send health check messages to instances in the pool. Supported values are `tcp` or `http`." - default = "http" + description = "The protocol used to send health check messages to instances in the pool. Supported values are `tcp`, `https`." + default = "https" + validation { + condition = contains(["tcp", "https"], var.application_loadbalancer_pool_health_type) + error_message = "Invalid protocol for health check. Allowed values are 'tcp', 'https'." + } } variable "application_loadbalancer_pool_protocol" { type = string - description = "The protocol used to send traffic to instances in the pool. Supported values are `tcp`, `http`, `https` or `udp`." - default = "http" + description = "The protocol used to send traffic to instances in the pool. Supported values are `tcp`, `https` or `udp`." + default = "https" + validation { + condition = contains(["tcp", "https", "udp"], var.application_loadbalancer_pool_protocol) + error_message = "Invalid protocol for loadbalancer pool. Allowed values are 'tcp', 'https' or 'udp'." + } } variable "application_loadbalancer_listener_port" { type = number description = "The listener port for the Private Path network load balancer." - default = 80 + default = 443 } variable "application_loadbalancer_listener_protocol" { type = string - description = "The protocol used to send traffic to instances in the pool. Supported values are `tcp`, `http`, `https` or `udp`." - default = "http" + description = "The listener protocol used by instances in the application loadbalancer pool. Supported values are `tcp`, `https` or `udp`." + default = "https" + validation { + condition = contains(["tcp", "https", "udp"], var.application_loadbalancer_listener_protocol) + error_message = "Invalid listener protocol for application loadbalancer. Allowed values are 'tcp', 'https' or 'udp'." + } } variable "application_loadbalancer_listener_idle_timeout" { type = number - description = "The protocol used to send traffic to instances in the pool. Supported values are `tcp`, `http`, `https` or `udp`." + description = "The idle connection timeout of the listener in seconds." default = 50 } -variable "application_loadbalancer_listener_certificate_instance" { +variable "application_loadbalancer_listener_certificate_crn" { type = string - description = "The CRN of the certificate in your secret manager, it is applicable(mandatory) only to https protocol." + description = "The CRN of existing secrets manager private certificate to use to create application loadbalancer listener. If the value is null, then new private certificate is created. [Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-certificates&interface=ui)" default = null validation { - condition = var.application_loadbalancer_listener_protocol == "https" ? var.application_loadbalancer_listener_certificate_instance != null ? true : false : true - error_message = "A value must be set for `application_loadbalancer_listener_certificate_instance` when `application_loadbalancer_listener_protocol` is set to `https`." + condition = var.application_loadbalancer_listener_certificate_crn == null ? var.private_cert_engine_config_template_name != null && var.private_cert_engine_config_root_ca_common_name != null : true + error_message = "Set 'private_cert_engine_config_root_ca_common_name' and 'private_cert_engine_config_template_name' input variables if a 'application_loadbalancer_listener_certificate_crn' input variable is not set" + } + + validation { + condition = var.application_loadbalancer_listener_certificate_crn != null ? var.private_cert_engine_config_template_name == null && var.private_cert_engine_config_root_ca_common_name == null : true + error_message = "'private_cert_engine_config_root_ca_common_name' and 'private_cert_engine_config_template_name' input variables can not be set if a 'application_loadbalancer_listener_certificate_crn' input variable is already set" } } +############################################################################## +# Secrets Manager resources +############################################################################## + +variable "existing_secrets_manager_instance_crn" { + type = string + description = "The CRN of existing secrets manager where the certificate to use for the ALB listener is stored or where the new private certificate will be created. [Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-getting-started)" +} + +variable "existing_secrets_manager_secret_group_id" { + type = string + description = "The ID of existing secrets manager secret group used for new created certificate. If the value is null, then new secrets manager secret group is created. [Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-secret-groups&interface=ui)" + default = null +} + +variable "private_cert_engine_config_root_ca_common_name" { + type = string + description = "A fully qualified domain name or host domain name for the certificate to be created. Required if `application_loadbalancer_listener_certificate_crn` input variable is `null`. [Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-private-certificates&interface=ui)" + default = null +} + +variable "private_cert_engine_config_template_name" { + type = string + description = "The name of the Certificate Template to create for a private certificate secret engine. When `application_loadbalancer_listener_certificate_crn` input variable is `null`, then it has to be the existing template name that exists in the private cert engine. [Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-private-certificates&interface=ui)" + default = null +} ############################################################################## # NLB Variables @@ -191,7 +243,7 @@ variable "network_loadbalancer_name" { variable "network_loadbalancer_listener_port" { type = number description = "The listener port for the Private Path network load balancer." - default = 80 + default = 443 } variable "network_loadbalancer_listener_accept_proxy_protocol" { @@ -226,8 +278,12 @@ variable "network_loadbalancer_pool_health_timeout" { variable "network_loadbalancer_pool_health_type" { type = string - description = "The protocol used to send health check messages to instances in the pool. Supported values are `tcp` or `http`." + description = "The protocol used to send health check messages to instances in the pool. Supported values are `tcp` or `https`." default = "tcp" + validation { + condition = contains(["tcp", "https"], var.network_loadbalancer_pool_health_type) + error_message = "Invalid network loadbalancer health type. Allowed values are 'tcp', 'https'." + } } variable "network_loadbalancer_pool_health_monitor_url" { @@ -239,13 +295,13 @@ variable "network_loadbalancer_pool_health_monitor_url" { variable "network_loadbalancer_pool_health_monitor_port" { type = number description = "The port on which the load balancer sends health check requests. By default, health checks are sent on the same port where traffic is sent to the instance." - default = 80 + default = 443 } variable "network_loadbalancer_pool_member_port" { type = number description = "The port where traffic is sent to the instance." - default = 80 + default = 443 } ############################################################################## @@ -256,6 +312,10 @@ variable "private_path_default_access_policy" { type = string description = "The policy to use for bindings from accounts without an explicit account policy. The default policy is set to Review all requests. Supported options are `permit`, `deny`, or `review`." default = "review" + validation { + condition = contains(["review", "permit", "deny"], var.private_path_default_access_policy) + error_message = "Invalid network loadbalancer health type. Allowed values are 'review', 'permit' or 'deny'." + } } variable "private_path_service_endpoints" { diff --git a/tests/go.mod b/tests/go.mod index 6933441..99492a3 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -1,22 +1,20 @@ module github.com/terraform-ibm-modules/terraform-ibm-vpc-private-path-external-connectivity -go 1.24.0 - -toolchain go1.25.4 +go 1.25.5 require ( + github.com/IBM/go-sdk-core/v5 v5.21.2 github.com/gruntwork-io/terratest v0.54.0 github.com/stretchr/testify v1.11.1 - github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper v1.60.34 + github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper v1.64.0 ) require ( dario.cat/mergo v1.0.0 // indirect github.com/IBM-Cloud/bluemix-go v0.0.0-20240719075425-078fcb3a55be // indirect - github.com/IBM-Cloud/power-go-client v1.14.3 // indirect + github.com/IBM-Cloud/power-go-client v1.14.4 // indirect github.com/IBM/cloud-databases-go-sdk v0.8.1 // indirect - github.com/IBM/go-sdk-core/v5 v5.21.2 // indirect - github.com/IBM/platform-services-go-sdk v0.90.4 // indirect + github.com/IBM/platform-services-go-sdk v0.91.0 // indirect github.com/IBM/project-go-sdk v0.4.0 // indirect github.com/IBM/schematics-go-sdk v0.4.0 // indirect github.com/IBM/vpc-go-sdk v1.0.2 // indirect @@ -37,7 +35,7 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.23.0 // indirect - github.com/go-openapi/errors v0.22.4 // indirect + github.com/go-openapi/errors v0.22.5 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/loads v0.22.0 // indirect @@ -90,13 +88,13 @@ require ( go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/crypto v0.45.0 // indirect - golang.org/x/mod v0.29.0 // indirect + golang.org/x/crypto v0.46.0 // indirect + golang.org/x/mod v0.30.0 // indirect golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect - golang.org/x/tools v0.38.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect + golang.org/x/tools v0.39.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/tests/go.sum b/tests/go.sum index 15db54f..d7a5dfb 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -2,15 +2,15 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/IBM-Cloud/bluemix-go v0.0.0-20240719075425-078fcb3a55be h1:USOcBHkYQ4o/ccoEvoHinrba8NQthLJpFXnAoBY+MI4= github.com/IBM-Cloud/bluemix-go v0.0.0-20240719075425-078fcb3a55be/go.mod h1:/7hMjdZA6fEpd/dQAOEABxKEwN0t72P3PlpEDu0Y7bE= -github.com/IBM-Cloud/power-go-client v1.14.3 h1:qRA+DnqKSRfcgCeJRDUQXlqipYd6nWtNblubDeRYmhY= -github.com/IBM-Cloud/power-go-client v1.14.3/go.mod h1:SpTK1ttW8bfMNUVQS8qOEuWn2KOkzaCLyzfze8MG1JE= +github.com/IBM-Cloud/power-go-client v1.14.4 h1:XXQp4atVwvCfDps1nF5zyi2FzNbsuHtszz5nBJ9yCYg= +github.com/IBM-Cloud/power-go-client v1.14.4/go.mod h1:TT0jJbptQuDRwB9PPYDcGe0ZwFSl5GiSrNuQeJzJYco= github.com/IBM/cloud-databases-go-sdk v0.8.1 h1:ULQ5L8V/9z79/qS185LqbIK2LD4kMtk3Hdhp4lFMVcw= github.com/IBM/cloud-databases-go-sdk v0.8.1/go.mod h1:JYucI1PdwqbAd8XGdDAchxzxRP7bxOh1zUnseovHKsc= github.com/IBM/go-sdk-core/v5 v5.9.2/go.mod h1:YlOwV9LeuclmT/qi/LAK2AsobbAP42veV0j68/rlZsE= github.com/IBM/go-sdk-core/v5 v5.21.2 h1:mJ5QbLPOm4g5qhZiVB6wbSllfpeUExftGoyPek2hk4M= github.com/IBM/go-sdk-core/v5 v5.21.2/go.mod h1:ngpMgwkjur1VNUjqn11LPk3o5eCyOCRbcfg/0YAY7Hc= -github.com/IBM/platform-services-go-sdk v0.90.4 h1:60oLi1S/9C23/BidRSC1MWNlQVEezk+x4giOb9NkEPQ= -github.com/IBM/platform-services-go-sdk v0.90.4/go.mod h1:KAnBhxKaYsu9It2aVXV6oCPEj78imvTs2qSG0ScZKpM= +github.com/IBM/platform-services-go-sdk v0.91.0 h1:5o4XotMmP9UfCg9BKG0cx/pTAMhBh0KzjyFQQyHZTgE= +github.com/IBM/platform-services-go-sdk v0.91.0/go.mod h1:KAnBhxKaYsu9It2aVXV6oCPEj78imvTs2qSG0ScZKpM= github.com/IBM/project-go-sdk v0.4.0 h1:72pEtVHJn434+MYRawER7Hk/kblapdfotoLBBhjv/jo= github.com/IBM/project-go-sdk v0.4.0/go.mod h1:FOJM9ihQV3EEAY6YigcWiTNfVCThtdY8bLC/nhQHFvo= github.com/IBM/schematics-go-sdk v0.4.0 h1:x01f/tPquYJYLQzJLGuxWfCbV/EdSMXRikOceNy/JLM= @@ -83,8 +83,8 @@ github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC0 github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= -github.com/go-openapi/errors v0.22.4 h1:oi2K9mHTOb5DPW2Zjdzs/NIvwi2N3fARKaTJLdNabaM= -github.com/go-openapi/errors v0.22.4/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk= +github.com/go-openapi/errors v0.22.5 h1:Yfv4O/PRYpNF3BNmVkEizcHb3uLVVsrDt3LNdgAKRY4= +github.com/go-openapi/errors v0.22.5/go.mod h1:z9S8ASTUqx7+CP1Q8dD8ewGH/1JWFFLX/2PmAYNQLgk= github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -297,8 +297,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper v1.60.34 h1:eED28nDc2If2KjsEi5VBAUt0at+FxVJw/lUcwzeW8Ck= -github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper v1.60.34/go.mod h1:VCaRJKjjwIqWMdXqMwvC/9WCwWe8lBTRZrkYrQbcNUU= +github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper v1.64.0 h1:uaGN3RMlOpmkqCXPjlygPiCqnGhszYP6YU3rWWsp0wc= +github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper v1.64.0/go.mod h1:HqX0S0Ue19y7TJRGR5+Np2Aq0xElgw3mM3zZCWwQc1I= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmccombs/hcl2json v0.6.4 h1:/FWnzS9JCuyZ4MNwrG4vMrFrzRgsWEOVi+1AyYUVLGw= github.com/tmccombs/hcl2json v0.6.4/go.mod h1:+ppKlIW3H5nsAsZddXPy2iMyvld3SHxyjswOZhavRDk= @@ -347,8 +347,8 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -359,8 +359,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -400,8 +400,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -438,8 +438,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -454,8 +454,8 @@ golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -472,8 +472,8 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -489,8 +489,8 @@ golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= +golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/tests/pr_test.go b/tests/pr_test.go index 4de6ae7..66afb4e 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -8,19 +8,22 @@ import ( "strings" "testing" + "github.com/IBM/go-sdk-core/v5/core" "github.com/gruntwork-io/terratest/modules/files" "github.com/gruntwork-io/terratest/modules/logger" "github.com/gruntwork-io/terratest/modules/random" "github.com/gruntwork-io/terratest/modules/terraform" "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/common" + "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testaddons" "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testschematic" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/cloudinfo" "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper" ) -const fullyConfigurableTerraformDir = "solutions/fully-configurable" +const fullyConfigurableSolutionDir = "solutions/fully-configurable" // Define a struct with fields that match the structure of the YAML data const yamlLocation = "../common-dev-assets/common-go-assets/common-permanent-resources.yaml" @@ -44,25 +47,30 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func validateEnvVariable(t *testing.T, varName string) string { - val, present := os.LookupEnv(varName) - require.True(t, present, "%s environment variable not set", varName) - require.NotEqual(t, "", val, "%s environment variable is empty", varName) - return val -} +func setupFullyConfigurableOptions(t *testing.T, prefix string) (*testschematic.TestSchematicOptions, *terraform.Options) { + + // ------------------------------------------------------------------------------------------------------ + // Create SLZ VPC, resource group first + // ------------------------------------------------------------------------------------------------------ + realTerraformDir := "./resources" + tempTerraformDir, _ := files.CopyTerraformFolderToTemp(realTerraformDir, fmt.Sprintf(prefix+"-%s", strings.ToLower(random.UniqueId()))) + + // Verify ibmcloud_api_key variable is set + checkVariable := "TF_VAR_ibmcloud_api_key" + val, present := os.LookupEnv(checkVariable) + require.True(t, present, checkVariable+" environment variable not set") + require.NotEqual(t, "", val, checkVariable+" environment variable is empty") -func setupTerraform(t *testing.T, prefix, realTerraformDir string) *terraform.Options { - tempTerraformDir, err := files.CopyTerraformFolderToTemp(realTerraformDir, prefix) - require.NoError(t, err, "Failed to create temporary Terraform folder") - apiKey := validateEnvVariable(t, "TF_VAR_ibmcloud_api_key") // pragma: allowlist secret - region, err := testhelper.GetBestVpcRegion(apiKey, "../common-dev-assets/common-go-assets/cloudinfo-region-vpc-gen2-prefs.yaml", "eu-de") - require.NoError(t, err, "Failed to get best VPC region") + // Programmatically determine region to use based on availability + region, _ := testhelper.GetBestVpcRegion(val, "../common-dev-assets/common-go-assets/cloudinfo-region-vpc-gen2-prefs.yaml", "eu-de") + logger.Log(t, "Tempdir: ", tempTerraformDir) existingTerraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ TerraformDir: tempTerraformDir, Vars: map[string]interface{}{ - "prefix": prefix, - "region": region, + "prefix": prefix, + "region": region, + "resource_tags": []string{"test-schematic"}, }, // Set Upgrade to true to ensure latest version of providers and modules are used by terratest. // This is the same as setting the -upgrade=true flag with terraform. @@ -70,39 +78,27 @@ func setupTerraform(t *testing.T, prefix, realTerraformDir string) *terraform.Op }) terraform.WorkspaceSelectOrNew(t, existingTerraformOptions, prefix) - _, err = terraform.InitAndApplyE(t, existingTerraformOptions) - require.NoError(t, err, "Init and Apply of temp existing resource failed") - - return existingTerraformOptions -} + _, existErr := terraform.InitAndApplyE(t, existingTerraformOptions) -func cleanupTerraform(t *testing.T, options *terraform.Options, prefix string) { - if t.Failed() && strings.ToLower(os.Getenv("DO_NOT_DESTROY_ON_FAILURE")) == "true" { - fmt.Println("Terratest failed. Debug the test and delete resources manually.") - return + if existErr != nil { + assert.True(t, existErr == nil, "Init and Apply of temp resources (SLZ VPC and Secrets Manager) failed") + return nil, nil } - logger.Log(t, "START: Destroy (existing resources)") - terraform.Destroy(t, options) - terraform.WorkspaceDelete(t, options, prefix) - logger.Log(t, "END: Destroy (existing resources)") -} - -func TestRunFullyConfigurableInSchematics(t *testing.T) { - t.Parallel() - - // Provision resources first - prefix := fmt.Sprintf("ce-pp-%s", strings.ToLower(random.UniqueId())) - existingTerraformOptions := setupTerraform(t, prefix, "./resources") options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ - Testing: t, - Prefix: "ce-pp", - TarIncludePatterns: []string{"*.tf", fullyConfigurableTerraformDir + "/*.*", "scripts/*.sh"}, - TemplateFolder: fullyConfigurableTerraformDir, - Tags: []string{"test-schematic"}, - DeleteWorkspaceOnFail: false, + Testing: t, + Prefix: prefix, + TarIncludePatterns: []string{ + fullyConfigurableSolutionDir + "/*.*", + "*.tf", + }, + ResourceGroup: terraform.Output(t, existingTerraformOptions, "resource_group_name"), + TemplateFolder: fullyConfigurableSolutionDir, + Tags: []string{"test-schematic"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 60, + Region: region, }) - options.TerraformVars = []testschematic.TestSchematicTerraformVar{ {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, {Name: "prefix", Value: prefix, DataType: "string"}, @@ -111,9 +107,36 @@ func TestRunFullyConfigurableInSchematics(t *testing.T) { {Name: "existing_subnet_id", Value: terraform.Output(t, existingTerraformOptions, "existing_subnet_id"), DataType: "string"}, {Name: "private_path_service_endpoints", Value: []string{"vpc-pps.dev.internal"}, DataType: "list(string)"}, {Name: "application_loadbalancer_pool_member_ip_address", Value: []string{terraform.Output(t, existingTerraformOptions, "member_ip_address")}, DataType: "list(string)"}, + {Name: "private_cert_engine_config_root_ca_common_name", Value: fmt.Sprintf("%s%s", options.Prefix, ".com"), DataType: "string"}, + {Name: "private_cert_engine_config_template_name", Value: permanentResources["privateCertTemplateName"], DataType: "string"}, + {Name: "existing_secrets_manager_instance_crn", Value: permanentResources["secretsManagerCRN"], DataType: "string"}, + } + + return options, existingTerraformOptions +} + +func TestRunFullyConfigurableInSchematics(t *testing.T) { + t.Parallel() + prefix := fmt.Sprintf("ce-pp-%s", strings.ToLower(random.UniqueId())) + options, existingTerraformOptions := setupFullyConfigurableOptions(t, prefix) + if options == nil || existingTerraformOptions == nil { + t.Fatal("Failed to create agent schematic options (prerequisite Terraform deployment failed)") + } + + err := options.RunSchematicTest() + assert.Nil(t, err, "This should not have errored") + + // Check if "DO_NOT_DESTROY_ON_FAILURE" is set + envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") + // Destroy the temporary existing resources if required + if t.Failed() && strings.ToLower(envVal) == "true" { + fmt.Println("Terratest failed. Debug the test and delete resources manually.") + } else { + logger.Log(t, "START: Destroy (existing resources)") + terraform.Destroy(t, existingTerraformOptions) + terraform.WorkspaceDelete(t, existingTerraformOptions, prefix) + logger.Log(t, "END: Destroy (existing resources)") } - require.NoError(t, options.RunSchematicTest(), "This should not have errored") - cleanupTerraform(t, existingTerraformOptions, prefix) } func TestRunUpgradeFullyConfigurable(t *testing.T) { @@ -121,27 +144,178 @@ func TestRunUpgradeFullyConfigurable(t *testing.T) { // Provision existing resources first prefix := fmt.Sprintf("pp-upg-%s", strings.ToLower(random.UniqueId())) - existingTerraformOptions := setupTerraform(t, prefix, "./resources") + options, existingTerraformOptions := setupFullyConfigurableOptions(t, prefix) - options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ - Testing: t, - Prefix: "ce-pp-upg", - TarIncludePatterns: []string{"*.tf", "scripts/*.sh", fullyConfigurableTerraformDir + "/*.*"}, - TemplateFolder: fullyConfigurableTerraformDir, - Tags: []string{"test-schematic"}, - DeleteWorkspaceOnFail: false, + if options == nil || existingTerraformOptions == nil { + t.Fatal("Failed to create agent schematic options (prerequisite Terraform deployment failed)") + } + options.CheckApplyResultForUpgrade = true + + err := options.RunSchematicUpgradeTest() + if !options.UpgradeTestSkipped { + assert.NoError(t, err, "Upgrade test should complete without errors") + } + + // Check if "DO_NOT_DESTROY_ON_FAILURE" is set + envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") + // Destroy the temporary existing resources if required + if t.Failed() && strings.ToLower(envVal) == "true" { + fmt.Println("Terratest failed. Debug the test and delete resources manually.") + } else { + logger.Log(t, "START: Destroy (existing resources)") + terraform.Destroy(t, existingTerraformOptions) + terraform.WorkspaceDelete(t, existingTerraformOptions, prefix) + logger.Log(t, "END: Destroy (existing resources)") + } +} + +func TestFullyConfigurableSolutionExistingResources(t *testing.T) { + t.Parallel() + + // ------------------------------------------------------------------------------------ + // Create SLZ VPC, SM private cert, resource group first + // ------------------------------------------------------------------------------------ + + prefix := fmt.Sprintf("cts-s-ext-%s", strings.ToLower(random.UniqueId())) + realTerraformDir := "./resources" + tempTerraformDir, _ := files.CopyTerraformFolderToTemp(realTerraformDir, fmt.Sprintf(prefix+"-%s", strings.ToLower(random.UniqueId()))) + tags := common.GetTagsFromTravis() + + // Verify ibmcloud_api_key variable is set + checkVariable := "TF_VAR_ibmcloud_api_key" + val, present := os.LookupEnv(checkVariable) + require.True(t, present, checkVariable+" environment variable not set") + require.NotEqual(t, "", val, checkVariable+" environment variable is empty") + + // Programmatically determine region to use based on availability + region, _ := testhelper.GetBestVpcRegion(val, "../common-dev-assets/common-go-assets/cloudinfo-region-vpc-gen2-prefs.yaml", "eu-de") + + logger.Log(t, "Tempdir: ", tempTerraformDir) + existingTerraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: tempTerraformDir, + Vars: map[string]interface{}{ + "prefix": prefix, + "region": region, + "resource_tags": tags, + "existing_secrets_manager_instance_crn": permanentResources["secretsManagerCRN"], + "certificate_template_name": permanentResources["privateCertTemplateName"], + }, + // Set Upgrade to true to ensure latest version of providers and modules are used by terratest. + // This is the same as setting the -upgrade=true flag with terraform. + Upgrade: true, }) - options.TerraformVars = []testschematic.TestSchematicTerraformVar{ - {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, - {Name: "prefix", Value: prefix, DataType: "string"}, - {Name: "region", Value: terraform.Output(t, existingTerraformOptions, "region"), DataType: "string"}, - {Name: "existing_resource_group_name", Value: terraform.Output(t, existingTerraformOptions, "resource_group_name"), DataType: "string"}, - {Name: "existing_subnet_id", Value: terraform.Output(t, existingTerraformOptions, "existing_subnet_id"), DataType: "string"}, - {Name: "private_path_service_endpoints", Value: []string{"vpc-pps.dev.internal"}, DataType: "list(string)"}, - {Name: "application_loadbalancer_pool_member_ip_address", Value: []string{terraform.Output(t, existingTerraformOptions, "member_ip_address")}, DataType: "list(string)"}, + terraform.WorkspaceSelectOrNew(t, existingTerraformOptions, prefix) + + _, existErr := terraform.InitAndApplyE(t, existingTerraformOptions) + if existErr != nil { + assert.True(t, existErr == nil, "Init and Apply of temp existing resource failed") + } else { + // ------------------------------------------------------------------------------------ + // Deploy Private path solution + // ------------------------------------------------------------------------------------ + options := testhelper.TestOptionsDefault(&testhelper.TestOptions{ + Testing: t, + TerraformDir: fullyConfigurableSolutionDir, + // Do not hard fail the test if the implicit destroy steps fail to allow a full destroy of resource to occur + ImplicitRequired: false, + TerraformVars: map[string]interface{}{ + "prefix": prefix, + "existing_resource_group_name": terraform.Output(t, existingTerraformOptions, "resource_group_name"), + "region": terraform.Output(t, existingTerraformOptions, "region"), + "application_loadbalancer_listener_certificate_crn": terraform.Output(t, existingTerraformOptions, "sm_private_cert_crn"), + "existing_subnet_id": terraform.Output(t, existingTerraformOptions, "existing_subnet_id"), + "existing_secrets_manager_instance_crn": permanentResources["secretsManagerCRN"], + "private_path_service_endpoints": []string{"vpc-pps.dev.internal"}, + "application_loadbalancer_pool_member_ip_address": []string{terraform.Output(t, existingTerraformOptions, "member_ip_address")}, + "provider_visibility": "public", + }, + }) + output, err := options.RunTestConsistency() + assert.Nil(t, err, "This should not have errored") + assert.NotNil(t, output, "Expected some output") + } + + // Check if "DO_NOT_DESTROY_ON_FAILURE" is set + envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") + // Destroy the temporary existing resources if required + if t.Failed() && strings.ToLower(envVal) == "true" { + fmt.Println("Terratest failed. Debug the test and delete resources manually.") + } else { + logger.Log(t, "START: Destroy (existing resources)") + terraform.Destroy(t, existingTerraformOptions) + terraform.WorkspaceDelete(t, existingTerraformOptions, prefix) + logger.Log(t, "END: Destroy (existing resources)") + } +} + +func TestAddonsDefaultConfiguration(t *testing.T) { + t.Parallel() + + // Verify ibmcloud_api_key variable is set + checkVariable := "TF_VAR_ibmcloud_api_key" + val, present := os.LookupEnv(checkVariable) + require.True(t, present, checkVariable+" environment variable not set") + require.NotEqual(t, "", val, checkVariable+" environment variable is empty") + + // Programmatically determine region to use based on availability + region, _ := testhelper.GetBestVpcRegion(val, "../common-dev-assets/common-go-assets/cloudinfo-region-vpc-gen2-prefs.yaml", "eu-de") + + options := testaddons.TestAddonsOptionsDefault(&testaddons.TestAddonOptions{ + Testing: t, + Prefix: "pp-vpc", + QuietMode: false, // Suppress logs except on failure + }) + + options.AddonConfig = cloudinfo.NewAddonConfigTerraform( + options.Prefix, + "deploy-arch-ibm-is-private-path-ext-conn", + "fully-configurable", + map[string]interface{}{ + "region": region, + "secrets_manager_service_plan": "trial", + "private_path_service_endpoints": []string{"vpc-pps.dev.internal"}, + }, + ) + + options.AddonConfig.Dependencies = []cloudinfo.AddonConfig{ + // The VPC must be created explicitly because it is disabled by default in the catalog configuration due to this issue(https://github.ibm.com/ibmcloud/content-catalog/issues/6057). + { + OfferingName: "deploy-arch-ibm-slz-vpc", + OfferingFlavor: "fully-configurable", + Enabled: core.BoolPtr(true), + Inputs: map[string]interface{}{ + "region": region, + }, + }, + // use existing secrets manager instance to prevent hitting 20 trial instance limit in account + { + OfferingName: "deploy-arch-ibm-secrets-manager", + OfferingFlavor: "fully-configurable", + Inputs: map[string]interface{}{ + "existing_secrets_manager_crn": permanentResources["secretsManagerCRN"], + "service_plan": "__NULL__", // no plan value needed when using existing SM + "skip_secrets_manager_iam_auth_policy": true, // since using an existing Secrets Manager instance, attempting to re-create auth policy can cause conflicts if the policy already exists + "secret_groups": []string{}, // passing empty array for secret groups as default value is creating general group and it will cause conflicts as we are using an existing SM + }, + }, + // Disable target / route creation to prevent hitting quota in account + { + OfferingName: "deploy-arch-ibm-cloud-monitoring", + OfferingFlavor: "fully-configurable", + Inputs: map[string]interface{}{ + "enable_metrics_routing_to_cloud_monitoring": false, + }, + }, + { + OfferingName: "deploy-arch-ibm-activity-tracker", + OfferingFlavor: "fully-configurable", + Inputs: map[string]interface{}{ + "enable_activity_tracker_event_routing_to_cloud_logs": false, + }, + }, } - require.NoError(t, options.RunSchematicUpgradeTest(), "This should not have errored") - cleanupTerraform(t, existingTerraformOptions, prefix) + err := options.RunAddonTest() + require.NoError(t, err) } diff --git a/tests/resources/main.tf b/tests/resources/main.tf index 21095c8..1c0afd9 100644 --- a/tests/resources/main.tf +++ b/tests/resources/main.tf @@ -4,7 +4,7 @@ module "mock_resource_group" { source = "terraform-ibm-modules/resource-group/ibm" - version = "1.4.0" + version = "1.4.3" resource_group_name = "${var.prefix}-mock-rg" } @@ -113,3 +113,50 @@ resource "ibm_is_subnet" "provider_subnet" { ipv4_cidr_block = "10.100.10.0/24" tags = var.resource_tags } + +################################################################################# +# Secrets Manager resources +################################################################################# + +locals { + sm_region = var.existing_secrets_manager_instance_crn != null ? module.existing_sm_crn_parser[0].region : null + secrets_manager_guid = var.existing_secrets_manager_instance_crn != null ? module.existing_sm_crn_parser[0].service_instance : null +} + +module "existing_sm_crn_parser" { + count = var.existing_secrets_manager_instance_crn != null ? 1 : 0 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.2.0" + crn = var.existing_secrets_manager_instance_crn +} + +# Create a secret group to place the certificate if provisioning a new certificate +module "secrets_manager_group" { + count = var.existing_secrets_manager_instance_crn != null ? 1 : 0 + source = "terraform-ibm-modules/secrets-manager-secret-group/ibm" + version = "1.3.15" + region = local.sm_region + secrets_manager_guid = local.secrets_manager_guid + secret_group_name = "${var.prefix}-cert-secret-group" + secret_group_description = "secret group used for private certificates" + providers = { + ibm = ibm.ibm-sm + } +} + +# Create private certificate to use for VPN server +module "secrets_manager_private_certificate" { + count = var.existing_secrets_manager_instance_crn != null ? 1 : 0 + source = "terraform-ibm-modules/secrets-manager-private-cert/ibm" + version = "1.7.0" + cert_name = "${var.prefix}-cts-vpn-private-cert" + cert_description = "an example private cert" + cert_template = var.certificate_template_name + cert_secrets_group_id = module.secrets_manager_group[0].secret_group_id + cert_common_name = "${var.prefix}-example.com" + secrets_manager_guid = local.secrets_manager_guid + secrets_manager_region = local.sm_region + providers = { + ibm = ibm.ibm-sm + } +} diff --git a/tests/resources/outputs.tf b/tests/resources/outputs.tf index c3bcaa9..fd8df67 100644 --- a/tests/resources/outputs.tf +++ b/tests/resources/outputs.tf @@ -26,3 +26,8 @@ output "region" { value = var.region description = "Region of the resources." } + +output "sm_private_cert_crn" { + value = var.existing_secrets_manager_instance_crn != null ? module.secrets_manager_private_certificate[0].secret_crn : null + description = "CRN of secrets manager private certificate" +} diff --git a/tests/resources/provider.tf b/tests/resources/provider.tf index df45ef5..1032b23 100644 --- a/tests/resources/provider.tf +++ b/tests/resources/provider.tf @@ -2,3 +2,9 @@ provider "ibm" { ibmcloud_api_key = var.ibmcloud_api_key region = var.region } + +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = local.sm_region + alias = "ibm-sm" +} diff --git a/tests/resources/variables.tf b/tests/resources/variables.tf index 0dcda66..0e7f0d2 100644 --- a/tests/resources/variables.tf +++ b/tests/resources/variables.tf @@ -21,3 +21,15 @@ variable "resource_tags" { description = "Optional list of tags to be added to created resources." default = ["private-path"] } + +variable "existing_secrets_manager_instance_crn" { + type = string + description = "The CRN of existing secrets manager to use to create service credential secrets." + default = null +} + +variable "certificate_template_name" { + type = string + description = "The name of the Certificate Template to create for a private certificate" + default = null +}