diff --git a/README.md b/README.md index ed93bef..5a5b4df 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,16 @@ unless real values don't help users know what to change. --> ```hcl +data "ibm_iam_auth_token" "auth_token" {} + +provider "restapi" { + uri = "https://resource-controller.cloud.ibm.com" # https://private.resource-controller.cloud.ibm.com for private + headers = { + Authorization = data.ibm_iam_auth_token.auth_token.iam_access_token + } + write_returns_object = true +} + module "scc_wp" { source = "terraform-ibm-modules/scc-workload-protection/ibm" version = "X.X.X" # Replace "X.X.X" with a release version to lock into a specific release @@ -52,9 +62,14 @@ module "scc_wp" { resource_group_id = "65xxxxxxxxxxxxxxxa3fd" resource_key_tags = ["scc-wp-tag"] cloud_monitoring_instance_crn = "crn:v1:bluemix:public:sysdig-monitor:us-south:a/xxXXxxXXxXxXXXXxxXxxxXXXXxXXXXX:xxXXxxXXxXxXXXXxxXxxxXXXXxXXXXX::" + app_config_crn = "crn:v1:bluemix:public:apprap:us-south:a/xxXXxxXXxXxXXXXxxXxxxXXXXxXXXXX:xxXXxxXXxXxXXXXxxXxxxXXXXxXXXXX::" } ``` +### Known issues +#### restapi_object.enable_cspm resource always identified for creation +There is currently a [known issue](https://github.com/terraform-ibm-modules/terraform-ibm-scc-workload-protection/issues/243) where you will always see the `restapi_object.enable_cspm` resource included in the terraform plan for creation, even after it has already been applied. It is safe to proceed with this apply and will be a no-op if the resource has already been applied. + ### Required IAM access policies ### Requirements @@ -16,6 +20,7 @@ This solution supports provisioning and configuring the following infrastructure |------|---------| | [terraform](#requirement\_terraform) | >= 1.4.0 | | [ibm](#requirement\_ibm) | 1.78.2 | +| [restapi](#requirement\_restapi) | 1.20.0 | ### Modules @@ -26,16 +31,21 @@ This solution supports provisioning and configuring the following infrastructure ### Resources -No resources. +| Name | Type | +|------|------| +| [ibm_iam_auth_token.auth_token](https://registry.terraform.io/providers/IBM-Cloud/ibm/1.78.2/docs/data-sources/iam_auth_token) | data source | ### Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| [app\_config\_crn](#input\_app\_config\_crn) | The CRN of an existing App Config instance to use with the SCC Workload Protection instance. Required if `cspm_enabled` is true. NOTE: Ensure the App Config instance has configuration aggregator enabled. | `string` | `null` | no | | [cbr\_rules](#input\_cbr\_rules) | The list of context-based restriction rules to create for the instance.[Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-scc-workload-protection/blob/main/solutions/fully-configurable/cbr-rules.md) |
list(object({
description = string
account_id = string
rule_contexts = list(object({
attributes = optional(list(object({
name = string
value = string
}))) }))
enforcement_mode = string
tags = optional(list(object({
name = string
value = string
})), [])
operations = optional(list(object({
api_types = list(object({
api_type_id = string
}))
})))
}))
| `[]` | no | +| [cspm\_enabled](#input\_cspm\_enabled) | Enable Cloud Security Posture Management (CSPM) for the Workload Protection instance. This will create a trusted profile associated with the SCC Workload Protection instance that has viewer / reader access to the App Config service and viewer access to the Enterprise service. [Learn more](https://cloud.ibm.com/docs/workload-protection?topic=workload-protection-about). | `bool` | `true` | no | | [existing\_monitoring\_crn](#input\_existing\_monitoring\_crn) | The CRN of an IBM Cloud Monitoring instance to to send Workload Protection data. If no value passed, metrics are sent to the instance associated to the container's location unless otherwise specified in the Metrics Router service configuration. | `string` | `null` | no | | [existing\_resource\_group\_name](#input\_existing\_resource\_group\_name) | The name of a an existing resource group in which to provision resources to. | `string` | `"Default"` | no | | [ibmcloud\_api\_key](#input\_ibmcloud\_api\_key) | The IBM Cloud API key to deploy resources. | `string` | n/a | yes | +| [ibmcloud\_resource\_controller\_api\_endpoint](#input\_ibmcloud\_resource\_controller\_api\_endpoint) | The URI of the Resource Controller service. This is used to update the Workload Protection instance to enable CSPM once the trusted profiles have been created. | `string` | `"https://resource-controller.cloud.ibm.com"` | no | | [prefix](#input\_prefix) | The prefix to add to all resources that this solution creates (e.g `prod`, `test`, `dev`). To not use any prefix value, you can set this value to `null` or an empty string. | `string` | n/a | yes | | [provider\_visibility](#input\_provider\_visibility) | 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). | `string` | `"private"` | no | | [region](#input\_region) | The region to provision Security and Compliance Center Workload Protection resources in. | `string` | `"us-south"` | no | @@ -44,6 +54,7 @@ No resources. | [scc\_workload\_protection\_instance\_tags](#input\_scc\_workload\_protection\_instance\_tags) | The list of tags to add to the Workload Protection instance. | `list(string)` | `[]` | no | | [scc\_workload\_protection\_resource\_key\_tags](#input\_scc\_workload\_protection\_resource\_key\_tags) | The tags associated with the Workload Protection resource key. | `list(string)` | `[]` | no | | [scc\_workload\_protection\_service\_plan](#input\_scc\_workload\_protection\_service\_plan) | The pricing plan for the Workload Protection instance service. Possible values: `free-trial`, `graduated-tier`. | `string` | `"graduated-tier"` | no | +| [scc\_workload\_protection\_trusted\_profile\_name](#input\_scc\_workload\_protection\_trusted\_profile\_name) | The name to give the trusted profile that is created by this module if `cspm_enabled` is `true. Must begin with a letter. If a prefix input variable is specified, the prefix is added to the name in the `-` format.` | `string` | `"workload-protection-trusted-profile"` | no | ### Outputs diff --git a/solutions/fully-configurable/main.tf b/solutions/fully-configurable/main.tf index 14c2ee0..8e86f76 100644 --- a/solutions/fully-configurable/main.tf +++ b/solutions/fully-configurable/main.tf @@ -1,8 +1,14 @@ +####################################################################################################################### +# Locals +####################################################################################################################### + locals { prefix_is_valid = var.prefix != null || trimspace(var.prefix) != "" ? true : false - scc_workload_protection_instance_name = local.prefix_is_valid ? "${var.prefix}-${var.scc_workload_protection_instance_name}" : var.scc_workload_protection_instance_name - scc_workload_protection_resource_key_name = local.prefix_is_valid ? "${var.prefix}-${var.scc_workload_protection_instance_name}-key" : "${var.scc_workload_protection_instance_name}-key" + # Compute names for SCC Workload Protection instance and trusted profile + scc_workload_protection_instance_name = local.prefix_is_valid ? "${var.prefix}-${var.scc_workload_protection_instance_name}" : var.scc_workload_protection_instance_name + scc_workload_protection_resource_key_name = local.prefix_is_valid ? "${var.prefix}-${var.scc_workload_protection_instance_name}-key" : "${var.scc_workload_protection_instance_name}-key" + scc_workload_protection_trusted_profile_name = local.prefix_is_valid ? "${var.prefix}-${var.scc_workload_protection_trusted_profile_name}" : var.scc_workload_protection_trusted_profile_name } ####################################################################################################################### @@ -20,15 +26,18 @@ module "resource_group" { ####################################################################################################################### module "scc_wp" { - source = "../.." - name = local.scc_workload_protection_instance_name - region = var.region - resource_group_id = module.resource_group.resource_group_id - resource_tags = var.scc_workload_protection_instance_tags - resource_key_name = local.scc_workload_protection_resource_key_name - resource_key_tags = var.scc_workload_protection_resource_key_tags - cloud_monitoring_instance_crn = var.existing_monitoring_crn - access_tags = var.scc_workload_protection_access_tags - scc_wp_service_plan = var.scc_workload_protection_service_plan - cbr_rules = var.cbr_rules + source = "../.." + name = local.scc_workload_protection_instance_name + region = var.region + resource_group_id = module.resource_group.resource_group_id + resource_tags = var.scc_workload_protection_instance_tags + resource_key_name = local.scc_workload_protection_resource_key_name + resource_key_tags = var.scc_workload_protection_resource_key_tags + cloud_monitoring_instance_crn = var.existing_monitoring_crn + access_tags = var.scc_workload_protection_access_tags + scc_wp_service_plan = var.scc_workload_protection_service_plan + cbr_rules = var.cbr_rules + cspm_enabled = var.cspm_enabled + app_config_crn = var.app_config_crn + scc_workload_protection_trusted_profile_name = local.scc_workload_protection_trusted_profile_name } diff --git a/solutions/fully-configurable/provider.tf b/solutions/fully-configurable/provider.tf index e669b7b..9322ae7 100644 --- a/solutions/fully-configurable/provider.tf +++ b/solutions/fully-configurable/provider.tf @@ -7,3 +7,14 @@ provider "ibm" { region = var.region visibility = var.provider_visibility } + +data "ibm_iam_auth_token" "auth_token" {} + +# Null resource replaced with restapi_object to enable CSPM +provider "restapi" { + uri = var.ibmcloud_resource_controller_api_endpoint + headers = { + Authorization = data.ibm_iam_auth_token.auth_token.iam_access_token + } + write_returns_object = true +} diff --git a/solutions/fully-configurable/variables.tf b/solutions/fully-configurable/variables.tf index e55e751..f935f5e 100644 --- a/solutions/fully-configurable/variables.tf +++ b/solutions/fully-configurable/variables.tf @@ -105,6 +105,59 @@ variable "scc_workload_protection_service_plan" { } } +############################################################## +# CSPM +############################################################## + +variable "cspm_enabled" { + description = "Enable Cloud Security Posture Management (CSPM) for the Workload Protection instance. This will create a trusted profile associated with the SCC Workload Protection instance that has viewer / reader access to the App Config service and viewer access to the Enterprise service. [Learn more](https://cloud.ibm.com/docs/workload-protection?topic=workload-protection-about)." + type = bool + default = true + nullable = false +} + +variable "app_config_crn" { + description = "The CRN of an existing App Config instance to use with the SCC Workload Protection instance. Required if `cspm_enabled` is true. NOTE: Ensure the App Config instance has configuration aggregator enabled." + type = string + default = null + validation { + condition = var.cspm_enabled ? var.app_config_crn != null : true + error_message = "Cannot be `null` if CSPM is enabled." + } + validation { + condition = anytrue([ + can(regex("^crn:(.*:){3}apprapp:(.*:){2}[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}::$", var.app_config_crn)), + var.app_config_crn == null, + ]) + error_message = "The provided CRN is not a valid App Config CRN." + } +} + +variable "ibmcloud_resource_controller_api_endpoint" { + description = "The URI of the Resource Controller service. This is used to update the Workload Protection instance to enable CSPM once the trusted profiles have been created." + type = string + # TODO: Use private endpoint: https://github.com/terraform-ibm-modules/terraform-ibm-scc-workload-protection/issues/244 + default = "https://resource-controller.cloud.ibm.com" + validation { + condition = !(var.cspm_enabled && var.ibmcloud_resource_controller_api_endpoint == null) + error_message = "This value cannot be `null` if `cspm_enabled` is set to `true`." + } +} + +variable "scc_workload_protection_trusted_profile_name" { + description = "The name to give the trusted profile that is created by this module if `cspm_enabled` is `true. Must begin with a letter. If a prefix input variable is specified, the prefix is added to the name in the `-` format." + type = string + default = "workload-protection-trusted-profile" + validation { + condition = can(regex("^[a-zA-Z][a-zA-Z0-9\\-_\\.]+$", var.scc_workload_protection_trusted_profile_name)) + error_message = "The trusted profile name must begin with a letter and can only contain letters, numbers, hyphens, underscores, and periods." + } + validation { + condition = !(var.cspm_enabled && var.scc_workload_protection_trusted_profile_name == null) + error_message = "Cannot be `null` if `cspm_enabled` is `true`." + } +} + ############################################################## # Context-based restriction (CBR) ############################################################## diff --git a/solutions/fully-configurable/version.tf b/solutions/fully-configurable/version.tf index 64deadd..5ec41a9 100644 --- a/solutions/fully-configurable/version.tf +++ b/solutions/fully-configurable/version.tf @@ -6,5 +6,9 @@ terraform { source = "IBM-Cloud/ibm" version = "1.78.2" } + restapi = { + source = "Mastercard/restapi" + version = "1.20.0" + } } } diff --git a/tests/existing-resources/README.md b/tests/existing-resources/README.md new file mode 100644 index 0000000..c731915 --- /dev/null +++ b/tests/existing-resources/README.md @@ -0,0 +1 @@ +The terraform code in this directory is used by the DA tests in tests/pr_test.go diff --git a/tests/existing-resources/main.tf b/tests/existing-resources/main.tf new file mode 100644 index 0000000..ee09820 --- /dev/null +++ b/tests/existing-resources/main.tf @@ -0,0 +1,28 @@ +############################################################################## +# Resource Group +############################################################################## + +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.2.0" + # if an existing resource group is not set (null) create a new one using prefix + resource_group_name = var.resource_group == null ? "${var.prefix}-resource-group" : null + existing_resource_group_name = var.resource_group +} + +######################################################################################################################## +# App Config +######################################################################################################################## + +# Create new App Config instance +module "app_config" { + source = "terraform-ibm-modules/app-configuration/ibm" + version = "1.5.1" + region = var.region + resource_group_id = module.resource_group.resource_group_id + app_config_name = "${var.prefix}-app-config" + app_config_tags = var.resource_tags + enable_config_aggregator = true + app_config_plan = "basic" + config_aggregator_trusted_profile_name = "${var.prefix}-app-config-tp" +} diff --git a/tests/existing-resources/outputs.tf b/tests/existing-resources/outputs.tf new file mode 100644 index 0000000..a580fd3 --- /dev/null +++ b/tests/existing-resources/outputs.tf @@ -0,0 +1,23 @@ +############################################################################## +# Outputs +############################################################################## + +output "resource_group_name" { + description = "Resource group name" + value = module.resource_group.resource_group_name +} + +output "prefix" { + value = var.prefix + description = "Prefix" +} + +output "region" { + value = var.region + description = "region" +} + +output "app_config_crn" { + description = "App Config CRN" + value = module.app_config.app_config_crn +} diff --git a/tests/existing-resources/provider.tf b/tests/existing-resources/provider.tf new file mode 100644 index 0000000..df45ef5 --- /dev/null +++ b/tests/existing-resources/provider.tf @@ -0,0 +1,4 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region +} diff --git a/tests/existing-resources/variables.tf b/tests/existing-resources/variables.tf new file mode 100644 index 0000000..e028edb --- /dev/null +++ b/tests/existing-resources/variables.tf @@ -0,0 +1,33 @@ +############################################################################## +# Input variables +############################################################################## + +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API Key" + sensitive = true +} + +variable "region" { + type = string + description = "Region" + default = "us-south" +} + +variable "prefix" { + type = string + description = "Prefix to append to all resources" + default = "test-ac" +} + +variable "resource_group" { + type = string + description = "The name of an existing resource group to provision resources in to. If not set a new resource group will be created using the prefix variable" + default = null +} + +variable "resource_tags" { + type = list(string) + description = "Optional list of tags to be added to created resources" + default = [] +} diff --git a/tests/existing-resources/version.tf b/tests/existing-resources/version.tf new file mode 100644 index 0000000..7db4be4 --- /dev/null +++ b/tests/existing-resources/version.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.9.0" + required_providers { + ibm = { + source = "ibm-cloud/ibm" + version = ">= 1.76.0" + } + } +} diff --git a/tests/pr_test.go b/tests/pr_test.go index 1dfc332..457b473 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -2,20 +2,30 @@ package test import ( + "fmt" "log" "os" + "strings" "testing" "math/rand/v2" + "github.com/gruntwork-io/terratest/modules/files" + "github.com/gruntwork-io/terratest/modules/logger" + "github.com/gruntwork-io/terratest/modules/random" + "github.com/gruntwork-io/terratest/modules/terraform" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "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" ) const resourceGroup = "geretain-test-resources" const fullyConfigurableDADir = "solutions/fully-configurable" +var existingResources = "./existing-resources" + // 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" @@ -46,70 +56,178 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestDAInSchematics(t *testing.T) { +func TestFullyConfigurable(t *testing.T) { t.Parallel() var region = validRegions[rand.IntN(len(validRegions))] - options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ - Testing: t, - Prefix: "wp-da", - TarIncludePatterns: []string{ - "*.tf", - fullyConfigurableDADir + "/*.tf", + // ------------------------------------------------------------------------------------ + // Provision App Config first + // ------------------------------------------------------------------------------------ + + prefix := fmt.Sprintf("wp-da-%s", strings.ToLower(random.UniqueId())) + realTerraformDir := existingResources + tempTerraformDir, _ := files.CopyTerraformFolderToTemp(realTerraformDir, fmt.Sprintf(prefix+"-%s", strings.ToLower(random.UniqueId()))) + tags := common.GetTagsFromTravis() + + // Verify ibmcloud_api_key variable is set + checkVariable := "TF_VAR_ibmcloud_api_key" + val, present := os.LookupEnv(checkVariable) + require.True(t, present, checkVariable+" environment variable not set") + require.NotEqual(t, "", val, checkVariable+" environment variable is empty") + + logger.Log(t, "Tempdir: ", tempTerraformDir) + existingTerraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: tempTerraformDir, + Vars: map[string]any{ + "prefix": prefix, + "region": region, + "resource_tags": tags, }, - ResourceGroup: resourceGroup, - TemplateFolder: fullyConfigurableDADir, - Tags: []string{"test-schematic"}, - DeleteWorkspaceOnFail: false, - WaitJobCompleteMinutes: 60, + // Set Upgrade to true to ensure latest version of providers and modules are used by terratest. + // This is the same as setting the -upgrade=true flag with terraform. + Upgrade: true, }) - options.TerraformVars = []testschematic.TestSchematicTerraformVar{ - {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, - {Name: "existing_resource_group_name", Value: resourceGroup, DataType: "string"}, - {Name: "region", Value: region, DataType: "string"}, - {Name: "scc_workload_protection_instance_tags", Value: options.Tags, DataType: "list(string)"}, - {Name: "scc_workload_protection_resource_key_tags", Value: options.Tags, DataType: "list(string)"}, - {Name: "scc_workload_protection_access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, - {Name: "prefix", Value: options.Prefix, DataType: "string"}, + terraform.WorkspaceSelectOrNew(t, existingTerraformOptions, prefix) + _, existErr := terraform.InitAndApplyE(t, existingTerraformOptions) + if existErr != nil { + assert.True(t, existErr == nil, "Init and Apply of pre-req resources failed in TestFullyConfigurable test") + } else { + // ------------------------------------------------------------------------------------ + // Deploy DA + // ------------------------------------------------------------------------------------ + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Prefix: "wp-da", + TarIncludePatterns: []string{ + "*.tf", + fullyConfigurableDADir + "/*.tf", + }, + ResourceGroup: resourceGroup, + TemplateFolder: fullyConfigurableDADir, + Tags: []string{"test-schematic"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 60, + // workaround for https://github.com/terraform-ibm-modules/terraform-ibm-scc-workload-protection/issues/243 + IgnoreAdds: testhelper.Exemptions{ + List: []string{"module.scc_wp.restapi_object.cspm"}, + }, + }) + + options.TerraformVars = []testschematic.TestSchematicTerraformVar{ + {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, + {Name: "existing_resource_group_name", Value: resourceGroup, DataType: "string"}, + {Name: "region", Value: region, DataType: "string"}, + {Name: "scc_workload_protection_instance_tags", Value: options.Tags, DataType: "list(string)"}, + {Name: "scc_workload_protection_resource_key_tags", Value: options.Tags, DataType: "list(string)"}, + {Name: "scc_workload_protection_access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, + {Name: "prefix", Value: options.Prefix, DataType: "string"}, + {Name: "app_config_crn", Value: terraform.Output(t, existingTerraformOptions, "app_config_crn"), DataType: "string"}, + {Name: "ibmcloud_resource_controller_api_endpoint", Value: "https://resource-controller.cloud.ibm.com", DataType: "string"}, + } + err := options.RunSchematicTest() + assert.Nil(t, err, "This should not have errored") } - err := options.RunSchematicTest() - assert.Nil(t, err, "This should not have errored") + // Check if "DO_NOT_DESTROY_ON_FAILURE" is set + envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") + // Destroy the temporary existing resources if required + if t.Failed() && strings.ToLower(envVal) == "true" { + fmt.Println("Terratest failed. Debug the test and delete resources manually.") + } else { + logger.Log(t, "START: Destroy (prereq resources)") + terraform.Destroy(t, existingTerraformOptions) + terraform.WorkspaceDelete(t, existingTerraformOptions, prefix) + logger.Log(t, "END: Destroy (prereq resources)") + } } -func TestRunUpgradeDA(t *testing.T) { +func TestFullyConfigurableUpgrade(t *testing.T) { t.Parallel() var region = validRegions[rand.IntN(len(validRegions))] - options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ - Testing: t, - Prefix: "wp-da", - TarIncludePatterns: []string{ - "*.tf", - fullyConfigurableDADir + "/*.tf", + // ------------------------------------------------------------------------------------ + // Provision App Config first + // ------------------------------------------------------------------------------------ + + prefix := fmt.Sprintf("wp-da-%s", strings.ToLower(random.UniqueId())) + realTerraformDir := existingResources + tempTerraformDir, _ := files.CopyTerraformFolderToTemp(realTerraformDir, fmt.Sprintf(prefix+"-%s", strings.ToLower(random.UniqueId()))) + tags := common.GetTagsFromTravis() + + // Verify ibmcloud_api_key variable is set + checkVariable := "TF_VAR_ibmcloud_api_key" + val, present := os.LookupEnv(checkVariable) + require.True(t, present, checkVariable+" environment variable not set") + require.NotEqual(t, "", val, checkVariable+" environment variable is empty") + + logger.Log(t, "Tempdir: ", tempTerraformDir) + existingTerraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: tempTerraformDir, + Vars: map[string]interface{}{ + "prefix": prefix, + "region": region, + "resource_tags": tags, }, - ResourceGroup: resourceGroup, - TemplateFolder: fullyConfigurableDADir, - Tags: []string{"test-schematic"}, - DeleteWorkspaceOnFail: false, - WaitJobCompleteMinutes: 60, + // Set Upgrade to true to ensure latest version of providers and modules are used by terratest. + // This is the same as setting the -upgrade=true flag with terraform. + Upgrade: true, }) - options.TerraformVars = []testschematic.TestSchematicTerraformVar{ - {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, - {Name: "existing_resource_group_name", Value: resourceGroup, DataType: "string"}, - {Name: "region", Value: region, DataType: "string"}, - {Name: "scc_workload_protection_instance_tags", Value: options.Tags, DataType: "list(string)"}, - {Name: "scc_workload_protection_resource_key_tags", Value: options.Tags, DataType: "list(string)"}, - {Name: "scc_workload_protection_access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, - {Name: "prefix", Value: options.Prefix, DataType: "string"}, + terraform.WorkspaceSelectOrNew(t, existingTerraformOptions, prefix) + _, existErr := terraform.InitAndApplyE(t, existingTerraformOptions) + if existErr != nil { + assert.True(t, existErr == nil, "Init and Apply of pre-req resources failed in TestFullyConfigurable test") + } else { + // ------------------------------------------------------------------------------------ + // Deploy DA + // ------------------------------------------------------------------------------------ + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Prefix: "wp-da", + TarIncludePatterns: []string{ + "*.tf", + fullyConfigurableDADir + "/*.tf", + }, + ResourceGroup: resourceGroup, + TemplateFolder: fullyConfigurableDADir, + Tags: []string{"test-schematic"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 60, + // workaround for https://github.com/terraform-ibm-modules/terraform-ibm-scc-workload-protection/issues/243 + IgnoreAdds: testhelper.Exemptions{ + List: []string{"module.scc_wp.restapi_object.cspm"}, + }, + }) + + options.TerraformVars = []testschematic.TestSchematicTerraformVar{ + {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, + {Name: "existing_resource_group_name", Value: resourceGroup, DataType: "string"}, + {Name: "region", Value: region, DataType: "string"}, + {Name: "scc_workload_protection_instance_tags", Value: options.Tags, DataType: "list(string)"}, + {Name: "scc_workload_protection_resource_key_tags", Value: options.Tags, DataType: "list(string)"}, + {Name: "scc_workload_protection_access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, + {Name: "prefix", Value: options.Prefix, DataType: "string"}, + {Name: "app_config_crn", Value: terraform.Output(t, existingTerraformOptions, "app_config_crn"), DataType: "string"}, + {Name: "ibmcloud_resource_controller_api_endpoint", Value: "https://resource-controller.cloud.ibm.com", DataType: "string"}, + } + err := options.RunSchematicUpgradeTest() + if !options.UpgradeTestSkipped { + assert.Nil(t, err, "This should not have errored") + } } - err := options.RunSchematicUpgradeTest() - if !options.UpgradeTestSkipped { - assert.Nil(t, err, "This should not have errored") + // Check if "DO_NOT_DESTROY_ON_FAILURE" is set + envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") + // Destroy the temporary existing resources if required + if t.Failed() && strings.ToLower(envVal) == "true" { + fmt.Println("Terratest failed. Debug the test and delete resources manually.") + } else { + logger.Log(t, "START: Destroy (prereq resources)") + terraform.Destroy(t, existingTerraformOptions) + terraform.WorkspaceDelete(t, existingTerraformOptions, prefix) + logger.Log(t, "END: Destroy (prereq resources)") } } diff --git a/variables.tf b/variables.tf index 5215dd4..d7d97ec 100644 --- a/variables.tf +++ b/variables.tf @@ -73,6 +73,48 @@ variable "cloud_monitoring_instance_crn" { default = null } +############################################################## +# CSPM +############################################################## + +variable "cspm_enabled" { + description = "Enable Cloud Security Posture Management (CSPM) for the Workload Protection instance. This will create a trusted profile associated with the SCC Workload Protection instance that has viewer / reader access to the App Config service and viewer access to the Enterprise service. [Learn more](https://cloud.ibm.com/docs/workload-protection?topic=workload-protection-about)." + type = bool + default = true + nullable = false +} + +variable "app_config_crn" { + description = "The CRN of an existing App Config instance to use with the SCC Workload Protection instance. Required if `cspm_enabled` is true. NOTE: Ensure the App Config instance has configuration aggregator enabled." + type = string + default = null + validation { + condition = var.cspm_enabled ? var.app_config_crn != null : true + error_message = "Cannot be `null` if CSPM is enabled." + } + validation { + condition = anytrue([ + can(regex("^crn:(.*:){3}apprapp:(.*:){2}[0-9a-fA-F]{8}(?:-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}::$", var.app_config_crn)), + var.app_config_crn == null, + ]) + error_message = "The provided CRN is not a valid App Config CRN." + } +} + +variable "scc_workload_protection_trusted_profile_name" { + description = "The name to give the trusted profile that is created by this module if `cspm_enabled` is `true. Must begin with a letter." + type = string + default = "workload-protection-trusted-profile" + validation { + condition = can(regex("^[a-zA-Z][a-zA-Z0-9\\-_\\.]+$", var.scc_workload_protection_trusted_profile_name)) + error_message = "The trusted profile name must begin with a letter and can only contain letters, numbers, hyphens, underscores, and periods." + } + validation { + condition = !(var.cspm_enabled && var.scc_workload_protection_trusted_profile_name == null) + error_message = "Cannot be `null` if `cspm_enabled` is `true`." + } +} + ############################################################## # Context-based restriction (CBR) ############################################################## diff --git a/version.tf b/version.tf index c9bf260..dc2c2f9 100644 --- a/version.tf +++ b/version.tf @@ -6,5 +6,9 @@ terraform { source = "ibm-cloud/ibm" version = ">= 1.70.0, <2.0.0" } + restapi = { + source = "Mastercard/restapi" + version = ">=1.20.0, <2.0.0" + } } }