diff --git a/.catalog-onboard-pipeline.yaml b/.catalog-onboard-pipeline.yaml new file mode 100644 index 0000000..6e5ed9d --- /dev/null +++ b/.catalog-onboard-pipeline.yaml @@ -0,0 +1,14 @@ +# The contents of this file are used by an IBM internal pipeline to onboard solutions to the IBM catalog +--- +apiVersion: v1 +offerings: + - name: deploy-arch-ibm-is-private-path-ext-conn + kind: solution + catalog_id: 7df1e4ca-d54c-4fd0-82ce-3d13247308cd + offering_id: 0be06773-9d63-439e-8211-c9787a0d27e7 + variations: + - name: fully-configurable + mark_ready: true + install_type: fullstack + pre_validation: "tests/scripts/pre-validation-private-path-vpc.sh" + post_validation: "tests/scripts/post-validation-private-path-vpc.sh" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d545b87..2c8de91 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # Primary owner should be listed first in list of global owners, followed by any secondary owners -* @ocofaigh @daniel-butler-irl +* @Aashiq-J @Aayush-Abhyarthi diff --git a/.secrets.baseline b/.secrets.baseline index 0a4efa4..aebc5b5 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "go.sum|^.secrets.baseline$", "lines": null }, - "generated_at": "2024-11-22T17:36:38Z", + "generated_at": "2025-05-27T05:56:18Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -76,18 +76,7 @@ "name": "TwilioKeyDetector" } ], - "results": { - "README.md": [ - { - "hashed_secret": "ff9ee043d85595eb255c05dfe32ece02a53efbb2", - "is_secret": false, - "is_verified": false, - "line_number": 74, - "type": "Secret Keyword", - "verified_result": null - } - ] - }, + "results": {}, "version": "0.13.1+ibm.62.dss", "word_list": { "file": null, diff --git a/README.md b/README.md index 563fdeb..07dc843 100644 --- a/README.md +++ b/README.md @@ -1,160 +1,24 @@ - -# Terraform modules template project +# 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) -[![latest release](https://img.shields.io/github/v/release/terraform-ibm-modules/terraform-ibm-vpc-private-path-external-connectivity?logo=GitHub&sort=semver)](https://github.com/terraform-ibm-modules/terraform-ibm-vpc-private-path-external-connectivity/releases/latest) +[![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/) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) - +:exclamation: **Important:** These solutions are not intended to be called by other modules because they contain provider configurations and are not compatible with the `for_each`, `count`, and `depends_on` Terraform arguments. For more information, see [Providers Within Modules](https://developer.hashicorp.com/terraform/language/modules/develop/providers). -TODO: Replace this with a description of the modules in this repo. +The repository includes the following artefacts for configuration. +- [A GitHub Actions workflow to run the common CI pipeline for Terraform](./.github/workflows). For more information, see the common-pipeline-assets [readme file](https://github.com/terraform-ibm-modules/common-pipeline-assets/blob/main/README.md)). +- A [common-dev-assets](./common-dev-assets) Git submodule with common automation that is used for CI and development. For more information, see [Local development setup](https://terraform-ibm-modules.github.io/documentation/#/local-dev-setup)). +- An [ibm_catalog.json](ibm_catalog.json) file that is used for onboarding the sample deployable architectures to the IBM Cloud catalog. +- A [.catalog-onboard-pipeline.yaml](.catalog-onboard-pipeline.yaml) file that is used by an IBM internal pipeline to onboard deployable architectures to the IBM catalog. +- A [renovate.json](renovate.json) file that supports automatic creation of PRs to update dependencies. The Renovate pipeline runs regularly against all repos in the [terraform-ibm-modules](https://github.com/terraform-ibm-modules) org. - - - -## Overview -* [terraform-ibm-vpc-private-path-external-connectivity](#terraform-ibm-vpc-private-path-external-connectivity) -* [Examples](./examples) - * [Advanced example](./examples/advanced) - * [Basic example](./examples/basic) -* [Contributing](#contributing) - - - - - - - - -## terraform-ibm-vpc-private-path-external-connectivity - -### Usage - - - -```hcl -terraform { - required_version = ">= 1.9.0" - required_providers { - ibm = { - source = "IBM-Cloud/ibm" - version = "X.Y.Z" # Lock into a provider version that satisfies the module constraints - } - } -} - -locals { - region = "us-south" -} - -provider "ibm" { - ibmcloud_api_key = "XXXXXXXXXX" # replace with apikey value - region = local.region -} - -module "module_template" { - source = "terraform-ibm-modules//ibm" - version = "X.Y.Z" # Replace "X.Y.Z" with a release version to lock into a specific release - region = local.region - name = "instance-name" - resource_group_id = "xxXXxxXXxXxXXXXxxXxxxXXXXxXXXXX" # Replace with the actual ID of resource group to use -} -``` - -### Required access policies - - - - - - - - - - - - -### Requirements - -| Name | Version | -|------|---------| -| [terraform](#requirement\_terraform) | >= 1.9.0 | -| [ibm](#requirement\_ibm) | >= 1.71.2, < 2.0.0 | - -### Modules - -No modules. - -### Resources - -| Name | Type | -|------|------| -| [ibm_resource_instance.cos_instance](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/resource_instance) | resource | - -### Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| [name](#input\_name) | A descriptive name used to identify the resource instance. | `string` | n/a | yes | -| [plan](#input\_plan) | The name of the plan type supported by service. | `string` | `"standard"` | no | -| [resource\_group\_id](#input\_resource\_group\_id) | The ID of the resource group where you want to create the service. | `string` | n/a | yes | -| [resource\_tags](#input\_resource\_tags) | List of resource tag to associate with the instance. | `list(string)` | `[]` | no | - -### Outputs - -| Name | Description | -|------|-------------| -| [account\_id](#output\_account\_id) | An alpha-numeric value identifying the account ID. | -| [crn](#output\_crn) | The CRN of the resource instance. | -| [guid](#output\_guid) | The GUID of the resource instance. | -| [id](#output\_id) | The unique identifier of the resource instance. | - - - + ## Contributing You can report issues and request features for this module in GitHub issues in the module repo. See [Report an issue or request a feature](https://github.com/terraform-ibm-modules/.github/blob/main/.github/SUPPORT.md). diff --git a/cra-config.yaml b/cra-config.yaml index 9a4c7fa..252ce2b 100644 --- a/cra-config.yaml +++ b/cra-config.yaml @@ -7,11 +7,15 @@ version: "v1" CRA_TARGETS: - - CRA_TARGET: "examples/advanced" # Target directory for CRA scan. If not provided, the CRA Scan will not be run. + - CRA_TARGET: "solutions/fully-configurable" # Target directory for CRA scan. If not provided, the CRA Scan will not be run. CRA_IGNORE_RULES_FILE: "cra-tf-validate-ignore-rules.json" PROFILE_ID: "fe96bd4d-9b37-40f2-b39f-a62760e326a3" # SCC profile ID (currently set to 'IBM Cloud Framework for Financial Services' '1.7.0' profile). # SCC_INSTANCE_ID: "" # The SCC instance ID to use to download profile for CRA scan. If not provided, a default global value will be used. # SCC_REGION: "" # The IBM Cloud region that the SCC instance is in. If not provided, a default global value will be used. - CRA_ENVIRONMENT_VARIABLES: # An optional map of environment variables for CRA, where the key is the variable name and value is the value. Useful for providing TF_VARs. + CRA_ENVIRONMENT_VARIABLES: # An optional map of environment variables for CRA, where the key is the variable name and value is the value. Useful for providing TF_VARs. TF_VAR_prefix: "mock" TF_VAR_region: "us-south" + TF_VAR_application_loadbalancer_pool_member_ip_address: ["159.23.92.5", "159.23.92.6"] + TF_VAR_private_path_service_endpoints: ["vpc-pp.example.com"] + TF_VAR_existing_vpc_id: "r006-a95450db-e5c3-49fb-b630-31aea1deb8d8" + TF_VAR_provider_visibility: "public" diff --git a/cra-tf-validate-ignore-rules.json b/cra-tf-validate-ignore-rules.json index adbff6e..a9ad3b2 100644 --- a/cra-tf-validate-ignore-rules.json +++ b/cra-tf-validate-ignore-rules.json @@ -1,3 +1,16 @@ { - "scc_rules": [] + "scc_rules": [ + { + "scc_rule_id": "rule-0e5151b1-9caf-433c-b4e5-be3d505e458e", + "description": "Check whether Application Load Balancer for VPC is configured with multiple members in the pool", + "ignore_reason": "This rule is not relevant to since we are attaching IP addresses to the ALB.", + "is_valid": false + }, + { + "scc_rule_id": "rule-d544f217-3723-4376-b3aa-037c5f201e8d", + "description": "Check whether Application Load Balancer for VPC uses HTTPS (SSL & TLS) instead of HTTP", + "ignore_reason": "This rule is not relevant since ALB will be a member of the Private path NLB.", + "is_valid": false + } + ] } diff --git a/examples/advanced/README.md b/examples/advanced/README.md deleted file mode 100644 index d52511a..0000000 --- a/examples/advanced/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Advanced example - - - diff --git a/examples/advanced/main.tf b/examples/advanced/main.tf deleted file mode 100644 index 88360af..0000000 --- a/examples/advanced/main.tf +++ /dev/null @@ -1,32 +0,0 @@ -######################################################################################################################## -# Resource group -######################################################################################################################## - -module "resource_group" { - source = "terraform-ibm-modules/resource-group/ibm" - version = "1.2.0" - # if an existing resource group is not set (null) create a new one using prefix - resource_group_name = var.resource_group == null ? "${var.prefix}-resource-group" : null - existing_resource_group_name = var.resource_group -} - -######################################################################################################################## -# COS -######################################################################################################################## - -# -# Developer tips: -# - Call the local module / modules in the example to show how they can be consumed -# - Include the actual module source as a code comment like below so consumers know how to consume from correct location -# - -module "cos" { - source = "../.." - # remove the above line and uncomment the below 2 lines to consume the module from the registry - # source = "terraform-ibm-modules//ibm" - # version = "X.Y.Z" # Replace "X.Y.Z" with a release version to lock into a specific release - name = "${var.prefix}-cos" - resource_group_id = module.resource_group.resource_group_id - resource_tags = var.resource_tags - plan = "cos-one-rate-plan" -} diff --git a/examples/advanced/outputs.tf b/examples/advanced/outputs.tf deleted file mode 100644 index 316751f..0000000 --- a/examples/advanced/outputs.tf +++ /dev/null @@ -1,38 +0,0 @@ -############################################################################## -# Outputs -############################################################################## - -# -# Developer tips: -# - Include all relevant outputs from the modules being called in the example -# - -output "account_id" { - description = "An alpha-numeric value identifying the account ID." - value = module.cos.account_id -} - -output "guid" { - description = "The GUID of the resource instance." - value = module.cos.account_id -} - -output "id" { - description = "The unique identifier of the resource instance." - value = module.cos.id -} - -output "crn" { - description = "The CRN of the resource instance." - value = module.cos.crn -} - -output "resource_group_name" { - description = "Resource group name." - value = module.resource_group.resource_group_name -} - -output "resource_group_id" { - description = "Resource group ID." - value = module.resource_group.resource_group_id -} diff --git a/examples/advanced/provider.tf b/examples/advanced/provider.tf deleted file mode 100644 index 2080946..0000000 --- a/examples/advanced/provider.tf +++ /dev/null @@ -1,8 +0,0 @@ -############################################################################## -# Provider config -############################################################################## - -provider "ibm" { - ibmcloud_api_key = var.ibmcloud_api_key - region = var.region -} diff --git a/examples/advanced/variables.tf b/examples/advanced/variables.tf deleted file mode 100644 index d460364..0000000 --- a/examples/advanced/variables.tf +++ /dev/null @@ -1,39 +0,0 @@ -######################################################################################################################## -# Input variables -######################################################################################################################## - -# -# Module developer tips: -# - Examples are references that consumers can use to see how the module can be consumed. They are not designed to be -# flexible re-usable solutions for general consumption, so do not expose any more variables here and instead hard -# code things in the example main.tf with code comments explaining the different configurations. -# - For the same reason as above, do not add default values to the example inputs. -# - -variable "ibmcloud_api_key" { - type = string - description = "The IBM Cloud API Key." - sensitive = true -} - -variable "region" { - type = string - description = "Region to provision all resources created by this example." -} - -variable "prefix" { - type = string - description = "A string value to prefix to all resources created by this example." -} - -variable "resource_group" { - type = string - description = "The name of an existing resource group to provision resources in to. If not set a new resource group will be created using the prefix variable." - default = null -} - -variable "resource_tags" { - type = list(string) - description = "List of resource tag to associate with all resource instances created by this example." - default = [] -} diff --git a/examples/advanced/version.tf b/examples/advanced/version.tf deleted file mode 100644 index ecfa978..0000000 --- a/examples/advanced/version.tf +++ /dev/null @@ -1,16 +0,0 @@ -terraform { - required_version = ">= 1.9.0" - - # - # Developer tips: - # - Ensure that there is always 1 example locked into the lowest provider version of the range defined in the main - # module's version.tf (usually a basic example), and 1 example that will always use the latest provider version. - # - - required_providers { - ibm = { - source = "IBM-Cloud/ibm" - version = ">= 1.71.2, < 2.0.0" - } - } -} diff --git a/examples/basic/README.md b/examples/basic/README.md deleted file mode 100644 index e5977ae..0000000 --- a/examples/basic/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Basic example - - - -An end-to-end basic example that will provision the following: -- A new resource group if one is not passed in. -- A new standard plan Cloud Object Storage instance using the root level module. diff --git a/examples/basic/main.tf b/examples/basic/main.tf deleted file mode 100644 index cf665db..0000000 --- a/examples/basic/main.tf +++ /dev/null @@ -1,31 +0,0 @@ -######################################################################################################################## -# Resource group -######################################################################################################################## - -module "resource_group" { - source = "terraform-ibm-modules/resource-group/ibm" - version = "1.2.0" - # if an existing resource group is not set (null) create a new one using prefix - resource_group_name = var.resource_group == null ? "${var.prefix}-resource-group" : null - existing_resource_group_name = var.resource_group -} - -######################################################################################################################## -# COS -######################################################################################################################## - -# -# Developer tips: -# - Call the local module / modules in the example to show how they can be consumed -# - include the actual module source as a code comment like below so consumers know how to consume from correct location -# - -module "cos" { - source = "../.." - # remove the above line and uncomment the below 2 lines to consume the module from the registry - # source = "terraform-ibm-modules//ibm" - # version = "X.Y.Z" # Replace "X.Y.Z" with a release version to lock into a specific release - name = "${var.prefix}-cos" - resource_group_id = module.resource_group.resource_group_id - resource_tags = var.resource_tags -} diff --git a/examples/basic/outputs.tf b/examples/basic/outputs.tf deleted file mode 100644 index 552db48..0000000 --- a/examples/basic/outputs.tf +++ /dev/null @@ -1,38 +0,0 @@ -######################################################################################################################## -# Outputs -######################################################################################################################## - -# -# Developer tips: -# - Include all relevant outputs from the modules being called in the example -# - -output "account_id" { - description = "An alpha-numeric value identifying the account ID." - value = module.cos.account_id -} - -output "guid" { - description = "The GUID of the resource instance." - value = module.cos.account_id -} - -output "id" { - description = "The unique identifier of the resource instance." - value = module.cos.id -} - -output "crn" { - description = "The CRN of the resource instance." - value = module.cos.crn -} - -output "resource_group_name" { - description = "Resource group name." - value = module.resource_group.resource_group_name -} - -output "resource_group_id" { - description = "Resource group ID." - value = module.resource_group.resource_group_id -} diff --git a/examples/basic/variables.tf b/examples/basic/variables.tf deleted file mode 100644 index d460364..0000000 --- a/examples/basic/variables.tf +++ /dev/null @@ -1,39 +0,0 @@ -######################################################################################################################## -# Input variables -######################################################################################################################## - -# -# Module developer tips: -# - Examples are references that consumers can use to see how the module can be consumed. They are not designed to be -# flexible re-usable solutions for general consumption, so do not expose any more variables here and instead hard -# code things in the example main.tf with code comments explaining the different configurations. -# - For the same reason as above, do not add default values to the example inputs. -# - -variable "ibmcloud_api_key" { - type = string - description = "The IBM Cloud API Key." - sensitive = true -} - -variable "region" { - type = string - description = "Region to provision all resources created by this example." -} - -variable "prefix" { - type = string - description = "A string value to prefix to all resources created by this example." -} - -variable "resource_group" { - type = string - description = "The name of an existing resource group to provision resources in to. If not set a new resource group will be created using the prefix variable." - default = null -} - -variable "resource_tags" { - type = list(string) - description = "List of resource tag to associate with all resource instances created by this example." - default = [] -} diff --git a/examples/basic/version.tf b/examples/basic/version.tf deleted file mode 100644 index 401504c..0000000 --- a/examples/basic/version.tf +++ /dev/null @@ -1,16 +0,0 @@ -terraform { - required_version = ">= 1.9.0" - - # - # Developer tips: - # - Ensure that there is always 1 example locked into the lowest provider version of the range defined in the main - # module's version.tf (usually a basic example), and 1 example that will always use the latest provider version. - # - - required_providers { - ibm = { - source = "IBM-Cloud/ibm" - version = "1.71.2" - } - } -} diff --git a/ibm_catalog.json b/ibm_catalog.json new file mode 100644 index 0000000..46fc036 --- /dev/null +++ b/ibm_catalog.json @@ -0,0 +1,379 @@ +{ + "products": [ + { + "name": "deploy-arch-ibm-is-private-path-ext-conn", + "label": "IBM Cloud Essentials Private Path service for external connectivity", + "product_kind": "solution", + "tags": [ + "ibm_created", + "target_terraform", + "terraform", + "solution", + "network" + ], + "keywords": [ + "vpc", + "private path", + "private", + "nlb", + "alb", + "IaC", + "infrastructure as code", + "terraform", + "solution" + ], + "short_description": "Automates the deployment of a VPC Private Path service on IBM Cloud with integration of Application Loadbalancer for external connectivity.", + "long_description": "Private network connectivity is essential for IBM Cloud® customers who prioritize privacy, security, and compliance.\n\nThrough Private Path services for VPC, providers can deliver their cloud and on-premises services over the IBM Cloud private network backbone, ensuring secure and private interactions for consumers.\n\nYou can use this solution to provision and configure a VPC Private Path service to securely connect services hosted in IBM Cloud VPC, on-premise or other reachable external locations.\n\nℹ️ This Terraform-based automation is part of a broader suite of IBM-maintained Infrastructure as Code (IaC) assets, each following the naming pattern \"Cloud automation for *servicename*\" and focusing on single IBM Cloud service. These single-service deployable architectures can be used on their own to streamline and automate service deployments through an [IaC approach](https://cloud.ibm.com/docs/secure-enterprise?topic=secure-enterprise-understanding-projects), or assembled together into a broader [automated IaC stack](https://cloud.ibm.com/docs/secure-enterprise?topic=secure-enterprise-config-stack) to automate the deployment of an end-to-end solution architecture.", + "offering_docs_url": "https://github.com/terraform-ibm-modules/terraform-ibm-vpc-private-path-external-connectivity/blob/main/solutions/fully-configurable/README.md", + "offering_icon_url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-vpc-private-path-external-connectivity/main/images/private_path.svg", + "provider_name": "IBM", + "features": [ + { + "title": "Creates an Application load balancer.", + "description": "Sets up an IBM Application load balancer with a backend pool to connect to the external service." + }, + { + "title": "Creates a Private Path service.", + "description": "Creates an IBM VPC Private Path service." + }, + { + "title": "Sets up a Private Path Network load balancer.", + "description": "Sets up an IBM Private Path Network load balancer with a backend pool to connect to the VPE Gateway." + } + ], + "flavors": [ + { + "label": "Fully configurable", + "name": "fully-configurable", + "install_type": "fullstack", + "working_directory": "solutions/fully-configurable", + "compliance": { + "authority": "scc-v3", + "profiles": [ + { + "profile_name": "IBM Cloud Framework for Financial Services", + "profile_version": "1.7.0" + } + ] + }, + "iam_permissions": [ + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Administrator" + ], + "service_name": "iam-identity", + "notes": "[Optional] Required if Cloud automation for account configuration is enabled." + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Administrator" + ], + "service_name": "is.vpc", + "notes": "[Optional] Required if Cloud automation for Virtual Private Cloud(VPC) is enabled." + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "service_name": "is.vpc", + "notes": "Required for creating Private-path service and Application Load balancer." + }, + { + "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." + } + ], + "configuration": [ + { + "key": "ibmcloud_api_key" + }, + { + "custom_config": { + "config_constraints": { + "generationType": "2" + }, + "grouping": "deployment", + "original_grouping": "deployment", + "type": "vpc_region" + }, + "key": "region", + "required": true + }, + { + "key": "prefix", + "required": true + }, + { + "key": "existing_resource_group_name", + "display_name": "resource_group", + "required": true, + "custom_config": { + "type": "resource_group", + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "identifier": "rg_name" + } + } + }, + { + "key": "existing_vpc_id" + }, + { + "key": "existing_subnet_id", + "required": true, + "custom_config": { + "type": "platform_resource", + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "resourceType": "subnet", + "selection": "single_select", + "valueType": "id" + } + } + }, + { + "key": "private_path_service_endpoints", + "required": true, + "custom_config": { + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "type": "string" + } + } + }, + { + "key": "private_path_tags", + "custom_config": { + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "type": "string" + } + } + }, + { + "key": "private_path_access_tags", + "custom_config": { + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "type": "string" + } + } + }, + { + "key": "private_path_name" + }, + { + "key": "private_path_default_access_policy", + "options": [ + { + "displayname": "Review", + "value": "review" + }, + { + "displayname": "Permit", + "value": "permit" + }, + { + "displayname": "Deny", + "value": "deny" + } + ] + }, + { + "key": "private_path_publish" + }, + { + "key": "private_path_zonal_affinity" + }, + { + "key": "private_path_account_policies" + }, + { + "key": "network_loadbalancer_name" + }, + { + "key": "application_loadbalancer_listener_certificate_instance" + }, + { + "key": "application_loadbalancer_listener_idle_timeout" + }, + { + "key": "application_loadbalancer_listener_port" + }, + { + "key": "application_loadbalancer_listener_protocol" + }, + { + "key": "application_loadbalancer_pool_algorithm" + }, + { + "key": "application_loadbalancer_pool_health_delay" + }, + { + "key": "application_loadbalancer_pool_health_retries" + }, + { + "key": "application_loadbalancer_pool_health_timeout" + }, + { + "key": "application_loadbalancer_pool_health_type" + }, + { + "key": "application_loadbalancer_pool_member_ip_address" + }, + { + "key": "application_loadbalancer_pool_member_port" + }, + { + "key": "application_loadbalancer_pool_protocol" + }, + { + "key": "application_loadbalancer_type", + "options": [ + { + "displayname": "public", + "value": "public" + }, + { + "displayname": "private", + "value": "private" + } + ] + }, + { + "key": "network_loadbalancer_listener_accept_proxy_protocol" + }, + { + "key": "network_loadbalancer_listener_port" + }, + { + "key": "network_loadbalancer_pool_algorithm" + }, + { + "key": "network_loadbalancer_pool_health_delay" + }, + { + "key": "network_loadbalancer_pool_health_monitor_port" + }, + { + "key": "network_loadbalancer_pool_health_monitor_url" + }, + { + "key": "network_loadbalancer_pool_health_retries" + }, + { + "key": "network_loadbalancer_pool_health_timeout" + }, + { + "key": "network_loadbalancer_pool_health_type" + }, + { + "key": "network_loadbalancer_pool_member_port" + }, + { + "key": "provider_visibility", + "hidden": true, + "options": [ + { + "displayname": "private", + "value": "private" + }, + { + "displayname": "public", + "value": "public" + }, + { + "displayname": "public-and-private", + "value": "public-and-private" + } + ] + } + ], + "architecture": { + "features": [ + { + "title": " ", + "description": "Configured to use IBM secure by default standards, but can be edited to fit your use case." + } + ], + "diagrams": [ + { + "diagram": { + "caption": "Private Path", + "url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-vpc-private-path-external-connectivity/main/reference-architecture/reference-architectures/private-path-external-connectivity.svg", + "type": "image/svg+xml" + }, + "description": "You can deploy a Private Path service on IBM Cloud to facilitate secure, private connectivity between IBM Cloud Virtual Private Cloud (VPC) environments and on-premises networks or external services using this solution. This solution automates the deployment of IBM Cloud's Private Path and Application Load Balancer (ALB) services to enable enterprises to establish robust, private, and scalable network connections.

