From 71d843dd84f524429b2544762eaf18a2dfde7ba8 Mon Sep 17 00:00:00 2001 From: Aditya-ranjan-16 Date: Wed, 6 Aug 2025 02:34:24 +0530 Subject: [PATCH 1/4] add: kv (key_value) secret type --- README.md | 2 ++ examples/complete/main.tf | 26 +++++++++++++++++++++++ examples/complete/outputs.tf | 22 +++++++++++++++++++ examples/private/main.tf | 41 ++++++++++++++++++++++++++++++------ examples/private/outputs.tf | 22 +++++++++++++++++++ main.tf | 19 +++++++++++++++-- variables.tf | 16 ++++++++++++-- 7 files changed, 137 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index cfd28aa..3871968 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,7 @@ No modules. |------|------| | [ibm_sm_arbitrary_secret.arbitrary_secret](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/sm_arbitrary_secret) | resource | | [ibm_sm_imported_certificate.imported_cert](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/sm_imported_certificate) | resource | +| [ibm_sm_kv_secret.kv_secret](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/sm_kv_secret) | resource | | [ibm_sm_service_credentials_secret.service_credentials_secret](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/sm_service_credentials_secret) | resource | | [ibm_sm_username_password_secret.username_password_secret](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/sm_username_password_secret) | resource | @@ -187,6 +188,7 @@ No modules. | [secret\_auto\_rotation\_unit](#input\_secret\_auto\_rotation\_unit) | Specifies the unit of time for rotation of a username\_password secret. Acceptable values are `day` or `month`. | `string` | `"day"` | no | | [secret\_description](#input\_secret\_description) | Description of the secret to create. | `string` | n/a | yes | | [secret\_group\_id](#input\_secret\_group\_id) | The ID of the secret group for the secret. If `null`, the `default` secret group is used. | `string` | `"default"` | no | +| [secret\_kv\_data](#input\_secret\_kv\_data) | key-value secret data | `map(any)` | `{}` | no | | [secret\_labels](#input\_secret\_labels) | Labels of the secret to create. Up to 30 labels can be created. Labels can be 2 - 30 characters, including spaces. Special characters that are not permitted include the angled brackets (<>), comma (,), colon (:), ampersand (&), and vertical pipe character (\|). | `list(string)` | `[]` | no | | [secret\_name](#input\_secret\_name) | Name of the secret to create. | `string` | n/a | yes | | [secret\_payload\_password](#input\_secret\_payload\_password) | The payload (for arbitrary secrets) or password (for username and password credentials) of the secret. | `string` | `""` | no | diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 6235008..26fb1e1 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -4,6 +4,7 @@ locals { payload = sensitive("secret-payload-example") + kv_data = sensitive({ "key1" : "value1" }) sm_guid = var.existing_sm_instance_guid == null ? module.secrets_manager[0].secrets_manager_guid : var.existing_sm_instance_guid sm_region = var.existing_sm_instance_region == null ? var.region : var.existing_sm_instance_region @@ -234,3 +235,28 @@ module "secret_manager_service_credential" { service_credentials_parameters = { "service-endpoints" : "public" } custom_metadata = { "metadata_custom_key" : "metadata_custom_value" } # can add any custom metadata here } + +############################################################################## +# Example working with key-value secret +############################################################################## + +# create key-value secret +module "secrets_manager_key_value_secret" { + source = "../.." + region = local.sm_region + secrets_manager_guid = local.sm_guid + secret_group_id = module.secrets_manager_group.secret_group_id + secret_name = "${var.prefix}-key-value-secret" + secret_description = "created by secrets-manager-secret-module complete example" + secret_type = "key_value" + secret_kv_data = local.kv_data + secret_labels = local.secret_labels + custom_metadata = { "metadata_custom_key" : "metadata_custom_value" } # can add any custom metadata here +} + +# retrieving information about the key-value secret +data "ibm_sm_kv_secret" "kv_secret" { + instance_id = local.sm_guid + region = local.sm_region + secret_id = module.secrets_manager_key_value_secret.secret_id +} diff --git a/examples/complete/outputs.tf b/examples/complete/outputs.tf index 8b04181..b669c54 100644 --- a/examples/complete/outputs.tf +++ b/examples/complete/outputs.tf @@ -83,3 +83,25 @@ output "service_credential_secret_crn" { description = "CRN of the created service_credential secret" value = module.secret_manager_service_credential.secret_crn } + +output "kv_secret_id" { + description = "ID of the created kv_secret_id secret" + value = module.secrets_manager_key_value_secret.secret_id +} + +output "kv_secret_crn" { + description = "CRN of the created kv_secret_id secret" + value = module.secrets_manager_key_value_secret.secret_crn +} + +output "kv_secret_nonsensitive_payload" { + value = nonsensitive(data.ibm_sm_kv_secret.kv_secret.data) + description = "accessing key value secret" + sensitive = false +} + +output "kv_secret_payload" { + value = data.ibm_sm_kv_secret.kv_secret.data + sensitive = true + description = "accessing key value secret" +} diff --git a/examples/private/main.tf b/examples/private/main.tf index 60f8859..0c5b81d 100644 --- a/examples/private/main.tf +++ b/examples/private/main.tf @@ -3,7 +3,9 @@ ############################################################################## locals { - payload = sensitive("secret-payload-example") + payload = sensitive("secret-payload-example") + kv_data = sensitive({ "key1" : "value1" }) + secret_labels = [var.prefix, var.region] sm_region = var.existing_sm_instance_region == null ? var.region : var.existing_sm_instance_region } @@ -48,7 +50,7 @@ module "secrets_manager_group" { region = local.sm_region secrets_manager_guid = module.secrets_manager.secrets_manager_guid secret_group_name = "${var.prefix}-group" - secret_group_description = "created by secrets-manager-secret-module complete example" + secret_group_description = "created by secrets-manager-secret-module private example" endpoint_type = "private" } @@ -63,7 +65,7 @@ module "secrets_manager_arbitrary_secret" { secrets_manager_guid = module.secrets_manager.secrets_manager_guid secret_group_id = module.secrets_manager_group.secret_group_id secret_name = "${var.prefix}-arbitrary-secret" - secret_description = "created by secrets-manager-secret-module complete example" + secret_description = "created by secrets-manager-secret-module private example" secret_type = "arbitrary" #checkov:skip=CKV_SECRET_6 secret_payload_password = local.payload secret_labels = local.secret_labels @@ -89,7 +91,7 @@ module "secrets_manager_user_pass_secret" { secrets_manager_guid = module.secrets_manager.secrets_manager_guid secret_group_id = module.secrets_manager_group.secret_group_id secret_name = "${var.prefix}-user-pass-secret" - secret_description = "created by secrets-manager-secret-module complete example" + secret_description = "created by secrets-manager-secret-module private example" secret_type = "username_password" #checkov:skip=CKV_SECRET_6 secret_payload_password = local.payload secret_username = "terraform-user" #checkov:skip=CKV_SECRET_6 @@ -116,7 +118,7 @@ module "secrets_manager_user_pass_no_rotate_secret" { secrets_manager_guid = module.secrets_manager.secrets_manager_guid secret_group_id = module.secrets_manager_group.secret_group_id secret_name = "${var.prefix}-user-pass-no-rotate-secret" - secret_description = "created by secrets-manager-secret-module complete example" + secret_description = "created by secrets-manager-secret-module private example" secret_type = "username_password" #checkov:skip=CKV_SECRET_6 secret_payload_password = local.payload secret_username = "terraform-user" #checkov:skip=CKV_SECRET_6 @@ -183,7 +185,7 @@ module "secret_manager_imported_cert" { secrets_manager_guid = module.secrets_manager.secrets_manager_guid secret_name = "${var.prefix}-imported-cert" secret_group_id = module.secrets_manager_group.secret_group_id - secret_description = "created by secrets-manager-secret-module complete example" + secret_description = "created by secrets-manager-secret-module private example" secret_type = "imported_cert" #checkov:skip=CKV_SECRET_6 imported_cert_certificate = resource.tls_locally_signed_cert.cert.cert_pem imported_cert_private_key = resource.tls_private_key.key.private_key_pem @@ -231,9 +233,34 @@ module "secret_manager_service_credential" { secrets_manager_guid = module.secrets_manager.secrets_manager_guid secret_name = "${var.prefix}-service-credentials" secret_group_id = module.secrets_manager_group.secret_group_id - secret_description = "created by secrets-manager-secret-module complete example" + secret_description = "created by secrets-manager-secret-module private example" secret_type = "service_credentials" #checkov:skip=CKV_SECRET_6 service_credentials_source_service_crn = module.cloud_object_storage.cos_instance_id service_credentials_source_service_role_crn = "crn:v1:bluemix:public:iam::::serviceRole:Writer" endpoint_type = "private" } +############################################################################## +# Example working with key-value secret +############################################################################## + +# create key-value secret +module "secrets_manager_key_value_secret" { + source = "../.." + region = local.sm_region + secrets_manager_guid = module.secrets_manager.secrets_manager_guid + secret_group_id = module.secrets_manager_group.secret_group_id + secret_name = "${var.prefix}-key-value-secret" + secret_description = "created by secrets-manager-secret-module private example" + secret_type = "key_value" + secret_kv_data = local.kv_data + secret_labels = local.secret_labels + endpoint_type = "private" +} + +# retrieving information about the key-value secret +data "ibm_sm_kv_secret" "kv_secret" { + instance_id = module.secrets_manager.secrets_manager_guid + region = local.sm_region + secret_id = module.secrets_manager_key_value_secret.secret_id + endpoint_type = "private" +} diff --git a/examples/private/outputs.tf b/examples/private/outputs.tf index 8b04181..b669c54 100644 --- a/examples/private/outputs.tf +++ b/examples/private/outputs.tf @@ -83,3 +83,25 @@ output "service_credential_secret_crn" { description = "CRN of the created service_credential secret" value = module.secret_manager_service_credential.secret_crn } + +output "kv_secret_id" { + description = "ID of the created kv_secret_id secret" + value = module.secrets_manager_key_value_secret.secret_id +} + +output "kv_secret_crn" { + description = "CRN of the created kv_secret_id secret" + value = module.secrets_manager_key_value_secret.secret_crn +} + +output "kv_secret_nonsensitive_payload" { + value = nonsensitive(data.ibm_sm_kv_secret.kv_secret.data) + description = "accessing key value secret" + sensitive = false +} + +output "kv_secret_payload" { + value = data.ibm_sm_kv_secret.kv_secret.data + sensitive = true + description = "accessing key value secret" +} diff --git a/main.tf b/main.tf index fa761c2..d1a3dcc 100644 --- a/main.tf +++ b/main.tf @@ -116,19 +116,34 @@ resource "ibm_sm_service_credentials_secret" "service_credentials_secret" { } } +resource "ibm_sm_kv_secret" "kv_secret" { + count = var.secret_type == "key_value" ? 1 : 0 + region = var.region + instance_id = var.secrets_manager_guid + secret_group_id = var.secret_group_id + name = var.secret_name + description = var.secret_description + labels = var.secret_labels + data = var.secret_kv_data + endpoint_type = var.endpoint_type + custom_metadata = var.custom_metadata +} + # Parse secret ID and generate data header for secrets locals { secret_id = ( var.secret_type == "username_password" ? ibm_sm_username_password_secret.username_password_secret[0].secret_id : var.secret_type == "imported_cert" ? ibm_sm_imported_certificate.imported_cert[0].secret_id : var.secret_type == "service_credentials" ? ibm_sm_service_credentials_secret.service_credentials_secret[0].secret_id : - var.secret_type == "arbitrary" ? ibm_sm_arbitrary_secret.arbitrary_secret[0].secret_id : null + var.secret_type == "arbitrary" ? ibm_sm_arbitrary_secret.arbitrary_secret[0].secret_id : + var.secret_type == "key_value" ? ibm_sm_kv_secret.kv_secret[0].secret_id : null ) secret_crn = ( var.secret_type == "username_password" ? ibm_sm_username_password_secret.username_password_secret[0].crn : var.secret_type == "imported_cert" ? ibm_sm_imported_certificate.imported_cert[0].crn : var.secret_type == "service_credentials" ? ibm_sm_service_credentials_secret.service_credentials_secret[0].crn : - var.secret_type == "arbitrary" ? ibm_sm_arbitrary_secret.arbitrary_secret[0].crn : null + var.secret_type == "arbitrary" ? ibm_sm_arbitrary_secret.arbitrary_secret[0].crn : + var.secret_type == "key_value" ? ibm_sm_kv_secret.kv_secret[0].crn : null ) #tfsec:ignore:general-secrets-no-plaintext-exposure secret_auto_rotation_frequency = var.secret_auto_rotation == true ? "${var.secret_auto_rotation_interval} ${var.secret_auto_rotation_unit}(s)" : null #tfsec:ignore:general-secrets-no-plaintext-exposure diff --git a/variables.tf b/variables.tf index 6c7e3ad..82b56bd 100644 --- a/variables.tf +++ b/variables.tf @@ -23,8 +23,8 @@ variable "secret_type" { description = "Type of secret to create, must be one of: arbitrary, username_password, imported_cert, service_credentials" validation { - condition = contains(["arbitrary", "username_password", "imported_cert", "service_credentials"], var.secret_type) #checkov:skip=CKV_SECRET_6 - error_message = "Only supported secrets types are arbitrary, username_password, imported_cert, or service_credentials" + condition = contains(["arbitrary", "username_password", "imported_cert", "key_value", "service_credentials"], var.secret_type) #checkov:skip=CKV_SECRET_6 + error_message = "Only supported secrets types are arbitrary, username_password, key_value , imported_cert, or service_credentials" } validation { @@ -32,6 +32,11 @@ variable "secret_type" { error_message = "When creating a username_password or arbitrary secret, a value for `secret_payload_password` is required." } + validation { + condition = var.secret_type == "key_value" ? var.secret_kv_data != "" : true + error_message = "When creating a key_value secret, a value for `secret_kv_data` is required." + } + validation { condition = var.secret_type == "imported_cert" ? var.imported_cert_certificate != null : true error_message = "When creating an imported_cert secret, value for `imported_cert_certificate` cannot be null." @@ -96,6 +101,13 @@ variable "secret_payload_password" { default = "" #tfsec:ignore:general-secrets-no-plaintext-exposure } +variable "secret_kv_data" { + type = map(any) + description = "key-value secret data" + sensitive = true + default = {} +} + variable "secret_auto_rotation" { type = bool description = "Whether to configure automatic rotation. Applies only to the `username_password` and `service_credentials` secret types." From a164217e105bbb0fabfb230640f8b4c9cda8dead Mon Sep 17 00:00:00 2001 From: Aditya-ranjan-16 Date: Wed, 6 Aug 2025 03:34:20 +0530 Subject: [PATCH 2/4] fix --- examples/complete/main.tf | 2 +- examples/private/main.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 26fb1e1..eb2edfd 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -4,7 +4,7 @@ locals { payload = sensitive("secret-payload-example") - kv_data = sensitive({ "key1" : "value1" }) + kv_data = { "key1" : "value1" } sm_guid = var.existing_sm_instance_guid == null ? module.secrets_manager[0].secrets_manager_guid : var.existing_sm_instance_guid sm_region = var.existing_sm_instance_region == null ? var.region : var.existing_sm_instance_region diff --git a/examples/private/main.tf b/examples/private/main.tf index 0c5b81d..c2a7aaa 100644 --- a/examples/private/main.tf +++ b/examples/private/main.tf @@ -4,7 +4,7 @@ locals { payload = sensitive("secret-payload-example") - kv_data = sensitive({ "key1" : "value1" }) + kv_data = { "key1" : "value1" } secret_labels = [var.prefix, var.region] sm_region = var.existing_sm_instance_region == null ? var.region : var.existing_sm_instance_region From a5c2a1a8b3fcaea589976dcdf6c58d4944b11794 Mon Sep 17 00:00:00 2001 From: Aditya-ranjan-16 Date: Wed, 6 Aug 2025 11:27:56 +0530 Subject: [PATCH 3/4] fix --- README.md | 2 +- variables.tf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3871968..f58af59 100644 --- a/README.md +++ b/README.md @@ -188,7 +188,7 @@ No modules. | [secret\_auto\_rotation\_unit](#input\_secret\_auto\_rotation\_unit) | Specifies the unit of time for rotation of a username\_password secret. Acceptable values are `day` or `month`. | `string` | `"day"` | no | | [secret\_description](#input\_secret\_description) | Description of the secret to create. | `string` | n/a | yes | | [secret\_group\_id](#input\_secret\_group\_id) | The ID of the secret group for the secret. If `null`, the `default` secret group is used. | `string` | `"default"` | no | -| [secret\_kv\_data](#input\_secret\_kv\_data) | key-value secret data | `map(any)` | `{}` | no | +| [secret\_kv\_data](#input\_secret\_kv\_data) | key-value secret data | `map(any)` | `null` | no | | [secret\_labels](#input\_secret\_labels) | Labels of the secret to create. Up to 30 labels can be created. Labels can be 2 - 30 characters, including spaces. Special characters that are not permitted include the angled brackets (<>), comma (,), colon (:), ampersand (&), and vertical pipe character (\|). | `list(string)` | `[]` | no | | [secret\_name](#input\_secret\_name) | Name of the secret to create. | `string` | n/a | yes | | [secret\_payload\_password](#input\_secret\_payload\_password) | The payload (for arbitrary secrets) or password (for username and password credentials) of the secret. | `string` | `""` | no | diff --git a/variables.tf b/variables.tf index 82b56bd..e423bff 100644 --- a/variables.tf +++ b/variables.tf @@ -33,7 +33,7 @@ variable "secret_type" { } validation { - condition = var.secret_type == "key_value" ? var.secret_kv_data != "" : true + condition = var.secret_type == "key_value" ? var.secret_kv_data != null : true error_message = "When creating a key_value secret, a value for `secret_kv_data` is required." } @@ -105,7 +105,7 @@ variable "secret_kv_data" { type = map(any) description = "key-value secret data" sensitive = true - default = {} + default = null } variable "secret_auto_rotation" { From c0a340c3a546812e1eecad4b43ec53e141319b64 Mon Sep 17 00:00:00 2001 From: Aditya-ranjan-16 Date: Mon, 18 Aug 2025 13:18:13 +0530 Subject: [PATCH 4/4] added docs --- README.md | 25 ++++++++++++++++++++++++- examples/complete/README.md | 1 + examples/complete/main.tf | 2 +- examples/private/main.tf | 2 +- variables.tf | 2 +- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f58af59..94f19c3 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ The module supports the following secret types: - [User credentials](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-user-credentials&interface=ui) - [Imported Certificate](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-certificates&interface=api#import-certificates) - [Service Credentials](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-service-credentials&interface=api) +- [Key Value](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-key-value&interface=ui) The following attributes and parameters are supported for all secret types: @@ -49,6 +50,11 @@ The following attributes and parameters are supported when creating service cred - `secret_auto_rotation_unit`: Specifies the unit type for the secret rotation. Accepted values are `day` or `month`. Default is `day`. - `secret_auto_rotation_interval`: Specifies the rotation interval for the rotation unit. Default is `89`. +The following attributes and parameters are supported when creating Key Value secrets: + +- `secret_kv_data`: key-value secret data. + + ## Overview @@ -140,6 +146,23 @@ module "secret_manager_service_credential" { } ``` +```hcl +############################################################################## +# Create Key Value Secret +############################################################################## + +module "secrets_manager_key_value_secret" { + source = "terraform-ibm-modules/secrets-manager-secret/ibm" + version = "latest" # Replace "latest" with a release version to lock into a specific release + region = "us-south" + secrets_manager_guid = "42454b3b-5b06-407b-a4b3-34d9ef323901" + secret_group_id = "432b91f1-ff6d-4b47-9f06-82debc236d90" + secret_name = "example-kv-secret" + secret_description = "Extended description for the key-value secret." + secret_type = "key_value" + secret_kv_data = {"key1":"value"} #pragma: allowlist secret +} +``` ### Required IAM access policies You need the following permissions to run this module. @@ -188,7 +211,7 @@ No modules. | [secret\_auto\_rotation\_unit](#input\_secret\_auto\_rotation\_unit) | Specifies the unit of time for rotation of a username\_password secret. Acceptable values are `day` or `month`. | `string` | `"day"` | no | | [secret\_description](#input\_secret\_description) | Description of the secret to create. | `string` | n/a | yes | | [secret\_group\_id](#input\_secret\_group\_id) | The ID of the secret group for the secret. If `null`, the `default` secret group is used. | `string` | `"default"` | no | -| [secret\_kv\_data](#input\_secret\_kv\_data) | key-value secret data | `map(any)` | `null` | no | +| [secret\_kv\_data](#input\_secret\_kv\_data) | key-value secret data | `map(string)` | `null` | no | | [secret\_labels](#input\_secret\_labels) | Labels of the secret to create. Up to 30 labels can be created. Labels can be 2 - 30 characters, including spaces. Special characters that are not permitted include the angled brackets (<>), comma (,), colon (:), ampersand (&), and vertical pipe character (\|). | `list(string)` | `[]` | no | | [secret\_name](#input\_secret\_name) | Name of the secret to create. | `string` | n/a | yes | | [secret\_payload\_password](#input\_secret\_payload\_password) | The payload (for arbitrary secrets) or password (for username and password credentials) of the secret. | `string` | `""` | no | diff --git a/examples/complete/README.md b/examples/complete/README.md index 0d020ed..9cf8c8c 100644 --- a/examples/complete/README.md +++ b/examples/complete/README.md @@ -3,6 +3,7 @@ - Creates new secrets-manager instance (if existing instance GUID not passed in) - Creates new secret group - Creates an arbitrary type secret in the secret group +- Creates an key-value type secret in the secret group - Creates a username_password type secret in the secret group - Creates a TLS cert, and adds it to secrets manager as an imported_cert secret type in the secret group - Retrieves metadata for all the secrets created diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 13d6eca..a480729 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -4,7 +4,7 @@ locals { payload = sensitive("secret-payload-example") - kv_data = { "key1" : "value1" } + kv_data = { "key1" : "value", "key2" : true, "key3" : 4 } sm_guid = var.existing_sm_instance_guid == null ? module.secrets_manager[0].secrets_manager_guid : var.existing_sm_instance_guid sm_region = var.existing_sm_instance_region == null ? var.region : var.existing_sm_instance_region diff --git a/examples/private/main.tf b/examples/private/main.tf index 993236b..c52f2f9 100644 --- a/examples/private/main.tf +++ b/examples/private/main.tf @@ -4,7 +4,7 @@ locals { payload = sensitive("secret-payload-example") - kv_data = { "key1" : "value1" } + kv_data = { "key1" : "value", "key2" : true, "key3" : 4 } secret_labels = [var.prefix, var.region] sm_region = var.existing_sm_instance_region == null ? var.region : var.existing_sm_instance_region diff --git a/variables.tf b/variables.tf index e423bff..3592d82 100644 --- a/variables.tf +++ b/variables.tf @@ -102,7 +102,7 @@ variable "secret_payload_password" { } variable "secret_kv_data" { - type = map(any) + type = map(string) description = "key-value secret data" sensitive = true default = null