Repository: terraform-ibm-modules/terraform-ibm-kms-all-inclusive
Pull request #646: feat: update standard -> fully-configurable and create security-enforced
Author: login: MatthewLemmond
Labels: name: released
Created: 2025-04-04T23:19:32Z | Merged: 2025-04-25T14:37:37Z

### Description

* Move the previous `standard` solution to `fully-configurable` and update ibm_catalog.json
* Add `security-enforced` solution which calls `fully-configurable` and add flavor to ibm_catalog.json
* update tests to standardized naming and setup functions where applicable

SKIP UPGRADE TEST due to the new `existing_resource_group_name` variable replacing the previous `resource_group_name` variable the upgrade test fails, details of a manual upgrade test to be included

### Release required?
<!--- Identify the type of release. For information about the changes in a semantic versioning release, see [Release versioning](https://terraform-ibm-modules.github.io/documentation/#/versioning). --->

- [ ] No release
- [ ] Patch release (`x.x.X`)
- [ ] Minor release (`x.X.x`)
- [x] Major release (`X.x.x`)

##### Release notes content

<!--- If a release is required, replace this text with information that users need to know about the release. Write the release notes to help users understand the changes, and include information about how to update from the previous version.

Your notes help the merger write the commit message for the PR that is published in the release notes for the module. --->

* `standard` flavor has been replaced by the `fully-configurable` flavor
  * if the previous solution was managing its own resource group, the following moved block can be used to prevent its deletion/deletion of the resources in the resource group
  * NOTE: if this moved block is used, the resource group will no longer be deleted if `terraform destroy` is run on this configuration and it will need to be manually deleted
```
moved {
  from = module.resource_group.ibm_resource_group.resource_group[0]
  to   = module.resource_group.data.ibm_resource_group.existing_resource_group[0]
}
```
* added `security-enforced` flavor

### Run the pipeline

If the CI pipeline doesn't run when you create the PR, the PR requires a user with GitHub collaborators access to run the pipeline.

Run the CI pipeline when the PR is ready for review and you expect tests to pass. Add a comment to the PR with the following text:

```
/run pipeline
```

### Checklist for reviewers

- [ ] If relevant, a test for the change is included or updated with this PR.
- [ ] If relevant, documentation for the change is included or updated with this PR.

### For mergers

- Use a conventional commit message to set the release level. Follow the [guidelines](https://terraform-ibm-modules.github.io/documentation/#/merging.md).
- Include information that users need to know about the PR in the commit message. The commit message becomes part of the GitHub release notes.
- Use the **Squash and merge** option.



Diff:
diff --git a/.catalog-onboard-pipeline.yaml b/.catalog-onboard-pipeline.yaml
index b4bb9ecf..9069f598 100644
--- a/.catalog-onboard-pipeline.yaml
+++ b/.catalog-onboard-pipeline.yaml
@@ -6,7 +6,13 @@ offerings:
     catalog_id: 7df1e4ca-d54c-4fd0-82ce-3d13247308cd
     offering_id: 2cad4789-fa90-4886-9c9e-857081c273ee
     variations:
-      - name: standard
+      - name: fully-configurable
+        mark_ready: true
+        install_type: fullstack
+        scc:
+          instance_id: 1c7d5f78-9262-44c3-b779-b28fe4d88c37
+          region: us-south
+      - name: security-enforced
         mark_ready: true
         install_type: fullstack
         scc:
diff --git a/cra-config.yaml b/cra-config.yaml
index d3589fe9..4735ccdc 100644
--- a/cra-config.yaml
+++ b/cra-config.yaml
@@ -1,9 +1,13 @@
 # More info about this file at https://github.com/terraform-ibm-modules/common-pipeline-assets/blob/main/.github/workflows/terraform-test-pipeline.md#cra-config-yaml
 version: "v1"
 CRA_TARGETS:
-  - CRA_TARGET: "solutions/standard" # 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" # CRA Ignore file to use. If not provided, it checks the repo root directory for `cra-tf-validate-ignore-rules.json`
+    PROFILE_ID: "48279384-3d29-4089-8259-8ed354774b4a"         # SCC profile ID (currently set to 'CIS IBM Cloud Foundations Benchmark v1.1.0' '1.1.0' profile).
+    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_provider_visibility: "public"
+  - CRA_TARGET: "solutions/security-enforced" # 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" # CRA Ignore file to use. If not provided, it checks the repo root directory for `cra-tf-validate-ignore-rules.json`
     PROFILE_ID: "48279384-3d29-4089-8259-8ed354774b4a"         # SCC profile ID (currently set to 'CIS IBM Cloud Foundations Benchmark v1.1.0' '1.1.0' profile).
     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_resource_group_name: "test"
       TF_VAR_provider_visibility: "public"
diff --git a/ibm_catalog.json b/ibm_catalog.json
index 2c304b4b..87a6d706 100644
--- a/ibm_catalog.json
+++ b/ibm_catalog.json
@@ -23,7 +23,7 @@
       ],
       "short_description": "Creates and configures IBM Cloud Key Management resources",
       "long_description": "This architecture supports creating and configuring an IBM Key Protect instance, Key Rings, and Keys.",
-      "offering_docs_url": "https://github.com/terraform-ibm-modules/terraform-ibm-kms-all-inclusive/blob/main/solutions/standard/README.md",
+      "offering_docs_url": "https://github.com/terraform-ibm-modules/terraform-ibm-kms-all-inclusive/blob/main/solutions/fully-configurable/README.md",
       "offering_icon_url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-kms-all-inclusive/main/images/key_protect_icon.svg",
       "provider_name": "IBM",
       "features": [
@@ -39,10 +39,10 @@
       "support_details": "This product is in the community registry, as such support is handled through the originated repo. If you experience issues please open an issue in that repository [https://github.com/terraform-ibm-modules/terraform-ibm-kms-all-inclusive/issues](https://github.com/terraform-ibm-modules/terraform-ibm-kms-all-inclusive/issues). Please note this product is not supported via the IBM Cloud Support Center.",
       "flavors": [
         {
-          "label": "Standard",
-          "name": "standard",
+          "label": "Fully configurable",
+          "name": "fully-configurable",
           "install_type": "fullstack",
-          "working_directory": "solutions/standard",
+          "working_directory": "solutions/fully-configurable",
           "compliance": {
             "authority": "scc-v3",
             "profiles": [
@@ -66,12 +66,16 @@
               "key": "ibmcloud_api_key"
             },
             {
-              "key": "use_existing_resource_group",
-              "required": true
-            },
-            {
-              "key": "resource_group_name",
-              "required": true
+              "key": "existing_resource_group_name",
+              "required": true,
+              "custom_config": {
+                "type": "resource_group",
+                "grouping": "deployment",
+                "original_grouping": "deployment",
+                "config_constraints": {
+                  "identifier": "rg_name"
+                }
+              }
             },
             {
               "key": "region",
@@ -239,6 +243,187 @@
               }
             ]
           }
+        },
+        {
+          "label": "Security enforced",
+          "name": "security-enforced",
+          "install_type": "fullstack",
+          "working_directory": "solutions/security-enforced",
+          "compliance": {
+            "authority": "scc-v3",
+            "profiles": [
+              {
+                "profile_name": "CIS IBM Cloud Foundations Benchmark v1.1.0",
+                "profile_version": "1.1.0"
+              }
+            ]
+          },
+          "iam_permissions": [
+            {
+              "role_crns": [
+                "crn:v1:bluemix:public:iam::::serviceRole:Manager",
+                "crn:v1:bluemix:public:iam::::role:Editor"
+              ],
+              "service_name": "kms"
+            }
+          ],
+          "configuration": [
+            {
+              "key": "ibmcloud_api_key"
+            },
+            {
+              "key": "existing_resource_group_name",
+              "required": true,
+              "custom_config": {
+                "type": "resource_group",
+                "grouping": "deployment",
+                "original_grouping": "deployment",
+                "config_constraints": {
+                  "identifier": "rg_name"
+                }
+              }
+            },
+            {
+              "key": "region",
+              "required": true,
+              "default_value": "",
+              "options": [
+                {
+                  "displayname": "Dallas (us-south)",
+                  "value": "us-south"
+                },
+                {
+                  "displayname": "Frankfurt (eu-de)",
+                  "value": "eu-de"
+                },
+                {
+                  "displayname": "London (eu-gb)",
+                  "value": "eu-gb"
+                },
+                {
+                  "displayname": "Madrid (eu-es)",
+                  "value": "eu-es"
+                },
+                {
+                  "displayname": "Osaka (jp-osa)",
+                  "value": "jp-osa"
+                },
+                {
+                  "displayname": "Sao Paulo (br-sao)",
+                  "value": "br-sao"
+                },
+                {
+                  "displayname": "Sydney (au-syd)",
+                  "value": "au-syd"
+                },
+                {
+                  "displayname": "Tokyo (jp-tok)",
+                  "value": "jp-tok"
+                },
+                {
+                  "displayname": "Toronto (ca-tor)",
+                  "value": "ca-tor"
+                },
+                {
+                  "displayname": "Washington (us-east)",
+                  "value": "us-east"
+                }
+              ]
+            },
+            {
+              "key": "prefix",
+              "required": true,
+              "description": "The prefix to add to all resources that this solution creates. To not use any prefix value, you can enter the string `__NULL__`."
+            },
+            {
+              "key": "provider_visibility",
+              "options": [
+                {
+                  "displayname": "private",
+                  "value": "private"
+                },
+                {
+                  "displayname": "public",
+                  "value": "public"
+                },
+                {
+                  "displayname": "public-and-private",
+                  "value": "public-and-private"
+                }
+              ]
+            },
+            {
+              "key": "key_protect_instance_name"
+            },
+            {
+              "key": "key_protect_plan",
+              "options": [
+                {
+                  "displayname": "Tiered Pricing",
+                  "value": "tiered-pricing"
+                },
+                {
+                  "displayname": "Cross Region Resiliency",
+                  "value": "cross-region-resiliency"
+                }
+              ]
+            },
+            {
+              "key": "key_protect_resource_tags",
+              "custom_config": {
+                "grouping": "deployment",
+                "original_grouping": "deployment",
+                "config_constraints": {
+                  "type": "string"
+                }
+              }
+            },
+            {
+              "key": "key_protect_access_tags",
+              "custom_config": {
+                "grouping": "deployment",
+                "original_grouping": "deployment",
+                "config_constraints": {
+                  "type": "string"
+                }
+              }
+            },
+            {
+              "key": "rotation_interval_month"
+            },
+            {
+              "key": "existing_kms_instance_crn"
+            },
+            {
+              "key": "keys"
+            },
+            {
+              "key": "key_protect_instance_cbr_rules"
+            }
+          ],
+          "architecture": {
+            "descriptions": "This architecture supports creating and configuring an Key Protect instance.",
+            "features": [
+              {
+                "title": "Creates a Key Protect instance.",
+                "description": "Creates and configures a Key Protect instance."
+              },
+              {
+                "title": "Creates Key Rings and Keys",
+                "description": "Creates Key Rings and Keys in a KMS instance (Key Protect or HPCS)."
+              }
+            ],
+            "diagrams": [
+              {
+                "diagram": {
+                  "caption": "IBM Key Management Services",
+                  "url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-kms-all-inclusive/main/reference-architecture/key_protect.svg",
+                  "type": "image/svg+xml"
+                },
+                "description": "This architecture supports creating and configuring IBM Key Management resources"
+              }
+            ]
+          }
         }
       ]
     }
