diff --git a/.catalog-onboard-pipeline.yaml b/.catalog-onboard-pipeline.yaml new file mode 100644 index 0000000..2cdd1f2 --- /dev/null +++ b/.catalog-onboard-pipeline.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: v1 +offerings: + - name: deploy-arch-secrets-manager-custom-credentials-engine + kind: solution + catalog_id: 7df1e4ca-d54c-4fd0-82ce-3d13247308cd + offering_id: 6a7e04c0-f3ef-439b-9d3c-cdb4e583ae9c + variations: + - name: fully-configurable + mark_ready: true + install_type: fullstack + scc: + instance_id: 1c7d5f78-9262-44c3-b779-b28fe4d88c37 + region: us-south + pre_validation: "tests/scripts/pre-validation-deploy-ce.sh" + post_validation: "tests/scripts/post-validation-destroy-ce.sh" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d545b87..ce67cd0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # Primary owner should be listed first in list of global owners, followed by any secondary owners -* @ocofaigh @daniel-butler-irl +* @alex-reiff @mukulpalit-ibm diff --git a/.github/settings.yml b/.github/settings.yml index e95112f..86624f7 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -22,7 +22,7 @@ repository: # Uncomment this description property # and update the description to the current repo description. - description: "Configures a custom credentials engine configuration for IBM Cloud Secrets Manager" + description: "Creates a custom credentials engine in a Secrets Manager instance" # Use a comma-separated list of topics to set on the repo (ensure not to use any caps in the topic string). topics: terraform, ibm-cloud, terraform-module, core-team, custom-credentials, secrets-manager diff --git a/.releaserc b/.releaserc index 708916f..4160e57 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 0a4efa4..eb18f9a 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "go.sum|^.secrets.baseline$", "lines": null }, - "generated_at": "2024-11-22T17:36:38Z", + "generated_at": "2025-08-19T08:13:41Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -76,18 +76,7 @@ "name": "TwilioKeyDetector" } ], - "results": { - "README.md": [ - { - "hashed_secret": "ff9ee043d85595eb255c05dfe32ece02a53efbb2", - "is_secret": false, - "is_verified": false, - "line_number": 74, - "type": "Secret Keyword", - "verified_result": null - } - ] - }, + "results": {}, "version": "0.13.1+ibm.62.dss", "word_list": { "file": null, diff --git a/README.md b/README.md index a5b9d3f..738fa9e 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,20 @@ - -# Secrets Manager custom credentials engine - - -[![Incubating (Not yet consumable)](https://img.shields.io/badge/status-Incubating%20(Not%20yet%20consumable)-red)](https://terraform-ibm-modules.github.io/documentation/#/badge-status) -[![latest release](https://img.shields.io/github/v/release/terraform-ibm-modules/terraform-ibm-secrets-manager-custom-credentials-engine?logo=GitHub&sort=semver)](https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager-custom-credentials-engine/releases/latest) +# Secrets Manager custom credentials engine module + +[![Graduated (Supported)](https://img.shields.io/badge/Status-Graduated%20(Supported)-brightgreen)](https://terraform-ibm-modules.github.io/documentation/#/badge-status) [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) +[![latest release](https://img.shields.io/github/v/release/terraform-ibm-modules/terraform-ibm-secrets-manager-custom-credentials-engine?logo=GitHub&sort=semver)](https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager-custom-credentials-engine/releases/latest) [![Renovate enabled](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://renovatebot.com/) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) - +- [IAM service authorization]((https://cloud.ibm.com/docs/account?topic=account-serviceauth&interface=ui)) policy creation between Secrets Manager as source and Code Engine Project as target +- [IAM credentials secret](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-iam-credentials&interface=terraform) creation for allowing code engine job to fetch secrets +- [Custom credentials engine](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-custom-credentials-config&interface=terraform) -TODO: Replace this with a description of the modules in this repo. +These components are needed in order to create the custom credentials secret in SM instance. @@ -28,130 +22,110 @@ TODO: Replace this with a description of the modules in this repo. ## Overview * [terraform-ibm-secrets-manager-custom-credentials-engine](#terraform-ibm-secrets-manager-custom-credentials-engine) * [Examples](./examples) - * [Advanced example](./examples/advanced) - * [Basic example](./examples/basic) + * [Complete example](./examples/complete) * [Contributing](#contributing) - - +## Reference architectures + +[Secrets Manager Custom Credential Engine](./reference-architecture/secrets_manager_custom_credentials_engine.svg) - ## terraform-ibm-secrets-manager-custom-credentials-engine ### Usage - - ```hcl -terraform { - required_version = ">= 1.9.0" - required_providers { - ibm = { - source = "IBM-Cloud/ibm" - version = "X.Y.Z" # Lock into a provider version that satisfies the module constraints - } - } -} - -locals { - region = "us-south" -} - -provider "ibm" { - ibmcloud_api_key = "XXXXXXXXXX" # replace with apikey value - region = local.region +module "custom_credential_engine" { + source = "terraform-ibm-modules/secrets-manager-custom-credentials-engine/ibm" + version = "X.X.X" # Replace "X.X.X" with a release version to lock into a specific release + secrets_manager_guid = "" + secrets_manager_region = "" + custom_credential_engine_name = "My Custom Credentials Engine" + endpoint_type = "public" + code_engine_project_id = "" + code_engine_job_name = "" + code_engine_region = "" + task_timeout = "5m" + service_id_name = "My Service ID" + iam_credential_secret_name = "My Credentials Secret" } -module "module_template" { - source = "terraform-ibm-modules//ibm" - version = "X.Y.Z" # Replace "X.Y.Z" with a release version to lock into a specific release - region = local.region - name = "instance-name" - resource_group_id = "xxXXxxXXxXxXXXXxxXxxxXXXXxXXXXX" # Replace with the actual ID of resource group to use -} ``` -### Required access policies - - - - - - +### Required IAM access policies +You need the following permissions to run this module. + +- Account Management + - **IAM Identity** services + - `Administrator` platform access + - `Service ID Creator` service access + - **All Identity and Access enabled** services + - `Administrator` platform access +- IAM Services + - **Secrets Manager** service + - `Administrator` platform access + - `Manager` service access - ### Requirements | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 1.9.0 | -| [ibm](#requirement\_ibm) | >= 1.71.2, < 2.0.0 | +| [ibm](#requirement\_ibm) | >= 1.79.2, < 2.0.0 | +| [time](#requirement\_time) | >= 0.9.1, < 1.0.0 | ### Modules -No modules. +| Name | Source | Version | +|------|--------|---------| +| [sm\_iam\_credential\_secret](#module\_sm\_iam\_credential\_secret) | terraform-ibm-modules/iam-serviceid-apikey-secrets-manager/ibm | 1.2.0 | ### Resources | Name | Type | |------|------| -| [ibm_resource_instance.cos_instance](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/resource_instance) | resource | +| [ibm_iam_authorization_policy.sm_ce_policy](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/iam_authorization_policy) | resource | +| [ibm_iam_service_id.sm_service_id](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/iam_service_id) | resource | +| [ibm_iam_service_policy.sm_service_id_policy](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/iam_service_policy) | resource | +| [ibm_sm_custom_credentials_configuration.custom_credentials_configuration](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/sm_custom_credentials_configuration) | resource | +| [time_sleep.wait_for_service_id](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource | +| [time_sleep.wait_for_sm_ce_authorization_policy](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource | ### Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [name](#input\_name) | A descriptive name used to identify the resource instance. | `string` | n/a | yes | -| [plan](#input\_plan) | The name of the plan type supported by service. | `string` | `"standard"` | no | -| [resource\_group\_id](#input\_resource\_group\_id) | The ID of the resource group where you want to create the service. | `string` | n/a | yes | -| [resource\_tags](#input\_resource\_tags) | List of resource tag to associate with the instance. | `list(string)` | `[]` | no | +| [code\_engine\_job\_name](#input\_code\_engine\_job\_name) | The code engine job name used by this custom credentials configuration. | `string` | n/a | yes | +| [code\_engine\_project\_id](#input\_code\_engine\_project\_id) | The Project ID of the code engine project used by the custom credentials configuration. | `string` | n/a | yes | +| [code\_engine\_region](#input\_code\_engine\_region) | The region of the code engine project. | `string` | n/a | yes | +| [custom\_credential\_engine\_name](#input\_custom\_credential\_engine\_name) | The name of the custom credentials engine to be created. | `string` | n/a | yes | +| [endpoint\_type](#input\_endpoint\_type) | The endpoint type to communicate with the provided secrets manager instance. Possible values are `public` or `private`. | `string` | `"public"` | no | +| [iam\_credential\_secret\_auto\_rotation\_interval](#input\_iam\_credential\_secret\_auto\_rotation\_interval) | The rotation interval for the rotation policy. | `string` | `60` | no | +| [iam\_credential\_secret\_auto\_rotation\_unit](#input\_iam\_credential\_secret\_auto\_rotation\_unit) | The unit of time for rotation policy. Acceptable values are `day` or `month`. | `string` | `"day"` | no | +| [iam\_credential\_secret\_group\_id](#input\_iam\_credential\_secret\_group\_id) | Secret Group ID of secret where IAM Secret will be added to, leave default (null) to add in the default secret group. | `string` | `null` | no | +| [iam\_credential\_secret\_labels](#input\_iam\_credential\_secret\_labels) | Optional list of up to 30 labels to be created on the secret. Labels can be used to search for secrets in the Secrets Manager instance. | `list(string)` | `[]` | no | +| [iam\_credential\_secret\_name](#input\_iam\_credential\_secret\_name) | The name of the IAM credential secret to allow code engine job to pull secrets from Secrets Manager. | `string` | n/a | yes | +| [iam\_credential\_secret\_ttl](#input\_iam\_credential\_secret\_ttl) | Specify validity / lease duration of ServiceID API key. Accepted values and formats are: SECONDS, Xm or Xh (where X is the number of minutes or hours appended to m or h respectively). | `string` | `"7776000"` | no | +| [secrets\_manager\_guid](#input\_secrets\_manager\_guid) | GUID of secrets manager instance to create the secret engine in. | `string` | n/a | yes | +| [secrets\_manager\_region](#input\_secrets\_manager\_region) | The region of the secrets manager instance. | `string` | n/a | yes | +| [service\_id\_name](#input\_service\_id\_name) | The name of the service ID to be created to allow code engine job to pull secrets from Secrets Manager. | `string` | n/a | yes | +| [skip\_secrets\_manager\_code\_engine\_auth\_policy](#input\_skip\_secrets\_manager\_code\_engine\_auth\_policy) | Whether to skip the creation of the IAM authorization policies required between the Code engine project and Secrets Manager instance. If set to false, policies will be created that grants the Secrets Manager instance 'Viewer' and 'Writer' access to the Code engine project. | `bool` | `false` | no | +| [task\_timeout](#input\_task\_timeout) | The maximum allowed time for a code engine job to be completed. | `string` | `"5m"` | no | ### Outputs | Name | Description | |------|-------------| -| [account\_id](#output\_account\_id) | An alpha-numeric value identifying the account ID. | -| [crn](#output\_crn) | The CRN of the resource instance. | -| [guid](#output\_guid) | The GUID of the resource instance. | -| [id](#output\_id) | The unique identifier of the resource instance. | +| [code\_engine\_key\_ref](#output\_code\_engine\_key\_ref) | The IAM API key used by the credentials system to access the secrets manager instance. | +| [custom\_config\_engine\_id](#output\_custom\_config\_engine\_id) | The unique identifier of the engine created. | +| [custom\_config\_engine\_name](#output\_custom\_config\_engine\_name) | The name of the engine created. | +| [secrets\_manager\_custom\_credentials\_configuration\_schema](#output\_secrets\_manager\_custom\_credentials\_configuration\_schema) | The schema that defines the format of the input and output parameters. | diff --git a/common-dev-assets b/common-dev-assets index 2ba5cc2..7179ae4 160000 --- a/common-dev-assets +++ b/common-dev-assets @@ -1 +1 @@ -Subproject commit 2ba5cc2c867361e8bcf34bd95f7359cc03d82b25 +Subproject commit 7179ae4f3446b3816fa2d72c873f8f8e86797836 diff --git a/cra-config.yaml b/cra-config.yaml index 9a4c7fa..db62db9 100644 --- a/cra-config.yaml +++ b/cra-config.yaml @@ -7,7 +7,7 @@ version: "v1" CRA_TARGETS: - - CRA_TARGET: "examples/advanced" # Target directory for CRA scan. If not provided, the CRA Scan will not be run. + - CRA_TARGET: "solutions/fully-configurable" # Target directory for CRA scan. If not provided, the CRA Scan will not be run. CRA_IGNORE_RULES_FILE: "cra-tf-validate-ignore-rules.json" PROFILE_ID: "fe96bd4d-9b37-40f2-b39f-a62760e326a3" # SCC profile ID (currently set to 'IBM Cloud Framework for Financial Services' '1.7.0' profile). # SCC_INSTANCE_ID: "" # The SCC instance ID to use to download profile for CRA scan. If not provided, a default global value will be used. @@ -15,3 +15,11 @@ CRA_TARGETS: CRA_ENVIRONMENT_VARIABLES: # An optional map of environment variables for CRA, where the key is the variable name and value is the value. Useful for providing TF_VARs. TF_VAR_prefix: "mock" TF_VAR_region: "us-south" + TF_VAR_provider_visibility: "public" + TF_VAR_existing_secrets_manager_crn: "crn:v1:bluemix:public:secrets-manager:us-south:a/abac0df06b644a9cabc6e44f55b3880e:79c6d411-c18f-4670-b009-b0044a238667::" + TF_VAR_custom_credential_engine_name: "test-engine" + TF_VAR_service_id_name: "test-service-id" + TF_VAR_iam_credential_secret_name: "test-credential-secret" + TF_VAR_existing_code_engine_project_id: "d731565f-835d-4c1b-b116-a03fa4e703df" + TF_VAR_existing_code_engine_job_name: "ce-job-name" + TF_VAR_existing_code_engine_region: "us-south" diff --git a/examples/advanced/README.md b/examples/advanced/README.md deleted file mode 100644 index d52511a..0000000 --- a/examples/advanced/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Advanced example - - - diff --git a/examples/advanced/main.tf b/examples/advanced/main.tf deleted file mode 100644 index 88360af..0000000 --- a/examples/advanced/main.tf +++ /dev/null @@ -1,32 +0,0 @@ -######################################################################################################################## -# Resource group -######################################################################################################################## - -module "resource_group" { - source = "terraform-ibm-modules/resource-group/ibm" - version = "1.2.0" - # if an existing resource group is not set (null) create a new one using prefix - resource_group_name = var.resource_group == null ? "${var.prefix}-resource-group" : null - existing_resource_group_name = var.resource_group -} - -######################################################################################################################## -# COS -######################################################################################################################## - -# -# Developer tips: -# - Call the local module / modules in the example to show how they can be consumed -# - Include the actual module source as a code comment like below so consumers know how to consume from correct location -# - -module "cos" { - source = "../.." - # remove the above line and uncomment the below 2 lines to consume the module from the registry - # source = "terraform-ibm-modules//ibm" - # version = "X.Y.Z" # Replace "X.Y.Z" with a release version to lock into a specific release - name = "${var.prefix}-cos" - resource_group_id = module.resource_group.resource_group_id - resource_tags = var.resource_tags - plan = "cos-one-rate-plan" -} diff --git a/examples/advanced/outputs.tf b/examples/advanced/outputs.tf deleted file mode 100644 index 316751f..0000000 --- a/examples/advanced/outputs.tf +++ /dev/null @@ -1,38 +0,0 @@ -############################################################################## -# Outputs -############################################################################## - -# -# Developer tips: -# - Include all relevant outputs from the modules being called in the example -# - -output "account_id" { - description = "An alpha-numeric value identifying the account ID." - value = module.cos.account_id -} - -output "guid" { - description = "The GUID of the resource instance." - value = module.cos.account_id -} - -output "id" { - description = "The unique identifier of the resource instance." - value = module.cos.id -} - -output "crn" { - description = "The CRN of the resource instance." - value = module.cos.crn -} - -output "resource_group_name" { - description = "Resource group name." - value = module.resource_group.resource_group_name -} - -output "resource_group_id" { - description = "Resource group ID." - value = module.resource_group.resource_group_id -} diff --git a/examples/advanced/provider.tf b/examples/advanced/provider.tf deleted file mode 100644 index 2080946..0000000 --- a/examples/advanced/provider.tf +++ /dev/null @@ -1,8 +0,0 @@ -############################################################################## -# Provider config -############################################################################## - -provider "ibm" { - ibmcloud_api_key = var.ibmcloud_api_key - region = var.region -} diff --git a/examples/advanced/version.tf b/examples/advanced/version.tf deleted file mode 100644 index ecfa978..0000000 --- a/examples/advanced/version.tf +++ /dev/null @@ -1,16 +0,0 @@ -terraform { - required_version = ">= 1.9.0" - - # - # Developer tips: - # - Ensure that there is always 1 example locked into the lowest provider version of the range defined in the main - # module's version.tf (usually a basic example), and 1 example that will always use the latest provider version. - # - - required_providers { - ibm = { - source = "IBM-Cloud/ibm" - version = ">= 1.71.2, < 2.0.0" - } - } -} diff --git a/examples/basic/README.md b/examples/basic/README.md deleted file mode 100644 index e5977ae..0000000 --- a/examples/basic/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Basic example - - - -An end-to-end basic example that will provision the following: -- A new resource group if one is not passed in. -- A new standard plan Cloud Object Storage instance using the root level module. diff --git a/examples/basic/main.tf b/examples/basic/main.tf deleted file mode 100644 index cf665db..0000000 --- a/examples/basic/main.tf +++ /dev/null @@ -1,31 +0,0 @@ -######################################################################################################################## -# Resource group -######################################################################################################################## - -module "resource_group" { - source = "terraform-ibm-modules/resource-group/ibm" - version = "1.2.0" - # if an existing resource group is not set (null) create a new one using prefix - resource_group_name = var.resource_group == null ? "${var.prefix}-resource-group" : null - existing_resource_group_name = var.resource_group -} - -######################################################################################################################## -# COS -######################################################################################################################## - -# -# Developer tips: -# - Call the local module / modules in the example to show how they can be consumed -# - include the actual module source as a code comment like below so consumers know how to consume from correct location -# - -module "cos" { - source = "../.." - # remove the above line and uncomment the below 2 lines to consume the module from the registry - # source = "terraform-ibm-modules//ibm" - # version = "X.Y.Z" # Replace "X.Y.Z" with a release version to lock into a specific release - name = "${var.prefix}-cos" - resource_group_id = module.resource_group.resource_group_id - resource_tags = var.resource_tags -} diff --git a/examples/basic/outputs.tf b/examples/basic/outputs.tf deleted file mode 100644 index 552db48..0000000 --- a/examples/basic/outputs.tf +++ /dev/null @@ -1,38 +0,0 @@ -######################################################################################################################## -# Outputs -######################################################################################################################## - -# -# Developer tips: -# - Include all relevant outputs from the modules being called in the example -# - -output "account_id" { - description = "An alpha-numeric value identifying the account ID." - value = module.cos.account_id -} - -output "guid" { - description = "The GUID of the resource instance." - value = module.cos.account_id -} - -output "id" { - description = "The unique identifier of the resource instance." - value = module.cos.id -} - -output "crn" { - description = "The CRN of the resource instance." - value = module.cos.crn -} - -output "resource_group_name" { - description = "Resource group name." - value = module.resource_group.resource_group_name -} - -output "resource_group_id" { - description = "Resource group ID." - value = module.resource_group.resource_group_id -} diff --git a/examples/basic/variables.tf b/examples/basic/variables.tf deleted file mode 100644 index d460364..0000000 --- a/examples/basic/variables.tf +++ /dev/null @@ -1,39 +0,0 @@ -######################################################################################################################## -# Input variables -######################################################################################################################## - -# -# Module developer tips: -# - Examples are references that consumers can use to see how the module can be consumed. They are not designed to be -# flexible re-usable solutions for general consumption, so do not expose any more variables here and instead hard -# code things in the example main.tf with code comments explaining the different configurations. -# - For the same reason as above, do not add default values to the example inputs. -# - -variable "ibmcloud_api_key" { - type = string - description = "The IBM Cloud API Key." - sensitive = true -} - -variable "region" { - type = string - description = "Region to provision all resources created by this example." -} - -variable "prefix" { - type = string - description = "A string value to prefix to all resources created by this example." -} - -variable "resource_group" { - type = string - description = "The name of an existing resource group to provision resources in to. If not set a new resource group will be created using the prefix variable." - default = null -} - -variable "resource_tags" { - type = list(string) - description = "List of resource tag to associate with all resource instances created by this example." - default = [] -} diff --git a/examples/basic/version.tf b/examples/basic/version.tf deleted file mode 100644 index 401504c..0000000 --- a/examples/basic/version.tf +++ /dev/null @@ -1,16 +0,0 @@ -terraform { - required_version = ">= 1.9.0" - - # - # Developer tips: - # - Ensure that there is always 1 example locked into the lowest provider version of the range defined in the main - # module's version.tf (usually a basic example), and 1 example that will always use the latest provider version. - # - - required_providers { - ibm = { - source = "IBM-Cloud/ibm" - version = "1.71.2" - } - } -} diff --git a/examples/complete/README.md b/examples/complete/README.md new file mode 100644 index 0000000..1eb7ae6 --- /dev/null +++ b/examples/complete/README.md @@ -0,0 +1,7 @@ +# Complete example + +An end-to-end complete example that will provision the following: +- A new resource group if one is not passed in. +- A new secrets manager instance with IAM credentials engine configured if an existing instance CRN is not passed. +- A new code engine project with code engine job. +- A custom credentials engine with IAM authorization policy for code engine job and IAM credential secret. diff --git a/examples/complete/main.tf b/examples/complete/main.tf new file mode 100644 index 0000000..7419332 --- /dev/null +++ b/examples/complete/main.tf @@ -0,0 +1,76 @@ +############################################################################## +# Resource Group +############################################################################## + +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.3.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 +} + +############################################################################## +# Code Engine +############################################################################## + +module "code_engine" { + source = "terraform-ibm-modules/code-engine/ibm" + version = "4.5.7" + resource_group_id = module.resource_group.resource_group_id + project_name = "${var.prefix}-project" + jobs = { + "${var.prefix}-job" = { + image_reference = "icr.io/codeengine/helloworld" + run_env_variables = [{ + type = "literal" + name = "SMOUT_TEST" # The code engine job must have an environment variable of type SMOUT_XXX to be added to custom engine configuration + value = "type:string, required:true" # The code engine job env variable must have a value containing the required:true attribute + }] + run_arguments = ["echo \"hello world\""] + run_commands = ["/bin/sh"] + } + } +} + +######################################################################################################################## +# Locals +######################################################################################################################## + +locals { + sm_guid = var.existing_sm_guid == null ? module.secrets_manager[0].secrets_manager_guid : var.existing_sm_guid + sm_region = var.existing_sm_region == null ? var.region : var.existing_sm_region +} + +############################################################################## +# Secrets Manager +############################################################################## + +module "secrets_manager" { + source = "terraform-ibm-modules/secrets-manager/ibm" + version = "2.7.5" + count = var.existing_sm_guid == null ? 1 : 0 + resource_group_id = module.resource_group.resource_group_id + region = var.region + secrets_manager_name = "${var.prefix}-secrets-manager" + sm_service_plan = "standard" + allowed_network = "public-and-private" + sm_tags = var.resource_tags +} + +############################################################################## +# Custom Credentials Engine Instance +############################################################################## + +module "custom_engine" { + depends_on = [module.code_engine, module.secrets_manager] + source = "../.." + secrets_manager_guid = local.sm_guid + secrets_manager_region = local.sm_region + custom_credential_engine_name = "${var.prefix}-engine" + code_engine_project_id = module.code_engine.project_id + code_engine_job_name = module.code_engine.job["${var.prefix}-job"].name + code_engine_region = var.region + service_id_name = "${var.prefix}-sm_custom_credential_service_id" + iam_credential_secret_name = "${var.prefix}-iam-secret-for-custom-credential" +} diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf new file mode 100644 index 0000000..2b8f7c8 --- /dev/null +++ b/examples/complete/outputs.tf @@ -0,0 +1,24 @@ +######################################################################################################################## +# Outputs +######################################################################################################################## + +output "custom_config_engine_id" { + description = "The unique identifier of the engine created." + value = module.custom_engine.custom_config_engine_id +} + +output "custom_config_engine_name" { + description = "The name of the engine created." + value = module.custom_engine.custom_config_engine_name +} + +output "code_engine_key_ref" { + description = "The IAM API key used by the credentials system to access the secrets manager instance." + sensitive = true + value = module.custom_engine.code_engine_key_ref +} + +output "secrets_manager_custom_credentials_configuration_schema" { + description = "The schema that defines the format of the input and output parameters." + value = module.custom_engine.secrets_manager_custom_credentials_configuration_schema +} diff --git a/examples/basic/provider.tf b/examples/complete/provider.tf similarity index 100% rename from examples/basic/provider.tf rename to examples/complete/provider.tf diff --git a/examples/advanced/variables.tf b/examples/complete/variables.tf similarity index 69% rename from examples/advanced/variables.tf rename to examples/complete/variables.tf index d460364..ac7c96e 100644 --- a/examples/advanced/variables.tf +++ b/examples/complete/variables.tf @@ -2,14 +2,6 @@ # Input variables ######################################################################################################################## -# -# Module developer tips: -# - Examples are references that consumers can use to see how the module can be consumed. They are not designed to be -# flexible re-usable solutions for general consumption, so do not expose any more variables here and instead hard -# code things in the example main.tf with code comments explaining the different configurations. -# - For the same reason as above, do not add default values to the example inputs. -# - variable "ibmcloud_api_key" { type = string description = "The IBM Cloud API Key." @@ -37,3 +29,15 @@ variable "resource_tags" { description = "List of resource tag to associate with all resource instances created by this example." default = [] } + +variable "existing_sm_guid" { + type = string + description = "Existing Secrets Manager GUID. The existing Secret Manager instance must have IAM credentials engine configured. If not provided, a new instance will be provisioned." + default = null +} + +variable "existing_sm_region" { + type = string + description = "Required if value is passed into var.existing_sm_guid" + default = null +} diff --git a/examples/complete/version.tf b/examples/complete/version.tf new file mode 100644 index 0000000..e4c66ae --- /dev/null +++ b/examples/complete/version.tf @@ -0,0 +1,10 @@ +terraform { + required_version = ">= 1.9.0" + + required_providers { + ibm = { + source = "IBM-Cloud/ibm" + version = "1.79.2" + } + } +} diff --git a/ibm_catalog.json b/ibm_catalog.json new file mode 100644 index 0000000..e9b2318 --- /dev/null +++ b/ibm_catalog.json @@ -0,0 +1,340 @@ +{ + "products": [ + { + "name": "deploy-arch-secrets-manager-custom-credentials-engine", + "label": "Cloud automation for Secrets Manager custom credentials engine", + "product_kind": "solution", + "tags": [ + "ibm_created", + "target_terraform", + "terraform", + "solution", + "security", + "converged_infra" + ], + "keywords": [ + "Custom credentials engine", + "Secrets Manager", + "IaC", + "infrastructure as code", + "terraform", + "solution" + ], + "short_description": "Creates and configures a Secrets Manager custom credentials engine.", + "long_description": "This deployable architecture is used to provision and configure a custom credentials engine that will be used to store [custom credential secrets](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-custom-credentials&interface=ui) for external systems in IBM Secrets Manager service. It creates an IAM Credentials secret, a service ID and service policy that will allow the job running in code engine to access secrets manager to update the custom secret. The architecture also creates an IAM service authorization between secrets manager and code engine project. In order to use this deployable architecture make sure a secrets manager instance is created with IAM credentials engine configured or use [Cloud automation for Secrets Manager](https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager) and a code engine job is present with the required image.\n\nℹ️ This Terraform-based automation is part of a broader suite of IBM-maintained Infrastructure as Code (IaC) assets, each following the naming pattern \"Cloud automation for *servicename*\" and focusing on single IBM Cloud service. These single-service deployable architectures can be used on their own to streamline and automate service deployments through an [IaC approach](https://cloud.ibm.com/docs/secure-enterprise?topic=secure-enterprise-understanding-projects), or assembled together into a broader [automated IaC stack](https://cloud.ibm.com/docs/secure-enterprise?topic=secure-enterprise-config-stack) to automate the deployment of an end-to-end solution architecture.", + "offering_docs_url": "https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager-custom-credentials-engine/blob/main/README.md", + "offering_icon_url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-secrets-manager-custom-credentials-engine/main/images/secrets_manager_custom_credentials_engine.svg", + "provider_name": "IBM", + "features": [ + { + "title": "IAM service authorization", + "description": "Creates a [service authorization policy](https://cloud.ibm.com/docs/account?topic=account-serviceauth&interface=ui) between secrets manager as source and code engine project as target." + }, + { + "title": "IAM credentials secret", + "description": "Creates an [IAM credentials secret](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-iam-credentials&interface=terraform) to allow code engine job to access/update secrets in secrets manager instance." + }, + { + "title": "Custom credentials engine", + "description": "Creates a [custom credentials engine](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-custom-credentials-config&interface=terraform) configuration which acts as a back-end for custom credentials secret." + } + ], + "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 [here](https://github.com/terraform-ibm-modules/terraform-ibm-secrets-manager-custom-credentials-engine/issues). Please note this product is not supported via the IBM Cloud Support Center.", + "flavors": [ + { + "label": "Fully configurable", + "name": "fully-configurable", + "index": 1, + "install_type": "fullstack", + "working_directory": "solutions/fully-configurable", + "configuration": [ + { + "key": "ibmcloud_api_key" + }, + { + "key": "provider_visibility", + "options": [ + { + "displayname": "private", + "value": "private" + }, + { + "displayname": "public", + "value": "public" + }, + { + "displayname": "public-and-private", + "value": "public-and-private" + } + ], + "hidden": true + }, + { + "key": "existing_secrets_manager_crn", + "required": true + }, + { + "key": "prefix", + "required": true + }, + { + "key": "custom_credential_engine_name", + "required": true + }, + { + "key": "existing_code_engine_project_id", + "required": true + }, + { + "key": "existing_code_engine_job_name", + "required": true + }, + { + "key": "existing_code_engine_region", + "required": true + }, + { + "key": "secrets_manager_region", + "required": true, + "virtual": true, + "default_value": "us-south", + "description": "The region to provision a new Secrets Manager instance in.", + "options": [ + { + "displayname": "Osaka (jp-osa)", + "value": "jp-osa" + }, + { + "displayname": "Sydney (au-syd)", + "value": "au-syd" + }, + { + "displayname": "Tokyo (jp-tok)", + "value": "jp-tok" + }, + { + "displayname": "Frankfurt (eu-de)", + "value": "eu-de" + }, + { + "displayname": "London (eu-gb)", + "value": "eu-gb" + }, + { + "displayname": "Madrid (eu-es)", + "value": "eu-es" + }, + { + "displayname": "Dallas (us-south)", + "value": "us-south" + }, + { + "displayname": "Montreal (ca-mon)", + "value": "ca-mon" + }, + { + "displayname": "Toronto (ca-tor)", + "value": "ca-tor" + }, + { + "displayname": "Washington DC (us-east)", + "value": "us-east" + }, + { + "displayname": "Sao Paulo (br-sao)", + "value": "br-sao" + } + ] + }, + { + "key": "secrets_manager_service_plan", + "required": true, + "virtual": true, + "type": "string", + "options": [ + { + "displayname": "Standard", + "value": "standard" + }, + { + "displayname": "Trial", + "value": "trial" + } + ], + "default_value": "standard", + "description": "The pricing plan to use when provisioning a Secrets Manager instance. Possible values: `standard`, `trial`. You can create only one Trial instance of Secrets Manager per account. Before you can create a new Trial instance, you must delete the existing Trial instance and its reclamation. [Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-create-instance&interface=ui#upgrade-instance-standard)." + }, + { + "key": "skip_secrets_manager_code_engine_auth_policy" + }, + { + "key": "service_id_name" + }, + { + "key": "iam_credential_secret_name" + }, + { + "key": "task_timeout" + }, + { + "key": "endpoint_type", + "options": [ + { + "displayname": "private", + "value": "private" + }, + { + "displayname": "public", + "value": "public" + } + ] + }, + { + "key": "iam_credential_secret_group_id" + }, + { + "key": "iam_credential_secret_ttl" + }, + { + "key": "iam_credential_secret_auto_rotation_interval" + }, + { + "key": "iam_credential_secret_auto_rotation_unit" + }, + { + "key": "iam_credential_secret_labels" + } + ], + "iam_permissions": [ + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Administrator", + "crn:v1:bluemix:public:iam::::serviceRole:Manager" + ], + "service_name": "secrets-manager", + "notes": "Required for creating secrets manager instance as well as custom credentials engine" + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Administrator", + "crn:v1:bluemix:public:iam-identity::::serviceRole:ServiceIdCreator" + ], + "service_name": "IAM Identity Service", + "notes": "Required for creating service ID for allowing code engine job to fetch secrets from secrets manager instance" + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Administrator" + ], + "service_name": "All Identity and Access enabled services", + "notes": "Required for creating IAM authorization policy between secrets manager and code engine project. Optionally required to deploy Cloud automation for account configuration which creates foundational IBM Cloud account resources, like resource group with account settings " + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::role:Administrator" + ], + "service_name": "All Account Management services", + "notes": "[Optional] Required to deploy Cloud automation for account configuration which creates resource group and to create trusted profile for App Configuration aggregator." + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "service_name": "event-notifications", + "notes": "[Optional] Required if you are configuring an Event Notifications Instance." + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "service_name": "sysdig-monitor", + "notes": "[Optional] Required if you are consuming the Observability deployable architecture which sets up Cloud Monitoring." + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "service_name": "logs", + "notes": "[Optional] Required if you are consuming the Observability deployable architecture which sets up Cloud Logs." + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "service_name": "hs-crypto", + "notes": "[Optional] Required if you are creating/configuring keys in an existing Hyper Protect Crypto Services (HPCS) instance for encryption." + }, + { + "role_crns": [ + "crn:v1:bluemix:public:iam::::serviceRole:Manager", + "crn:v1:bluemix:public:iam::::role:Editor" + ], + "service_name": "kms", + "notes": "[Optional] Required if you are creating/configuring Key Protect instance and keys for encryption." + } + ], + "architecture": { + "descriptions": "This architecture supports creating and configuring a Secrets Manager Custom Credentials Engine.", + "features": [ + { + "title": " ", + "description": "Configured to use IBM secure-by-default standards, but can be edited to fit your use case." + } + ], + "diagrams": [ + { + "diagram": { + "caption": "Secrets Manager Custom Credentials Engine", + "url": "https://raw.githubusercontent.com/terraform-ibm-modules/terraform-ibm-secrets-manager-custom-credentials-engine/main/reference-architecture/secrets_manager_custom_credentials_engine.svg", + "type": "image/svg+xml" + }, + "description": "This architecture automates the setup of IBM Secrets Manager Custom Credentials Engine. The modular design includes the creation of a service authorization policy and IAM credentials secrets that are needed for provisioning the engine configuration." + } + ] + }, + "dependencies": [ + { + "name": "deploy-arch-ibm-secrets-manager", + "description": "Create a new Secrets Manager instance.", + "id": "6d6ebc76-7bbd-42f5-8bc7-78f4fabd5944-global", + "version": "v2.8.0", + "flavors": [ + "fully-configurable" + ], + "catalog_id": "7a4d68b4-cf8b-40cd-a3d1-f49aff526eb3", + "optional": true, + "on_by_default": true, + "input_mapping": [ + { + "dependency_input": "prefix", + "version_input": "prefix", + "reference_version": true + }, + { + "dependency_input": "region", + "version_input": "secrets_manager_region", + "reference_version": true + }, + { + "dependency_input": "service_plan", + "version_input": "secrets_manager_service_plan", + "reference_version": true + }, + { + "dependency_output": "secrets_manager_crn", + "version_input": "existing_secrets_manager_crn" + } + ] + } + ], + "dependency_version_2": true, + "terraform_version": "1.10.5" + } + ] + } + ] +} diff --git a/images/secrets_manager_custom_credentials_engine.svg b/images/secrets_manager_custom_credentials_engine.svg new file mode 100644 index 0000000..0b6e9f7 --- /dev/null +++ b/images/secrets_manager_custom_credentials_engine.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/main.tf b/main.tf index b6b879e..fd7d64e 100644 --- a/main.tf +++ b/main.tf @@ -1,13 +1,86 @@ -# -# Developer tips: -# - Below code should be replaced with the code for the root level module -# - -resource "ibm_resource_instance" "cos_instance" { - name = var.name - resource_group_id = var.resource_group_id - service = "cloud-object-storage" - plan = var.plan - location = "global" - tags = var.resource_tags +############################################################################## +# ServiceIDs and Policy +############################################################################## + +resource "ibm_iam_service_id" "sm_service_id" { + name = var.service_id_name + description = "ServiceID that can pull secrets from Secret Manager" +} + +resource "ibm_iam_service_policy" "sm_service_id_policy" { + iam_id = ibm_iam_service_id.sm_service_id.iam_id + # iam_service_id = ibm_iam_service_id.sm_service_id.id + roles = ["SecretsReader", "SecretTaskUpdater"] + + resources { + service = "secrets-manager" + resource_instance_id = var.secrets_manager_guid + } +} + +resource "time_sleep" "wait_for_service_id" { + depends_on = [ibm_iam_service_id.sm_service_id, ibm_iam_service_policy.sm_service_id_policy] + create_duration = "60s" +} + +############################################################################## +# IAM Credential Secret +############################################################################## + +module "sm_iam_credential_secret" { + depends_on = [time_sleep.wait_for_service_id] + source = "terraform-ibm-modules/iam-serviceid-apikey-secrets-manager/ibm" + version = "1.2.0" + region = var.secrets_manager_region + secrets_manager_guid = var.secrets_manager_guid + secret_group_id = var.iam_credential_secret_group_id + sm_iam_secret_ttl = var.iam_credential_secret_ttl + service_endpoints = var.endpoint_type + serviceid_id = ibm_iam_service_id.sm_service_id.id + sm_iam_secret_description = "The iam credential secret to provides SM access to code engine job" + sm_iam_secret_name = var.iam_credential_secret_name + sm_iam_secret_api_key_persistence = true # Set to true as a requirement to be used for custom credential + sm_iam_secret_auto_rotation = true # Set to true as a requirement to be used for custom credential + sm_iam_secret_auto_rotation_interval = var.iam_credential_secret_auto_rotation_interval + sm_iam_secret_auto_rotation_unit = var.iam_credential_secret_auto_rotation_unit + labels = var.iam_credential_secret_labels +} + +############################################################################## +# Authorization Policy between Secrets Manager and Code Engine project +############################################################################## + +resource "ibm_iam_authorization_policy" "sm_ce_policy" { + count = var.skip_secrets_manager_code_engine_auth_policy ? 0 : 1 + source_service_name = "secrets-manager" + source_resource_instance_id = var.secrets_manager_guid + target_service_name = "codeengine" + target_resource_instance_id = var.code_engine_project_id + roles = ["Viewer", "Writer"] +} + +# workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478 +resource "time_sleep" "wait_for_sm_ce_authorization_policy" { + count = var.skip_secrets_manager_code_engine_auth_policy ? 0 : 1 + depends_on = [ibm_iam_authorization_policy.sm_ce_policy] + create_duration = "30s" +} + +############################################################################## +# Secrets Manager Custom Credentials Engine Module +############################################################################## + +resource "ibm_sm_custom_credentials_configuration" "custom_credentials_configuration" { + depends_on = [time_sleep.wait_for_sm_ce_authorization_policy] + instance_id = var.secrets_manager_guid + region = var.secrets_manager_region + name = var.custom_credential_engine_name + endpoint_type = var.endpoint_type + api_key_ref = module.sm_iam_credential_secret.secret_id + code_engine { + project_id = var.code_engine_project_id + job_name = var.code_engine_job_name + region = var.code_engine_region + } + task_timeout = var.task_timeout } diff --git a/outputs.tf b/outputs.tf index 0286200..f0797a1 100644 --- a/outputs.tf +++ b/outputs.tf @@ -2,29 +2,23 @@ # Outputs ######################################################################################################################## -# -# Developer tips: -# - Below are some good practise sample outputs -# - They should be updated for outputs applicable to the module being added -# - Use variable validation when possible -# - -output "account_id" { - description = "An alpha-numeric value identifying the account ID." - value = ibm_resource_instance.cos_instance.account_id +output "custom_config_engine_id" { + description = "The unique identifier of the engine created." + value = ibm_sm_custom_credentials_configuration.custom_credentials_configuration.id } -output "guid" { - description = "The GUID of the resource instance." - value = ibm_resource_instance.cos_instance.guid +output "custom_config_engine_name" { + description = "The name of the engine created." + value = ibm_sm_custom_credentials_configuration.custom_credentials_configuration.name } -output "id" { - description = "The unique identifier of the resource instance." - value = ibm_resource_instance.cos_instance.id +output "code_engine_key_ref" { + description = "The IAM API key used by the credentials system to access the secrets manager instance." + sensitive = true + value = ibm_sm_custom_credentials_configuration.custom_credentials_configuration.code_engine_key_ref } -output "crn" { - description = "The CRN of the resource instance." - value = ibm_resource_instance.cos_instance.crn +output "secrets_manager_custom_credentials_configuration_schema" { + description = "The schema that defines the format of the input and output parameters." + value = ibm_sm_custom_credentials_configuration.custom_credentials_configuration.schema } diff --git a/reference-architecture/secrets_manager_custom_credentials_engine.svg b/reference-architecture/secrets_manager_custom_credentials_engine.svg new file mode 100644 index 0000000..1ba9104 --- /dev/null +++ b/reference-architecture/secrets_manager_custom_credentials_engine.svg @@ -0,0 +1,4 @@ + + + +
Cloud
    Region