The Private Path service establishes private Layer 3 network connectivity between IBM Cloud VPC resources and external destinations such as on-premises data centers or other cloud environments. It leverages secure IPsec tunnels and Direct Link connections to maintain encrypted, low-latency communication, ensuring sensitive workloads can operate within a highly secure, isolated network path that bypasses the public internet.

By automating the provisioning and configuration of these components, the solution delivers a seamless, production-ready framework for establishing hybrid connectivity, optimizing both performance and security. This approach enhances operational agility while maintaining enterprise-grade standards for secure data exchange across cloud and on-premises boundaries." + } + ] + }, + "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", + "flavors": [ + "resource-group-only", + "resource-groups-with-account-settings" + ], + "default_flavor": "resource-group-only", + "id": "63641cec-6093-4b4f-b7b0-98d2f4185cd6-global", + "input_mapping": [ + { + "dependency_input": "prefix", + "version_input": "prefix", + "reference_version": true + }, + { + "dependency_output": "workload_resource_group_name", + "version_input": "existing_resource_group_name" + } + ], + "optional": true, + "on_by_default": false, + "version": "v3.0.7" + }, + { + "name": "deploy-arch-ibm-vpc", + "description": "Cloud automation for VPC provisions and configures an IBM Virtual Private Cloud (VPC) environment with subnets, network ACLs, public gateways, and routing tables.", + "catalog_id": "7a4d68b4-cf8b-40cd-a3d1-f49aff526eb3", + "flavors": [ + "fully-configurable" + ], + "id": "2af61763-f8ef-4527-a815-b92166f29bc8-global", + "input_mapping": [ + { + "dependency_input": "prefix", + "version_input": "prefix", + "reference_version": true + }, + { + "dependency_input": "existing_resource_group_name", + "version_input": "existing_resource_group_name", + "reference_version": true + }, + { + "dependency_output": "vpc_id", + "version_input": "existing_vpc_id" + } + ], + "optional": true, + "on_by_default": true, + "version": "^v7.24.0" + } + ], + "dependency_version_2": true, + "terraform_version": "1.10.5" + } + ] + } + ] +} diff --git a/main.tf b/main.tf deleted file mode 100644 index b6b879e..0000000 --- a/main.tf +++ /dev/null @@ -1,13 +0,0 @@ -# -# Developer tips: -# - Below code should be replaced with the code for the root level module -# - -resource "ibm_resource_instance" "cos_instance" { - name = var.name - resource_group_id = var.resource_group_id - service = "cloud-object-storage" - plan = var.plan - location = "global" - tags = var.resource_tags -} diff --git a/outputs.tf b/outputs.tf deleted file mode 100644 index 0286200..0000000 --- a/outputs.tf +++ /dev/null @@ -1,30 +0,0 @@ -######################################################################################################################## -# Outputs -######################################################################################################################## - -# -# Developer tips: -# - Below are some good practise sample outputs -# - They should be updated for outputs applicable to the module being added -# - Use variable validation when possible -# - -output "account_id" { - description = "An alpha-numeric value identifying the account ID." - value = ibm_resource_instance.cos_instance.account_id -} - -output "guid" { - description = "The GUID of the resource instance." - value = ibm_resource_instance.cos_instance.guid -} - -output "id" { - description = "The unique identifier of the resource instance." - value = ibm_resource_instance.cos_instance.id -} - -output "crn" { - description = "The CRN of the resource instance." - value = ibm_resource_instance.cos_instance.crn -} diff --git a/reference-architectures/private-path-external-connectivity.svg b/reference-architectures/private-path-external-connectivity.svg new file mode 100644 index 0000000..0d0ae14 --- /dev/null +++ b/reference-architectures/private-path-external-connectivity.svg @@ -0,0 +1,4 @@ + + + +IBM CloudRegionResource GroupVPCSubnet load-balancer--application ApplicationLoadBalancer load-balancer--network Private PathNLB
Private Path service
On-Premisesworkload / service
\ No newline at end of file diff --git a/solutions/fully-configurable/README.md b/solutions/fully-configurable/README.md new file mode 100644 index 0000000..6e385e3 --- /dev/null +++ b/solutions/fully-configurable/README.md @@ -0,0 +1,3 @@ +# Cloud essentials for private-path external connectivity (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/fully-configurable/catalogValidationValues.json.template b/solutions/fully-configurable/catalogValidationValues.json.template new file mode 100644 index 0000000..a84b4bc --- /dev/null +++ b/solutions/fully-configurable/catalogValidationValues.json.template @@ -0,0 +1,7 @@ +{ + "ibmcloud_api_key": $VALIDATION_APIKEY, + "region": "us-south", + "prefix": $PREFIX, + "private_path_name": $PREFIX, + "private_path_service_endpoints": ["vpc-pps.example.com"] +} diff --git a/solutions/fully-configurable/main.tf b/solutions/fully-configurable/main.tf new file mode 100644 index 0000000..6d5a5c4 --- /dev/null +++ b/solutions/fully-configurable/main.tf @@ -0,0 +1,87 @@ +####################################################################################################################### +# Resource Group +####################################################################################################################### + +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.2.0" + existing_resource_group_name = var.existing_resource_group_name +} + +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 +} + +data "ibm_is_vpc" "vpc" { + count = var.existing_vpc_id != null && var.existing_subnet_id == null ? 1 : 0 + identifier = var.existing_vpc_id +} + +resource "ibm_is_lb" "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" + lb = ibm_is_lb.alb.id + algorithm = var.application_loadbalancer_pool_algorithm + protocol = var.application_loadbalancer_pool_protocol + health_delay = var.application_loadbalancer_pool_health_delay + health_retries = var.application_loadbalancer_pool_health_retries + health_timeout = var.application_loadbalancer_pool_health_timeout + health_type = var.application_loadbalancer_pool_health_type +} + +resource "ibm_is_lb_pool_member" "alb_pool_members" { + count = length(var.application_loadbalancer_pool_member_ip_address) + port = var.application_loadbalancer_pool_member_port + lb = ibm_is_lb.alb.id + pool = element(split("/", ibm_is_lb_pool.alb_backend_pool.pool_id), 1) + target_address = var.application_loadbalancer_pool_member_ip_address[count.index] +} + +resource "ibm_is_lb_listener" "alb_frontend_listener" { + lb = ibm_is_lb.alb.id + 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 + default_pool = ibm_is_lb_pool.alb_backend_pool.pool_id +} + +module "private_path" { + source = "terraform-ibm-modules/vpc-private-path/ibm" + version = "1.0.0" + resource_group_id = module.resource_group.resource_group_id + subnet_id = local.subnet_id + tags = var.private_path_tags + access_tags = var.private_path_access_tags + nlb_name = local.network_loadbalancer_name + nlb_backend_pools = [ + { + pool_name = "on-prem" + pool_member_application_load_balancer_id = ibm_is_lb.alb.id + pool_algorithm = var.network_loadbalancer_pool_algorithm + pool_health_delay = var.network_loadbalancer_pool_health_delay + pool_health_retries = var.network_loadbalancer_pool_health_retries + pool_health_timeout = var.network_loadbalancer_pool_health_timeout + pool_health_type = var.network_loadbalancer_pool_health_type + pool_health_monitor_url = var.network_loadbalancer_pool_health_monitor_url + pool_health_monitor_port = var.network_loadbalancer_pool_health_monitor_port + pool_member_port = var.network_loadbalancer_pool_member_port + listener_port = var.network_loadbalancer_listener_port + listener_accept_proxy_protocol = var.network_loadbalancer_listener_accept_proxy_protocol + }] + private_path_name = local.private_path_name + private_path_default_access_policy = var.private_path_default_access_policy + private_path_service_endpoints = var.private_path_service_endpoints + private_path_zonal_affinity = var.private_path_zonal_affinity + private_path_publish = var.private_path_publish + private_path_account_policies = var.private_path_account_policies +} diff --git a/solutions/fully-configurable/outputs.tf b/solutions/fully-configurable/outputs.tf new file mode 100644 index 0000000..39f775a --- /dev/null +++ b/solutions/fully-configurable/outputs.tf @@ -0,0 +1,13 @@ +# ############################################################################## +# # Outputs +# ############################################################################## + +# output "access_group_id" { +# description = "Access group ID." +# value = ibm_iam_access_group_policy.policy.access_group_id +# } + +# output "website_endpoint" { +# description = "Website endpoint." +# value = ibm_cos_bucket_website_configuration.website.website_endpoint +# } diff --git a/examples/basic/provider.tf b/solutions/fully-configurable/provider.tf similarity index 53% rename from examples/basic/provider.tf rename to solutions/fully-configurable/provider.tf index 84b6985..17cfc77 100644 --- a/examples/basic/provider.tf +++ b/solutions/fully-configurable/provider.tf @@ -3,6 +3,8 @@ ######################################################################################################################## provider "ibm" { - ibmcloud_api_key = var.ibmcloud_api_key - region = var.region + 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/fully-configurable/variables.tf b/solutions/fully-configurable/variables.tf new file mode 100644 index 0000000..951042b --- /dev/null +++ b/solutions/fully-configurable/variables.tf @@ -0,0 +1,290 @@ +######################################################################################################################## +# Common variables +######################################################################################################################## + +variable "ibmcloud_api_key" { + type = string + description = "The API key to use for IBM Cloud." + sensitive = true +} + +variable "existing_resource_group_name" { + type = string + description = "The name of an existing resource group in which to provision the private path services in." + default = "Default" +} + +variable "region" { + type = string + description = "The region in which the VPC resources are provisioned." +} + +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 "prefix" { + type = string + nullable = true + description = "The prefix to be added to all resources created by this solution. To skip using a prefix, set this value to null or an empty string. The prefix must begin with a lowercase letter and may contain only lowercase letters, digits, and hyphens '-'. It should not exceed 16 characters, must not end with a hyphen('-'), and can not contain consecutive hyphens ('--'). Example: prod-0205-pp. [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 = length(var.prefix) <= 16 + error_message = "Prefix must not exceed 16 characters." + } +} + +variable "private_path_tags" { + type = list(string) + description = "Optional list of tags to be added to the private path service." + default = [] +} + +variable "private_path_access_tags" { + type = list(string) + description = "A list of access tags to apply to the private path service created by the module, see https://cloud.ibm.com/docs/account?topic=account-access-tags-tutorial for more details" + default = [] +} + +############################################################################## +# 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." + type = string + 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." + } +} + +variable "existing_subnet_id" { + description = "The ID of an existing subnet." + type = string + default = null +} + +############################################################################## +# ALB Variables +############################################################################## + +variable "application_loadbalancer_pool_member_ip_address" { + type = list(string) + default = [] + description = "List of IP address of the application load balancer pool members." +} + +variable "application_loadbalancer_type" { + type = string + default = "private" + description = "The type of the application load balancer. Supported values are `private`, `public`." +} + +variable "application_loadbalancer_pool_algorithm" { + type = string + description = "The load-balancing algorithm for private path netwrok load balancer pool members. Supported values are `round_robin` or `weighted_round_robin`." + default = "round_robin" +} + +variable "application_loadbalancer_pool_member_port" { + type = number + description = "The port where traffic is sent to the instance." + default = 80 +} + +variable "application_loadbalancer_pool_health_delay" { + type = number + description = "The interval between 2 consecutive health check attempts. The default is 5 seconds. Interval must be greater than `network_loadbalancer_pool_health_timeout` value." + default = 5 +} + +variable "application_loadbalancer_pool_health_retries" { + type = number + description = "The maximum number of health check attempts made before an instance is declared unhealthy. The default is 2 failed health checks." + default = 2 +} + +variable "application_loadbalancer_pool_health_timeout" { + type = number + description = "The maximum time the system waits for a response from a health check request. The default is 2 seconds." + default = 2 +} + +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" +} + +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" +} + +variable "application_loadbalancer_listener_port" { + type = number + description = "The listener port for the private path netwrok load balancer." + default = 80 +} + +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" +} + +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`." + default = 50 +} + +variable "application_loadbalancer_listener_certificate_instance" { + type = string + description = "The CRN of the certificate in your secret manager, it is applicable(mandatory) only to https protocol." + 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`." + } +} + + +############################################################################## +# NLB Variables +############################################################################## + +variable "network_loadbalancer_name" { + type = string + description = "The name of the private path netwrok load balancer." + default = "pp-nlb" +} + +variable "network_loadbalancer_listener_port" { + type = number + description = "The listener port for the private path netwrok load balancer." + default = 80 +} + +variable "network_loadbalancer_listener_accept_proxy_protocol" { + type = bool + description = "If set to true, listener forwards proxy protocol information that are supported by load balancers in the application family. Default value is false." + default = false +} + +variable "network_loadbalancer_pool_algorithm" { + type = string + description = "The load-balancing algorithm for private path netwrok load balancer pool members. Supported values are `round_robin` or `weighted_round_robin`." + default = "round_robin" +} + +variable "network_loadbalancer_pool_health_delay" { + type = number + description = "The interval between 2 consecutive health check attempts. The default is 5 seconds. Interval must be greater than `network_loadbalancer_pool_health_timeout` value." + default = 5 +} + +variable "network_loadbalancer_pool_health_retries" { + type = number + description = "The maximum number of health check attempts made before an instance is declared unhealthy. The default is 2 failed health checks." + default = 2 +} + +variable "network_loadbalancer_pool_health_timeout" { + type = number + description = "The maximum time the system waits for a response from a health check request. The default is 2 seconds." + default = 2 +} + +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`." + default = "tcp" +} + +variable "network_loadbalancer_pool_health_monitor_url" { + type = string + description = "If you select HTTP as the health check protocol, this URL is used to send health check requests to the instances in the pool. By default, this is the root path `/`" + default = "/" +} + +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 +} + +variable "network_loadbalancer_pool_member_port" { + type = number + description = "The port where traffic is sent to the instance." + default = 80 +} + +############################################################################## +# Private Path Variables +############################################################################## + +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" +} + +variable "private_path_service_endpoints" { + type = list(string) + description = "The list of name for the service endpoint where you want to connect your Private Path service. Enter a maximum number of 10 unique endpoint names for your service." +} + +variable "private_path_zonal_affinity" { + type = bool + description = "When enabled, the endpoint service preferentially permits connection requests from endpoints in the same zone. Without zonal affinity, requests are distributed to all instances in any zone." + default = false +} + +variable "private_path_name" { + type = string + description = "The name of the Private Path service for VPC." + default = "private-path" +} + +variable "private_path_publish" { + type = bool + description = "Set this variable to `true` to allows any account to request access to to the Private Path service. If need be, you can also unpublish where access is restricted to the account that created the Private Path service by setting this variable to `false`." + default = false +} + +variable "private_path_account_policies" { + type = list(object({ + account = string + access_policy = string + })) + description = "The account-specific connection request policies. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-vpc-private-path/tree/main/solutions/fully-configurable/DA_inputs.md#options-with-acc-policies)." + default = [] +} diff --git a/solutions/fully-configurable/version.tf b/solutions/fully-configurable/version.tf new file mode 100644 index 0000000..3ff065b --- /dev/null +++ b/solutions/fully-configurable/version.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + # Lock deployable architecture into an exact provider version - renovate automation will keep it updated + ibm = { + source = "ibm-cloud/ibm" + version = "1.78.2" + } + } +} diff --git a/tests/other_test.go b/tests/other_test.go deleted file mode 100644 index 88d360d..0000000 --- a/tests/other_test.go +++ /dev/null @@ -1,2 +0,0 @@ -// Tests in this file are NOT run in the PR pipeline. They are run in the continuous testing pipeline along with the ones in pr_test.go -package test diff --git a/tests/pr_test.go b/tests/pr_test.go index 8867ed0..d3e6501 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -1,60 +1,147 @@ -// Tests in this file are run in the PR pipeline and the continuous testing pipeline +// Tests in this file are run in the PR pipeline package test import ( + "fmt" + "log" + "os" + "strings" "testing" - "github.com/stretchr/testify/assert" + "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/testschematic" + + "github.com/stretchr/testify/require" + "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/cloudinfo" "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper" ) -// Use existing resource group -const resourceGroup = "geretain-test-resources" +const fullyConfigurableTerraformDir = "solutions/fully-configurable" -// Ensure every example directory has a corresponding test -const advancedExampleDir = "examples/advanced" -const basicExampleDir = "examples/basic" +// 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" -func setupOptions(t *testing.T, prefix string, dir string) *testhelper.TestOptions { - options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ - Testing: t, - TerraformDir: dir, - Prefix: prefix, - ResourceGroup: resourceGroup, - }) - return options +var ( + sharedInfoSvc *cloudinfo.CloudInfoService + permanentResources map[string]interface{} +) + +// TestMain will be run before any parallel tests, used to set up a shared InfoService object to track region usage +// for multiple tests +func TestMain(m *testing.M) { + sharedInfoSvc, _ = cloudinfo.NewCloudInfoServiceFromEnv("TF_VAR_ibmcloud_api_key", cloudinfo.CloudInfoServiceOptions{}) + + var err error + permanentResources, err = common.LoadMapFromYaml(yamlLocation) + if err != nil { + log.Fatal(err) + } + + os.Exit(m.Run()) } -// Consistency test for the basic example -func TestRunBasicExample(t *testing.T) { - t.Parallel() +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 +} - options := setupOptions(t, "mod-template-basic", basicExampleDir) +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") + + existingTerraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: tempTerraformDir, + Vars: map[string]interface{}{ + "prefix": prefix, + "region": region, + }, + // 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, + }) - output, err := options.RunTestConsistency() - assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") + terraform.WorkspaceSelectOrNew(t, existingTerraformOptions, prefix) + _, err = terraform.InitAndApplyE(t, existingTerraformOptions) + require.NoError(t, err, "Init and Apply of temp existing resource failed") + + return existingTerraformOptions } -func TestRunAdvancedExample(t *testing.T) { +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 + } + 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() - options := setupOptions(t, "mod-template-adv", advancedExampleDir) + // 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, + }) - output, err := options.RunTestConsistency() - assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") + 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.example.com"}, DataType: "list(string)"}, + {Name: "application_loadbalancer_pool_member_ip_address", Value: []string{terraform.Output(t, existingTerraformOptions, "member_ip_address")}, DataType: "list(string)"}, + } + require.NoError(t, options.RunSchematicTest(), "This should not have errored") + cleanupTerraform(t, existingTerraformOptions, prefix) } -// Upgrade test (using advanced example) -func TestRunUpgradeExample(t *testing.T) { +func TestRunUpgradeFullyConfigurable(t *testing.T) { t.Parallel() - options := setupOptions(t, "mod-template-adv-upg", advancedExampleDir) + // Provision existing resources first + prefix := fmt.Sprintf("pp-existing-%s", strings.ToLower(random.UniqueId())) + existingTerraformOptions := setupTerraform(t, prefix, "./resources") - output, err := options.RunTestUpgrade() - if !options.UpgradeTestSkipped { - assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") + 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, + }) + + 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.example.com"}, DataType: "list(string)"}, + {Name: "application_loadbalancer_pool_member_ip_address", Value: []string{terraform.Output(t, existingTerraformOptions, "member_ip_address")}, DataType: "list(string)"}, } + + require.NoError(t, options.RunSchematicUpgradeTest(), "This should not have errored") + cleanupTerraform(t, existingTerraformOptions, prefix) } diff --git a/tests/resources/README.md b/tests/resources/README.md new file mode 100644 index 0000000..4bb3621 --- /dev/null +++ b/tests/resources/README.md @@ -0,0 +1 @@ +The terraform code in this directory is used by the existing resource test in tests/pr_test.go diff --git a/tests/resources/main.tf b/tests/resources/main.tf new file mode 100644 index 0000000..fe9308d --- /dev/null +++ b/tests/resources/main.tf @@ -0,0 +1,115 @@ +############################################################################## +# Resource group +############################################################################## + +module "mock_resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.2.0" + resource_group_name = "${var.prefix}-mock-rg" +} + +############################################################################## +# MOCK ON-PREM +############################################################################## + + +resource "ibm_is_vpc" "mock_vpc" { + name = "${var.prefix}-mock-vpc" + resource_group = module.mock_resource_group.resource_group_id +} + +resource "ibm_is_vpc_address_prefix" "mock_prefix" { + name = "${var.prefix}-mock-prefix" + zone = "${var.region}-1" + vpc = ibm_is_vpc.mock_vpc.id + cidr = "10.100.20.0/24" +} + +resource "ibm_is_subnet" "mock_subnet" { + depends_on = [ + ibm_is_vpc_address_prefix.mock_prefix + ] + name = "${var.prefix}-mock-vpc" + vpc = ibm_is_vpc.mock_vpc.id + zone = "${var.region}-1" + ipv4_cidr_block = "10.100.20.0/24" + resource_group = module.mock_resource_group.resource_group_id + tags = var.resource_tags +} + +resource "tls_private_key" "ssh_key" { + algorithm = "RSA" +} + +resource "ibm_is_ssh_key" "public_key" { + name = "${var.prefix}-key" + public_key = trimspace(tls_private_key.ssh_key.public_key_openssh) + resource_group = module.mock_resource_group.resource_group_id +} + +data "ibm_is_image" "image" { + name = "ibm-ubuntu-22-04-3-minimal-amd64-1" +} + +resource "ibm_is_instance" "vsi" { + name = "${var.prefix}-vsi-0" + image = data.ibm_is_image.image.id + resource_group = module.mock_resource_group.resource_group_id + profile = "bx2-2x8" + + primary_network_attachment { + name = "${var.prefix}-vsi-0" + virtual_network_interface { + subnet = ibm_is_subnet.mock_subnet.id + } + } + + vpc = ibm_is_vpc.mock_vpc.id + zone = "${var.region}-1" + keys = [ibm_is_ssh_key.public_key.id] + user_data = file("./userdata.sh") +} + +resource "ibm_is_floating_ip" "ip" { + name = "${var.prefix}-fip" + target = ibm_is_instance.vsi.primary_network_attachment[0].virtual_network_interface[0].id + resource_group = module.mock_resource_group.resource_group_id +} + +################################################################################ +# Resource Group +################################################################################ +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.2.0" + resource_group_name = "${var.prefix}-rg" +} + +############################################################################## +# Provider VPC +############################################################################## + + +resource "ibm_is_vpc" "provider_vpc" { + name = "${var.prefix}-provider-vpc" + resource_group = module.resource_group.resource_group_id +} + +resource "ibm_is_vpc_address_prefix" "prefix" { + name = "${var.prefix}-prefix" + zone = "${var.region}-1" + vpc = ibm_is_vpc.provider_vpc.id + cidr = "10.100.10.0/24" +} + +resource "ibm_is_subnet" "provider_subnet" { + depends_on = [ + ibm_is_vpc_address_prefix.prefix + ] + name = "${var.prefix}-provider-vpc" + vpc = ibm_is_vpc.provider_vpc.id + resource_group = module.resource_group.resource_group_id + zone = "${var.region}-1" + ipv4_cidr_block = "10.100.10.0/24" + tags = var.resource_tags +} diff --git a/tests/resources/outputs.tf b/tests/resources/outputs.tf new file mode 100644 index 0000000..2b60829 --- /dev/null +++ b/tests/resources/outputs.tf @@ -0,0 +1,28 @@ +############################################################################## +# Outputs +############################################################################## + +output "member_ip_address_list" { + value = [ibm_is_floating_ip.ip.address] + description = "IP address of the Provider VSI" +} + +output "member_ip_address" { + value = ibm_is_floating_ip.ip.address + description = "IP address of the Provider VSI" +} + +output "existing_subnet_id" { + value = ibm_is_subnet.provider_subnet.id + description = "The subnet ID" +} + +output "resource_group_name" { + value = module.resource_group.resource_group_name + description = "Resource group name" +} + +output "region" { + value = var.region + description = "Region of the resources" +} diff --git a/tests/resources/provider.tf b/tests/resources/provider.tf new file mode 100644 index 0000000..df45ef5 --- /dev/null +++ b/tests/resources/provider.tf @@ -0,0 +1,4 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region +} diff --git a/tests/resources/userdata.sh b/tests/resources/userdata.sh new file mode 100755 index 0000000..15d3adc --- /dev/null +++ b/tests/resources/userdata.sh @@ -0,0 +1,2 @@ +#!/bin/bash +yum install -y bind-utils diff --git a/tests/resources/variables.tf b/tests/resources/variables.tf new file mode 100644 index 0000000..0dcda66 --- /dev/null +++ b/tests/resources/variables.tf @@ -0,0 +1,23 @@ +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API Key." + sensitive = true +} + +variable "region" { + type = string + description = "Region to provision all resources created by this example." + default = "us-south" +} + +variable "prefix" { + type = string + description = "Prefix to append to all resources created by this example." + default = "ce-pp-da" +} + +variable "resource_tags" { + type = list(string) + description = "Optional list of tags to be added to created resources." + default = ["private-path"] +} diff --git a/tests/resources/version.tf b/tests/resources/version.tf new file mode 100644 index 0000000..e3327d1 --- /dev/null +++ b/tests/resources/version.tf @@ -0,0 +1,14 @@ + +terraform { + required_version = ">= 1.9.0" + required_providers { + ibm = { + source = "ibm-cloud/ibm" + version = ">= 1.78.2" + } + tls = { + source = "hashicorp/tls" + version = ">= 4.0.5" + } + } +} diff --git a/tests/scripts/post-validation-private-path-vpc.sh b/tests/scripts/post-validation-private-path-vpc.sh new file mode 100755 index 0000000..e4aed72 --- /dev/null +++ b/tests/scripts/post-validation-private-path-vpc.sh @@ -0,0 +1,20 @@ +#! /bin/bash + +######################################################################################################################## +## This script is used by the catalog pipeline to destroy the VPC, which was provisioned as a ## +## prerequisite for the VPC Private path extension solutions that is published to the catalog ## +######################################################################################################################## + +set -e + +TERRAFORM_SOURCE_DIR="tests/resources" +TF_VARS_FILE="terraform.tfvars" + +( + cd ${TERRAFORM_SOURCE_DIR} + echo "Destroying prerequisite VPC .." + terraform destroy -input=false -auto-approve -var-file=${TF_VARS_FILE} || exit 1 + rm -f "${TF_VARS_FILE}" + + echo "Post-validation complete successfully" +) diff --git a/tests/scripts/pre-validation-private-path-vpc.sh b/tests/scripts/pre-validation-private-path-vpc.sh new file mode 100755 index 0000000..72da182 --- /dev/null +++ b/tests/scripts/pre-validation-private-path-vpc.sh @@ -0,0 +1,48 @@ +#! /bin/bash + +######################################################################################################################## +## This script is used by the catalog pipeline to deploy the VPC, which is a prerequisite for the Private path ## +## after catalog validation has complete. ## +######################################################################################################################## + +set -e + +DA_DIR="solutions/fully-configurable" +TERRAFORM_SOURCE_DIR="tests/resources" +JSON_FILE="${DA_DIR}/catalogValidationValues.json" +REGION="us-south" +TF_VARS_FILE="terraform.tfvars" + +( + cwd=$(pwd) + cd ${TERRAFORM_SOURCE_DIR} + echo "Provisioning prerequisite VPC .." + terraform init || exit 1 + # $VALIDATION_APIKEY is available in the catalog runtime + { + echo "ibmcloud_api_key=\"${VALIDATION_APIKEY}\"" + echo "prefix=\"pp-$(openssl rand -hex 2)\"" + echo "region=\"${REGION}\"" + } >>${TF_VARS_FILE} + terraform apply -input=false -auto-approve -var-file=${TF_VARS_FILE} || exit 1 + + resource_group_name_var_name="existing_resource_group_name" + resource_group_name_var_value=$(terraform output -state=terraform.tfstate -raw resource_group_name) + existing_subnet_id_var_name="existing_subnet_id" + existing_subnet_id_var_value=$(terraform output -state=terraform.tfstate -raw existing_subnet_id) + alb_member_ip_address_var_name="application_loadbalancer_pool_member_ip_address" + alb_member_ip_address_var_value=$(terraform output -state=terraform.tfstate -json member_ip_address_list) + + echo "Appending '${resource_group_name_var_name}' and '${existing_subnet_id_var_name}' input variable values to ${JSON_FILE}.." + + cd "${cwd}" + jq -r --arg resource_group_name_var_name "${resource_group_name_var_name}" \ + --arg resource_group_name_var_value "${resource_group_name_var_value}" \ + --arg existing_subnet_id_var_name "${existing_subnet_id_var_name}" \ + --arg existing_subnet_id_var_value "${existing_subnet_id_var_value}" \ + --arg alb_member_ip_address_var_name "${alb_member_ip_address_var_name}" \ + --argjson alb_member_ip_address_var_value "${alb_member_ip_address_var_value}" \ + '. + {($resource_group_name_var_name): $resource_group_name_var_value, ($existing_subnet_id_var_name): $existing_subnet_id_var_value, ($alb_member_ip_address_var_name): $alb_member_ip_address_var_value}' "${JSON_FILE}" >tmpfile && mv tmpfile "${JSON_FILE}" || exit 1 + + echo "Pre-validation complete successfully" +) diff --git a/variables.tf b/variables.tf deleted file mode 100644 index a9d9899..0000000 --- a/variables.tf +++ /dev/null @@ -1,36 +0,0 @@ -######################################################################################################################## -# Input Variables -######################################################################################################################## - -# -# Developer tips: -# - Below are some common module input variables -# - They should be updated for input variables applicable to the module being added -# - Use variable validation when possible -# - -variable "name" { - type = string - description = "A descriptive name used to identify the resource instance." -} - -variable "plan" { - type = string - description = "The name of the plan type supported by service." - default = "standard" - validation { - condition = contains(["standard", "cos-one-rate-plan"], var.plan) - error_message = "The specified pricing plan is not available. The following plans are supported: 'standard', 'cos-one-rate-plan'" - } -} - -variable "resource_group_id" { - type = string - description = "The ID of the resource group where you want to create the service." -} - -variable "resource_tags" { - type = list(string) - description = "List of resource tag to associate with the instance." - default = [] -} diff --git a/version.tf b/version.tf deleted file mode 100644 index e51de7f..0000000 --- a/version.tf +++ /dev/null @@ -1,18 +0,0 @@ -terraform { - # require 1.9 or later to make use of cross-object referencing for input variable validations - # more info: https://www.hashicorp.com/blog/terraform-1-9-enhances-input-variable-validations - required_version = ">= 1.9.0" - - # - # Developer tips: - # - If your module requires any terraform providers, add them the "required_providers" section below. - # - Each required provider's version should be a flexible range to future proof the module's usage with upcoming minor and patch versions. - # - - required_providers { - ibm = { - source = "IBM-Cloud/ibm" - version = ">= 1.71.2, < 2.0.0" - } - } -}