diff --git a/main.tf b/main.tf
index 50bf6ad1..0169a594 100644
--- a/main.tf
+++ b/main.tf
@@ -10,14 +10,8 @@ locals {
 
   # set key_protect_guid as either the ID of the passed in name of instance or the one created by this module
   kms_guid = var.create_key_protect_instance ? module.key_protect[0].key_protect_guid : local.existing_kms_instance_guid
-
   # set kms_account_id as either the ID of the passed in instance or the one created by this module
   kms_account_id = var.create_key_protect_instance ? module.key_protect[0].key_protect_account_id : local.existing_kms_account_id
-
-  # set key_protect_crn as either the crn of the passed in name of instance or the one created by this module
-  kms_crn = var.create_key_protect_instance ? module.key_protect[0].key_protect_crn : var.existing_kms_instance_crn
-  # tflint-ignore: terraform_unused_declarations
-  cbr_validation = length(regexall(".*hs-crypto.*", local.kms_crn)) > 0 && length(var.cbr_rules) > 0 ? tobool("When passing an HPCS instance as value for `existing_kms_instance_crn` you cannot provide `cbr_rules`. Context-based restrictions is not supported for HPCS instances. For more information, see https://cloud.ibm.com/docs/account?topic=account-context-restrictions-whatis#cbr-adopters for the services that supports CBR") : true
 }
 
 ##############################################################################