Resource Group
Secrets Manager
Secrets Manager Custom Credentials Engine
\ No newline at end of file diff --git a/solutions/fully-configurable/README.md b/solutions/fully-configurable/README.md new file mode 100644 index 0000000..e78ef13 --- /dev/null +++ b/solutions/fully-configurable/README.md @@ -0,0 +1,3 @@ +# Cloud automation for Secrets Manager Custom Credential Engine (Fully configurable) + +:exclamation: **Important:** This solution is not intended to be called by other modules because it contains a provider configuration and is not compatible with the `for_each`, `count`, and `depends_on` arguments. For more information, see [Providers Within Modules](https://developer.hashicorp.com/terraform/language/modules/develop/providers). diff --git a/solutions/fully-configurable/catalogValidationValues.json.template b/solutions/fully-configurable/catalogValidationValues.json.template new file mode 100644 index 0000000..abec622 --- /dev/null +++ b/solutions/fully-configurable/catalogValidationValues.json.template @@ -0,0 +1,8 @@ +{ + "ibmcloud_api_key": $VALIDATION_APIKEY, + "existing_secrets_manager_crn": $SM_CRN, + "prefix": $PREFIX, + "custom_credential_engine_name": "test-engine", + "service_id_name": "test-service-id", + "iam_credential_secret_name": "test-credential-secret" +} diff --git a/solutions/fully-configurable/main.tf b/solutions/fully-configurable/main.tf new file mode 100644 index 0000000..fc02962 --- /dev/null +++ b/solutions/fully-configurable/main.tf @@ -0,0 +1,38 @@ +######################################################################################################################## +# Custom Credential Engine +######################################################################################################################## + +locals { + prefix = var.prefix != null ? (trimspace(var.prefix) != "" ? "${trimspace(var.prefix)}-" : "") : "" +} + +module "crn_parser" { + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.2.0" + crn = var.existing_secrets_manager_crn +} + +locals { + existing_secrets_manager_guid = module.crn_parser.service_instance + existing_secrets_manager_region = module.crn_parser.region +} + +module "custom_credential_engine" { + source = "../.." + secrets_manager_guid = local.existing_secrets_manager_guid + secrets_manager_region = local.existing_secrets_manager_region + custom_credential_engine_name = "${local.prefix}${var.custom_credential_engine_name}" + skip_secrets_manager_code_engine_auth_policy = var.skip_secrets_manager_code_engine_auth_policy + endpoint_type = var.endpoint_type + code_engine_project_id = var.existing_code_engine_project_id + code_engine_job_name = var.existing_code_engine_job_name + code_engine_region = var.existing_code_engine_region + task_timeout = var.task_timeout + service_id_name = "${local.prefix}${var.service_id_name}" + iam_credential_secret_name = "${local.prefix}${var.iam_credential_secret_name}" + iam_credential_secret_group_id = var.iam_credential_secret_group_id + iam_credential_secret_ttl = var.iam_credential_secret_ttl + iam_credential_secret_auto_rotation_interval = var.iam_credential_secret_auto_rotation_interval + iam_credential_secret_auto_rotation_unit = var.iam_credential_secret_auto_rotation_unit + iam_credential_secret_labels = var.iam_credential_secret_labels +} diff --git a/solutions/fully-configurable/outputs.tf b/solutions/fully-configurable/outputs.tf new file mode 100644 index 0000000..c09638a --- /dev/null +++ b/solutions/fully-configurable/outputs.tf @@ -0,0 +1,20 @@ +output "custom_config_engine_id" { + description = "The unique identifier of the engine created." + value = module.custom_credential_engine.custom_config_engine_id +} + +output "custom_config_engine_name" { + description = "The name of the engine created." + value = module.custom_credential_engine.custom_config_engine_name +} + +output "code_engine_key_ref" { + description = "The IAM API key used by the credentials system to access the secrets manager instance." + sensitive = true + value = module.custom_credential_engine.code_engine_key_ref +} + +output "secrets_manager_custom_credentials_configuration_schema" { + description = "The schema that defines the format of the input and output parameters." + value = module.custom_credential_engine.secrets_manager_custom_credentials_configuration_schema +} diff --git a/solutions/fully-configurable/provider.tf b/solutions/fully-configurable/provider.tf new file mode 100644 index 0000000..f2902a0 --- /dev/null +++ b/solutions/fully-configurable/provider.tf @@ -0,0 +1,6 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = local.existing_secrets_manager_region + visibility = var.provider_visibility + private_endpoint_type = (var.provider_visibility == "private" && local.existing_secrets_manager_region == "ca-mon") ? "vpe" : null +} diff --git a/solutions/fully-configurable/variables.tf b/solutions/fully-configurable/variables.tf new file mode 100644 index 0000000..7f34a32 --- /dev/null +++ b/solutions/fully-configurable/variables.tf @@ -0,0 +1,152 @@ +######################################################################################################################## +# common variables +######################################################################################################################## + +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API key used to provision resources." + 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" + nullable = false + + validation { + condition = contains(["public", "private", "public-and-private"], var.provider_visibility) + error_message = "Invalid value for 'provider_visibility'. Allowed values are 'public', 'private', or 'public-and-private'." + } +} + +variable "prefix" { + type = string + nullable = true + description = "The prefix to be added to all resources created by this solution. To skip using a prefix, set this value to null or an empty string. The prefix must begin with a lowercase letter and may contain only lowercase letters, digits, and hyphens '-'. It should not exceed 16 characters, must not end with a hyphen('-'), and can not contain consecutive hyphens ('--'). Example: prod-us-south. [Learn more](https://terraform-ibm-modules.github.io/documentation/#/prefix.md)." + + validation { + # - null and empty string is allowed + # - Must not contain consecutive hyphens (--): length(regexall("--", var.prefix)) == 0 + # - Starts with a lowercase letter: [a-z] + # - Contains only lowercase letters (a–z), digits (0–9), and hyphens (-) and must not exceed 16 characters in length: [a-z0-9-]{0,14} + # - Must not end with a hyphen (-): [a-z0-9] + condition = (var.prefix == null || var.prefix == "" ? true : + alltrue([ + can(regex("^[a-z][-a-z0-9]{0,14}[a-z0-9]$", var.prefix)), + length(regexall("--", var.prefix)) == 0 + ]) + ) + error_message = "Prefix must begin with a lowercase letter and may contain only lowercase letters, digits, and hyphens '-'. It should not exceed 16 characters, must not end with a hyphen('-'), and cannot contain consecutive hyphens ('--')." + } +} + +variable "existing_secrets_manager_crn" { + type = string + description = "The CRN of secrets manager instance to create the secret engine in." + nullable = false +} + +######################################################################################################################## +# custom credential engine variables +######################################################################################################################## + +variable "custom_credential_engine_name" { + type = string + description = "The name of the custom credentials engine to be created." +} + +variable "skip_secrets_manager_code_engine_auth_policy" { + type = bool + description = "Whether to skip the creation of the IAM authorization policies required between the Code engine project and Secrets Manager instance. If set to false, policies will be created that grants the Secrets Manager instance 'Viewer' and 'Writer' access to the Code engine project." + default = false +} + +variable "endpoint_type" { + type = string + description = "The endpoint type to communicate with the provided secrets manager instance. Possible values are `public` or `private`" + default = "public" + + validation { + condition = contains(["public", "private"], var.endpoint_type) + error_message = "The specified endpoint_type is not a valid selection!" + } +} + +variable "existing_code_engine_project_id" { + type = string + description = "The Project ID of the code engine project used by the custom credentials configuration. [Learn more](https://cloud.ibm.com/docs/codeengine?topic=codeengine-manage-project)" +} + +variable "existing_code_engine_job_name" { + type = string + description = "The code engine job name used by this custom credentials configuration. [Learn more](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-engine-custom-ce-job&interface=ui)" +} + +variable "existing_code_engine_region" { + type = string + description = "The region of the code engine project." +} + +variable "task_timeout" { + type = string + description = "The maximum allowed time for a code engine job to be completed." + default = "5m" + + validation { + condition = can(regex("^\\d+[smh]$", var.task_timeout)) + error_message = "task_timeout must be a string with a number followed by 's', 'm', or 'h' (e.g., '30s', '3m', '1h')." + } +} + +variable "service_id_name" { + type = string + description = "The name of the service ID to be created to allow code engine job to pull secrets from Secrets Manager." + default = "custom-cred-engine-service-id" +} + +variable "iam_credential_secret_name" { + type = string + description = "The name of the IAM credential secret to allow code engine job to pull secrets from Secrets Manager." + default = "custom-cred-engine-iam-secret" +} + +variable "iam_credential_secret_group_id" { + type = string + description = "Secret Group ID of secret where IAM Secret will be added to, leave default (null) to add in default secret-group." + default = null #tfsec:ignore:GEN001 +} + +variable "iam_credential_secret_ttl" { + type = string + description = "The validity / lease duration of ServiceID API key. Accepted values and formats are: SECONDS, Xm or Xh (where X is the number of minutes or hours appended to m or h respectively)." + default = "7776000" #tfsec:ignore:general-secrets-no-plaintext-exposure Default set to 90days +} + +variable "iam_credential_secret_auto_rotation_interval" { + type = string + description = "The rotation interval for the rotation policy." + default = 60 + + validation { + condition = var.iam_credential_secret_auto_rotation_interval > 0 + error_message = "Value for `iam_credential_secret_auto_rotation_intervals` must be greater than 0 when auto-rotation is enabled." + } +} + +variable "iam_credential_secret_auto_rotation_unit" { + type = string + description = "The unit of time for rotation policy. Acceptable values are `day` or `month`." + default = "day" #tfsec:ignore:general-secrets-no-plaintext-exposure + + validation { + condition = contains(["day", "month"], var.iam_credential_secret_auto_rotation_unit) + error_message = "Value for `iam_credential_secret_auto_rotation_unit` must be either 'day' or 'month' when auto-rotation is enabled." + } +} + +variable "iam_credential_secret_labels" { + type = list(string) + description = "Optional list of up to 30 labels to be created on the secret. Labels can be used to search for secrets in the Secrets Manager instance." + default = [] +} diff --git a/solutions/fully-configurable/version.tf b/solutions/fully-configurable/version.tf new file mode 100644 index 0000000..a7da027 --- /dev/null +++ b/solutions/fully-configurable/version.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= v1.9.0" + required_providers { + ibm = { + source = "IBM-Cloud/ibm" + version = "1.79.2" + } + } +} diff --git a/tests/existing-resources/README.md b/tests/existing-resources/README.md new file mode 100644 index 0000000..4bb3621 --- /dev/null +++ b/tests/existing-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/existing-resources/main.tf b/tests/existing-resources/main.tf new file mode 100644 index 0000000..f4626a7 --- /dev/null +++ b/tests/existing-resources/main.tf @@ -0,0 +1,34 @@ +############################################################################## +# Resource Group +############################################################################## + +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.3.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 +} + +############################################################################## +# Code Engine +############################################################################## + +module "code_engine" { + source = "terraform-ibm-modules/code-engine/ibm" + version = "4.5.7" + resource_group_id = module.resource_group.resource_group_id + project_name = "${var.prefix}-project" + jobs = { + "${var.prefix}-job" = { + image_reference = "icr.io/codeengine/helloworld" + run_env_variables = [{ + type = "literal" + name = "SMOUT_TEST" # The code engine job must have an environment variable of type SMOUT_XXX to be added to custom engine configuration + value = "type:string, required:true" # The code engine job env variable must have a value containing the required:true attribute + }] + run_arguments = ["echo \"hello world\""] + run_commands = ["/bin/sh"] + } + } +} diff --git a/tests/existing-resources/outputs.tf b/tests/existing-resources/outputs.tf new file mode 100644 index 0000000..2ba5db5 --- /dev/null +++ b/tests/existing-resources/outputs.tf @@ -0,0 +1,33 @@ +######################################################################################################################## +# Outputs +######################################################################################################################## + +output "resource_group_id" { + description = "The id of the resource group where resources are created." + value = module.resource_group.resource_group_id +} + +output "resource_group_name" { + description = "The name of the resource group where resources are created." + value = module.resource_group.resource_group_name +} + +output "code_engine_project_id" { + value = module.code_engine.project_id + description = "Code Engine Project ID." +} + +output "code_engine_job_name" { + value = module.code_engine.job["${var.prefix}-job"].name + description = "Code Engine Job Name" +} + +output "prefix" { + description = "Prefix to append to all resources created by this example." + value = var.prefix +} + +output "region" { + value = var.region + description = "region." +} 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..77b2fdb --- /dev/null +++ b/tests/existing-resources/variables.tf @@ -0,0 +1,26 @@ +############################################################################## +# Input variables +############################################################################## + +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API Key." + sensitive = true +} + +variable "region" { + type = string + description = "Region to provision all resources created by this example." + default = "us-south" +} + +variable "prefix" { + type = string + description = "Prefix to append to all resources created by this example." +} + +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 +} diff --git a/tests/existing-resources/version.tf b/tests/existing-resources/version.tf new file mode 100644 index 0000000..a7da027 --- /dev/null +++ b/tests/existing-resources/version.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= v1.9.0" + required_providers { + ibm = { + source = "IBM-Cloud/ibm" + version = "1.79.2" + } + } +} diff --git a/tests/go.mod b/tests/go.mod index 7c69e50..5a95b04 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -2,11 +2,11 @@ module github.com/terraform-ibm-modules/terraform-ibm-secrets-manager-custom-cre go 1.24.0 -toolchain go1.24.5 +toolchain go1.25.0 require ( - github.com/stretchr/testify v1.10.0 - github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper v1.58.5 + github.com/stretchr/testify v1.11.0 + github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper v1.59.1 ) require ( @@ -15,7 +15,7 @@ require ( github.com/IBM-Cloud/power-go-client v1.12.0 // indirect github.com/IBM/cloud-databases-go-sdk v0.8.0 // indirect github.com/IBM/go-sdk-core/v5 v5.21.0 // indirect - github.com/IBM/platform-services-go-sdk v0.85.1 // indirect + github.com/IBM/platform-services-go-sdk v0.86.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/vpc-go-sdk v1.0.2 // indirect @@ -37,7 +37,7 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.23.0 // indirect - github.com/go-openapi/errors v0.22.1 // indirect + github.com/go-openapi/errors v0.22.2 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/loads v0.22.0 // indirect @@ -61,7 +61,7 @@ require ( github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/hcl/v2 v2.22.0 // indirect - github.com/hashicorp/terraform-json v0.25.0 // indirect + github.com/hashicorp/terraform-json v0.26.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jinzhu/copier v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -84,19 +84,19 @@ require ( github.com/tmccombs/hcl2json v0.6.4 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - github.com/zclconf/go-cty v1.16.2 // indirect + github.com/zclconf/go-cty v1.16.3 // indirect go.mongodb.org/mongo-driver v1.17.3 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/otel v1.35.0 // indirect go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/mod v0.25.0 // indirect - golang.org/x/net v0.41.0 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/mod v0.26.0 // indirect + golang.org/x/net v0.42.0 // indirect golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/text v0.27.0 // indirect - golang.org/x/tools v0.34.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/tools v0.35.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/tests/go.sum b/tests/go.sum index 53204c9..267a583 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -9,8 +9,8 @@ github.com/IBM/cloud-databases-go-sdk v0.8.0/go.mod h1:JYucI1PdwqbAd8XGdDAchxzxR github.com/IBM/go-sdk-core/v5 v5.9.2/go.mod h1:YlOwV9LeuclmT/qi/LAK2AsobbAP42veV0j68/rlZsE= github.com/IBM/go-sdk-core/v5 v5.21.0 h1:DUnYhvC4SoC8T84rx5omnhY3+xcQg/Whyoa3mDPIMkk= github.com/IBM/go-sdk-core/v5 v5.21.0/go.mod h1:Q3BYO6iDA2zweQPDGbNTtqft5tDcEpm6RTuqMlPcvbw= -github.com/IBM/platform-services-go-sdk v0.85.1 h1:lrBEeGaIajhSPMB6cPVAx53XTtVGrKOeA36gIXh2FYI= -github.com/IBM/platform-services-go-sdk v0.85.1/go.mod h1:aGD045m6I8pfcB77wft8w2cHqWOJjcM3YSSV55BX0Js= +github.com/IBM/platform-services-go-sdk v0.86.0 h1:Uqne0Z/P9e++WfRt1aN8DD55kyo/T15+7EipYktRIDQ= +github.com/IBM/platform-services-go-sdk v0.86.0/go.mod h1:aGD045m6I8pfcB77wft8w2cHqWOJjcM3YSSV55BX0Js= github.com/IBM/project-go-sdk v0.3.6 h1:DRiANKnAePevFsIKSvR89SUaMa2xsd7YKK71Ka1eqKI= 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= @@ -84,8 +84,8 @@ github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC0 github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= -github.com/go-openapi/errors v0.22.1 h1:kslMRRnK7NCb/CvR1q1VWuEQCEIsBGn5GgKD9e+HYhU= -github.com/go-openapi/errors v0.22.1/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0= +github.com/go-openapi/errors v0.22.2 h1:rdxhzcBUazEcGccKqbY1Y7NS8FDcMyIRr0934jrYnZg= +github.com/go-openapi/errors v0.22.2/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0= github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -171,8 +171,8 @@ github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKe github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hcl/v2 v2.22.0 h1:hkZ3nCtqeJsDhPRFz5EA9iwcG1hNWGePOTw6oyul12M= github.com/hashicorp/hcl/v2 v2.22.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= -github.com/hashicorp/terraform-json v0.25.0 h1:rmNqc/CIfcWawGiwXmRuiXJKEiJu1ntGoxseG1hLhoQ= -github.com/hashicorp/terraform-json v0.25.0/go.mod h1:sMKS8fiRDX4rVlR6EJUMudg1WcanxCMoWwTLkgZP/vc= +github.com/hashicorp/terraform-json v0.26.0 h1:+BnJavhRH+oyNWPnfzrfQwVWCZBFMvjdiH2Vi38Udz4= +github.com/hashicorp/terraform-json v0.26.0/go.mod h1:eyWCeC3nrZamyrKLFnrvwpc3LQPIJsx8hWHQ/nu2/v4= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -293,10 +293,10 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper v1.58.5 h1:RxWdD+20e5Yb9K8W6iIjgDG1KDMPLnXHw4SlPhtHdf8= -github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper v1.58.5/go.mod h1:2d7vW9ehuOaVZl38OO/aXPPO42EijYfgMe29HPJbI+I= +github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8= +github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper v1.59.1 h1:9/uYvUFFLIH91F16AiJqP/LZeGi4t2CYtc8iz3bBXdQ= +github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper v1.59.1/go.mod h1:kdhZ+FeS71D+tB0E2Sh1ISD3zQ+RThPX5SyFqduo7G8= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmccombs/hcl2json v0.6.4 h1:/FWnzS9JCuyZ4MNwrG4vMrFrzRgsWEOVi+1AyYUVLGw= github.com/tmccombs/hcl2json v0.6.4/go.mod h1:+ppKlIW3H5nsAsZddXPy2iMyvld3SHxyjswOZhavRDk= @@ -313,8 +313,8 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7Jul github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zclconf/go-cty v1.16.2 h1:LAJSwc3v81IRBZyUVQDUdZ7hs3SYs9jv0eZJDWHD/70= -github.com/zclconf/go-cty v1.16.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk= +github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= @@ -343,8 +343,8 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -355,8 +355,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -384,8 +384,8 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -434,8 +434,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -450,8 +450,8 @@ golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -468,8 +468,8 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -485,8 +485,8 @@ golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= -golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/tests/other_test.go b/tests/other_test.go index 88d360d..026f170 100644 --- a/tests/other_test.go +++ b/tests/other_test.go @@ -1,2 +1,59 @@ // Tests in this file are NOT run in the PR pipeline. They are run in the continuous testing pipeline along with the ones in pr_test.go package test + +import ( + "math/rand" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper" +) + +var validRegions = []string{ + "jp-osa", + "au-syd", + "jp-tok", + "eu-de", + "eu-gb", + "eu-es", + "us-south", + "ca-mon", + "ca-tor", + "us-east", + "br-sao", +} + +func setupCompleteOptions(t *testing.T, prefix string, dir string) *testhelper.TestOptions { + options := testhelper.TestOptionsDefault(&testhelper.TestOptions{ + Testing: t, + TerraformDir: dir, + Region: validRegions[rand.Intn(len(validRegions))], + Prefix: prefix, + }) + + // need to ignore because of a provider issue: https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4719 + options.IgnoreUpdates = testhelper.Exemptions{ + List: []string{ + "module.code_engine.module.job[\"" + options.Prefix + "-job\"].ibm_code_engine_job.ce_job", + }, + } + + options.TerraformVars = map[string]interface{}{ + "prefix": options.Prefix, + "region": options.Region, + "existing_sm_guid": permanentResources["secretsManagerGuid"], + "existing_sm_region": permanentResources["secretsManagerRegion"], + } + + return options +} + +func TestRunAdvancedExample(t *testing.T) { + t.Parallel() + + options := setupCompleteOptions(t, "custom-engine", completeExampleDir) + + output, err := options.RunTestConsistency() + assert.Nil(t, err, "This should not have errored") + assert.NotNil(t, output, "Expected some output") +} diff --git a/tests/pr_test.go b/tests/pr_test.go index 8867ed0..bbe5905 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -2,59 +2,189 @@ package test import ( + "fmt" + "log" + "os" + "strings" "testing" + "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" ) -// Use existing resource group -const resourceGroup = "geretain-test-resources" +const completeExampleDir = "examples/complete" +const yamlLocation = "../common-dev-assets/common-go-assets/common-permanent-resources.yaml" +const fullyConfigFlavorDir = "solutions/fully-configurable" -// Ensure every example directory has a corresponding test -const advancedExampleDir = "examples/advanced" -const basicExampleDir = "examples/basic" +var permanentResources map[string]interface{} -func setupOptions(t *testing.T, prefix string, dir string) *testhelper.TestOptions { - options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ - Testing: t, - TerraformDir: dir, - Prefix: prefix, - ResourceGroup: resourceGroup, - }) - return options +// TestMain will be run before any parallel tests, used to read data from yaml for use with tests +func TestMain(m *testing.M) { + + var err error + permanentResources, err = common.LoadMapFromYaml(yamlLocation) + if err != nil { + log.Fatal(err) + } + + os.Exit(m.Run()) } -// Consistency test for the basic example -func TestRunBasicExample(t *testing.T) { - t.Parallel() +func provisionPreReq(t *testing.T, p string) (string, *terraform.Options, error) { + // ------------------------------------------------------------------------------------ + // Provision existing resources first + // ------------------------------------------------------------------------------------ + prefix := fmt.Sprintf("%s-%s", p, strings.ToLower(random.UniqueId())) + realTerraformDir := "./existing-resources" + tempTerraformDir, _ := files.CopyTerraformFolderToTemp(realTerraformDir, fmt.Sprintf(prefix+"-%s", strings.ToLower(random.UniqueId()))) - options := setupOptions(t, "mod-template-basic", basicExampleDir) + // 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") - output, err := options.RunTestConsistency() - assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") + logger.Log(t, "Tempdir: ", tempTerraformDir) + existingTerraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ + TerraformDir: tempTerraformDir, + Vars: map[string]interface{}{ + "prefix": prefix, + }, + // 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, + }) + + terraform.WorkspaceSelectOrNew(t, existingTerraformOptions, prefix) + _, existErr := terraform.InitAndApplyE(t, existingTerraformOptions) + if existErr != nil { + // assert.True(t, existErr == nil, "Init and Apply of temp existing resource failed") + return "", nil, existErr + } + return prefix, existingTerraformOptions, nil } -func TestRunAdvancedExample(t *testing.T) { +func TestRunSolutionsFullyConfigurableSchematics(t *testing.T) { t.Parallel() - options := setupOptions(t, "mod-template-adv", advancedExampleDir) + prefix, existingTerraformOptions, existErr := provisionPreReq(t, "cus-eng") + + if existErr != nil { + assert.True(t, existErr == nil, "Init and Apply of temp existing resource failed") + } else { + // ------------------------------------------------------------------------------------ + // Deploy DA + // ------------------------------------------------------------------------------------ + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Prefix: prefix, + TarIncludePatterns: []string{ + "*.tf", + fullyConfigFlavorDir + "/*.tf", + }, + TemplateFolder: fullyConfigFlavorDir, + Tags: []string{"custom-config-test"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 60, + }) - output, err := options.RunTestConsistency() - assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") + options.TerraformVars = []testschematic.TestSchematicTerraformVar{ + {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, + {Name: "prefix", Value: terraform.Output(t, existingTerraformOptions, "prefix"), DataType: "string"}, + {Name: "existing_secrets_manager_crn", Value: permanentResources["secretsManagerCRN"], DataType: "string"}, + {Name: "custom_credential_engine_name", Value: "test-engine", DataType: "string"}, + {Name: "existing_code_engine_project_id", Value: terraform.Output(t, existingTerraformOptions, "code_engine_project_id"), DataType: "string"}, + {Name: "existing_code_engine_job_name", Value: terraform.Output(t, existingTerraformOptions, "code_engine_job_name"), DataType: "string"}, + {Name: "existing_code_engine_region", Value: terraform.Output(t, existingTerraformOptions, "region"), DataType: "string"}, + {Name: "service_id_name", Value: "test-service-id", DataType: "string"}, + {Name: "iam_credential_secret_name", Value: "test-cred-secret", DataType: "string"}, + } + + // need to ignore because of a provider issue: https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4719 + options.IgnoreUpdates = testhelper.Exemptions{ + List: []string{ + "module.code_engine.module.job[\"" + options.Prefix + "-job\"].ibm_code_engine_job.ce_job", + }, + } + 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)") + } } -// Upgrade test (using advanced example) -func TestRunUpgradeExample(t *testing.T) { +func TestRunSolutionsFullyConfigurableUpgradeSchematics(t *testing.T) { t.Parallel() - options := setupOptions(t, "mod-template-adv-upg", advancedExampleDir) + prefix, existingTerraformOptions, existErr := provisionPreReq(t, "cus-upg") - output, err := options.RunTestUpgrade() - if !options.UpgradeTestSkipped { + if existErr != nil { + assert.True(t, existErr == nil, "Init and Apply of temp existing resource failed") + } else { + // ------------------------------------------------------------------------------------ + // Deploy DA + // ------------------------------------------------------------------------------------ + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + Prefix: prefix, + TarIncludePatterns: []string{ + "*.tf", + fullyConfigFlavorDir + "/*.tf", + }, + TemplateFolder: fullyConfigFlavorDir, + Tags: []string{"custom-config-test"}, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 60, + }) + + options.TerraformVars = []testschematic.TestSchematicTerraformVar{ + {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, + {Name: "prefix", Value: terraform.Output(t, existingTerraformOptions, "prefix"), DataType: "string"}, + {Name: "existing_secrets_manager_crn", Value: permanentResources["secretsManagerCRN"], DataType: "string"}, + {Name: "custom_credential_engine_name", Value: "test-engine", DataType: "string"}, + {Name: "existing_code_engine_project_id", Value: terraform.Output(t, existingTerraformOptions, "code_engine_project_id"), DataType: "string"}, + {Name: "existing_code_engine_job_name", Value: terraform.Output(t, existingTerraformOptions, "code_engine_job_name"), DataType: "string"}, + {Name: "existing_code_engine_region", Value: terraform.Output(t, existingTerraformOptions, "region"), DataType: "string"}, + {Name: "service_id_name", Value: "test-service-id", DataType: "string"}, + {Name: "iam_credential_secret_name", Value: "test-cred-secret", DataType: "string"}, + } + + // need to ignore because of a provider issue: https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4719 + options.IgnoreUpdates = testhelper.Exemptions{ + List: []string{ + "module.code_engine.module.job[\"" + options.Prefix + "-job\"].ibm_code_engine_job.ce_job", + }, + } + err := options.RunSchematicUpgradeTest() assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") + } + + // Check if "DO_NOT_DESTROY_ON_FAILURE" is set + envVal, _ := os.LookupEnv("DO_NOT_DESTROY_ON_FAILURE") + // Destroy the temporary existing resources if required + if t.Failed() && strings.ToLower(envVal) == "true" { + fmt.Println("Terratest failed. Debug the test and delete resources manually.") + } else { + logger.Log(t, "START: Destroy (prereq resources)") + terraform.Destroy(t, existingTerraformOptions) + terraform.WorkspaceDelete(t, existingTerraformOptions, prefix) + logger.Log(t, "END: Destroy (prereq resources)") } } diff --git a/tests/scripts/post-validation-destroy-ce.sh b/tests/scripts/post-validation-destroy-ce.sh new file mode 100755 index 0000000..4e2aa1f --- /dev/null +++ b/tests/scripts/post-validation-destroy-ce.sh @@ -0,0 +1,19 @@ +#! /bin/bash + +######################################################################################################################## +## This script is used by the catalog pipeline to destroy the Code Engine, which was provisioned as a ## +## prerequisite for the fully-configurable custom engine that is published to the catalog ## +######################################################################################################################## + +set -e + +TERRAFORM_SOURCE_DIR="tests/existing-resources" +TF_VARS_FILE="terraform.tfvars" + +( + cd ${TERRAFORM_SOURCE_DIR} + echo "Destroying prerequisite Code Engine .." + 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-ce.sh b/tests/scripts/pre-validation-deploy-ce.sh new file mode 100755 index 0000000..d336574 --- /dev/null +++ b/tests/scripts/pre-validation-deploy-ce.sh @@ -0,0 +1,48 @@ +#! /bin/bash + +############################################################################################################ +## This script is used by the catalog pipeline to deploy the Code Engine +## which are the prerequisites for the fully-configurable custom credentials engine +############################################################################################################ + +set -e + +DA_DIR="solutions/fully-configurable" +TERRAFORM_SOURCE_DIR="tests/existing-resources" +JSON_FILE="${DA_DIR}/catalogValidationValues.json" +REGION="us-south" +TF_VARS_FILE="terraform.tfvars" + +( + cwd=$(pwd) + cd ${TERRAFORM_SOURCE_DIR} + echo "Provisioning prerequisite code engine.." + terraform init || exit 1 + # $VALIDATION_APIKEY is available in the catalog runtime + { + echo "ibmcloud_api_key=\"${VALIDATION_APIKEY}\"" + echo "region=\"${REGION}\"" + echo "prefix=\"cus-eng-$(openssl rand -hex 2)\"" + } >>${TF_VARS_FILE} + terraform apply -input=false -auto-approve -var-file=${TF_VARS_FILE} || exit 1 + + existing_code_engine_project_id="existing_code_engine_project_id" + existing_code_engine_project_id_value=$(terraform output -state=terraform.tfstate -raw code_engine_project_id) + existing_code_engine_job_name="existing_code_engine_job_name" + existing_code_engine_job_name_value=$(terraform output -state=terraform.tfstate -raw code_engine_job_name) + existing_code_engine_region="existing_code_engine_region" + existing_code_engine_region_value=$(terraform output -state=terraform.tfstate -raw region) + + echo "Appending '${existing_code_engine_project_id}', '${existing_code_engine_job_name}' and '${existing_code_engine_region}' input variable values to ${JSON_FILE}.." + + cd "${cwd}" + jq -r --arg existing_code_engine_project_id "${existing_code_engine_project_id}" \ + --arg existing_code_engine_project_id_value "${existing_code_engine_project_id_value}" \ + --arg existing_code_engine_job_name "${existing_code_engine_job_name}" \ + --arg existing_code_engine_job_name_value "${existing_code_engine_job_name_value}" \ + --arg existing_code_engine_region "${existing_code_engine_region}" \ + --arg existing_code_engine_region_value "${existing_code_engine_region_value}" \ + '. + {($existing_code_engine_project_id): $existing_code_engine_project_id_value, ($existing_code_engine_job_name): $existing_code_engine_job_name_value, ($existing_code_engine_region): $existing_code_engine_region_value}' "${JSON_FILE}" >tmpfile && mv tmpfile "${JSON_FILE}" || exit 1 + + echo "Pre-validation complete successfully" +) diff --git a/variables.tf b/variables.tf index a9d9899..031c0d5 100644 --- a/variables.tf +++ b/variables.tf @@ -2,35 +2,109 @@ # Input Variables ######################################################################################################################## -# -# Developer tips: -# - Below are some common module input variables -# - They should be updated for input variables applicable to the module being added -# - Use variable validation when possible -# +variable "secrets_manager_guid" { + type = string + description = "GUID of secrets manager instance to create the secret engine in." +} + +variable "secrets_manager_region" { + type = string + description = "The region of the secrets manager instance." +} + +variable "custom_credential_engine_name" { + type = string + description = "The name of the custom credentials engine to be created." +} + +variable "skip_secrets_manager_code_engine_auth_policy" { + type = bool + description = "Whether to skip the creation of the IAM authorization policies required between the Code engine project and Secrets Manager instance. If set to false, policies will be created that grants the Secrets Manager instance 'Viewer' and 'Writer' access to the Code engine project." + default = false +} + +variable "endpoint_type" { + type = string + description = "The endpoint type to communicate with the provided secrets manager instance. Possible values are `public` or `private`." + default = "public" + validation { + condition = contains(["public", "private"], var.endpoint_type) + error_message = "The specified endpoint_type is not a valid selection!" + } +} -variable "name" { +variable "code_engine_project_id" { type = string - description = "A descriptive name used to identify the resource instance." + description = "The Project ID of the code engine project used by the custom credentials configuration." } -variable "plan" { +variable "code_engine_job_name" { type = string - description = "The name of the plan type supported by service." - default = "standard" + description = "The code engine job name used by this custom credentials configuration." +} + +variable "code_engine_region" { + type = string + description = "The region of the code engine project." +} + +variable "task_timeout" { + type = string + description = "The maximum allowed time for a code engine job to be completed." + default = "5m" + validation { - condition = contains(["standard", "cos-one-rate-plan"], var.plan) - error_message = "The specified pricing plan is not available. The following plans are supported: 'standard', 'cos-one-rate-plan'" + condition = can(regex("^\\d+[smh]$", var.task_timeout)) + error_message = "task_timeout must be a string with a number followed by 's', 'm', or 'h' (e.g., '30s', '3m', '1h')." } } -variable "resource_group_id" { +variable "service_id_name" { type = string - description = "The ID of the resource group where you want to create the service." + description = "The name of the service ID to be created to allow code engine job to pull secrets from Secrets Manager." +} + +variable "iam_credential_secret_name" { + type = string + description = "The name of the IAM credential secret to allow code engine job to pull secrets from Secrets Manager." +} + +variable "iam_credential_secret_group_id" { + type = string + description = "Secret Group ID of secret where IAM Secret will be added to, leave default (null) to add in the default secret group." + default = null #tfsec:ignore:GEN001 +} + +variable "iam_credential_secret_ttl" { + type = string + description = "Specify validity / lease duration of ServiceID API key. Accepted values and formats are: SECONDS, Xm or Xh (where X is the number of minutes or hours appended to m or h respectively)." + default = "7776000" #tfsec:ignore:general-secrets-no-plaintext-exposure Default set to 90days +} + +variable "iam_credential_secret_auto_rotation_interval" { + type = string + description = "The rotation interval for the rotation policy." + default = 60 + + validation { + condition = var.iam_credential_secret_auto_rotation_interval > 0 + error_message = "Value for `iam_credential_secret_auto_rotation_intervals` must be greater than 0 when auto-rotation is enabled." + } +} + +variable "iam_credential_secret_auto_rotation_unit" { + type = string + description = "The unit of time for rotation policy. Acceptable values are `day` or `month`." + default = "day" #tfsec:ignore:general-secrets-no-plaintext-exposure + + validation { + condition = contains(["day", "month"], var.iam_credential_secret_auto_rotation_unit) + error_message = "Value for `iam_credential_secret_auto_rotation_unit` must be either 'day' or 'month' when auto-rotation is enabled." + } } -variable "resource_tags" { +variable "iam_credential_secret_labels" { type = list(string) - description = "List of resource tag to associate with the instance." + description = "Optional list of up to 30 labels to be created on the secret. Labels can be used to search for secrets in the Secrets Manager instance." default = [] } diff --git a/version.tf b/version.tf index e51de7f..3ec9b06 100644 --- a/version.tf +++ b/version.tf @@ -1,18 +1,14 @@ terraform { - # require 1.9 or later to make use of cross-object referencing for input variable validations - # more info: https://www.hashicorp.com/blog/terraform-1-9-enhances-input-variable-validations required_version = ">= 1.9.0" - # - # Developer tips: - # - If your module requires any terraform providers, add them the "required_providers" section below. - # - Each required provider's version should be a flexible range to future proof the module's usage with upcoming minor and patch versions. - # - required_providers { ibm = { source = "IBM-Cloud/ibm" - version = ">= 1.71.2, < 2.0.0" + version = ">= 1.79.2, < 2.0.0" + } + time = { + source = "hashicorp/time" + version = ">= 0.9.1, < 1.0.0" } } }