diff --git a/.catalog-onboard-pipeline.yaml b/.catalog-onboard-pipeline.yaml new file mode 100644 index 00000000..3191d23d --- /dev/null +++ b/.catalog-onboard-pipeline.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: v1 +offerings: + - name: deploy-arch-ibm-watsonx-self-managed + kind: solution + catalog_id: 7df1e4ca-d54c-4fd0-82ce-3d13247308cd + offering_id: 86425cf1-a763-4d17-9bb9-75276274a5f6 + variations: + - name: fully-configurable + mark_ready: true + install_type: fullstack + pre_validation: "tests/scripts/pre-validation-deploy-ocp-instances.sh" + post_validation: "tests/scripts/post-validation-destroy-ocp-instances.sh" + scc: + instance_id: 1c7d5f78-9262-44c3-b779-b28fe4d88c37 + region: us-south diff --git a/.releaserc b/.releaserc index 708916f7..4160e575 100644 --- a/.releaserc +++ b/.releaserc @@ -10,6 +10,9 @@ }], ["@semantic-release/exec", { "successCmd": "echo \"SEMVER_VERSION=${nextRelease.version}\" >> $GITHUB_ENV" + }], + ["@semantic-release/exec",{ + "publishCmd": "./ci/trigger-catalog-onboarding-pipeline.sh --version=v${nextRelease.version}" }] ] } diff --git a/.secrets.baseline b/.secrets.baseline index 25003cdc..a472fcdf 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "go.sum|^.secrets.baseline$", "lines": null }, - "generated_at": "2025-02-11T22:49:03Z", + "generated_at": "2025-02-11T22:49:04Z", "plugins_used": [ { "name": "AWSKeyDetector" diff --git a/README.md b/README.md index 5dab4db6..5f856618 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,141 @@ - -# IBM Cloud Pak for Data deployment on OpenShift +# Watsonx (Self-Managed) on Red Hat OpenShift - -[-red)](https://terraform-ibm-modules.github.io/documentation/#/badge-status) -[](https://github.com/terraform-ibm-modules/terraform-ibm-cloudpak-data/releases/latest) +[-green)](https://terraform-ibm-modules.github.io/documentation/#/badge-status) +[](https://github.com/terraform-ibm-modules/terraform-ibm-watsonx-self-managed-ocp/releases/latest) [](https://github.com/pre-commit/pre-commit) [](https://renovatebot.com/) [](https://github.com/semantic-release/semantic-release) +Deploy Watsonx services on an existing Red Hat OpenShift cluster. + + + + + + +## Overview +* [terraform-ibm-watsonx-self-managed-ocp](#terraform-ibm-watsonx-self-managed-ocp) +* [Submodules](./modules) +* [Examples](./examples) + * [Basic example](./examples/basic) +* [Contributing](#contributing) + + +## terraform-ibm-watsonx-self-managed-ocp + +### Usage + +```hcl +module "watsonx_self_managed_ocp" { + source = "terraform-ibm-modules/watsonx-self-managed-ocp/ibm" + version = "X.X.X" # Replace "X.X.X" with a release version to lock into a specific release + ibmcloud_api_key = "xxxxxxxxxxxxxxxxx" # pragma: allowlist secret + resource_group_id = "xxxxxxxxxxxxxxxxx" + region = "us-south" + prefix = "cp4d" + cluster_name = "my-ocp-cluster" + cluster_rg_id = "xxxxxxxxxxxxxxxxx" + install_odf_cluster_addon = true + watsonx_ai_install = true + watsonx_data_install = true + watson_assistant_install = true + watson_discovery_install = true + cpd_admin_password = "Passw0rd!" # pragma: allowlist secret + cpd_entitlement_key = "entitlementKey" + # Add other configuration options as needed +} +``` + +### Required IAM access policies + +You need the following permissions to run this module. + +* Account Management + * **All Resource Groups** service + * `Viewer` platform access +* IAM Services + * **Kubernetes Service** (OpenShift) + * `Administrator` platform access + * `Manager` service access + * **VPC Infrastructure** + * `Administrator` platform access + * `Manager` service access + * **Container Registry** + * `Administrator` platform access + * `Manager` service access + +For more information on access and permissions, see [IBM Cloud IAM service roles and actions](https://cloud.ibm.com/docs/account?topic=account-iam-service-roles-actions). + + + +### Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 1.9.0 | +| [ibm](#requirement\_ibm) | >=1.79.1 | + +### Modules + +| Name | Source | Version | +|------|--------|---------| +| [build\_cpd\_image](#module\_build\_cpd\_image) | ./modules/cpd-image-build | n/a | +| [cloud\_pak\_deployer](#module\_cloud\_pak\_deployer) | ./modules/cloud-pak-deployer | n/a | +| [config](#module\_config) | ./modules/cloud-pak-deployer/config | n/a | +| [watsonx\_ai](#module\_watsonx\_ai) | ./modules/watsonx-ai | n/a | +| [watsonx\_data](#module\_watsonx\_data) | ./modules/watsonx-data | n/a | + +### Resources + +| Name | Type | +|------|------| +| [ibm_container_addons.odf_cluster_addon](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/resources/container_addons) | resource | +| [ibm_container_vpc_cluster.cluster_info](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/data-sources/container_vpc_cluster) | data source | + +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [cloud\_pak\_deployer\_image](#input\_cloud\_pak\_deployer\_image) | Cloud Pak Deployer image to use. If `null`, the image will be built using Code Engine. | `string` | `null` | no | +| [cloud\_pak\_deployer\_release](#input\_cloud\_pak\_deployer\_release) | Release of Cloud Pak Deployer version to use. View releases at: https://github.com/IBM/cloud-pak-deployer/releases. | `string` | `"v3.1.8"` | no | +| [cloud\_pak\_deployer\_secret](#input\_cloud\_pak\_deployer\_secret) | Secret for accessing the Cloud Pak Deployer image. If `null`, a default secret will be created # pragma: allowlist secret. |
object({
    username = string
    password = string
    server   = string
    email    = string
  }) | `null` | no |
+|  [cluster\_name](#input\_cluster\_name) | Name of Red Hat OpenShift cluster to install watsonx onto | `string` | n/a | yes |
+|  [cluster\_rg\_id](#input\_cluster\_rg\_id) | Resource group id of the cluster | `string` | n/a | yes |
+|  [code\_engine\_project\_id](#input\_code\_engine\_project\_id) | If you want to use an existing project, you can pass the code engine project ID and the Cloud Pak Deployer build will be built within the existing project instead of creating a new one. | `string` | `null` | no |
+|  [code\_engine\_project\_name](#input\_code\_engine\_project\_name) | If `cloud_pak_deployer_image` is `null`, it will build the image with code engine and store it within a private ICR registry. Provide a name if you want to set the name. If not defined, default will be `{prefix}-cpd-{random-suffix}`. | `string` | `null` | no |
+|  [cpd\_accept\_license](#input\_cpd\_accept\_license) | When set to 'true', it is understood that the user has read the terms of the Cloud Pak license(s) and agrees to the terms outlined. | `bool` | `true` | no |
+|  [cpd\_admin\_password](#input\_cpd\_admin\_password) | Password for the Cloud Pak for Data admin user. | `string` | n/a | yes |
+|  [cpd\_entitlement\_key](#input\_cpd\_entitlement\_key) | Cloud Pak for Data entitlement key for access to the IBM Entitled Registry. Can be fetched from https://myibm.ibm.com/products-services/containerlibrary. | `string` | n/a | yes |
+|  [cpd\_version](#input\_cpd\_version) | Cloud Pak for Data version to install.  Only version 5.x.x is supported, latest versions can be found [here](https://www.ibm.com/docs/en/cloud-paks/cp-data?topic=versions-cloud-pak-data). | `string` | `"5.0.3"` | no |
+|  [ibmcloud\_api\_key](#input\_ibmcloud\_api\_key) | The IBM Cloud API key to deploy resources. | `string` | n/a | yes |
+|  [install\_odf\_cluster\_addon](#input\_install\_odf\_cluster\_addon) | Install the ODF cluster addon. | `bool` | `true` | no |
+|  [odf\_config](#input\_odf\_config) | Configuration for the ODF addon. | `map(string)` | {
  "addSingleReplicaPool": "false",
  "billingType": "essentials",
  "clusterEncryption": "false",
  "disableNoobaaLB": "false",
  "enableNFS": "false",
  "encryptionInTransit": "false",
  "hpcsBaseUrl": "",
  "hpcsEncryption": "false",
  "hpcsInstanceId": "",
  "hpcsSecretName": "",
  "hpcsServiceName": "",
  "hpcsTokenUrl": "",
  "ignoreNoobaa": "true",
  "numOfOsd": "1",
  "ocsUpgrade": "false",
  "odfDeploy": "true",
  "osdDevicePaths": "",
  "osdSize": "512Gi",
  "osdStorageClassName": "ibmc-vpc-block-metro-10iops-tier",
  "prepareForDisasterRecovery": "false",
  "resourceProfile": "balanced",
  "taintNodes": "false",
  "useCephRBDAsDefaultStorageClass": "false",
  "workerNodes": "all",
  "workerPool": ""
} | no |
+|  [odf\_version](#input\_odf\_version) | Version of ODF to install. | `string` | `"4.16.0"` | no |
+|  [prefix](#input\_prefix) | A unique identifier for resources that is prepended to resources that are provisioned. Must begin with a lowercase letter and end with a lowercase letter or number. Must be 16 or fewer characters. | `string` | `null` | no |
+|  [region](#input\_region) | Region where resources will be created. To find your VPC region, use `ibmcloud is regions` command to find available regions. | `string` | n/a | yes |
+|  [resource\_group](#input\_resource\_group) | Resource group to provision services within. If not defined, a resource group called `{prefix}-cpd` will be created. | `string` | `null` | no |
+|  [watson\_assistant\_install](#input\_watson\_assistant\_install) | If watsonx.ai is being installed, also install watson assistant | `bool` | `false` | no |
+|  [watson\_discovery\_install](#input\_watson\_discovery\_install) | If watsonx.ai is being installed, also install watson discovery | `bool` | `false` | no |
+|  [watsonx\_ai\_install](#input\_watsonx\_ai\_install) | Determine whether the watsonx.ai cartridge for the deployer will be installed | `bool` | `false` | no |
+|  [watsonx\_ai\_models](#input\_watsonx\_ai\_models) | List of watsonx.ai models to install.  Information on the foundation models including pre-reqs can be found here - https://www.ibm.com/docs/en/cloud-paks/cp-data/5.0.x?topic=install-foundation-models.  Use the ModelID as input | `list(string)` | [| no | +| [watsonx\_data\_install](#input\_watsonx\_data\_install) | Determine whether the watsonx.data cartridge for the deployer will be installed | `bool` | `false` | no | -This repository contains the following deployment on an Red Hat OpenShift cluster: -- [IBM Cloud Pak for Data](./solutions/deploy) +### Outputs -**NB:** These solutions are not intended to be called by one or more other modules since they contain a provider configurations, meaning they are 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) +| Name | Description | +|------|-------------| +| [cloud\_pak\_deployer\_image](#output\_cloud\_pak\_deployer\_image) | The Cloud Pak Deployer image used. | +| [cloud\_pak\_deployer\_secret](#output\_cloud\_pak\_deployer\_secret) | The secret used for accessing the Cloud Pak Deployer image. | +| [cluster\_name](#output\_cluster\_name) | The name of the OpenShift cluster. | +| [code\_engine\_project\_name](#output\_code\_engine\_project\_name) | The name of the code engine project that was created | + - + ## 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/chart/cloud-pak-deployer/templates/install-job.yaml b/chart/cloud-pak-deployer/templates/install-job.yaml index 47b4650b..2e371cdb 100644 --- a/chart/cloud-pak-deployer/templates/install-job.yaml +++ b/chart/cloud-pak-deployer/templates/install-job.yaml @@ -6,7 +6,7 @@ metadata: labels: App: {{ .Values.deployer.prefix }} annotations: - # https://github.com/terraform-ibm-modules/terraform-ibm-cloudpak-data/issues/14 + # https://github.com/terraform-ibm-modules/terraform-ibm-watsonx-self-managed-ocp/issues/14 checkov.io/skip1: CKV_K8S_10 checkov.io/skip2: CKV_K8S_11 checkov.io/skip3: CKV_K8S_12 diff --git a/chart/cloud-pak-deployer/templates/uninstall-job.yaml b/chart/cloud-pak-deployer/templates/uninstall-job.yaml index e8c06861..dcea93c9 100644 --- a/chart/cloud-pak-deployer/templates/uninstall-job.yaml +++ b/chart/cloud-pak-deployer/templates/uninstall-job.yaml @@ -9,7 +9,7 @@ metadata: "helm.sh/hook": pre-delete "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded,hook-failed "helm.sh/hook-weight": "4" - # https://github.com/terraform-ibm-modules/terraform-ibm-cloudpak-data/issues/14 + # https://github.com/terraform-ibm-modules/terraform-ibm-watsonx-self-managed-ocp/issues/14 checkov.io/skip1: CKV_K8S_21 checkov.io/skip2: CKV_K8S_30 checkov.io/skip3: CKV_K8S_28 diff --git a/cra-config.yaml b/cra-config.yaml index 404ea490..fac6f6a8 100644 --- a/cra-config.yaml +++ b/cra-config.yaml @@ -13,5 +13,5 @@ CRA_TARGETS: [] # disable CRA since no SCC instance exists in the test account # # 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. - # TF_VAR_prefix: "roks-cpd" + # TF_VAR_prefix: "ocp-cpd" # TF_VAR_region: "au-syd" diff --git a/examples/basic/README.md b/examples/basic/README.md index e69de29b..df9548e1 100644 --- a/examples/basic/README.md +++ b/examples/basic/README.md @@ -0,0 +1,17 @@ +# Basic example + + + +This basic example demonstrates how to deploy IBM Cloud Pak for Data and optional watsonx services on a new Red Hat OpenShift cluster using this module. + +It provisions the following resources: + +- A new resource group (if not provided) +- A new VPC, subnet, and public gateway +- A new Red Hat OpenShift cluster with configurable worker pools +- IBM Cloud Pak for Data installation +- Optional watsonx services (watsonx.ai, watsonx.data, Watson Assistant, Watson Discovery) diff --git a/examples/basic/main.tf b/examples/basic/main.tf index 357aaae8..193625b7 100644 --- a/examples/basic/main.tf +++ b/examples/basic/main.tf @@ -1,7 +1,6 @@ ############################################################################## locals { - cluster_name = var.existing_cluster_name != null ? var.existing_cluster_name : module.ocp_base[0].cluster_name - cluster_rg_id = var.existing_cluster_rg_id != null ? var.existing_cluster_rg_id : module.resource_group[0].resource_group_id + cluster_name = var.existing_cluster_name != null ? var.existing_cluster_name : module.ocp_base[0].cluster_name } ############################################################################### @@ -10,11 +9,11 @@ locals { ############################################################################## module "resource_group" { - count = var.existing_cluster_rg_id == null ? 1 : 0 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.prefix}-resource-group" + resource_group_name = var.resource_group == null ? "${var.prefix}-resource-group" : null + existing_resource_group_name = var.resource_group } ######################################################################################################################## @@ -28,7 +27,7 @@ module "resource_group" { resource "ibm_is_vpc" "vpc" { name = "${var.prefix}-vpc" - resource_group = local.cluster_rg_id + resource_group = module.resource_group.resource_group_id address_prefix_management = "auto" tags = var.resource_tags } @@ -36,14 +35,14 @@ resource "ibm_is_vpc" "vpc" { resource "ibm_is_public_gateway" "gateway" { name = "${var.prefix}-gateway-1" vpc = ibm_is_vpc.vpc.id - resource_group = local.cluster_rg_id + resource_group = module.resource_group.resource_group_id zone = "${var.region}-1" } resource "ibm_is_subnet" "subnet_zone_1" { name = "${var.prefix}-subnet-1" vpc = ibm_is_vpc.vpc.id - resource_group = local.cluster_rg_id + resource_group = module.resource_group.resource_group_id zone = "${var.region}-1" total_ipv4_address_count = 256 public_gateway = ibm_is_public_gateway.gateway.id @@ -67,10 +66,17 @@ locals { worker_pools = [ { subnet_prefix = "default" - pool_name = "default" # ibm_container_vpc_cluster automatically names default pool "default" (See https://github.com/IBM-Cloud/terraform-provider-ibm/issues/2849) - machine_type = "bx2.16x64" + pool_name = "default" # Unique name for the general-purpose worker pool + machine_type = "bx3d.64x320" # CPU-based machine type operating_system = "REDHAT_8_64" - workers_per_zone = 3 # minimum of 2 is allowed when using single zone + workers_per_zone = 3 # Minimum 3 workers to install ODF and ensure high availability + }, + { + subnet_prefix = "default" + pool_name = "gpu-pool" # Unique name for the GPU-enabled worker pool + machine_type = "gx3.64x320.4l4" # GPU-based machine type + operating_system = "REDHAT_8_64" + workers_per_zone = 2 # Minimum 2 workers per zone for high availability } ] } @@ -79,7 +85,7 @@ module "ocp_base" { count = var.existing_cluster_name == null ? 1 : 0 source = "terraform-ibm-modules/base-ocp-vpc/ibm" version = "3.46.14" - resource_group_id = local.cluster_rg_id + resource_group_id = module.resource_group.resource_group_id region = var.region tags = var.resource_tags cluster_name = var.prefix @@ -91,18 +97,19 @@ module "ocp_base" { } ############################################################################## -# Deploy cloudpak_data +# Deploy watsonx-self-managed-ocp ############################################################################## -module "cloudpak_data" { - source = "../../solutions/deploy" +module "watsonx_self_managed_ocp" { + source = "../.." ibmcloud_api_key = var.ibmcloud_api_key prefix = var.prefix region = var.region cluster_name = local.cluster_name - cluster_rg_id = local.cluster_rg_id + cluster_rg_id = module.resource_group.resource_group_id cloud_pak_deployer_image = "quay.io/cloud-pak-deployer/cloud-pak-deployer" cpd_admin_password = "Passw0rd" #pragma: allowlist secret cpd_entitlement_key = "entitlementKey" install_odf_cluster_addon = var.install_odf_cluster_addon + watsonx_ai_install = false } diff --git a/examples/basic/outputs.tf b/examples/basic/outputs.tf index e69de29b..56e35f81 100644 --- a/examples/basic/outputs.tf +++ b/examples/basic/outputs.tf @@ -0,0 +1,30 @@ +output "cloud_pak_deployer_image" { + description = "The Cloud Pak Deployer image used." + value = module.watsonx_self_managed_ocp.cloud_pak_deployer_image +} + +output "cloud_pak_deployer_secret" { + description = "The secret used for accessing the Cloud Pak Deployer image." + value = module.watsonx_self_managed_ocp.cloud_pak_deployer_secret + sensitive = true +} + +output "cluster_name" { + description = "The name of the OpenShift cluster." + value = module.watsonx_self_managed_ocp.cluster_name +} + +output "code_engine_project_name" { + description = "The name of the code engine project that was created" + value = module.watsonx_self_managed_ocp.code_engine_project_name +} + +output "cluster_id" { + value = module.ocp_base[0].cluster_id + description = "Cluster ID." +} + +output "cluster_resource_group_id" { + value = module.ocp_base[0].resource_group_id + description = "Cluster resource group ID." +} diff --git a/examples/basic/variables.tf b/examples/basic/variables.tf index 5a4c1aba..1ae18a75 100644 --- a/examples/basic/variables.tf +++ b/examples/basic/variables.tf @@ -11,7 +11,7 @@ variable "ibmcloud_api_key" { variable "prefix" { description = "A unique identifier for resources that is prepended to resources that are provisioned. Must begin with a lowercase letter and end with a lowercase letter or number. Must be 13 or fewer characters." type = string - default = "lz-roks-cp4d" + default = "ocp-cp4d" validation { error_message = "Prefix must begin with a letter and contain only lowercase letters, numbers, and - characters. Prefixes must end with a lowercase letter or number and be 16 or fewer characters." @@ -41,14 +41,10 @@ variable "existing_cluster_name" { description = "Existing cluster name" type = string default = null - validation { - condition = can(regex("^[a-z][a-z0-9-]{0,12}[a-z0-9]$", var.existing_cluster_name)) - error_message = "Existing cluster name must begin with a letter and contain only lowercase letters, numbers, and - characters. Existing cluster names must end with a lowercase letter or number and be 13 or fewer characters." - } } -variable "existing_cluster_rg_id" { - description = "Existing resource group id" +variable "resource_group" { + description = "Existing resource group name" type = string default = null } diff --git a/examples/basic/version.tf b/examples/basic/version.tf index 429df1d4..06700bc1 100644 --- a/examples/basic/version.tf +++ b/examples/basic/version.tf @@ -4,7 +4,7 @@ terraform { # renovate is set up to keep provider version at the latest for all DA solutions ibm = { source = "ibm-cloud/ibm" - version = "1.78.0" + version = "1.79.1" } } } diff --git a/ibm_catalog.json b/ibm_catalog.json new file mode 100644 index 00000000..7acaba6e --- /dev/null +++ b/ibm_catalog.json @@ -0,0 +1,178 @@ +{ + "products": [ + { + "label": "Watsonx (Self-Managed) on Red Hat OpenShift", + "name": "deploy-arch-ibm-watsonx-self-managed", + "product_kind": "solution", + "tags": [ + "ibm_created", + "terraform", + "target_terraform", + "solution", + "watson", + "ai" + ], + "keywords": [ + "IaC", + "infrastructure as code", + "terraform", + "solution", + "watson", + "ai", + "watsonx", + "cloudpak", + "self-managed" + ], + "provider_name": "IBM", + "short_description": "Deploys IBM Watsonx services to a Red Hat OpenShift cluster", + "long_description": "Solution that deploys Watsonx services (watsonx.ai, watsonx.data, Watson Assistant, Watson Discovery) on an existing Red Hat OpenShift cluster.", + "offering_docs_url": "https://github.com/terraform-ibm-modules/terraform-ibm-watsonx-self-managed-ocp/blob/main/solutions/fully-configurable/README.md", + "offering_icon_url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-watsonx-self-managed-ocp/main/images/watsonx-self-managed-ocp.svg", + "features": [ + { + "title": "Flexible Cluster Deployment", + "description": "Deploys IBM Watsonx services on either an existing Red Hat OpenShift cluster." + }, + { + "title": "Modular watsonx Services", + "description": "Supports installation of watsonx.ai, watsonx.data, Watson Assistant, and Watson Discovery as optional services." + }, + { + "title": "Automated Infrastructure Provisioning", + "description": "Automates creation of required IBM Cloud infrastructure, including VPC, subnets, gateways, and worker pools." + }, + { + "title": "Customizable Resource Configuration", + "description": "Allows customization of machine types, worker pools, and storage options to fit workload requirements." + } + ], + "flavors": [ + { + "label": "Fully configurable", + "name": "fully-configurable", + "working_directory": "solutions/fully-configurable", + "iam_permissions": [ + { + "service_name": "containers-kubernetes", + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Viewer" + ] + } + ], + "architecture": { + "features": [ + { + "title": "Cluster must meet minimum requirements to install any Watson services you wish to add.", + "description": "Yes" + }, + { + "title": "Deploys Watsonx services on an existing cluster.", + "description": "Yes" + } + ], + "diagrams": [ + { + "diagram": { + "url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-watsonx-self-managed-ocp/refs/heads/main/reference-architecture/deployable-architecture-cp4d.svg", + "caption": "Watsonx (Self-Managed) on Red Hat OpenShift", + "type": "image/svg+xml" + }, + "description": "This diagram illustrates the architecture for deploying Watsonx Cloud Pak for Data on Red Hat OpenShift, including the optional watsonx services." + } + ] + }, + "configuration": [ + { + "key": "ibmcloud_api_key", + "required": true + }, + { + "key": "prefix", + "required": true + }, + { + "key": "region", + "required": true + }, + { + "key": "existing_cluster_name", + "required": true + }, + { + "key": "existing_resource_group_name", + "custom_config": { + "type": "resource_group", + "grouping": "deployment", + "original_grouping": "deployment", + "config_constraints": { + "identifier": "rg_id" + } + }, + "required": true + }, + { + "key": "install_odf_cluster_addon", + "required": true + }, + { + "key": "cpd_entitlement_key", + "required": true + }, + { + "key": "watsonx_ai_install", + "required": true + }, + { + "key": "watsonx_ai_models", + "required": true + }, + { + "key": "watsonx_data_install", + "required": true + }, + { + "key": "watson_discovery_install", + "required": true + }, + { + "key": "watson_assistant_install", + "required": true + }, + { + "key": "code_engine_project_name" + }, + { + "key": "code_engine_project_id" + }, + { + "key": "cloud_pak_deployer_image" + }, + { + "key": "cloud_pak_deployer_release" + }, + { + "key": "cloud_pak_deployer_secret" + }, + { + "key": "odf_version" + }, + { + "key": "odf_config" + }, + { + "key": "cpd_accept_license" + }, + { + "key": "cpd_admin_password" + }, + { + "key": "cpd_version" + } + ], + "install_type": "fullstack" + } + ] + } + ] +} diff --git a/images/redhat.svg b/images/redhat.svg new file mode 100644 index 00000000..4f016c3c --- /dev/null +++ b/images/redhat.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/images/watsonx-ai.svg b/images/watsonx-ai.svg new file mode 100644 index 00000000..1fb7dea1 --- /dev/null +++ b/images/watsonx-ai.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/images/watsonx-assistant.svg b/images/watsonx-assistant.svg new file mode 100644 index 00000000..def60c39 --- /dev/null +++ b/images/watsonx-assistant.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/images/watsonx-data.svg b/images/watsonx-data.svg new file mode 100644 index 00000000..dababdfa --- /dev/null +++ b/images/watsonx-data.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/images/watsonx-discovery.svg b/images/watsonx-discovery.svg new file mode 100644 index 00000000..b85e43d5 --- /dev/null +++ b/images/watsonx-discovery.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/images/watsonx-self-managed-ocp.svg b/images/watsonx-self-managed-ocp.svg new file mode 100644 index 00000000..79e3fcf3 --- /dev/null +++ b/images/watsonx-self-managed-ocp.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/solutions/deploy/main.tf b/main.tf similarity index 82% rename from solutions/deploy/main.tf rename to main.tf index d08ed441..1246b1fd 100644 --- a/solutions/deploy/main.tf +++ b/main.tf @@ -1,13 +1,5 @@ locals { - kube_config_dir = local.schematics_workspace.persistent_dir_exists ? local.schematics_workspace.persistent_dir_path : path.module openshift_version = join(".", slice(split(".", data.ibm_container_vpc_cluster.cluster_info.kube_version), 0, 2)) # Only use major and minor — no patch - paths = { - scripts = "${path.module}/scripts" - } - schematics_workspace = { - persistent_dir_exists = data.external.schematics.result.schematics_tmp_dir_exists ? true : false - persistent_dir_path = "/tmp/.schematics" - } } # Retrieve the openshift cluster info @@ -18,14 +10,13 @@ data "ibm_container_vpc_cluster" "cluster_info" { module "build_cpd_image" { count = var.cloud_pak_deployer_image == null ? 1 : 0 - source = "./cpd-image-build" - ibmcloud_api_key = var.ibmcloud_api_key + source = "./modules/cpd-image-build" prefix = var.prefix + ibmcloud_api_key = var.ibmcloud_api_key region = var.region code_engine_project_name = var.code_engine_project_name code_engine_project_id = var.code_engine_project_id resource_group = var.resource_group - resource_group_exists = var.resource_group_exists cloud_pak_deployer_release = var.cloud_pak_deployer_release } @@ -41,7 +32,7 @@ resource "ibm_container_addons" "odf_cluster_addon" { } module "watsonx_ai" { - source = "./watsonx-ai" + source = "./modules/watsonx-ai" depends_on = [ibm_container_addons.odf_cluster_addon] watson_assistant_install = var.watson_assistant_install watson_discovery_install = var.watson_discovery_install @@ -50,7 +41,7 @@ module "watsonx_ai" { } module "watsonx_data" { - source = "./watsonx-data" + source = "./modules/watsonx-data" depends_on = [ibm_container_addons.odf_cluster_addon] watsonx_data_install = var.watsonx_data_install } @@ -61,7 +52,7 @@ module "cloud_pak_deployer" { module.watsonx_data, module.build_cpd_image ] - source = "./cloud-pak-deployer" + source = "./modules/cloud-pak-deployer" cloud_pak_deployer_config = merge( module.config.cloud_pak_deployer_config_base, { @@ -90,7 +81,7 @@ module "cloud_pak_deployer" { # Cloud Pak Deployer configuration file local variable(s) only module "config" { - source = "./cloud-pak-deployer/config" + source = "./modules/cloud-pak-deployer/config" cluster_name = var.cluster_name cpd_version = var.cpd_version openshift_version = local.openshift_version diff --git a/solutions/deploy/cloud-pak-deployer/.secrets.baseline b/modules/cloud-pak-deployer/.secrets.baseline similarity index 100% rename from solutions/deploy/cloud-pak-deployer/.secrets.baseline rename to modules/cloud-pak-deployer/.secrets.baseline diff --git a/solutions/deploy/cloud-pak-deployer/config/main.tf b/modules/cloud-pak-deployer/config/main.tf similarity index 100% rename from solutions/deploy/cloud-pak-deployer/config/main.tf rename to modules/cloud-pak-deployer/config/main.tf diff --git a/solutions/deploy/cloud-pak-deployer/config/outputs.tf b/modules/cloud-pak-deployer/config/outputs.tf similarity index 100% rename from solutions/deploy/cloud-pak-deployer/config/outputs.tf rename to modules/cloud-pak-deployer/config/outputs.tf diff --git a/solutions/deploy/cloud-pak-deployer/config/variables.tf b/modules/cloud-pak-deployer/config/variables.tf similarity index 56% rename from solutions/deploy/cloud-pak-deployer/config/variables.tf rename to modules/cloud-pak-deployer/config/variables.tf index 32ef728a..05d7e49e 100644 --- a/solutions/deploy/cloud-pak-deployer/config/variables.tf +++ b/modules/cloud-pak-deployer/config/variables.tf @@ -4,8 +4,8 @@ variable "cluster_name" { } variable "cpd_version" { - default = "5.0.2" - description = "Cloud Pak for Data version to install" + default = "5.1.1" + description = "Cloud Pak for Data version to install. Only version 5.x.x is supported, latest versions can be found [here](https://www.ibm.com/docs/en/cloud-paks/cp-data?topic=versions-cloud-pak-data)." type = string } diff --git a/solutions/deploy/cloud-pak-deployer/config/versions.tf b/modules/cloud-pak-deployer/config/versions.tf similarity index 100% rename from solutions/deploy/cloud-pak-deployer/config/versions.tf rename to modules/cloud-pak-deployer/config/versions.tf diff --git a/solutions/deploy/cloud-pak-deployer/main.tf b/modules/cloud-pak-deployer/main.tf similarity index 96% rename from solutions/deploy/cloud-pak-deployer/main.tf rename to modules/cloud-pak-deployer/main.tf index 93df1f9a..c5dbc62a 100644 --- a/solutions/deploy/cloud-pak-deployer/main.tf +++ b/modules/cloud-pak-deployer/main.tf @@ -16,13 +16,13 @@ locals { resource "helm_release" "cloud_pak_deployer_helm_release" { name = "cloud-pak-deployer" - chart = "${path.module}/../../../chart/cloud-pak-deployer" + chart = "${path.module}/../../chart/cloud-pak-deployer" namespace = local.cloud_pak_deployer.namespace_name create_namespace = true - timeout = 600 + timeout = 1200 dependency_update = true - force_update = false + force_update = true cleanup_on_fail = false wait = true diff --git a/solutions/deploy/cloud-pak-deployer/outputs.tf b/modules/cloud-pak-deployer/outputs.tf similarity index 100% rename from solutions/deploy/cloud-pak-deployer/outputs.tf rename to modules/cloud-pak-deployer/outputs.tf diff --git a/solutions/deploy/cloud-pak-deployer/variables.tf b/modules/cloud-pak-deployer/variables.tf similarity index 100% rename from solutions/deploy/cloud-pak-deployer/variables.tf rename to modules/cloud-pak-deployer/variables.tf diff --git a/solutions/deploy/cloud-pak-deployer/version.tf b/modules/cloud-pak-deployer/version.tf similarity index 100% rename from solutions/deploy/cloud-pak-deployer/version.tf rename to modules/cloud-pak-deployer/version.tf diff --git a/solutions/deploy/cpd-image-build/main.tf b/modules/cpd-image-build/main.tf similarity index 94% rename from solutions/deploy/cpd-image-build/main.tf rename to modules/cpd-image-build/main.tf index 62e3d2a6..13d11f1d 100644 --- a/solutions/deploy/cpd-image-build/main.tf +++ b/modules/cpd-image-build/main.tf @@ -41,8 +41,9 @@ module "resource_group" { source = "terraform-ibm-modules/resource-group/ibm" version = "1.2.0" - resource_group_name = var.resource_group_exists ? null : local.resource_group_name - existing_resource_group_name = var.resource_group_exists ? var.resource_group : null + # 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 } ############################################################################## diff --git a/solutions/deploy/cpd-image-build/outputs.tf b/modules/cpd-image-build/outputs.tf similarity index 100% rename from solutions/deploy/cpd-image-build/outputs.tf rename to modules/cpd-image-build/outputs.tf diff --git a/solutions/deploy/cpd-image-build/scripts/image-build.sh b/modules/cpd-image-build/scripts/image-build.sh similarity index 100% rename from solutions/deploy/cpd-image-build/scripts/image-build.sh rename to modules/cpd-image-build/scripts/image-build.sh diff --git a/solutions/deploy/cpd-image-build/variables.tf b/modules/cpd-image-build/variables.tf similarity index 89% rename from solutions/deploy/cpd-image-build/variables.tf rename to modules/cpd-image-build/variables.tf index 0023ac07..4c0c2acc 100644 --- a/solutions/deploy/cpd-image-build/variables.tf +++ b/modules/cpd-image-build/variables.tf @@ -22,13 +22,6 @@ variable "resource_group" { default = null } -variable "resource_group_exists" { - description = "Boolean value representing if the resource groups exists or not" - type = bool - - default = false -} - variable "container_registry_namespace" { description = "The name of the container registry namespace" type = string diff --git a/solutions/deploy/cpd-image-build/version.tf b/modules/cpd-image-build/version.tf similarity index 89% rename from solutions/deploy/cpd-image-build/version.tf rename to modules/cpd-image-build/version.tf index 791ebccf..cbc24ff8 100644 --- a/solutions/deploy/cpd-image-build/version.tf +++ b/modules/cpd-image-build/version.tf @@ -3,7 +3,7 @@ terraform { required_providers { ibm = { source = "ibm-cloud/ibm" - version = "1.78.0" + version = ">=1.79.1, <2.0.0" } random = { source = "hashicorp/random" diff --git a/solutions/deploy/watsonx-ai/main.tf b/modules/watsonx-ai/main.tf similarity index 100% rename from solutions/deploy/watsonx-ai/main.tf rename to modules/watsonx-ai/main.tf diff --git a/solutions/deploy/watsonx-ai/outputs.tf b/modules/watsonx-ai/outputs.tf similarity index 100% rename from solutions/deploy/watsonx-ai/outputs.tf rename to modules/watsonx-ai/outputs.tf diff --git a/solutions/deploy/watsonx-ai/variables.tf b/modules/watsonx-ai/variables.tf similarity index 100% rename from solutions/deploy/watsonx-ai/variables.tf rename to modules/watsonx-ai/variables.tf diff --git a/solutions/deploy/watsonx-ai/versions.tf b/modules/watsonx-ai/versions.tf similarity index 100% rename from solutions/deploy/watsonx-ai/versions.tf rename to modules/watsonx-ai/versions.tf diff --git a/solutions/deploy/watsonx-data/main.tf b/modules/watsonx-data/main.tf similarity index 100% rename from solutions/deploy/watsonx-data/main.tf rename to modules/watsonx-data/main.tf diff --git a/solutions/deploy/watsonx-data/outputs.tf b/modules/watsonx-data/outputs.tf similarity index 100% rename from solutions/deploy/watsonx-data/outputs.tf rename to modules/watsonx-data/outputs.tf diff --git a/solutions/deploy/watsonx-data/variables.tf b/modules/watsonx-data/variables.tf similarity index 100% rename from solutions/deploy/watsonx-data/variables.tf rename to modules/watsonx-data/variables.tf diff --git a/solutions/deploy/watsonx-data/versions.tf b/modules/watsonx-data/versions.tf similarity index 100% rename from solutions/deploy/watsonx-data/versions.tf rename to modules/watsonx-data/versions.tf diff --git a/solutions/deploy/outputs.tf b/outputs.tf similarity index 95% rename from solutions/deploy/outputs.tf rename to outputs.tf index 0afd9799..0c780e17 100644 --- a/solutions/deploy/outputs.tf +++ b/outputs.tf @@ -16,5 +16,5 @@ output "cluster_name" { output "code_engine_project_name" { description = "The name of the code engine project that was created" - value = var.cloud_pak_deployer_image == null ? module.build_cpd_image.code_engine_project_name : null + value = var.cloud_pak_deployer_image == null ? module.build_cpd_image[0].code_engine_project_name : null } diff --git a/reference-architecture/deployable-architecture-cp4d.svg b/reference-architecture/deployable-architecture-cp4d.svg new file mode 100644 index 00000000..437d574e --- /dev/null +++ b/reference-architecture/deployable-architecture-cp4d.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/scripts/wait_for_cpd_pod.sh b/scripts/wait_for_cpd_pod.sh new file mode 100755 index 00000000..647e88b2 --- /dev/null +++ b/scripts/wait_for_cpd_pod.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# filepath: ./scripts/wait_for_cpd_pod.sh + +set -e +NAMESPACE="cloud-pak-deployer" +POD_NAME=$(kubectl get pods -n $NAMESPACE -o jsonpath='{.items[0].metadata.name}') +STATUS="" +while true; do + # shellcheck disable=SC2086 + STATUS=$(kubectl get pod $POD_NAME -n $NAMESPACE -o jsonpath='{.status.phase}') + echo "Pod status: $STATUS" + if [[ "$STATUS" == "Succeeded" || "$STATUS" == "Failed" ]]; then + break + fi + sleep 300 +done diff --git a/solutions/deploy/data.tf b/solutions/deploy/data.tf deleted file mode 100644 index 22c4bdea..00000000 --- a/solutions/deploy/data.tf +++ /dev/null @@ -1,3 +0,0 @@ -data "external" "schematics" { - program = ["bash", "${local.paths.scripts}/get-schematics-tmp-dir.sh"] -} diff --git a/solutions/deploy/providers.tf b/solutions/deploy/providers.tf deleted file mode 100644 index 4eab448e..00000000 --- a/solutions/deploy/providers.tf +++ /dev/null @@ -1,25 +0,0 @@ -provider "ibm" { - ibmcloud_api_key = var.ibmcloud_api_key - - region = var.region -} - -data "ibm_container_cluster_config" "cluster_config" { - cluster_name_id = var.cluster_name - config_dir = local.kube_config_dir -} - -data "ibm_iam_auth_token" "tokendata" {} - -provider "shell" { - sensitive_environment = { - TOKEN = data.ibm_iam_auth_token.tokendata.iam_access_token - } -} - -provider "helm" { - kubernetes { - host = data.ibm_container_cluster_config.cluster_config.host - token = data.ibm_container_cluster_config.cluster_config.token - } -} diff --git a/solutions/deploy/watsonx-ai/data.tf b/solutions/deploy/watsonx-ai/data.tf deleted file mode 100644 index e69de29b..00000000 diff --git a/solutions/deploy/watsonx-data/data.tf b/solutions/deploy/watsonx-data/data.tf deleted file mode 100644 index e69de29b..00000000 diff --git a/solutions/fully-configurable/DA-types.md b/solutions/fully-configurable/DA-types.md new file mode 100644 index 00000000..409c3dc6 --- /dev/null +++ b/solutions/fully-configurable/DA-types.md @@ -0,0 +1,66 @@ +# Variable: `cloud_pak_deployer_secret` + +## Overview + +The `cloud_pak_deployer_secret` variable is used to provide credentials for accessing a private Cloud Pak Deployer image registry. This is required if you are using a custom or private image that is not publicly accessible. If this variable is set to `null`, a default secret will be created automatically. + +## Variable Definition + +```hcl +variable "cloud_pak_deployer_secret" { + description = "Secret for accessing the Cloud Pak Deployer image. If `null`, a default secret will be created # pragma: allowlist secret." + type = object({ + username = string + password = string + server = string + email = string + }) + default = null +} +``` + +## Usage + +The value should be an object with the following fields: + +- `username`: The username for the container registry. +- `password`: The password or token for the container registry. +- `server`: The registry server URL (e.g., `quay.io`, `icr.io`, etc.). +- `email`: The email address associated with the registry account. + +### Example + +```hcl +cloud_pak_deployer_secret = { + username = "my-registry-user" + password = "my-registry-password" # pragma: allowlist secret + server = "quay.io" + email = "user@example.com" +} +``` + +If you are using IBM Cloud Container Registry (ICR), your `server` might look like `us.icr.io` or `de.icr.io`. + +### When to Use + +- **Required**: When your `cloud_pak_deployer_image` is hosted in a private registry that requires authentication. +- **Optional**: If using the default public image, you can leave this variable as `null`. + +## References + +- [IBM Cloud Pak Deployer Documentation](https://github.com/IBM/cloud-pak-deployer) +- [IBM Cloud Container Registry Docs](https://cloud.ibm.com/docs/container-registry) +- [Terraform Input Variables](https://developer.hashicorp.com/terraform/language/values/variables) + +## Example usage + +```hcl +cloud_pak_deployer_secret = { + username = "myuser" + password = "mypassword" # pragma: allowlist secret + server = "us.icr.io" + email = "myuser@example.com" +} +``` + +> **Note:** If you do not need a custom image or private registry, you can omit this variable or set it to `null`. diff --git a/solutions/deploy/README.md b/solutions/fully-configurable/README.md similarity index 67% rename from solutions/deploy/README.md rename to solutions/fully-configurable/README.md index 1a014957..18c6c649 100644 --- a/solutions/deploy/README.md +++ b/solutions/fully-configurable/README.md @@ -1,5 +1,5 @@ -# IBM Cloud Pak for Data module +# Watsonx (Self-Managed) on Red Hat OpenShift [-red)](https://terraform-ibm-modules.github.io/documentation/#/badge-status) -[](https://github.com/terraform-ibm-modules/terraform-ibm-cloudpak-data/releases/latest) +[](https://github.com/terraform-ibm-modules/terraform-ibm-watsonx-self-managed-ocp/releases/latest) [](https://github.com/pre-commit/pre-commit) [](https://renovatebot.com/) [](https://github.com/semantic-release/semantic-release) @@ -20,15 +20,25 @@ For information, see "Module names and descriptions" at https://terraform-ibm-modules.github.io/documentation/#/implementation-guidelines?id=module-names-and-descriptions --> -Install IBM Cloud Pak for Data and its services onto an existing Red Hat OpenShift cluster. The following services are currently supported: +Deploy Watsonx services on an existing Red Hat OpenShift cluster. + +You can: +- **Use an existing OpenShift cluster** by providing its name and resource group + +Cluster must meet the minimum requirements to deploy Cloud Pak for Data [Learn more](https://www.ibm.com/docs/en/cloud-paks/cp-data/5.1.x?topic=overview-cloud-pak-data). + +The following services are currently supported: - watsonx.ai - Watson Assistant - Watson Discovery +- Watson Data + + ## Overview -* [terraform-ibm-cloudpak-data](#terraform-ibm-cloudpak-data) +* [terraform-ibm-watsonx-self-managed-ocp](#terraform-ibm-watsonx-self-managed-ocp) * [Contributing](#contributing) @@ -43,7 +53,7 @@ https://terraform-ibm-modules.github.io/documentation/#/implementation-guideline -## terraform-ibm-cloudpak-data +## terraform-ibm-watsonx-self-managed-ocp ### Usage @@ -55,14 +65,15 @@ unless real values don't help users know what to change. --> ```hcl -module "cloudpak_data" { - source = "../../solutions/deploy" - ibmcloud_api_key = var.ibmcloud_api_key #pragma: allowlist secret - prefix = var.prefix - region = var.region - cluster_name =
"ibm-granite-13b-instruct-v2"
]
object({
    username = string
    password = string
    server   = string
    email    = string
  }) | `null` | no |
-|  [cluster\_name](#input\_cluster\_name) | Name of Red Hat OpenShift cluster to install watsonx onto | `string` | n/a | yes |
-|  [cluster\_rg\_id](#input\_cluster\_rg\_id) | Resource group id of the cluster | `string` | n/a | yes |
+|  [cloud\_pak\_deployer\_image](#input\_cloud\_pak\_deployer\_image) | Cloud Pak Deployer image to use. If `null`, the image will be built using Code Engine. | `string` | `"quay.io/cloud-pak-deployer/cloud-pak-deployer"` | no |
+|  [cloud\_pak\_deployer\_release](#input\_cloud\_pak\_deployer\_release) | Release of Cloud Pak Deployer version to use. View releases at: https://github.com/IBM/cloud-pak-deployer/releases. | `string` | `"v3.1.8"` | no |
+|  [cloud\_pak\_deployer\_secret](#input\_cloud\_pak\_deployer\_secret) | Secret for accessing the Cloud Pak Deployer image. If `null`, a default secret will be created # pragma: allowlist secret. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-watsonx-self-managed-ocp/tree/main/solutions/fully-configurable/DA-types.md). | object({
    username = string
    password = string
    server   = string
    email    = string
  }) | `null` | no |
 |  [code\_engine\_project\_id](#input\_code\_engine\_project\_id) | If you want to use an existing project, you can pass the code engine project ID and the Cloud Pak Deployer build will be built within the existing project instead of creating a new one. | `string` | `null` | no |
-|  [code\_engine\_project\_name](#input\_code\_engine\_project\_name) | If the variable cloud\_pak\_deployer\_image is null, it will build the image with code engine and store it within a private ICR registry. Provide a name if you want to set the name. If not defined, default will be `{prefix}-cpd-{random-suffix}`. | `string` | `null` | no |
-|  [cpd\_accept\_license](#input\_cpd\_accept\_license) | When set to 'true', it is understood that the user has read the terms of the Cloud Pak license(s) and agrees to the terms outlined. | `bool` | `true` | no |
-|  [cpd\_admin\_password](#input\_cpd\_admin\_password) | Password for the Cloud Pak for Data admin user. | `string` | n/a | yes |
+|  [code\_engine\_project\_name](#input\_code\_engine\_project\_name) | If `cloud_pak_deployer_image` is `null`, it will build the image with code engine and store it within a private ICR registry. Provide a name if you want to set the name. If not defined, default will be `{prefix}-cpd-{random-suffix}`. | `string` | `null` | no |
+|  [cpd\_accept\_license](#input\_cpd\_accept\_license) | When set to `true`, it is understood that the user has read the terms of the Cloud Pak license(s) and agrees to the terms outlined. | `bool` | `true` | no |
+|  [cpd\_admin\_password](#input\_cpd\_admin\_password) | Password for the Cloud Pak for Data admin user. | `string` | `"passw0rd"` | no |
 |  [cpd\_entitlement\_key](#input\_cpd\_entitlement\_key) | Cloud Pak for Data entitlement key for access to the IBM Entitled Registry. Can be fetched from https://myibm.ibm.com/products-services/containerlibrary. | `string` | n/a | yes |
-|  [cpd\_version](#input\_cpd\_version) | Cloud Pak for Data version to install.  Only version 5.x.x is supported | `string` | `"5.0.3"` | no |
+|  [cpd\_version](#input\_cpd\_version) | Cloud Pak for Data version to install.  Only version 5.x.x is supported, latest versions can be found [here](https://www.ibm.com/docs/en/cloud-paks/cp-data?topic=versions-cloud-pak-data). | `string` | `"5.0.3"` | no |
+|  [existing\_cluster\_name](#input\_existing\_cluster\_name) | Name of an existing Red Hat OpenShift cluster to create and install watsonx onto. | `string` | n/a | yes |
+|  [existing\_resource\_group\_name](#input\_existing\_resource\_group\_name) | Resource group id of the cluster | `string` | n/a | yes |
 |  [ibmcloud\_api\_key](#input\_ibmcloud\_api\_key) | The IBM Cloud API key to deploy resources. | `string` | n/a | yes |
 |  [install\_odf\_cluster\_addon](#input\_install\_odf\_cluster\_addon) | Install the ODF cluster addon. | `bool` | `true` | no |
-|  [odf\_config](#input\_odf\_config) | Configuration for the ODF addon. | `map(string)` | {
  "addSingleReplicaPool": "false",
  "billingType": "essentials",
  "clusterEncryption": "false",
  "disableNoobaaLB": "false",
  "enableNFS": "false",
  "encryptionInTransit": "false",
  "hpcsBaseUrl": "",
  "hpcsEncryption": "false",
  "hpcsInstanceId": "",
  "hpcsSecretName": "",
  "hpcsServiceName": "",
  "hpcsTokenUrl": "",
  "ignoreNoobaa": "true",
  "numOfOsd": "1",
  "ocsUpgrade": "false",
  "odfDeploy": "true",
  "osdDevicePaths": "",
  "osdSize": "512Gi",
  "osdStorageClassName": "ibmc-vpc-block-metro-10iops-tier",
  "prepareForDisasterRecovery": "false",
  "resourceProfile": "balanced",
  "taintNodes": "false",
  "useCephRBDAsDefaultStorageClass": "false",
  "workerNodes": "all",
  "workerPool": ""
} | no |
+|  [odf\_config](#input\_odf\_config) | Configuration for the ODF addon. Example add on options can be found [here](https://cloud.ibm.com/docs/openshift?topic=openshift-deploy-odf-classic&interface=cli#install-odf-cli-classic) | `map(string)` | {
  "addSingleReplicaPool": "false",
  "billingType": "essentials",
  "clusterEncryption": "false",
  "disableNoobaaLB": "false",
  "enableNFS": "false",
  "encryptionInTransit": "false",
  "hpcsBaseUrl": "",
  "hpcsEncryption": "false",
  "hpcsInstanceId": "",
  "hpcsSecretName": "",
  "hpcsServiceName": "",
  "hpcsTokenUrl": "",
  "ignoreNoobaa": "true",
  "numOfOsd": "1",
  "ocsUpgrade": "false",
  "odfDeploy": "true",
  "osdDevicePaths": "",
  "osdSize": "512Gi",
  "osdStorageClassName": "ibmc-vpc-block-metro-10iops-tier",
  "prepareForDisasterRecovery": "false",
  "resourceProfile": "balanced",
  "taintNodes": "false",
  "useCephRBDAsDefaultStorageClass": "false",
  "workerNodes": "all",
  "workerPool": ""
} | no |
 |  [odf\_version](#input\_odf\_version) | Version of ODF to install. | `string` | `"4.16.0"` | no |
-|  [prefix](#input\_prefix) | A unique identifier for resources that is prepended to resources that are provisioned. Must begin with a lowercase letter and end with a lowercase letter or number. Must be 16 or fewer characters. | `string` | `null` | no |
-|  [region](#input\_region) | Region where resources will be created. To find your VPC region, use `ibmcloud is regions` command to find available regions. | `string` | n/a | yes |
-|  [resource\_group](#input\_resource\_group) | Resource group to provision services within. If not defined, a resource group called `{prefix}-cpd` will be created. | `string` | `null` | no |
-|  [resource\_group\_exists](#input\_resource\_group\_exists) | Resource group exists or not within the account. | `bool` | `false` | no |
+|  [prefix](#input\_prefix) | A unique identifier for resources that is prepended to resources that are provisioned. Must begin with a lowercase letter and end with a lowercase letter or number. Must be 16 or fewer characters. | `string` | `"cp4d"` | no |
+|  [region](#input\_region) | Region where resources will be created. To find your VPC region, use `ibmcloud is regions` command to find available regions. | `string` | `"us-south"` | no |
 |  [watson\_assistant\_install](#input\_watson\_assistant\_install) | If watsonx.ai is being installed, also install watson assistant | `bool` | `false` | no |
 |  [watson\_discovery\_install](#input\_watson\_discovery\_install) | If watsonx.ai is being installed, also install watson discovery | `bool` | `false` | no |
 |  [watsonx\_ai\_install](#input\_watsonx\_ai\_install) | Determine whether the watsonx.ai cartridge for the deployer will be installed | `bool` | `false` | no |
diff --git a/solutions/fully-configurable/catalogValidationValues.json.template b/solutions/fully-configurable/catalogValidationValues.json.template
new file mode 100644
index 00000000..18b78fec
--- /dev/null
+++ b/solutions/fully-configurable/catalogValidationValues.json.template
@@ -0,0 +1,6 @@
+{
+  "ibmcloud_api_key": $VALIDATION_APIKEY,
+  "cpd_entitlement_key": $SOFTWARE_ENTITLEMENT_KEY,
+  "prefix": $PREFIX,
+  "region": "us-south"
+}
diff --git a/solutions/fully-configurable/kubeconfig/.gitignore b/solutions/fully-configurable/kubeconfig/.gitignore
new file mode 100644
index 00000000..632a28fb
--- /dev/null
+++ b/solutions/fully-configurable/kubeconfig/.gitignore
@@ -0,0 +1,6 @@
+# Ignore everything
+*
+
+# But not these files...
+!.gitignore
+!README.md
diff --git a/solutions/fully-configurable/kubeconfig/README.md b/solutions/fully-configurable/kubeconfig/README.md
new file mode 100644
index 00000000..e85afee8
--- /dev/null
+++ b/solutions/fully-configurable/kubeconfig/README.md
@@ -0,0 +1,2 @@
+This directory must exist in source control so the `ibm_container_cluster_config` data lookup can use it to place the
+config.yml used to connect to a kubernetes cluster.
diff --git a/solutions/fully-configurable/main.tf b/solutions/fully-configurable/main.tf
new file mode 100644
index 00000000..494c9080
--- /dev/null
+++ b/solutions/fully-configurable/main.tf
@@ -0,0 +1,56 @@
+##############################################################################
+# 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
+}
+
+
+########################################################################################################################
+# Watsonx Self Managed OCP module
+########################################################################################################################
+
+module "watsonx_self_managed_ocp" {
+  source                     = "../.."
+  prefix                     = var.prefix
+  ibmcloud_api_key           = var.ibmcloud_api_key
+  region                     = var.region
+  resource_group             = module.resource_group.resource_group_id
+  code_engine_project_name   = var.code_engine_project_name
+  code_engine_project_id     = var.code_engine_project_id
+  cloud_pak_deployer_image   = var.cloud_pak_deployer_image
+  cloud_pak_deployer_release = var.cloud_pak_deployer_release
+  cloud_pak_deployer_secret  = var.cloud_pak_deployer_secret
+  cluster_name               = var.existing_cluster_name
+  cluster_rg_id              = module.resource_group.resource_group_id
+  install_odf_cluster_addon  = var.install_odf_cluster_addon
+  odf_version                = var.odf_version
+  odf_config                 = var.odf_config
+  cpd_version                = var.cpd_version
+  cpd_accept_license         = var.cpd_accept_license
+  cpd_admin_password         = var.cpd_admin_password
+  cpd_entitlement_key        = var.cpd_entitlement_key
+  watsonx_ai_install         = var.watsonx_ai_install
+  watsonx_ai_models          = var.watsonx_ai_models
+  watsonx_data_install       = var.watsonx_data_install
+  watson_discovery_install   = var.watson_discovery_install
+  watson_assistant_install   = var.watson_assistant_install
+}
+
+resource "null_resource" "wait_for_cloud_pak_deployer_complete" {
+  provisioner "local-exec" {
+    command = "${path.module}/../../scripts/wait_for_cpd_pod.sh"
+
+    environment = {
+      KUBECONFIG = data.ibm_container_cluster_config.cluster_config.config_file_path
+    }
+  }
+  triggers = {
+    always_run = timestamp()
+  }
+
+  depends_on = [module.watsonx_self_managed_ocp]
+}
diff --git a/solutions/fully-configurable/outputs.tf b/solutions/fully-configurable/outputs.tf
new file mode 100644
index 00000000..dadba504
--- /dev/null
+++ b/solutions/fully-configurable/outputs.tf
@@ -0,0 +1,20 @@
+output "cloud_pak_deployer_image" {
+  description = "The Cloud Pak Deployer image used."
+  value       = module.watsonx_self_managed_ocp.cloud_pak_deployer_image
+}
+
+output "cloud_pak_deployer_secret" {
+  description = "The secret used for accessing the Cloud Pak Deployer image."
+  value       = module.watsonx_self_managed_ocp.cloud_pak_deployer_secret
+  sensitive   = true
+}
+
+output "cluster_name" {
+  description = "The name of the OpenShift cluster."
+  value       = module.watsonx_self_managed_ocp.cluster_name
+}
+
+output "code_engine_project_name" {
+  description = "The name of the code engine project that was created"
+  value       = module.watsonx_self_managed_ocp.code_engine_project_name
+}
diff --git a/solutions/fully-configurable/providers.tf b/solutions/fully-configurable/providers.tf
new file mode 100644
index 00000000..88c2a2a4
--- /dev/null
+++ b/solutions/fully-configurable/providers.tf
@@ -0,0 +1,43 @@
+provider "ibm" {
+  ibmcloud_api_key = var.ibmcloud_api_key
+
+  region = var.region
+}
+
+locals {
+  paths = {
+    scripts = "${path.module}/scripts"
+  }
+}
+
+data "external" "schematics" {
+  program = ["bash", "${local.paths.scripts}/get-schematics-tmp-dir.sh"]
+}
+
+locals {
+  schematics_workspace = {
+    persistent_dir_exists = data.external.schematics.result.schematics_tmp_dir_exists ? true : false
+    persistent_dir_path   = "/tmp/.schematics"
+  }
+  kube_config_dir = local.schematics_workspace.persistent_dir_exists ? local.schematics_workspace.persistent_dir_path : "${path.module}/kubeconfig"
+}
+
+data "ibm_container_cluster_config" "cluster_config" {
+  cluster_name_id = var.existing_cluster_name
+  config_dir      = local.kube_config_dir
+}
+
+data "ibm_iam_auth_token" "tokendata" {}
+
+provider "shell" {
+  sensitive_environment = {
+    TOKEN = data.ibm_iam_auth_token.tokendata.iam_access_token
+  }
+}
+
+provider "helm" {
+  kubernetes {
+    host  = data.ibm_container_cluster_config.cluster_config.host
+    token = data.ibm_container_cluster_config.cluster_config.token
+  }
+}
diff --git a/solutions/deploy/scripts/get-schematics-tmp-dir.sh b/solutions/fully-configurable/scripts/get-schematics-tmp-dir.sh
similarity index 100%
rename from solutions/deploy/scripts/get-schematics-tmp-dir.sh
rename to solutions/fully-configurable/scripts/get-schematics-tmp-dir.sh
diff --git a/solutions/fully-configurable/variables.tf b/solutions/fully-configurable/variables.tf
new file mode 100644
index 00000000..5904a5de
--- /dev/null
+++ b/solutions/fully-configurable/variables.tf
@@ -0,0 +1,192 @@
+variable "ibmcloud_api_key" {
+  description = "The IBM Cloud API key to deploy resources."
+  type        = string
+  sensitive   = true
+}
+
+variable "prefix" {
+  description = "A unique identifier for resources that is prepended to resources that are provisioned. Must begin with a lowercase letter and end with a lowercase letter or number. Must be 16 or fewer characters."
+  type        = string
+  default     = "cp4d"
+  validation {
+    error_message = "Prefix must begin with a letter and contain only lowercase letters, numbers, and - characters. Prefixes must end with a lowercase letter or number and be 16 or fewer characters."
+    condition     = can(regex("^([a-z]|[a-z][-a-z0-9]*[a-z0-9])$", var.prefix)) && length(var.prefix) <= 16
+  }
+}
+
+variable "region" {
+  description = "Region where resources will be created. To find your VPC region, use `ibmcloud is regions` command to find available regions."
+  type        = string
+  nullable    = false
+  default     = "us-south"
+}
+
+variable "code_engine_project_name" {
+  description = "If `cloud_pak_deployer_image` is `null`, it will build the image with code engine and store it within a private ICR registry. Provide a name if you want to set the name. If not defined, default will be `{prefix}-cpd-{random-suffix}`."
+  type        = string
+  default     = null
+}
+
+variable "code_engine_project_id" {
+  description = "If you want to use an existing project, you can pass the code engine project ID and the Cloud Pak Deployer build will be built within the existing project instead of creating a new one."
+  type        = string
+  default     = null
+}
+
+variable "cloud_pak_deployer_image" {
+  description = "Cloud Pak Deployer image to use. If `null`, the image will be built using Code Engine."
+  type        = string
+  default     = "quay.io/cloud-pak-deployer/cloud-pak-deployer"
+}
+
+variable "cloud_pak_deployer_release" {
+  description = "Release of Cloud Pak Deployer version to use. View releases at: https://github.com/IBM/cloud-pak-deployer/releases."
+  type        = string
+  default     = "v3.1.8"
+}
+
+variable "cloud_pak_deployer_secret" {
+  description = "Secret for accessing the Cloud Pak Deployer image. If `null`, a default secret will be created # pragma: allowlist secret. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-watsonx-self-managed-ocp/tree/main/solutions/fully-configurable/DA-types.md)."
+  type = object({
+    username = string
+    password = string
+    server   = string
+    email    = string
+  })
+  default = null
+}
+
+variable "existing_cluster_name" {
+  description = "Name of an existing Red Hat OpenShift cluster to create and install watsonx onto."
+  type        = string
+}
+
+variable "existing_resource_group_name" {
+  description = "Resource group id of the cluster"
+  type        = string
+  validation {
+    error_message = "`existing_resource_group_name` cannot be null if `existing_cluster_name` is not null."
+    condition     = var.existing_cluster_name == null || var.existing_resource_group_name != null
+  }
+}
+
+variable "install_odf_cluster_addon" {
+  description = "Install the ODF cluster addon."
+  type        = bool
+  default     = true
+  nullable    = false
+}
+
+variable "odf_version" {
+  description = "Version of ODF to install."
+  type        = string
+  default     = "4.16.0"
+  validation {
+    error_message = "ODF version must be in the format 'x.y.z'."
+    condition     = can(regex("^[0-9]+\\.[0-9]+\\.[0-9]+$", var.odf_version))
+  }
+  nullable = false
+}
+
+variable "odf_config" {
+  description = "Configuration for the ODF addon. Example add on options can be found [here](https://cloud.ibm.com/docs/openshift?topic=openshift-deploy-odf-classic&interface=cli#install-odf-cli-classic)"
+  type        = map(string)
+  default = {
+    "odfDeploy"                       = "true"
+    "workerNodes"                     = "all"
+    "workerPool"                      = ""
+    "resourceProfile"                 = "balanced"
+    "billingType"                     = "essentials"
+    "osdSize"                         = "512Gi"
+    "osdStorageClassName"             = "ibmc-vpc-block-metro-10iops-tier"
+    "numOfOsd"                        = "1"
+    "ocsUpgrade"                      = "false"
+    "enableNFS"                       = "false"
+    "hpcsEncryption"                  = "false"
+    "hpcsInstanceId"                  = ""
+    "hpcsServiceName"                 = ""
+    "hpcsSecretName"                  = ""
+    "hpcsBaseUrl"                     = ""
+    "hpcsTokenUrl"                    = ""
+    "clusterEncryption"               = "false"
+    "encryptionInTransit"             = "false"
+    "addSingleReplicaPool"            = "false"
+    "ignoreNoobaa"                    = "true"
+    "disableNoobaaLB"                 = "false"
+    "prepareForDisasterRecovery"      = "false"
+    "useCephRBDAsDefaultStorageClass" = "false"
+    "osdDevicePaths"                  = ""
+    "taintNodes"                      = "false"
+  }
+}
+
+variable "cpd_accept_license" {
+  description = "When set to `true`, it is understood that the user has read the terms of the Cloud Pak license(s) and agrees to the terms outlined."
+  type        = bool
+  default     = true
+  nullable    = false
+}
+
+variable "cpd_admin_password" {
+  description = "Password for the Cloud Pak for Data admin user."
+  sensitive   = true
+  type        = string
+  nullable    = false
+  default     = "passw0rd" # pragma: allowlist secret
+}
+
+variable "cpd_entitlement_key" {
+  description = "Cloud Pak for Data entitlement key for access to the IBM Entitled Registry. Can be fetched from https://myibm.ibm.com/products-services/containerlibrary."
+  sensitive   = true
+  type        = string
+  nullable    = false
+}
+
+variable "cpd_version" {
+  description = "Cloud Pak for Data version to install.  Only version 5.x.x is supported, latest versions can be found [here](https://www.ibm.com/docs/en/cloud-paks/cp-data?topic=versions-cloud-pak-data)."
+  type        = string
+  validation {
+    error_message = "Cloud pak for data major version 5 is supported."
+    condition     = split(".", var.cpd_version)[0] == "5"
+  }
+  default  = "5.0.3"
+  nullable = false
+}
+
+#  Only used in the watsonx.ai offering flavour
+variable "watsonx_ai_install" {
+  description = "Determine whether the watsonx.ai cartridge for the deployer will be installed"
+  type        = bool
+  default     = false
+  nullable    = false
+}
+
+#  Only used in the watsonx.ai offering flavour
+variable "watsonx_ai_models" {
+  description = "List of watsonx.ai models to install.  Information on the foundation models including pre-reqs can be found here - https://www.ibm.com/docs/en/cloud-paks/cp-data/5.0.x?topic=install-foundation-models.  Use the ModelID as input"
+  type        = list(string)
+  default     = ["ibm-granite-13b-instruct-v2"]
+  nullable    = false
+}
+
+#  Only used in the watsonx.data offering flavour
+variable "watsonx_data_install" {
+  description = "Determine whether the watsonx.data cartridge for the deployer will be installed"
+  type        = bool
+  default     = false
+  nullable    = false
+}
+
+variable "watson_discovery_install" {
+  description = "If watsonx.ai is being installed, also install watson discovery"
+  type        = bool
+  default     = false
+  nullable    = false
+}
+
+variable "watson_assistant_install" {
+  description = "If watsonx.ai is being installed, also install watson assistant"
+  type        = bool
+  default     = false
+  nullable    = false
+}
diff --git a/solutions/deploy/version.tf b/solutions/fully-configurable/version.tf
similarity index 77%
rename from solutions/deploy/version.tf
rename to solutions/fully-configurable/version.tf
index b5f18606..0810d93b 100644
--- a/solutions/deploy/version.tf
+++ b/solutions/fully-configurable/version.tf
@@ -3,7 +3,7 @@ terraform {
   required_providers {
     ibm = {
       source  = "ibm-cloud/ibm"
-      version = "1.78.0"
+      version = "1.79.1"
     }
     external = {
       source  = "hashicorp/external"
@@ -17,5 +17,9 @@ terraform {
       source  = "scottwinkler/shell"
       version = "1.7.10"
     }
+    null = {
+      source  = "hashicorp/null"
+      version = ">= 3.2.1, < 4.0.0"
+    }
   }
 }
diff --git a/tests/go.mod b/tests/go.mod
index 7d0d6e46..06f8d162 100644
--- a/tests/go.mod
+++ b/tests/go.mod
@@ -19,6 +19,7 @@ require (
 	github.com/IBM/platform-services-go-sdk v0.82.0 // indirect
 	github.com/IBM/project-go-sdk v0.3.6 // indirect
 	github.com/IBM/schematics-go-sdk v0.4.0 // indirect
+	github.com/IBM/secrets-manager-go-sdk v1.2.0
 	github.com/IBM/vpc-go-sdk v1.0.2 // indirect
 	github.com/Microsoft/go-winio v0.6.2 // indirect
 	github.com/ProtonMail/go-crypto v1.1.6 // indirect
diff --git a/tests/go.sum b/tests/go.sum
index f10f4d5f..991b2b58 100644
--- a/tests/go.sum
+++ b/tests/go.sum
@@ -15,6 +15,8 @@ github.com/IBM/project-go-sdk v0.3.6 h1:DRiANKnAePevFsIKSvR89SUaMa2xsd7YKK71Ka1e
 github.com/IBM/project-go-sdk v0.3.6/go.mod h1:FOJM9ihQV3EEAY6YigcWiTNfVCThtdY8bLC/nhQHFvo=
 github.com/IBM/schematics-go-sdk v0.4.0 h1:x01f/tPquYJYLQzJLGuxWfCbV/EdSMXRikOceNy/JLM=
 github.com/IBM/schematics-go-sdk v0.4.0/go.mod h1:Xe7R7xgwmXBHu09w2CbBe8lkWZaYxNQo19bS4dpLrUA=
+github.com/IBM/secrets-manager-go-sdk v1.2.0 h1:bgFfBF+LjHLtUfV3hTLkfgE8EjFsJaeU2icA2Hg+M50=
+github.com/IBM/secrets-manager-go-sdk v1.2.0/go.mod h1:qv+tQg8Z3Vb11DQYxDjEGeROHDtTLQxUWuOIrIdWg6E=
 github.com/IBM/vpc-go-sdk v1.0.2 h1:WhI1Cb8atA8glUdFg0SEUh9u8afjnKHxZAj9onQBi04=
 github.com/IBM/vpc-go-sdk v1.0.2/go.mod h1:42NO/XCXsyrYqpvtxoX5xwSEv/jBU1MKEoyaYkIUico=
 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
diff --git a/tests/pr_test.go b/tests/pr_test.go
index db86379e..187dcc0f 100644
--- a/tests/pr_test.go
+++ b/tests/pr_test.go
@@ -10,6 +10,8 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/IBM/go-sdk-core/v5/core"
+	"github.com/IBM/secrets-manager-go-sdk/secretsmanagerv2"
 	"github.com/gruntwork-io/terratest/modules/files"
 	"github.com/gruntwork-io/terratest/modules/logger"
 	"github.com/gruntwork-io/terratest/modules/random"
@@ -21,9 +23,11 @@ import (
 )
 
 // Ensure every example directory has a corresponding test
-const instanceFlavorDir = "solutions/deploy"
+const instanceFlavorDir = "solutions/fully-configurable"
 
-var permanentResources map[string]interface{}
+const cpdEntitlementKeySecretId = "a4292c24-f093-2b8b-9016-37132b7b8788"
+
+var permanentResources map[string]any
 
 // 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"
@@ -39,7 +43,7 @@ func TestMain(m *testing.M) {
 	os.Exit(m.Run())
 }
 
-// A test to pass existing resources to the CloudPak DA
+// A test to pass existing resources to the WatsonX Self Managed OCP DA
 func TestRunStandardSolution(t *testing.T) {
 	t.Parallel()
 	// ------------------------------------------------------------------------------------
@@ -61,7 +65,7 @@ func TestRunStandardSolution(t *testing.T) {
 	logger.Log(t, "Tempdir: ", tempTerraformDir)
 	existingTerraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
 		TerraformDir: tempTerraformDir,
-		Vars: map[string]interface{}{
+		Vars: map[string]any{
 			"prefix":        prefix,
 			"region":        region,
 			"resource_tags": tags,
@@ -77,28 +81,51 @@ func TestRunStandardSolution(t *testing.T) {
 		assert.True(t, existErr == nil, "Init and Apply of temp existing resource failed")
 	} else {
 		// ------------------------------------------------------------------------------------
-		// Deploy Cloudpak DA passing using existing ROKS instance
+		// Deploy WatsonX Self Managed OCP DA passing using existing OCP instance
 		// ------------------------------------------------------------------------------------
+		cpdEntitlementKey, cpdEntitlementKeyErr := GetSecretsManagerKey(
+			permanentResources["secretsManagerGuid"].(string),
+			permanentResources["secretsManagerRegion"].(string),
+			cpdEntitlementKeySecretId,
+		)
+
+		if !assert.NoError(t, cpdEntitlementKeyErr) {
+			t.Error("TestRunStandardUpgradeSolution Failed - geretain-software-entitlement-key not found in secrets manager")
+			panic(cpdEntitlementKeyErr)
+		}
+
 		options := testhelper.TestOptionsDefault(&testhelper.TestOptions{
 			Testing:      t,
 			TerraformDir: instanceFlavorDir,
 			// 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,
-				"region":                    region,
-				"cluster_name":              terraform.Output(t, existingTerraformOptions, "workload_cluster_id"),
-				"cluster_rg_id":             terraform.Output(t, existingTerraformOptions, "workload_rg_id"),
-				"cloud_pak_deployer_image":  "quay.io/cloud-pak-deployer/cloud-pak-deployer",
-				"cpd_admin_password":        GetRandomAdminPassword(t),
-				"cpd_entitlement_key":       "entitlementKey",
-				"install_odf_cluster_addon": false,
+			TerraformVars: map[string]any{
+				"prefix":                       prefix,
+				"region":                       region,
+				"existing_cluster_name":        terraform.Output(t, existingTerraformOptions, "cluster_name"),
+				"existing_resource_group_name": terraform.Output(t, existingTerraformOptions, "cluster_resource_group_name"),
+				"cloud_pak_deployer_image":     "quay.io/cloud-pak-deployer/cloud-pak-deployer",
+				"cpd_admin_password":           GetRandomAdminPassword(t),
+				"cpd_entitlement_key":          *cpdEntitlementKey,
+				"install_odf_cluster_addon":    true,
 			},
 		})
 
 		options.IgnoreUpdates = testhelper.Exemptions{
 			List: []string{
-				"module.cloud_pak_deployer.helm_release.cloud_pak_deployer_helm_release",
+				"module.watsonx_self_managed_ocp.module.cloud_pak_deployer.helm_release.cloud_pak_deployer_helm_release",
+			},
+		}
+
+		options.IgnoreAdds = testhelper.Exemptions{
+			List: []string{
+				"null_resource.wait_for_cloud_pak_deployer_complete",
+			},
+		}
+
+		options.IgnoreDestroys = testhelper.Exemptions{
+			List: []string{
+				"null_resource.wait_for_cloud_pak_deployer_complete",
 			},
 		}
 
@@ -154,28 +181,51 @@ func TestRunStandardUpgradeSolution(t *testing.T) {
 		assert.True(t, existErr == nil, "Init and Apply of temp existing resource failed")
 	} else {
 		// ------------------------------------------------------------------------------------
-		// Deploy Cloudpak DA passing using existing ROKS instance
+		// Deploy WatsonX Self Managed OCP DA passing using existing OCP instance
 		// ------------------------------------------------------------------------------------
+		cpdEntitlementKey, cpdEntitlementKeyErr := GetSecretsManagerKey(
+			permanentResources["secretsManagerGuid"].(string),
+			permanentResources["secretsManagerRegion"].(string),
+			cpdEntitlementKeySecretId,
+		)
+
+		if !assert.NoError(t, cpdEntitlementKeyErr) {
+			t.Error("TestRunStandardUpgradeSolution Failed - geretain-software-entitlement-key not found in secrets manager")
+			panic(cpdEntitlementKeyErr)
+		}
+
 		options := testhelper.TestOptionsDefault(&testhelper.TestOptions{
 			Testing:      t,
 			TerraformDir: instanceFlavorDir,
 			// 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,
-				"region":                    region,
-				"cluster_name":              terraform.Output(t, existingTerraformOptions, "workload_cluster_id"),
-				"cluster_rg_id":             terraform.Output(t, existingTerraformOptions, "workload_rg_id"),
-				"cloud_pak_deployer_image":  "quay.io/cloud-pak-deployer/cloud-pak-deployer",
-				"cpd_admin_password":        GetRandomAdminPassword(t),
-				"cpd_entitlement_key":       "entitlementKey",
-				"install_odf_cluster_addon": false,
+			TerraformVars: map[string]any{
+				"prefix":                       prefix,
+				"region":                       region,
+				"existing_cluster_name":        terraform.Output(t, existingTerraformOptions, "cluster_name"),
+				"existing_resource_group_name": terraform.Output(t, existingTerraformOptions, "cluster_resource_group_name"),
+				"cloud_pak_deployer_image":     "quay.io/cloud-pak-deployer/cloud-pak-deployer",
+				"cpd_admin_password":           GetRandomAdminPassword(t),
+				"cpd_entitlement_key":          *cpdEntitlementKey,
+				"install_odf_cluster_addon":    true,
 			},
 		})
 
 		options.IgnoreUpdates = testhelper.Exemptions{
 			List: []string{
-				"module.cloud_pak_deployer.helm_release.cloud_pak_deployer_helm_release",
+				"module.watsonx_self_managed_ocp.module.cloud_pak_deployer.helm_release.cloud_pak_deployer_helm_release",
+			},
+		}
+
+		options.IgnoreAdds = testhelper.Exemptions{
+			List: []string{
+				"null_resource.wait_for_cloud_pak_deployer_complete",
+			},
+		}
+
+		options.IgnoreDestroys = testhelper.Exemptions{
+			List: []string{
+				"null_resource.wait_for_cloud_pak_deployer_complete",
 			},
 		}
 
@@ -209,3 +259,26 @@ func GetRandomAdminPassword(t *testing.T) string {
 
 	return randomPass
 }
+
+// GetSecretsManagerKey retrieves a secret from Secrets Manager
+func GetSecretsManagerKey(smId string, smRegion string, smKeyId string) (*string, error) {
+	secretsManagerService, err := secretsmanagerv2.NewSecretsManagerV2(&secretsmanagerv2.SecretsManagerV2Options{
+		URL: fmt.Sprintf("https://%s.%s.secrets-manager.appdomain.cloud", smId, smRegion),
+		Authenticator: &core.IamAuthenticator{
+			ApiKey: os.Getenv("TF_VAR_ibmcloud_api_key"),
+		},
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	getSecretOptions := secretsManagerService.NewGetSecretOptions(
+		smKeyId,
+	)
+
+	secret, _, err := secretsManagerService.GetSecret(getSecretOptions)
+	if err != nil {
+		return nil, err
+	}
+	return secret.(*secretsmanagerv2.ArbitrarySecret).Payload, nil
+}
diff --git a/tests/resources/README.md b/tests/resources/README.md
new file mode 100644
index 00000000..4bb3621d
--- /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
index a00bca8a..c20bd5bc 100644
--- a/tests/resources/main.tf
+++ b/tests/resources/main.tf
@@ -1,11 +1,84 @@
 ##############################################################################
-# SLZ VPC
+# Resource Group
 ##############################################################################
 
-module "landing_zone" {
-  source           = "git::https://github.com/terraform-ibm-modules/terraform-ibm-landing-zone//patterns//roks-quickstart?ref=v8.2.0"
-  ibmcloud_api_key = var.ibmcloud_api_key
-  region           = var.region
-  prefix           = var.prefix
-  resource_tags    = var.resource_tags
+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
+}
+
+########################################################################################################################
+# VPC + Subnet + Public Gateway
+#
+# NOTE: This is a very simple VPC with single subnet in a single zone with a public gateway enabled, that will allow
+# all traffic ingress/egress by default.
+# For production use cases this would need to be enhanced by adding more subnets and zones for resiliency, and
+# ACLs/Security Groups for network security.
+########################################################################################################################
+
+resource "ibm_is_vpc" "vpc" {
+  name                      = "${var.prefix}-vpc"
+  resource_group            = module.resource_group.resource_group_id
+  address_prefix_management = "auto"
+  tags                      = var.resource_tags
+}
+
+resource "ibm_is_public_gateway" "gateway" {
+  name           = "${var.prefix}-gateway-1"
+  vpc            = ibm_is_vpc.vpc.id
+  resource_group = module.resource_group.resource_group_id
+  zone           = "${var.region}-1"
+}
+
+resource "ibm_is_subnet" "subnet_zone_1" {
+  name                     = "${var.prefix}-subnet-1"
+  vpc                      = ibm_is_vpc.vpc.id
+  resource_group           = module.resource_group.resource_group_id
+  zone                     = "${var.region}-1"
+  total_ipv4_address_count = 256
+  public_gateway           = ibm_is_public_gateway.gateway.id
+}
+
+########################################################################################################################
+# OCP VPC cluster (single zone)
+########################################################################################################################
+
+locals {
+  cluster_vpc_subnets = {
+    default = [
+      {
+        id         = ibm_is_subnet.subnet_zone_1.id
+        cidr_block = ibm_is_subnet.subnet_zone_1.ipv4_cidr_block
+        zone       = ibm_is_subnet.subnet_zone_1.zone
+      }
+    ]
+  }
+
+  worker_pools = [
+    {
+      subnet_prefix    = "default"
+      pool_name        = "default" # ibm_container_vpc_cluster automatically names default pool "default" (See https://github.com/IBM-Cloud/terraform-provider-ibm/issues/2849)
+      machine_type     = "bx2.16x64"
+      workers_per_zone = 3 # Minimum 3 workers to install ODF and ensure high availability
+      operating_system = "REDHAT_8_64"
+    }
+  ]
+}
+
+module "ocp_base" {
+  source                              = "terraform-ibm-modules/base-ocp-vpc/ibm"
+  version                             = "3.49.0"
+  resource_group_id                   = module.resource_group.resource_group_id
+  region                              = var.region
+  tags                                = var.resource_tags
+  cluster_name                        = "${var.prefix}-cluster"
+  force_delete_storage                = true
+  vpc_id                              = ibm_is_vpc.vpc.id
+  vpc_subnets                         = local.cluster_vpc_subnets
+  worker_pools                        = local.worker_pools
+  access_tags                         = []
+  disable_outbound_traffic_protection = true
 }
diff --git a/tests/resources/outputs.tf b/tests/resources/outputs.tf
index 4411b4f8..e056ce4d 100644
--- a/tests/resources/outputs.tf
+++ b/tests/resources/outputs.tf
@@ -1,23 +1,19 @@
-##############################################################################
-# Outputs
-##############################################################################
-
-output "prefix" {
-  value       = module.landing_zone.prefix
-  description = "prefix"
+output "cluster_name" {
+  description = "The name of the OpenShift cluster."
+  value       = module.ocp_base.cluster_name
 }
 
-output "region" {
-  value       = var.region
-  description = "Region where SLZ ROKS Cluster is deployed."
+output "cluster_id" {
+  value       = module.ocp_base.cluster_id
+  description = "Cluster ID."
 }
 
-output "workload_cluster_id" {
-  value       = module.landing_zone.workload_cluster_id
-  description = "workload cluster ID."
+output "cluster_resource_group_id" {
+  value       = module.ocp_base.resource_group_id
+  description = "Cluster resource group ID."
 }
 
-output "workload_rg_id" {
-  value       = module.landing_zone.workload_rg_id
-  description = "workload resource group ID."
+output "cluster_resource_group_name" {
+  value       = module.resource_group.resource_group_name
+  description = "Cluster resource group name."
 }
diff --git a/tests/resources/variables.tf b/tests/resources/variables.tf
index 71532bb7..a654f4ff 100644
--- a/tests/resources/variables.tf
+++ b/tests/resources/variables.tf
@@ -1,23 +1,29 @@
 variable "ibmcloud_api_key" {
   type        = string
-  description = "The IBM Cloud API key to deploy resources."
+  description = "The IBM Cloud API Key."
   sensitive   = true
 }
 
 variable "region" {
   type        = string
-  description = "Region to provision all resources created by this example"
+  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     = "slz-vpc"
+  description = "Prefix to append to all resources created by this example."
+  default     = "cp-da"
 }
 
 variable "resource_tags" {
   type        = list(string)
-  description = "Optional list of tags to be added to created resources"
-  default     = []
+  description = "Optional list of tags to be added to created resources."
+  default     = ["cp-ocp"]
+}
+
+variable "resource_group" {
+  type        = string
+  description = "The name of an existing resource group to provision resources in. If not specified, a new resource group is created with the `prefix` variable."
+  default     = null
 }
diff --git a/tests/resources/version.tf b/tests/resources/version.tf
index 050d0fcc..2ac74bab 100644
--- a/tests/resources/version.tf
+++ b/tests/resources/version.tf
@@ -1,9 +1,10 @@
+
 terraform {
-  required_version = ">= 1.0.0"
+  required_version = ">= 1.9.0"
   required_providers {
     ibm = {
       source  = "ibm-cloud/ibm"
-      version = ">= 1.49.0, < 2.0.0"
+      version = ">= 1.76.1"
     }
   }
 }
diff --git a/tests/scripts/post-validation-destroy-ocp-instances.sh b/tests/scripts/post-validation-destroy-ocp-instances.sh
new file mode 100755
index 00000000..1d34e77f
--- /dev/null
+++ b/tests/scripts/post-validation-destroy-ocp-instances.sh
@@ -0,0 +1,19 @@
+#! /bin/bash
+
+########################################################################################################################
+## This script is used by the catalog pipeline to destroy the OCP Cluster, which was provisioned as a            ##
+## prerequisite for the WAS extension 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 OCP instances .."
+  terraform destroy -input=false -auto-approve -var-file=${TF_VARS_FILE} || exit 1
+
+  echo "Post-validation completed successfully"
+)
diff --git a/tests/scripts/pre-validation-deploy-ocp-instances.sh b/tests/scripts/pre-validation-deploy-ocp-instances.sh
new file mode 100755
index 00000000..4d78812b
--- /dev/null
+++ b/tests/scripts/pre-validation-deploy-ocp-instances.sh
@@ -0,0 +1,55 @@
+#! /bin/bash
+
+############################################################################################################
+## This script is used by the catalog pipeline to deploy the OCP instances,
+## which are the prerequisites for the SCC workload protection standard fullstack.
+############################################################################################################
+
+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 OCP instances .."
+  terraform init || exit 1
+  # $VALIDATION_APIKEY is available in the catalog runtime
+  {
+    echo "ibmcloud_api_key=\"${VALIDATION_APIKEY}\""
+    echo "region=\"${REGION}\""
+    echo "prefix=\"ocp-$(openssl rand -hex 2)\""
+  } >> ${TF_VARS_FILE}
+  terraform apply -input=false -auto-approve -var-file=${TF_VARS_FILE} || exit 1
+
+  region_var_name="region"
+  prefix_var_name="prefix"
+  prefix_value="ocp-$(openssl rand -hex 2)"
+  cpd_entitlement_key_var_name="cpd_entitlement_key"
+  cpd_entitlement_key_value="${SOFTWARE_ENTITLEMENT_KEY}"
+  existing_cluster_name_var_name="existing_cluster_name"
+  existing_cluster_name_value=$(terraform output -state=terraform.tfstate -raw cluster_name)
+  existing_resource_group_name_var_name="existing_resource_group_name"
+  existing_resource_group_name_value=$(terraform output -state=terraform.tfstate -raw cluster_resource_group_name)
+
+  echo "Appending '${existing_cluster_name_var_name}', '${existing_resource_group_name_var_name}', '${cpd_entitlement_key_var_name}', and '${region_var_name}' input variable values to ${JSON_FILE}.."
+
+  cd "${cwd}"
+  jq -r --arg region_var_name "${region_var_name}" \
+        --arg region_var_value "${REGION}" \
+        --arg prefix_var_name "${prefix_var_name}" \
+        --arg prefix_value "${prefix_value}" \
+        --arg cpd_entitlement_key_var_name "${cpd_entitlement_key_var_name}" \
+        --arg cpd_entitlement_key_value "${cpd_entitlement_key_value}" \
+        --arg existing_cluster_name_var_name "${existing_cluster_name_var_name}" \
+        --arg existing_cluster_name_value "${existing_cluster_name_value}" \
+        --arg existing_resource_group_name_var_name "${existing_resource_group_name_var_name}" \
+        --arg existing_resource_group_name_value "${existing_resource_group_name_value}" \
+        '. + {($region_var_name): $region_var_value, ($prefix_var_name): $prefix_value, ($cpd_entitlement_key_var_name): $cpd_entitlement_key_value, ($existing_cluster_name_var_name): $existing_cluster_name_value, ($existing_resource_group_name_var_name): $existing_resource_group_name_value}' "${JSON_FILE}" > tmpfile && mv tmpfile "${JSON_FILE}" || exit 1
+
+  echo "Pre-validation complete successfully"
+)
diff --git a/solutions/deploy/variables.tf b/variables.tf
similarity index 93%
rename from solutions/deploy/variables.tf
rename to variables.tf
index cef6c2b5..c1b57db2 100644
--- a/solutions/deploy/variables.tf
+++ b/variables.tf
@@ -26,14 +26,8 @@ variable "resource_group" {
   default     = null
 }
 
-variable "resource_group_exists" {
-  description = "Resource group exists or not within the account."
-  type        = bool
-  default     = false
-}
-
 variable "code_engine_project_name" {
-  description = "If the variable cloud_pak_deployer_image is null, it will build the image with code engine and store it within a private ICR registry. Provide a name if you want to set the name. If not defined, default will be `{prefix}-cpd-{random-suffix}`."
+  description = "If `cloud_pak_deployer_image` is `null`, it will build the image with code engine and store it within a private ICR registry. Provide a name if you want to set the name. If not defined, default will be `{prefix}-cpd-{random-suffix}`."
   type        = string
   default     = null
 }
@@ -53,7 +47,7 @@ variable "cloud_pak_deployer_image" {
 variable "cloud_pak_deployer_release" {
   description = "Release of Cloud Pak Deployer version to use. View releases at: https://github.com/IBM/cloud-pak-deployer/releases."
   type        = string
-  default     = "v3.1.3"
+  default     = "v3.1.8"
 }
 
 variable "cloud_pak_deployer_secret" {
@@ -140,7 +134,7 @@ variable "cpd_entitlement_key" {
 }
 
 variable "cpd_version" {
-  description = "Cloud Pak for Data version to install.  Only version 5.x.x is supported"
+  description = "Cloud Pak for Data version to install.  Only version 5.x.x is supported, latest versions can be found [here](https://www.ibm.com/docs/en/cloud-paks/cp-data?topic=versions-cloud-pak-data)."
   type        = string
 
   validation {
diff --git a/version.tf b/version.tf
new file mode 100644
index 00000000..7ba5e655
--- /dev/null
+++ b/version.tf
@@ -0,0 +1,9 @@
+terraform {
+  required_version = ">= 1.9.0"
+  required_providers {
+    ibm = {
+      source  = "ibm-cloud/ibm"
+      version = ">=1.79.1"
+    }
+  }
+}