diff --git a/solutions/standard/DA-cbr_rules.md b/solutions/fully-configurable/DA-cbr_rules.md
similarity index 100%
rename from solutions/standard/DA-cbr_rules.md
rename to solutions/fully-configurable/DA-cbr_rules.md
diff --git a/solutions/standard/DA-keys.md b/solutions/fully-configurable/DA-keys.md
similarity index 100%
rename from solutions/standard/DA-keys.md
rename to solutions/fully-configurable/DA-keys.md
diff --git a/solutions/standard/README.md b/solutions/fully-configurable/README.md
similarity index 69%
rename from solutions/standard/README.md
rename to solutions/fully-configurable/README.md
index f78a098c..e8010a1a 100644
--- a/solutions/standard/README.md
+++ b/solutions/fully-configurable/README.md
@@ -1,9 +1,8 @@
-# KMS standard solution
+# KMS fully configurable solution
 
 This solution supports the following:
-- Creating a new resource group, or taking in an existing one.
-- Provisioning of a Key Protect instance (private and public-and-private), or taking in an existing KMS instance (Key Protect or Hyper Protect Crypto Services).
-- Creation of a KMS Key Rings and Keys.
+- Provisioning of a Key Protect instance (private and public-and-private) in an existing resource group, or taking in an existing KMS instance (Key Protect or Hyper Protect Crypto Services).
+- Creation of a KMS Key Rings and Keys inside the KMS instance.
 
 **NB:** This solution is not intended to be called by one or more other modules since it contains a provider configurations, meaning it 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/standard/catalogValidationValues.json.template b/solutions/fully-configurable/catalogValidationValues.json.template
similarity index 60%
rename from solutions/standard/catalogValidationValues.json.template
rename to solutions/fully-configurable/catalogValidationValues.json.template
index 62dfd281..1a1a2a29 100644
--- a/solutions/standard/catalogValidationValues.json.template
+++ b/solutions/fully-configurable/catalogValidationValues.json.template
@@ -1,5 +1,5 @@
 {
   "ibmcloud_api_key": $VALIDATION_APIKEY,
-  "resource_group_name": $PREFIX,
+  "existing_resource_group_name": "Default",
   "region": "us-south"
 }
diff --git a/solutions/standard/main.tf b/solutions/fully-configurable/main.tf
similarity index 90%
rename from solutions/standard/main.tf
rename to solutions/fully-configurable/main.tf
index 5acbf896..3726ac8f 100644
--- a/solutions/standard/main.tf
+++ b/solutions/fully-configurable/main.tf
@@ -5,8 +5,7 @@
 module "resource_group" {
   source                       = "terraform-ibm-modules/resource-group/ibm"
   version                      = "1.2.0"
-  resource_group_name          = var.use_existing_resource_group == false ? try("${local.prefix}-${var.resource_group_name}", var.resource_group_name) : null
-  existing_resource_group_name = var.use_existing_resource_group == true ? var.resource_group_name : null
+  existing_resource_group_name = var.existing_resource_group_name
 }
 
 ########################################################################################################################
diff --git a/solutions/standard/outputs.tf b/solutions/fully-configurable/outputs.tf
similarity index 100%
rename from solutions/standard/outputs.tf
rename to solutions/fully-configurable/outputs.tf
diff --git a/solutions/standard/provider.tf b/solutions/fully-configurable/provider.tf
similarity index 100%
rename from solutions/standard/provider.tf
rename to solutions/fully-configurable/provider.tf
diff --git a/solutions/standard/variables.tf b/solutions/fully-configurable/variables.tf
similarity index 95%
rename from solutions/standard/variables.tf
rename to solutions/fully-configurable/variables.tf
index fbbaa8ff..68e39d15 100644
--- a/solutions/standard/variables.tf
+++ b/solutions/fully-configurable/variables.tf
@@ -18,15 +18,12 @@ variable "provider_visibility" {
     error_message = "Invalid visibility option. Allowed values are 'public', 'private', or 'public-and-private'."
   }
 }
-variable "use_existing_resource_group" {
-  type        = bool
-  description = "Whether to use an existing resource group."
-  default     = false
-}
 
