Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

Expand All @@ -187,6 +188,7 @@ No modules.
| <a name="input_secret_auto_rotation_unit"></a> [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 |
| <a name="input_secret_description"></a> [secret\_description](#input\_secret\_description) | Description of the secret to create. | `string` | n/a | yes |
| <a name="input_secret_group_id"></a> [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 |
| <a name="input_secret_kv_data"></a> [secret\_kv\_data](#input\_secret\_kv\_data) | key-value secret data | `map(any)` | `null` | no |
| <a name="input_secret_labels"></a> [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 |
| <a name="input_secret_name"></a> [secret\_name](#input\_secret\_name) | Name of the secret to create. | `string` | n/a | yes |
| <a name="input_secret_payload_password"></a> [secret\_payload\_password](#input\_secret\_payload\_password) | The payload (for arbitrary secrets) or password (for username and password credentials) of the secret. | `string` | `""` | no |
Expand Down
26 changes: 26 additions & 0 deletions examples/complete/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

locals {
payload = sensitive("secret-payload-example")
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
Expand Down Expand Up @@ -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
}
22 changes: 22 additions & 0 deletions examples/complete/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
41 changes: 34 additions & 7 deletions examples/private/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
##############################################################################

locals {
payload = sensitive("secret-payload-example")
payload = sensitive("secret-payload-example")
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
}
Expand Down Expand Up @@ -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"
}

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"
}
22 changes: 22 additions & 0 deletions examples/private/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
19 changes: 17 additions & 2 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 14 additions & 2 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,20 @@ 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 {
condition = (var.secret_type == "username_password" || var.secret_type == "arbitrary") ? var.secret_payload_password != "" : true
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 != null : 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."
Expand Down Expand Up @@ -96,6 +101,13 @@ variable "secret_payload_password" {
default = "" #tfsec:ignore:general-secrets-no-plaintext-exposure
}

variable "secret_kv_data" {
type = map(any)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a concern.

It clearly works here, but I wonder how it becomes accessible at higher levels. Specifically, if exposing from the DA, how is it documented?

Usually at the DA level, it is a list of objects, so it would become a list of maps of any.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Aditya-ranjan-16 Why are you using map(any) instead of map(string) ? Wont it always be a key value of strings? Or am I missing something?

@shemau FYI we don't support creating secrets through the Secrets Manager DA (on purpose) - only secret groups. It is however supported in the secrets submodule, and root level secrets-manager module so will need to be exposed in both places too after this is merged

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ocofaigh the cloud docs example was using map(any) , but I will change it map(string) makes more sense

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about the any/string as well. I figured it applied to the value part, since the key will always be a string. At this point you could have secrets that would be multi-part, eg ssh key with a private key and public key. I could not see a use case in the documentation, would it be

{
"private_key": "value",
"public_key": "value"
}

or

{
"key": { "private": "value", "public": "value"}
}

The issue specifically said to store JSON. jsondecode (https://developer.hashicorp.com/terraform/language/functions/jsondecode) returns a type of any (as far as I can tell); although it would also be possible to store as the string form.

I approved, because it seemed appropriate to follow the provider documentation and not become opinionated on the content.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Planning failed. Terraform encountered an error while generating this plan.

╷
│ Error: Incorrect attribute value type
│ 
│   on ../../main.tf line 127, in resource "ibm_sm_kv_secret" "kv_secret":
│  127:   data            = var.secret_kv_data
│ 
│ Inappropriate value for attribute "data": incorrect map element type: string required.

The function errors when values other than string are specified. So should be map(string).

The following are string (or equivalent) and work:

kv_data = { "key1" : "value", "key2" : "value2" }
kv_data = { "key1" : false, "key2" : true }
kv_data = { "key1" : 1, "key2" : 2 }
kv_data = { "key1" : "value", "key2" : true, "key3" : 4 }

It would be good to have an example, maybe complete, that uses the final format.

The following are fail with the error above:

kv_data = { "key1" : { "sub_key1" : "value1" }, "key2" : { "sub_key2" : "value2" } }
kv_data = { "key1" : [ "tag1" ], "key2" : [ "tag2" ] }

description = "key-value secret data"
sensitive = true
default = null
}

variable "secret_auto_rotation" {
type = bool
description = "Whether to configure automatic rotation. Applies only to the `username_password` and `service_credentials` secret types."
Expand Down