-variable "resource_group_name" {
+variable "existing_resource_group_name" {
   type        = string
   description = "The name of a new or an existing resource group in which to provision key management resources to. If a prefix input variable is specified, it's added to the value in the `<prefix>-value` format."
+  default     = "Default"
+  nullable    = false
 }
 
 variable "region" {
@@ -38,7 +35,7 @@ variable "region" {
 variable "prefix" {
   type        = string
   description = "The prefix to add to all resources that this solution creates. To not use any prefix value, you can set this value to `null` or an empty string."
-  default     = "dev"
+  default     = null
 }
 
 ########################################################################################################################
@@ -102,7 +99,7 @@ variable "rotation_interval_month" {
 variable "existing_kms_instance_crn" {
   type        = string
   default     = null
-  description = "The CRN of the existed Hyper Protect Crypto Services or Key Protect instance. If not supplied, a new instance will be created."
+  description = "The CRN of the existing Hyper Protect Crypto Services or Key Protect instance. If not supplied, a new instance will be created."
 }
 
 variable "kms_endpoint_type" {
diff --git a/solutions/standard/version.tf b/solutions/fully-configurable/version.tf
similarity index 100%
rename from solutions/standard/version.tf
rename to solutions/fully-configurable/version.tf
diff --git a/solutions/security-enforced/README.md b/solutions/security-enforced/README.md
new file mode 100644
index 00000000..54089c5c
--- /dev/null
+++ b/solutions/security-enforced/README.md
@@ -0,0 +1,9 @@
+# KMS security enforced solution
+
+This solution supports the following:
+- Provisioning of a Key Protect instance (private only) in an existing resource group, or taking in an existing KMS instance (Key Protect or Hyper Protect Crypto Services).
+- Creation of a KMS Key Rings and Keys inside the KMS instance.
+
+**NB:** This solution is not intended to be called by one or more other modules since it contains a provider configurations, meaning it 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)
+
+![key-protect](../../reference-architecture/key_protect.svg)
diff --git a/solutions/security-enforced/catalogValidationValues.json.template b/solutions/security-enforced/catalogValidationValues.json.template
new file mode 100644
index 00000000..1a1a2a29
--- /dev/null
+++ b/solutions/security-enforced/catalogValidationValues.json.template
@@ -0,0 +1,5 @@
+{
+  "ibmcloud_api_key": $VALIDATION_APIKEY,
+  "existing_resource_group_name": "Default",
+  "region": "us-south"
+}
diff --git a/solutions/security-enforced/main.tf b/solutions/security-enforced/main.tf
new file mode 100644
index 00000000..caacf8bb
--- /dev/null
+++ b/solutions/security-enforced/main.tf
@@ -0,0 +1,22 @@
+########################################################################################################################
+# KMS
+########################################################################################################################
+
+module "security_enforced" {
+  source                         = "../fully-configurable"
+  ibmcloud_api_key               = var.ibmcloud_api_key
+  provider_visibility            = var.provider_visibility
+  existing_resource_group_name   = var.existing_resource_group_name
+  region                         = var.region
+  prefix                         = var.prefix
+  key_protect_instance_name      = var.key_protect_instance_name
+  key_protect_plan               = var.key_protect_plan
+  key_protect_allowed_network    = "private-only"
+  key_protect_resource_tags      = var.key_protect_resource_tags
+  key_protect_access_tags        = var.key_protect_access_tags
+  rotation_interval_month        = var.rotation_interval_month
+  existing_kms_instance_crn      = var.existing_kms_instance_crn
+  kms_endpoint_type              = "private"
+  keys                           = var.keys
+  key_protect_instance_cbr_rules = var.key_protect_instance_cbr_rules
+}
diff --git a/solutions/security-enforced/outputs.tf b/solutions/security-enforced/outputs.tf
new file mode 100644
index 00000000..ac90650a
--- /dev/null
+++ b/solutions/security-enforced/outputs.tf
@@ -0,0 +1,63 @@
+########################################################################################################################
+# Outputs
+########################################################################################################################
+
+output "resource_group_name" {
+  description = "Resource group name"
+  value       = module.security_enforced.resource_group_name
+}
+
+output "resource_group_id" {
+  description = "Resource group ID"
+  value       = module.security_enforced.resource_group_id
+}
+
+output "kms_guid" {
+  description = "Key Protect instance GUID or the KMS instance GUID if an existing KMS GUID was set"
+  value       = module.security_enforced.kms_guid
+}
+
+output "kms_account_id" {
+  description = "The account ID of the KMS instance."
+  value       = module.security_enforced.kms_account_id
+}
+
+output "key_protect_id" {
+  description = "Key Protect instance ID when an instance is created, otherwise null"
+  value       = module.security_enforced.key_protect_id
+}
+
+output "kms_instance_crn" {
+  value       = module.security_enforced.kms_instance_crn
+  description = "The CRN of the Hyper Protect Crypto Service instance or Key Protect instance"
+}
+
+output "key_protect_name" {
+  description = "Key Protect instance name when an instance is created, otherwise null"
+  value       = module.security_enforced.key_protect_name
+}
+
+output "key_protect_instance_policies" {
+  description = "Key Protect instance Polices of the Key Protect instance when an instance is created, otherwise null"
+  value       = module.security_enforced.key_protect_instance_policies
+}
+
+output "key_rings" {
+  description = "IDs of Key Rings created by the solution"
+  value       = module.security_enforced.key_rings
+}
+
+output "keys" {
+  description = "Keys created by the solution"
+  value       = module.security_enforced.keys
+}
+
+output "kms_private_endpoint" {
+  description = "Key Management Service instance private endpoint URL."
+  value       = module.security_enforced.kms_private_endpoint
+}
+
+output "kms_public_endpoint" {
+  description = "Key Management Service instance public endpoint URL."
+  value       = module.security_enforced.kms_public_endpoint
+}
diff --git a/solutions/security-enforced/provider.tf b/solutions/security-enforced/provider.tf
new file mode 100644
index 00000000..f69fb6da
--- /dev/null
+++ b/solutions/security-enforced/provider.tf
@@ -0,0 +1,5 @@
+provider "ibm" {
+  ibmcloud_api_key = var.ibmcloud_api_key
+  region           = var.region
+  visibility       = var.provider_visibility
+}
diff --git a/solutions/security-enforced/variables.tf b/solutions/security-enforced/variables.tf
new file mode 100644
index 00000000..568e2a5f
--- /dev/null
+++ b/solutions/security-enforced/variables.tf
@@ -0,0 +1,147 @@
+########################################################################################################################
+# Common variables
+########################################################################################################################
+
+variable "ibmcloud_api_key" {
+  type        = string
+  description = "The API Key to use for IBM Cloud."
+  sensitive   = true
+}
+
+variable "provider_visibility" {
+  description = "Set the visibility value for the IBM terraform provider. Supported values are `public`, `private`, `public-and-private`. [Learn more](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/guides/custom-service-endpoints)."
+  type        = string
+  default     = "private"
+
+  validation {
+    condition     = contains(["public", "private", "public-and-private"], var.provider_visibility)
+    error_message = "Invalid visibility option. Allowed values are 'public', 'private', or 'public-and-private'."
+  }
+}
+
+variable "existing_resource_group_name" {
+  type        = string
+  description = "The name of a new or an existing resource group in which to provision key management resources to. If a prefix input variable is specified, it's added to the value in the `<prefix>-value` format."
+  default     = "Default"
+  nullable    = false
+}
+
+variable "region" {
+  type        = string
+  default     = "us-south"
+  description = "The region in which to provision key management resources. If using an existing Key Protect or Hyper Protect Crypto Services instance, set this to the region where it was provisioned."
+}
+
+variable "prefix" {
+  type        = string
+  description = "The prefix to add to all resources that this solution creates. To not use any prefix value, you can set this value to `null` or an empty string."
+  default     = null
+}
+
+########################################################################################################################
+# Key Protect instance variables
+########################################################################################################################
+
+variable "key_protect_instance_name" {
+  type        = string
+  default     = "key-protect"
+  description = "The name to give the Key Protect instance that will be provisioned by this solution. Only used if not supplying an existing Key Protect or Hyper Protect Crypto Services instance. If a prefix input variable is specified, it's added to the value in the `<prefix>-value` format."
+}
+
+variable "key_protect_plan" {
+  type        = string
+  default     = "tiered-pricing"
+  description = "The service plan of the Key Protect instance that will be provisioned by this solution. Only used if not supplying an existing Key Protect or Hyper Protect Crypto Services instance."
+
+  validation {
+    condition     = contains(["tiered-pricing", "cross-region-resiliency"], var.key_protect_plan)
+    error_message = "`key_protect_plan` must be one of: 'tiered-pricing', 'cross-region-resiliency'."
+  }
+
+  validation {
+    condition     = var.key_protect_plan == "tiered-pricing" ? true : (var.key_protect_plan == "cross-region-resiliency" && contains(["us-south", "eu-de", "jp-tok"], var.region))
+    error_message = "'cross-region-resiliency' is only available for the following regions: 'us-south', 'eu-de', 'jp-tok'."
+  }
+}
+
+variable "key_protect_resource_tags" {
+  type        = list(string)
+  description = "Optional list of tags to be added to the Key Protect instance. Only used if not supplying an existing Key Protect or Hyper Protect Crypto Services instance."
+  default     = []
+}
+
+variable "key_protect_access_tags" {
+  type        = list(string)
+  description = "Optional list of access tags to apply to the Key Protect instance. Only used if not supplying an existing Key Protect or Hyper Protect Crypto Services instance."
+  default     = []
+}
+
+variable "rotation_interval_month" {
+  type        = number
+  description = "Specifies the key rotation time interval in months. Possible values: `1` through `12`. Applies only if an existing Key Protect or Hyper Protect Crypto Services instance is not specified."
+  default     = 12
+}
+
+########################################################################################################################
+# Existing KMS variables
+########################################################################################################################
+
+variable "existing_kms_instance_crn" {
+  type        = string
+  default     = null
+  description = "The CRN of the existing Hyper Protect Crypto Services or Key Protect instance. If not supplied, a new instance will be created."
+}
+
+########################################################################################################################
+# Key Ring / Key variables
+########################################################################################################################
+
+variable "keys" {
+  type = list(object({
+    key_ring_name     = string
+    existing_key_ring = optional(bool, false)
+    keys = list(object({
+      key_name                 = string
+      standard_key             = optional(bool, false)
+      rotation_interval_month  = optional(number, 1)
+      dual_auth_delete_enabled = optional(bool, false)
+      force_delete             = optional(bool, true)
+      kmip = optional(list(object({
+        name        = string
+        description = optional(string)
+        certificates = optional(list(object({
+          name        = optional(string)
+          certificate = string
+        })), [])
+      })), [])
+    }))
+  }))
+  description = "A list of key ring objects each containing one or more key objects. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-kms-all-inclusive/tree/main/solutions/standard/DA-keys.md)."
+  sensitive   = true
+  default     = []
+}
+
+##############################################################
+# Context-based restriction (CBR)
+##############################################################
+
+variable "key_protect_instance_cbr_rules" {
+  type = list(object({
+    description = string
+    account_id  = string
+    rule_contexts = list(object({
+      attributes = optional(list(object({
+        name  = string
+        value = string
+    }))) }))
+    enforcement_mode = string
+    operations = optional(list(object({
+      api_types = list(object({
+        api_type_id = string
+      }))
+    })))
+  }))
+  description = "(Optional, list) List of context-based restrictions rules to create. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-kms-all-inclusive/tree/main/solutions/standard/DA-cbr_rules.md)"
+  default     = []
+  # NOTE: Context-based restrictions rule applies to Key Protect instances and is not supported for HPCS instances
+}
diff --git a/solutions/security-enforced/version.tf b/solutions/security-enforced/version.tf
new file mode 100644
index 00000000..df4fb624
--- /dev/null
+++ b/solutions/security-enforced/version.tf
@@ -0,0 +1,10 @@
+terraform {
+  required_version = ">= 1.9.0"
+  # Lock DA into an exact provider version - renovate automation will keep it updated
+  required_providers {
+    ibm = {
+      source  = "IBM-Cloud/ibm"
+      version = "1.77.1"
+    }
+  }
+}
diff --git a/tests/pr_test.go b/tests/pr_test.go
index 00ebc678..e9c924b2 100644
--- a/tests/pr_test.go
+++ b/tests/pr_test.go
@@ -2,19 +2,20 @@
 package test
 
 import (
+	"fmt"
 	"log"
 	"os"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/common"
-	"github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper"
 	"github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testschematic"
 )
 
 // Use existing resource group for tests
 const resourceGroup = "geretain-test-key-protect-all-inclusive"
-const solutionDADir = "solutions/standard"
+const fullyConfigurableDADir = "solutions/fully-configurable"
+const securityEnforcedDADir = "solutions/security-enforced"
 const advancedExampleTerraformDir = "examples/advanced"
 
 // Define a struct with fields that match the structure of the YAML data
@@ -33,88 +34,72 @@ func TestMain(m *testing.M) {
 	os.Exit(m.Run())
 }
 
-func TestDASolutionInSchematics(t *testing.T) {
-	t.Parallel()
-
-	const region = "us-south"
-
+func setupSchematicOptions(t *testing.T, prefix string, dir string) *testschematic.TestSchematicOptions {
 	options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{
-		Testing: t,
-		Prefix:  "kp-solution",
-		TarIncludePatterns: []string{
-			"*.tf",
-			solutionDADir + "/*.tf",
-		},
-		ResourceGroup:          resourceGroup,
-		TemplateFolder:         solutionDADir,
+		Testing:                t,
+		TarIncludePatterns:     []string{"*.tf", fmt.Sprintf("%s/*.tf", dir)},
+		TemplateFolder:         dir,
+		Prefix:                 prefix,
 		Tags:                   []string{"test-schematic"},
 		DeleteWorkspaceOnFail:  false,
 		WaitJobCompleteMinutes: 60,
-		Region:                 region,
 	})
 
 	options.TerraformVars = []testschematic.TestSchematicTerraformVar{
 		{Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true},
-		{Name: "resource_group_name", Value: options.Prefix, DataType: "string"},
+		{Name: "existing_resource_group_name", Value: resourceGroup, DataType: "string"},
 		{Name: "key_protect_resource_tags", Value: options.Tags, DataType: "list(string)"},
 		{Name: "key_protect_access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"},
 		{Name: "keys", Value: []map[string]interface{}{{"key_ring_name": "my-key-ring", "keys": []map[string]interface{}{{"key_name": "some-key-name-1"}, {"key_name": "some-key-name-2"}}}}, DataType: "list(object)"},
 		{Name: "prefix", Value: options.Prefix, DataType: "string"},
+		{Name: "region", Value: options.Region, DataType: "string"},
 	}
 
-	err := options.RunSchematicTest()
-	assert.Nil(t, err, "This should not have errored")
+	return options
 }
 
-func TestRunUpgradeDASolution(t *testing.T) {
+func TestRunFullyConfigurableDA(t *testing.T) {
 	t.Parallel()
 
-	options := testhelper.TestOptionsDefault(&testhelper.TestOptions{
-		Testing:      t,
-		TerraformDir: solutionDADir,
-		Prefix:       "kms-da-upg",
-	})
+	options := setupSchematicOptions(t, "kms-fc-da", fullyConfigurableDADir)
 
-	terraformVars := map[string]interface{}{
-		"resource_group_name":       options.Prefix,
-		"kms_endpoint_type":         "public",
-		"provider_visibility":       "public",
-		"existing_kms_instance_crn": permanentResources["hpcs_south_crn"],
-		"keys":                      []map[string]interface{}{{"key_ring_name": "my-key-ring", "keys": []map[string]interface{}{{"key_name": "some-key-name-1"}, {"key_name": "some-key-name-2"}}}},
-		"key_protect_resource_tags": []string{"kms-da-upg"},
-	}
+	err := options.RunSchematicTest()
+	assert.NoError(t, err, "Schematic Test had an unexpected error")
+}
+
+func TestRunUpgradeFullyConfigurableDA(t *testing.T) {
+	t.Parallel()
 
-	options.TerraformVars = terraformVars
-	output, err := options.RunTestUpgrade()
+	options := setupSchematicOptions(t, "kms-fc-da-upg", fullyConfigurableDADir)
+
+	err := options.RunSchematicUpgradeTest()
 	if !options.UpgradeTestSkipped {
-		assert.Nil(t, err, "This should not have errored")
-		assert.NotNil(t, output, "Expected some output")
+		assert.NoError(t, err, "Schematic Test had an unexpected error")
 	}
 }
 
-func TestRunAdvanceExample(t *testing.T) {
+func TestRunSecurityEnforcedDA(t *testing.T) {
 	t.Parallel()
 
-	options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{
-		Testing: t,
-		Prefix:  "kms-all-inc-adv",
-		TarIncludePatterns: []string{
-			"*.tf",
-			advancedExampleTerraformDir + "/*.tf",
-		},
-
-		ResourceGroup:          resourceGroup,
-		TemplateFolder:         advancedExampleTerraformDir,
-		Tags:                   []string{"test-schematic"},
-		DeleteWorkspaceOnFail:  false,
-		WaitJobCompleteMinutes: 60,
-	})
+	options := setupSchematicOptions(t, "kms-se-da", securityEnforcedDADir)
+	options.TarIncludePatterns = append(options.TarIncludePatterns, fmt.Sprintf("%s/*.tf", fullyConfigurableDADir))
+	additionalOptions := []testschematic.TestSchematicTerraformVar{
+		{Name: "existing_kms_instance_crn", Value: permanentResources["hpcs_south_crn"], DataType: "string"},
+	}
+	options.TerraformVars = append(options.TerraformVars, additionalOptions...)
+
+	err := options.RunSchematicTest()
+	assert.NoError(t, err, "Schematic Test had an unexpected error")
+}
+
+func TestRunAdvancedExample(t *testing.T) {
+	t.Parallel()
 
+	options := setupSchematicOptions(t, "kms-adv", advancedExampleTerraformDir)
 	options.TerraformVars = []testschematic.TestSchematicTerraformVar{
 		{Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true},
 		{Name: "region", Value: options.Region, DataType: "string"},
 		{Name: "prefix", Value: options.Prefix, DataType: "string"},
-		{Name: "resource_group", Value: options.ResourceGroup, DataType: "string"},
 		{Name: "existing_secrets_manager_crn", Value: permanentResources["secretsManagerCRN"], DataType: "string"},
 		{Name: "existing_cert_template_name", Value: permanentResources["privateCertTemplateName"], DataType: "string"},
 	}
diff --git a/variables.tf b/variables.tf
index b1ebe57c..2b451ae7 100644
--- a/variables.tf
+++ b/variables.tf
@@ -186,4 +186,10 @@ variable "cbr_rules" {
   default     = []
   # Validation happens in the rule module
   # NOTE: Context-based restrictions rule applies to Key Protect instances and is not supported for HPCS instances
+
+  validation {
+    # condition     = (length(regexall(".*hs-crypto.*", var.existing_kms_instance_crn)) > 0 && length(var.cbr_rules) == 0) || (length(regexall(".*kms.*", var.existing_kms_instance_crn)) > 0) || (var.existing_kms_instance_crn == null)
+    condition     = var.existing_kms_instance_crn == null ? true : length(regexall(".*hscrypto.*", var.existing_kms_instance_crn)) > 0 ? length(var.cbr_rules) == 0 : true
+    error_message = "When passing an HPCS instance as value for `existing_kms_instance_crn` you cannot provide `cbr_rules`. Context-based restrictions is not supported for HPCS instances. For more information, see https://cloud.ibm.com/docs/account?topic=account-context-restrictions-whatis#cbr-adopters for the services that supports CBR."
+  }
 }


Reviews:
user: login: shemau
body: There are some inconsistencies and possibly some catalog pipeline validation issues.

There is alot of change I will take another pass.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T17:35:10Z
user: login: MatthewLemmond
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T21:56:57Z
user: login: shemau
body: LGTM

One small spelling in a validation
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T15:54:19Z
user: login: ocofaigh
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T10:17:14Z
user: login: ocofaigh
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T10:18:29Z
user: login: ocofaigh
body: I left a few comments - can you also address the conflicts please
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T10:20:23Z
user: login: MatthewLemmond
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-25T13:31:44Z
user: login: MatthewLemmond
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-25T13:31:56Z
user: login: ocofaigh
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T13:43:14Z
user: login: shemau
body: LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-25T14:32:48Z

Review comments:
diff_hunk: @@ -1,5 +1,5 @@
 {
   "ibmcloud_api_key": $VALIDATION_APIKEY,
-  "resource_group_name": $PREFIX,
+  "existing_resource_group_name": $PREFIX,
path: solutions/fully-configurable/catalogValidationValues.json.template
user: login: shemau
body: This might need to be something that actually exists.
created_at: 2025-04-10T17:17:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -38,7 +35,7 @@ variable "region" {
 variable "prefix" {
   type        = string
   description = "The prefix to add to all resources that this solution creates. To not use any prefix value, you can set this value to `null` or an empty string."
-  default     = "dev"
+  default     = null
path: solutions/fully-configurable/variables.tf
user: login: shemau
body: This seems to be inconsistent between fully-configurable and security-enforced
created_at: 2025-04-10T17:21:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,5 @@
+{
+  "ibmcloud_api_key": $VALIDATION_APIKEY,
+  "existing_resource_group_name": $PREFIX,
path: solutions/security-enforced/catalogValidationValues.json.template
user: login: shemau
body: This might need to actually exist.
created_at: 2025-04-10T17:21:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,147 @@
+########################################################################################################################
+# Common variables
+########################################################################################################################
+
+variable "ibmcloud_api_key" {
+  type        = string
+  description = "The API Key to use for IBM Cloud."
+  sensitive   = true
+}
+
+variable "provider_visibility" {
+  description = "Set the visibility value for the IBM terraform provider. Supported values are `public`, `private`, `public-and-private`. [Learn more](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/guides/custom-service-endpoints)."
+  type        = string
+  default     = "private"
+
+  validation {
+    condition     = contains(["public", "private", "public-and-private"], var.provider_visibility)
+    error_message = "Invalid visibility option. Allowed values are 'public', 'private', or 'public-and-private'."
+  }
+}
+
+variable "existing_resource_group_name" {
+  type        = string
+  description = "The name of a new or an existing resource group in which to provision key management resources to. If a prefix input variable is specified, it's added to the value in the `<prefix>-value` format."
+  default     = "Default"
+  nullable    = false
+}
+
+variable "region" {
+  type        = string
+  default     = "us-south"
+  description = "The region in which to provision key management resources. If using an existing Key Protect or Hyper Protect Crypto Services instance, set this to the region where it was provisioned."
+}
+
+variable "prefix" {
+  type        = string
+  description = "The prefix to add to all resources that this solution creates. To not use any prefix value, you can set this value to `null` or an empty string."
+  default     = "dev"
path: solutions/security-enforced/variables.tf
user: login: shemau
body: This is the second half of the inconsistent comment. The default is different between the two DAs.
created_at: 2025-04-10T17:22:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,147 @@
+########################################################################################################################
+# Common variables
+########################################################################################################################
+
+variable "ibmcloud_api_key" {
+  type        = string
+  description = "The API Key to use for IBM Cloud."
+  sensitive   = true
+}
+
+variable "provider_visibility" {
+  description = "Set the visibility value for the IBM terraform provider. Supported values are `public`, `private`, `public-and-private`. [Learn more](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/guides/custom-service-endpoints)."
+  type        = string
+  default     = "private"
+
+  validation {
+    condition     = contains(["public", "private", "public-and-private"], var.provider_visibility)
+    error_message = "Invalid visibility option. Allowed values are 'public', 'private', or 'public-and-private'."
+  }
+}
+
+variable "existing_resource_group_name" {
+  type        = string
+  description = "The name of a new or an existing resource group in which to provision key management resources to. If a prefix input variable is specified, it's added to the value in the `<prefix>-value` format."
+  default     = "Default"
+  nullable    = false
+}
+
+variable "region" {
+  type        = string
+  default     = "us-south"
+  description = "The region in which to provision key management resources. If using an existing Key Protect or Hyper Protect Crypto Services instance, set this to the region where it was provisioned."
+}
+
+variable "prefix" {
+  type        = string
+  description = "The prefix to add to all resources that this solution creates. To not use any prefix value, you can set this value to `null` or an empty string."
+  default     = "dev"
path: solutions/security-enforced/variables.tf
user: login: MatthewLemmond
body: changed to null to keep consistency with other solutions
created_at: 2025-04-11T21:56:56Z
author_association: MEMBER
diff_hunk: @@ -171,4 +182,10 @@ variable "cbr_rules" {
   default     = []
   # Validation happens in the rule module
   # NOTE: Context-based restrictions rule applies to Key Protect instances and is not supported for HPCS instances
+
+  validation {
+    # condition     = (length(regexall(".*hs-crypto.*", var.existing_kms_instance_crn)) > 0 && length(var.cbr_rules) == 0) || (length(regexall(".*kms.*", var.existing_kms_instance_crn)) > 0) || (var.existing_kms_instance_crn == null)
+    condition     = var.existing_kms_instance_crn == null ? true : length(regexall(".*hscrypto.*", var.existing_kms_instance_crn)) > 0 ? length(var.cbr_rules) == 0 : true
+    error_message = "When passing an HPCS instance as value for `existing_kms_instance_crn` you cannot provid `cbr_rules`. Context-based restrictions is not supported for HPCS instances. For more information, see https://cloud.ibm.com/docs/account?topic=account-context-restrictions-whatis#cbr-adopters for the services that supports CBR."
path: variables.tf
user: login: shemau
body: Spelling on provid(e)
created_at: 2025-04-17T15:48:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -43,3 +42,8 @@ module "kms" {
   keys                              = var.keys
   cbr_rules                         = var.key_protect_instance_cbr_rules
 }
+
+moved {
path: solutions/fully-configurable/main.tf
user: login: ocofaigh
body: This moved block is pointless - there is not upgrade path since we are changing the variation name and bumping the major version
created_at: 2025-04-25T10:17:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -21,7 +20,7 @@ locals {
   prefix                           = var.prefix != null ? (var.prefix != "" ? var.prefix : null) : null
 }
 
-module "kms" {
+module "fully_configurable" {
path: solutions/fully-configurable/main.tf
user: login: ocofaigh
body: why would you name the module `fully_configurable`? This is the name of the DA variation. I would of thought `kms` is an appropriate name for the module as thats the module you are calling
created_at: 2025-04-25T10:18:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -21,7 +20,7 @@ locals {
   prefix                           = var.prefix != null ? (var.prefix != "" ? var.prefix : null) : null
 }
 
-module "kms" {
+module "fully_configurable" {
path: solutions/fully-configurable/main.tf
user: login: MatthewLemmond
body: switched back to kms
created_at: 2025-04-25T13:31:44Z
author_association: MEMBER
diff_hunk: @@ -43,3 +42,8 @@ module "kms" {
   keys                              = var.keys
   cbr_rules                         = var.key_protect_instance_cbr_rules
 }
+
+moved {
path: solutions/fully-configurable/main.tf
user: login: MatthewLemmond
body: removed since we switched back
created_at: 2025-04-25T13:31:55Z
author_association: MEMBER

PR comments:
user: login: MatthewLemmond
created_at: 2025-04-04T23:19:40Z
body: /run pipeline
author_association: MEMBER
user: login: MatthewLemmond
created_at: 2025-04-04T23:20:18Z
body: /run pipeline
author_association: MEMBER
user: login: MatthewLemmond
created_at: 2025-04-05T00:57:09Z
body: /run pipeline
author_association: MEMBER
user: login: MatthewLemmond
created_at: 2025-04-07T13:47:45Z
body: /run pipeline
author_association: MEMBER
user: login: MatthewLemmond
created_at: 2025-04-07T15:05:23Z
body: /run pipeline
author_association: MEMBER
user: login: MatthewLemmond
created_at: 2025-04-11T21:59:47Z
body: /run pipeline
author_association: MEMBER
user: login: MatthewLemmond
created_at: 2025-04-15T21:33:56Z
body: /run pipeline
author_association: MEMBER
user: login: MatthewLemmond
created_at: 2025-04-16T15:33:26Z
body: /run pipeline
author_association: MEMBER
user: login: MatthewLemmond
created_at: 2025-04-21T21:01:54Z
body: /run pipeline
author_association: MEMBER
user: login: MatthewLemmond
created_at: 2025-04-24T20:55:34Z
body: /run pipeline
author_association: MEMBER
user: login: MatthewLemmond
created_at: 2025-04-25T13:36:19Z
body: /run pipeline
author_association: MEMBER
user: login: terraform-ibm-modules-ops
created_at: 2025-04-25T14:38:27Z
body: :tada: This PR is included in version 5.0.0 :tada:

The release is available on:
- [GitHub release](https://github.com/terraform-ibm-modules/terraform-ibm-kms-all-inclusive/releases/tag/v5.0.0)
- `v5.0.0`

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package::rocket:
author_association: CONTRIBUTOR